From f94644999766e752f7d60ce52c14a7db79005035 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Tue, 18 Nov 2025 12:20:23 +0000 Subject: [PATCH 01/44] 8366178: Implement JEP 526: Lazy Constants (Second Preview) 8371882: Improve documentation for JEP 526: Lazy Constants Reviewed-by: jvernee, mcimadamore --- .../share/classes/java/lang/LazyConstant.java | 307 +++++++ .../share/classes/java/lang/StableValue.java | 756 ------------------ .../classes/java/nio/charset/Charset.java | 8 +- .../share/classes/java/util/Currency.java | 4 +- .../java/util/ImmutableCollections.java | 384 +-------- .../classes/java/util/LazyCollections.java | 584 ++++++++++++++ .../share/classes/java/util/List.java | 80 +- .../share/classes/java/util/Locale.java | 8 +- .../classes/java/util/LocaleISOData.java | 16 +- .../share/classes/java/util/Map.java | 85 +- .../share/classes/java/util/Optional.java | 6 +- .../classes/java/util/ResourceBundle.java | 2 +- .../access/JavaUtilCollectionAccess.java | 6 - .../internal/foreign/CaptureStateUtil.java | 33 +- .../jdk/internal/io/JdkConsoleImpl.java | 38 +- .../jdk/internal/javac/PreviewFeature.java | 4 +- .../jdk/internal/lang/LazyConstantImpl.java | 173 ++++ .../lang/stable/StableEnumFunction.java | 118 --- .../internal/lang/stable/StableFunction.java | 83 -- .../lang/stable/StableIntFunction.java | 80 -- .../internal/lang/stable/StableSupplier.java | 69 -- .../jdk/internal/lang/stable/StableUtil.java | 105 --- .../internal/lang/stable/StableValueImpl.java | 218 ----- .../share/classes/sun/nio/ch/Net.java | 7 +- .../classes/sun/util/locale/BaseLocale.java | 4 +- .../BreakIteratorResourceBundle.java | 2 +- .../resources/OpenListResourceBundle.java | 4 +- .../DemoContainerInjectionTest.java | 153 ++++ .../lang/LazyConstant/DemoImperativeTest.java | 83 ++ .../java/lang/LazyConstant/DemoMapTest.java | 119 +++ .../LazyConstantSafePublicationTest.java} | 71 +- .../lang/LazyConstant/LazyConstantTest.java | 236 ++++++ .../LazyConstantTestUtil.java} | 58 +- .../LazyListTest.java} | 250 ++---- .../java/lang/LazyConstant/LazyMapTest.java | 573 +++++++++++++ .../TrustedFieldTypeTest.java | 63 +- .../lang/StableValue/StableFunctionTest.java | 250 ------ .../StableValue/StableIntFunctionTest.java | 109 --- .../java/lang/StableValue/StableMapTest.java | 388 --------- .../lang/StableValue/StableSupplierTest.java | 104 --- .../StableValue/StableValueFactoriesTest.java | 43 - .../lang/StableValue/StableValueTest.java | 389 --------- test/jdk/java/util/Collection/MOAT.java | 28 +- .../jdk/jshell/CompletionSuggestionTest.java | 2 +- ...enchmark.java => StableListBenchmark.java} | 12 +- ...rk.java => StableListSingleBenchmark.java} | 12 +- ...Benchmark.java => StableMapBenchmark.java} | 14 +- ...ark.java => StableMapSingleBenchmark.java} | 33 +- .../stable/StableMethodHandleBenchmark.java | 16 +- .../lang/stable/StableSupplierBenchmark.java | 35 +- .../lang/stable/StableValueBenchmark.java | 84 +- .../lang/stable/VarHandleHolderBenchmark.java | 11 +- 52 files changed, 2784 insertions(+), 3536 deletions(-) create mode 100644 src/java.base/share/classes/java/lang/LazyConstant.java delete mode 100644 src/java.base/share/classes/java/lang/StableValue.java create mode 100644 src/java.base/share/classes/java/util/LazyCollections.java create mode 100644 src/java.base/share/classes/jdk/internal/lang/LazyConstantImpl.java delete mode 100644 src/java.base/share/classes/jdk/internal/lang/stable/StableEnumFunction.java delete mode 100644 src/java.base/share/classes/jdk/internal/lang/stable/StableFunction.java delete mode 100644 src/java.base/share/classes/jdk/internal/lang/stable/StableIntFunction.java delete mode 100644 src/java.base/share/classes/jdk/internal/lang/stable/StableSupplier.java delete mode 100644 src/java.base/share/classes/jdk/internal/lang/stable/StableUtil.java delete mode 100644 src/java.base/share/classes/jdk/internal/lang/stable/StableValueImpl.java create mode 100644 test/jdk/java/lang/LazyConstant/DemoContainerInjectionTest.java create mode 100644 test/jdk/java/lang/LazyConstant/DemoImperativeTest.java create mode 100644 test/jdk/java/lang/LazyConstant/DemoMapTest.java rename test/jdk/java/lang/{StableValue/StableValuesSafePublicationTest.java => LazyConstant/LazyConstantSafePublicationTest.java} (73%) create mode 100644 test/jdk/java/lang/LazyConstant/LazyConstantTest.java rename test/jdk/java/lang/{StableValue/StableTestUtil.java => LazyConstant/LazyConstantTestUtil.java} (64%) rename test/jdk/java/lang/{StableValue/StableListTest.java => LazyConstant/LazyListTest.java} (60%) create mode 100644 test/jdk/java/lang/LazyConstant/LazyMapTest.java rename test/jdk/java/lang/{StableValue => LazyConstant}/TrustedFieldTypeTest.java (61%) delete mode 100644 test/jdk/java/lang/StableValue/StableFunctionTest.java delete mode 100644 test/jdk/java/lang/StableValue/StableIntFunctionTest.java delete mode 100644 test/jdk/java/lang/StableValue/StableMapTest.java delete mode 100644 test/jdk/java/lang/StableValue/StableSupplierTest.java delete mode 100644 test/jdk/java/lang/StableValue/StableValueFactoriesTest.java delete mode 100644 test/jdk/java/lang/StableValue/StableValueTest.java rename test/micro/org/openjdk/bench/java/lang/stable/{StableIntFunctionBenchmark.java => StableListBenchmark.java} (86%) rename test/micro/org/openjdk/bench/java/lang/stable/{StableIntFunctionSingleBenchmark.java => StableListSingleBenchmark.java} (84%) rename test/micro/org/openjdk/bench/java/lang/stable/{StableFunctionBenchmark.java => StableMapBenchmark.java} (85%) rename test/micro/org/openjdk/bench/java/lang/stable/{StableFunctionSingleBenchmark.java => StableMapSingleBenchmark.java} (70%) diff --git a/src/java.base/share/classes/java/lang/LazyConstant.java b/src/java.base/share/classes/java/lang/LazyConstant.java new file mode 100644 index 0000000000000..34f3d754a10f3 --- /dev/null +++ b/src/java.base/share/classes/java/lang/LazyConstant.java @@ -0,0 +1,307 @@ +/* + * 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 java.lang; + +import jdk.internal.javac.PreviewFeature; +import jdk.internal.lang.LazyConstantImpl; + +import java.io.Serializable; +import java.util.*; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +/** + * A lazy constant is a holder of contents that can be set at most once. + *

+ * A lazy constant is created using the factory method + * {@linkplain LazyConstant#of(Supplier) LazyConstant.of({@code })}. + * When created, the lazy constant is not initialized, meaning it has no contents. + * The lazy constant (of type {@code T}) can then be initialized + * (and its contents retrieved) by calling {@linkplain #get() get()}. The first time + * {@linkplain #get() get()} is called, the underlying computing function + * (provided at construction) will be invoked and the result will be used to initialize + * the constant. Once a lazy constant is initialized, its contents can never change + * and will be retrieved over and over again upon subsequent {@linkplain #get() get()} + * invocations. + *

+ * Consider the following example where a lazy constant field "{@code logger}" holds + * an object of type {@code Logger}: + * + * {@snippet lang = java: + * public class Component { + * + * // Creates a new uninitialized lazy constant + * private final LazyConstant logger = + * // @link substring="of" target="#of" : + * LazyConstant.of( () -> Logger.create(Component.class) ); + * + * public void process() { + * logger.get().info("Process started"); + * // ... + * } + * } + *} + *

+ * Initially, the lazy constant is not initialized. When {@code logger.get()} + * is first invoked, it evaluates the computing function and initializes the constant to + * the result; the result is then returned to the client. Hence, {@linkplain #get() get()} + * guarantees that the constant is initialized before it returns, barring + * any exceptions. + *

+ * Furthermore, {@linkplain #get() get()} guarantees that, out of several threads trying to + * invoke the computing function simultaneously, {@linkplain ##thread-safety only one is + * ever selected} for computation. This property is crucial as evaluation of the computing + * function may have side effects, for example, the call above to {@code Logger.create()} + * may result in storage resources being prepared. + * + *

Exception handling

+ * If the computing function returns {@code null}, a {@linkplain NullPointerException} + * is thrown. Hence, a lazy constant can never hold a {@code null} value. Clients who + * want to use a nullable constant can wrap the value into an {@linkplain Optional} holder. + *

+ * If the computing function recursively invokes itself (directly or indirectly via + * the lazy constant), an {@linkplain IllegalStateException} is thrown, and the lazy + * constant is not initialized. + * + *

Composing lazy constants

+ * A lazy constant can depend on other lazy constants, forming a dependency graph + * that can be lazily computed but where access to individual elements can still be + * performant. In the following example, a single {@code Foo} and a {@code Bar} + * instance (that is dependent on the {@code Foo} instance) are lazily created, both of + * which are held by lazy constants: + * + * {@snippet lang = java: + * public final class DependencyUtil { + * + * private DependencyUtil() {} + * + * public static class Foo { + * // ... + * } + * + * public static class Bar { + * public Bar(Foo foo) { + * // ... + * } + * } + * + * private static final LazyConstant FOO = LazyConstant.of( Foo::new ); + * private static final LazyConstant BAR = LazyConstant.of( () -> new Bar(FOO.get()) ); + * + * public static Foo foo() { + * return FOO.get(); + * } + * + * public static Bar bar() { + * return BAR.get(); + * } + * + * } + *} + * Calling {@code BAR.get()} will create the {@code Bar} singleton if it is not already + * created. Upon such a creation, a dependent {@code Foo} will first be created if + * the {@code Foo} does not already exist. + * + *

Thread Safety

+ * A lazy constant is guaranteed to be initialized atomically and at most once. If + * competing threads are racing to initialize a lazy constant, only one updating thread + * runs the computing function (which runs on the caller's thread and is hereafter denoted + * the computing thread), while the other threads are blocked until the constant + * is initialized, after which the other threads observe the lazy constant is initialized + * and leave the constant unchanged and will never invoke any computation. + *

+ * The invocation of the computing function and the resulting initialization of + * the constant {@linkplain java.util.concurrent##MemoryVisibility happens-before} + * the initialized constant's content is read. Hence, the initialized constant's content, + * including any {@code final} fields of any newly created objects, is safely published. + *

+ * Thread interruption does not cancel the initialization of a lazy constant. In other + * words, if the computing thread is interrupted, {@code LazyConstant::get} doesn't clear + * the interrupted thread’s status, nor does it throw an {@linkplain InterruptedException}. + *

+ * If the computing function blocks indefinitely, other threads operating on this + * lazy constant may block indefinitely; no timeouts or cancellations are provided. + * + *

Performance

+ * The contents of a lazy constant can never change after the lazy constant has been + * initialized. Therefore, a JVM implementation may, for an initialized lazy constant, + * elide all future reads of that lazy constant's contents and instead use the contents + * that has been previously observed. We call this optimization constant folding. + * This is only possible if there is a direct reference from a {@code static final} field + * to a lazy constant or if there is a chain from a {@code static final} field -- via one + * or more trusted fields (i.e., {@code static final} fields, + * {@linkplain Record record} fields, or final instance fields in hidden classes) -- + * to a lazy constant. + * + *

Miscellaneous

+ * Except for {@linkplain Object#equals(Object) equals(obj)} and + * {@linkplain #orElse(Object) orElse(other)} parameters, all method parameters + * must be non-null, or a {@link NullPointerException} will be thrown. + * + * @apiNote Once a lazy constant is initialized, its contents cannot ever be removed. + * This can be a source of an unintended memory leak. More specifically, + * a lazy constant {@linkplain java.lang.ref##reachability strongly references} + * it contents. Hence, the contents of a lazy constant will be reachable as long + * as the lazy constant itself is reachable. + *

+ * While it's possible to store an array inside a lazy constant, doing so will + * not result in improved access performance of the array elements. Instead, a + * {@linkplain List#ofLazy(int, IntFunction) lazy list} of arbitrary depth can + * be used, which provides constant components. + *

+ * The {@code LazyConstant} type is not {@link Serializable}. + *

+ * Use in static initializers may interact with class initialization order; + * cyclic initialization may result in initialization errors as described + * in section {@jls 12.4} of The Java Language Specification. + * + * @implNote + * A lazy constant is free to synchronize on itself. Hence, care must be + * taken when directly or indirectly synchronizing on a lazy constant. + * A lazy constant is unmodifiable but its contents may or may not be + * immutable (e.g., it may hold an {@linkplain ArrayList}). + * + * @param type of the constant + * + * @since 26 + * + * @see Optional + * @see Supplier + * @see List#ofLazy(int, IntFunction) + * @see Map#ofLazy(Set, Function) + * @jls 12.4 Initialization of Classes and Interfaces + * @jls 17.4.5 Happens-before Order + */ +@PreviewFeature(feature = PreviewFeature.Feature.LAZY_CONSTANTS) +public sealed interface LazyConstant + extends Supplier + permits LazyConstantImpl { + + /** + * {@return the contents of this lazy constant if initialized, otherwise, + * returns {@code other}} + *

+ * This method never triggers initialization of this lazy constant and will observe + * initialization by other threads atomically (i.e., it returns the contents + * if and only if the initialization has already completed). + * + * @param other value to return if the content is not initialized + * (can be {@code null}) + */ + T orElse(T other); + + /** + * {@return the contents of this initialized constant. If not initialized, first + * computes and initializes this constant using the computing function} + *

+ * After this method returns successfully, the constant is guaranteed to be + * initialized. + *

+ * If the computing function throws, the throwable is relayed to the caller and + * the lazy constant remains uninitialized; a subsequent call to get() may then + * attempt the computation again. + */ + T get(); + + /** + * {@return {@code true} if the constant is initialized, {@code false} otherwise} + *

+ * This method never triggers initialization of this lazy constant and will observe + * changes in the initialization state made by other threads atomically. + */ + boolean isInitialized(); + + // Object methods + + /** + * {@return if this lazy constant is the same as the provided {@code obj}} + *

+ * In other words, equals compares the identity of this lazy constant and {@code obj} + * to determine equality. Hence, two lazy constants with the same contents are + * not equal. + *

+ * This method never triggers initialization of this lazy constant. + */ + @Override + boolean equals(Object obj); + + /** + * {@return the {@linkplain System#identityHashCode(Object) identity hash code} for + * this lazy constant} + * + * This method never triggers initialization of this lazy constant. + */ + @Override + int hashCode(); + + /** + * {@return a string suitable for debugging} + *

+ * This method never triggers initialization of this lazy constant and will observe + * initialization by other threads atomically (i.e., it observes the + * contents if and only if the initialization has already completed). + *

+ * If this lazy constant is initialized, an implementation-dependent string + * containing the {@linkplain Object#toString()} of the + * contents will be returned; otherwise, an implementation-dependent string is + * returned that indicates this lazy constant is not yet initialized. + */ + @Override + String toString(); + + // Factory + + /** + * {@return a lazy constant whose contents is to be computed later via the provided + * {@code computingFunction}} + *

+ * The returned lazy constant strongly references the provided + * {@code computingFunction} at least until initialization completes successfully. + *

+ * If the provided computing function is already an instance of + * {@code LazyConstant}, the method is free to return the provided computing function + * directly. + * + * @implNote after initialization completes successfully, the computing function is + * no longer strongly referenced and becomes eligible for + * garbage collection. + * + * @param computingFunction in the form of a {@linkplain Supplier} to be used + * to initialize the constant + * @param type of the constant + * + */ + @SuppressWarnings("unchecked") + static LazyConstant of(Supplier computingFunction) { + Objects.requireNonNull(computingFunction); + if (computingFunction instanceof LazyConstant lc) { + return (LazyConstant) lc; + } + return LazyConstantImpl.ofLazy(computingFunction); + } + +} diff --git a/src/java.base/share/classes/java/lang/StableValue.java b/src/java.base/share/classes/java/lang/StableValue.java deleted file mode 100644 index 1815cb1a5b198..0000000000000 --- a/src/java.base/share/classes/java/lang/StableValue.java +++ /dev/null @@ -1,756 +0,0 @@ -/* - * 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 java.lang; - -import jdk.internal.access.SharedSecrets; -import jdk.internal.javac.PreviewFeature; -import jdk.internal.lang.stable.StableEnumFunction; -import jdk.internal.lang.stable.StableFunction; -import jdk.internal.lang.stable.StableIntFunction; -import jdk.internal.lang.stable.StableSupplier; -import jdk.internal.lang.stable.StableUtil; -import jdk.internal.lang.stable.StableValueImpl; - -import java.io.Serializable; -import java.util.Collection; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.RandomAccess; -import java.util.Set; -import java.util.function.Function; -import java.util.function.IntFunction; -import java.util.function.Supplier; - -/** - * A stable value is a holder of contents that can be set at most once. - *

- * A {@code StableValue} is typically created using the factory method - * {@linkplain StableValue#of() {@code StableValue.of()}}. When created this way, - * the stable value is unset, which means it holds no contents. - * Its contents, of type {@code T}, can be set by calling - * {@linkplain #trySet(Object) trySet()}, {@linkplain #setOrThrow(Object) setOrThrow()}, - * or {@linkplain #orElseSet(Supplier) orElseSet()}. Once set, the contents - * can never change and can be retrieved by calling {@linkplain #orElseThrow() orElseThrow()} - * , {@linkplain #orElse(Object) orElse()}, or {@linkplain #orElseSet(Supplier) orElseSet()}. - *

- * Consider the following example where a stable value field "{@code logger}" is a - * shallowly immutable holder of contents of type {@code Logger} and that is initially - * created as unset, which means it holds no contents. Later in the example, the - * state of the "{@code logger}" field is checked and if it is still unset, - * the contents is set: - * - * {@snippet lang = java: - * public class Component { - * - * // Creates a new unset stable value with no contents - * // @link substring="of" target="#of" : - * private final StableValue logger = StableValue.of(); - * - * private Logger getLogger() { - * if (!logger.isSet()) { - * logger.trySet(Logger.create(Component.class)); - * } - * return logger.orElseThrow(); - * } - * - * public void process() { - * getLogger().info("Process started"); - * // ... - * } - * } - *} - *

- * If {@code getLogger()} is called from several threads, several instances of - * {@code Logger} might be created. However, the contents can only be set at most once - * meaning the first writer wins. - *

- * In order to guarantee that, even under races, only one instance of {@code Logger} is - * ever created, the {@linkplain #orElseSet(Supplier) orElseSet()} method can be used - * instead, where the contents are lazily computed, and atomically set, via a - * {@linkplain Supplier supplier}. In the example below, the supplier is provided in the - * form of a lambda expression: - * - * {@snippet lang = java: - * public class Component { - * - * // Creates a new unset stable value with no contents - * // @link substring="of" target="#of" : - * private final StableValue logger = StableValue.of(); - * - * private Logger getLogger() { - * return logger.orElseSet( () -> Logger.create(Component.class) ); - * } - * - * public void process() { - * getLogger().info("Process started"); - * // ... - * } - * } - *} - *

- * The {@code getLogger()} method calls {@code logger.orElseSet()} on the stable value to - * retrieve its contents. If the stable value is unset, then {@code orElseSet()} - * evaluates the given supplier, and sets the contents to the result; the result is then - * returned to the client. In other words, {@code orElseSet()} guarantees that a - * stable value's contents is set before it returns. - *

- * Furthermore, {@code orElseSet()} guarantees that out of one or more suppliers provided, - * only at most one is ever evaluated, and that one is only ever evaluated once, - * even when {@code logger.orElseSet()} is invoked concurrently. This property is crucial - * as evaluation of the supplier may have side effects, for example, the call above to - * {@code Logger.create()} may result in storage resources being prepared. - * - *

Stable Functions

- * Stable values provide the foundation for higher-level functional abstractions. A - * stable supplier is a supplier that computes a value and then caches it into - * a backing stable value storage for subsequent use. A stable supplier is created via the - * {@linkplain StableValue#supplier(Supplier) StableValue.supplier()} factory, by - * providing an underlying {@linkplain Supplier} which is invoked when the stable supplier - * is first accessed: - * - * {@snippet lang = java: - * public class Component { - * - * private final Supplier logger = - * // @link substring="supplier" target="#supplier(Supplier)" : - * StableValue.supplier( () -> Logger.getLogger(Component.class) ); - * - * public void process() { - * logger.get().info("Process started"); - * // ... - * } - * } - *} - * A stable supplier encapsulates access to its backing stable value storage. This means - * that code inside {@code Component} can obtain the logger object directly from the - * stable supplier, without having to go through an accessor method like {@code getLogger()}. - *

- * A stable int function is a function that takes an {@code int} parameter and - * uses it to compute a result that is then cached by the backing stable value storage - * for that parameter value. A stable {@link IntFunction} is created via the - * {@linkplain StableValue#intFunction(int, IntFunction) StableValue.intFunction()} - * factory. Upon creation, the input range (i.e. {@code [0, size)}) is specified together - * with an underlying {@linkplain IntFunction} which is invoked at most once per input - * value. In effect, the stable int function will act like a cache for the underlying - * {@linkplain IntFunction}: - * - * {@snippet lang = java: - * final class PowerOf2Util { - * - * private PowerOf2Util() {} - * - * private static final int SIZE = 6; - * private static final IntFunction UNDERLYING_POWER_OF_TWO = - * v -> 1 << v; - * - * private static final IntFunction POWER_OF_TWO = - * // @link substring="intFunction" target="#intFunction(int,IntFunction)" : - * StableValue.intFunction(SIZE, UNDERLYING_POWER_OF_TWO); - * - * public static int powerOfTwo(int a) { - * return POWER_OF_TWO.apply(a); - * } - * } - * - * int result = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime - * - *} - * The {@code PowerOf2Util.powerOfTwo()} function is a partial function that only - * allows a subset {@code [0, 5]} of the underlying function's {@code UNDERLYING_POWER_OF_TWO} - * input range. - * - *

- * A stable function is a function that takes a parameter (of type {@code T}) and - * uses it to compute a result (of type {@code R}) that is then cached by the backing - * stable value storage for that parameter value. A stable function is created via the - * {@linkplain StableValue#function(Set, Function) StableValue.function()} factory. - * Upon creation, the input {@linkplain Set} is specified together with an underlying - * {@linkplain Function} which is invoked at most once per input value. In effect, the - * stable function will act like a cache for the underlying {@linkplain Function}: - * - * {@snippet lang = java: - * class Log2Util { - * - * private Log2Util() {} - * - * private static final Set KEYS = - * Set.of(1, 2, 4, 8, 16, 32); - * private static final UnaryOperator UNDERLYING_LOG2 = - * i -> 31 - Integer.numberOfLeadingZeros(i); - * - * private static final Function LOG2 = - * // @link substring="function" target="#function(Set,Function)" : - * StableValue.function(KEYS, UNDERLYING_LOG2); - * - * public static int log2(int a) { - * return LOG2.apply(a); - * } - * - * } - * - * int result = Log2Util.log2(16); // May eventually constant fold to 4 at runtime - *} - * - * The {@code Log2Util.log2()} function is a partial function that only allows - * a subset {@code {1, 2, 4, 8, 16, 32}} of the underlying function's - * {@code UNDERLYING_LOG2} input range. - * - *

Stable Collections

- * Stable values can also be used as backing storage for - * {@linkplain Collection##unmodifiable unmodifiable collections}. A stable list - * is an unmodifiable list, backed by an array of stable values. The stable list elements - * are computed when they are first accessed, using a provided {@linkplain IntFunction}: - * - * {@snippet lang = java: - * final class PowerOf2Util { - * - * private PowerOf2Util() {} - * - * private static final int SIZE = 6; - * private static final IntFunction UNDERLYING_POWER_OF_TWO = - * v -> 1 << v; - * - * private static final List POWER_OF_TWO = - * // @link substring="list" target="#list(int,IntFunction)" : - * StableValue.list(SIZE, UNDERLYING_POWER_OF_TWO); - * - * public static int powerOfTwo(int a) { - * return POWER_OF_TWO.get(a); - * } - * } - * - * int result = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime - * - * } - *

- * Similarly, a stable map is an unmodifiable map whose keys are known at - * construction. The stable map values are computed when they are first accessed, - * using a provided {@linkplain Function}: - * - * {@snippet lang = java: - * class Log2Util { - * - * private Log2Util() {} - * - * private static final Set KEYS = - * Set.of(1, 2, 4, 8, 16, 32); - * private static final UnaryOperator UNDERLYING_LOG2 = - * i -> 31 - Integer.numberOfLeadingZeros(i); - * - * private static final Map LOG2 = - * // @link substring="map" target="#map(Set,Function)" : - * StableValue.map(CACHED_KEYS, UNDERLYING_LOG2); - * - * public static int log2(int a) { - * return LOG2.get(a); - * } - * - * } - * - * int result = Log2Util.log2(16); // May eventually constant fold to 4 at runtime - * - *} - * - *

Composing stable values

- * A stable value can depend on other stable values, forming a dependency graph - * that can be lazily computed but where access to individual elements can still be - * performant. In the following example, a single {@code Foo} and a {@code Bar} - * instance (that is dependent on the {@code Foo} instance) are lazily created, both of - * which are held by stable values: - * {@snippet lang = java: - * public final class DependencyUtil { - * - * private DependencyUtil() {} - * - * public static class Foo { - * // ... - * } - * - * public static class Bar { - * public Bar(Foo foo) { - * // ... - * } - * } - * - * private static final Supplier FOO = StableValue.supplier(Foo::new); - * private static final Supplier BAR = StableValue.supplier(() -> new Bar(FOO.get())); - * - * public static Foo foo() { - * return FOO.get(); - * } - * - * public static Bar bar() { - * return BAR.get(); - * } - * - * } - *} - * Calling {@code bar()} will create the {@code Bar} singleton if it is not already - * created. Upon such a creation, the dependent {@code Foo} will first be created if - * the {@code Foo} does not already exist. - *

- * Another example, which has a more complex dependency graph, is to compute the - * Fibonacci sequence lazily: - * {@snippet lang = java: - * public final class Fibonacci { - * - * private Fibonacci() {} - * - * private static final int MAX_SIZE_INT = 46; - * - * private static final IntFunction FIB = - * StableValue.intFunction(MAX_SIZE_INT, Fibonacci::fib); - * - * public static int fib(int n) { - * return n < 2 - * ? n - * : FIB.apply(n - 1) + FIB.apply(n - 2); - * } - * - * } - *} - * Both {@code FIB} and {@code Fibonacci::fib} recurse into each other. Because the - * stable int function {@code FIB} caches intermediate results, the initial - * computational complexity is reduced from exponential to linear compared to a - * traditional non-caching recursive fibonacci method. Once computed, the VM is free to - * constant-fold expressions like {@code Fibonacci.fib(5)}. - *

- * The fibonacci example above is a directed acyclic graph (i.e., - * it has no circular dependencies and is therefore a dependency tree): - *{@snippet lang=text : - * - * ___________fib(5)____________ - * / \ - * ____fib(4)____ ____fib(3)____ - * / \ / \ - * fib(3) fib(2) fib(2) fib(1) - * / \ / \ / \ - * fib(2) fib(1) fib(1) fib(0) fib(1) fib(0) - *} - * - * If there are circular dependencies in a dependency graph, a stable value will - * eventually throw an {@linkplain IllegalStateException} upon referencing elements in - * a circularity. - * - *

Thread Safety

- * The contents of a stable value is guaranteed to be set at most once. If competing - * threads are racing to set a stable value, only one update succeeds, while the other - * updates are blocked until the stable value is set, whereafter the other updates - * observes the stable value is set and leave the stable value unchanged. - *

- * The at-most-once write operation on a stable value that succeeds - * (e.g. {@linkplain #trySet(Object) trySet()}) - * {@linkplain java.util.concurrent##MemoryVisibility happens-before} - * any successful read operation (e.g. {@linkplain #orElseThrow()}). - * A successful write operation can be either: - *

    - *
  • a {@link #trySet(Object)} that returns {@code true},
  • - *
  • a {@link #setOrThrow(Object)} that does not throw, or
  • - *
  • an {@link #orElseSet(Supplier)} that successfully runs the supplier
  • - *
- * A successful read operation can be either: - *
    - *
  • a {@link #orElseThrow()} that does not throw,
  • - *
  • a {@link #orElse(Object) orElse(other)} that does not return the {@code other} value
  • - *
  • an {@link #orElseSet(Supplier)} that does not {@code throw}, or
  • - *
  • an {@link #isSet()} that returns {@code true}
  • - *
- *

- * The method {@link #orElseSet(Supplier)} guarantees that the provided - * {@linkplain Supplier} is invoked successfully at most once, even under race. - * Invocations of {@link #orElseSet(Supplier)} form a total order of zero or - * more exceptional invocations followed by zero (if the contents were already set) or one - * successful invocation. Since stable functions and stable collections are built on top - * of the same principles as {@linkplain StableValue#orElseSet(Supplier) orElseSet()} they - * too are thread safe and guarantee at-most-once-per-input invocation. - * - *

Performance

- * As the contents of a stable value can never change after it has been set, a JVM - * implementation may, for a set stable value, elide all future reads of that - * stable value, and instead directly use any contents that it has previously observed. - * This is true if the reference to the stable value is a constant (e.g. in cases where - * the stable value itself is stored in a {@code static final} field). Stable functions - * and collections are built on top of StableValue. As such, they might also be eligible - * for the same JVM optimizations as for StableValue. - * - * @implSpec Implementing classes of {@code StableValue} are free to synchronize on - * {@code this} and consequently, it should be avoided to - * (directly or indirectly) synchronize on a {@code StableValue}. Hence, - * synchronizing on {@code this} may lead to deadlock. - *

- * Except for a {@code StableValue}'s contents itself, - * an {@linkplain #orElse(Object) orElse(other)} parameter, and - * an {@linkplain #equals(Object) equals(obj)} parameter; all - * method parameters must be non-null or a {@link NullPointerException} - * will be thrown. - * - * @implNote A {@code StableValue} is mainly intended to be a non-public field in - * a class and is usually neither exposed directly via accessors nor passed as - * a method parameter. - *

- * Stable functions and collections make reasonable efforts to provide - * {@link Object#toString()} operations that do not trigger evaluation - * of the internal stable values when called. - * Stable collections have {@link Object#equals(Object)} operations that try - * to minimize evaluation of the internal stable values when called. - *

- * As objects can be set via stable values but never removed, this can be a - * source of unintended memory leaks. A stable value's contents are - * {@linkplain java.lang.ref##reachability strongly reachable}. - * Be advised that reachable stable values will hold their set contents until - * the stable value itself is collected. - *

- * A {@code StableValue} that has a type parameter {@code T} that is an array - * type (of arbitrary rank) will only allow the JVM to treat the - * array reference as a stable value but not its components. - * Instead, a {@linkplain #list(int, IntFunction) a stable list} of arbitrary - * depth can be used, which provides stable components. More generally, a - * stable value can hold other stable values of arbitrary depth and still - * provide transitive constantness. - *

- * Stable values, functions, and collections are not {@link Serializable}. - * - * @param type of the contents - * - * @since 25 - */ -@PreviewFeature(feature = PreviewFeature.Feature.STABLE_VALUES) -public sealed interface StableValue - permits StableValueImpl { - - /** - * Tries to set the contents of this StableValue to the provided {@code contents}. - * The contents of this StableValue can only be set once, implying this method only - * returns {@code true} once. - *

- * When this method returns, the contents of this StableValue is always set. - * - * @return {@code true} if the contents of this StableValue was set to the - * provided {@code contents}, {@code false} otherwise - * @param contents to set - * @throws IllegalStateException if a supplier invoked by {@link #orElseSet(Supplier)} - * recursively attempts to set this stable value by calling this method - * directly or indirectly. - */ - boolean trySet(T contents); - - /** - * {@return the contents if set, otherwise, returns the provided {@code other} value} - * - * @param other to return if the contents is not set - */ - T orElse(T other); - - /** - * {@return the contents if set, otherwise, throws {@code NoSuchElementException}} - * - * @throws NoSuchElementException if no contents is set - */ - T orElseThrow(); - - /** - * {@return {@code true} if the contents is set, {@code false} otherwise} - */ - boolean isSet(); - - /** - * {@return the contents; if unset, first attempts to compute and set the - * contents using the provided {@code supplier}} - *

- * The provided {@code supplier} is guaranteed to be invoked at most once if it - * completes without throwing an exception. If this method is invoked several times - * with different suppliers, only one of them will be invoked provided it completes - * without throwing an exception. - *

- * If the supplier throws an (unchecked) exception, the exception is rethrown and no - * contents is set. The most common usage is to construct a new object serving - * as a lazily computed value or memoized result, as in: - * - * {@snippet lang=java: - * Value v = stable.orElseSet(Value::new); - * } - *

- * When this method returns successfully, the contents is always set. - *

- * The provided {@code supplier} will only be invoked once even if invoked from - * several threads unless the {@code supplier} throws an exception. - * - * @param supplier to be used for computing the contents, if not previously set - * @throws IllegalStateException if the provided {@code supplier} recursively - * attempts to set this stable value. - */ - T orElseSet(Supplier supplier); - - /** - * Sets the contents of this StableValue to the provided {@code contents}, or, if - * already set, throws {@code IllegalStateException}. - *

- * When this method returns (or throws an exception), the contents is always set. - * - * @param contents to set - * @throws IllegalStateException if the contents was already set - * @throws IllegalStateException if a supplier invoked by {@link #orElseSet(Supplier)} - * recursively attempts to set this stable value by calling this method - * directly or indirectly. - */ - void setOrThrow(T contents); - - // Object methods - - /** - * {@return {@code true} if {@code this == obj}, {@code false} otherwise} - * - * @param obj to check for equality - */ - boolean equals(Object obj); - - /** - * {@return the {@linkplain System#identityHashCode(Object) identity hash code} of - * {@code this} object} - */ - int hashCode(); - - // Factories - - /** - * {@return a new unset stable value} - *

- * An unset stable value has no contents. - * - * @param type of the contents - */ - static StableValue of() { - return StableValueImpl.of(); - } - - /** - * {@return a new pre-set stable value with the provided {@code contents}} - * - * @param contents to set - * @param type of the contents - */ - static StableValue of(T contents) { - final StableValue stableValue = StableValue.of(); - stableValue.trySet(contents); - return stableValue; - } - - /** - * {@return a new stable supplier} - *

- * The returned {@linkplain Supplier supplier} is a caching supplier that records - * the value of the provided {@code underlying} supplier upon being first accessed via - * the returned supplier's {@linkplain Supplier#get() get()} method. - *

- * The provided {@code underlying} supplier is guaranteed to be successfully invoked - * at most once even in a multi-threaded environment. Competing threads invoking the - * returned supplier's {@linkplain Supplier#get() get()} method when a value is - * already under computation will block until a value is computed or an exception is - * thrown by the computing thread. The competing threads will then observe the newly - * computed value (if any) and will then never execute the {@code underlying} supplier. - *

- * If the provided {@code underlying} supplier throws an exception, it is rethrown - * to the initial caller and no contents is recorded. - *

- * If the provided {@code underlying} supplier recursively calls the returned - * supplier, an {@linkplain IllegalStateException} will be thrown. - * - * @param underlying supplier used to compute a cached value - * @param the type of results supplied by the returned supplier - */ - static Supplier supplier(Supplier underlying) { - Objects.requireNonNull(underlying); - return StableSupplier.of(underlying); - } - - /** - * {@return a new stable {@linkplain IntFunction}} - *

- * The returned function is a caching function that, for each allowed {@code int} - * input, records the values of the provided {@code underlying} - * function upon being first accessed via the returned function's - * {@linkplain IntFunction#apply(int) apply()} method. If the returned function is - * invoked with an input that is not in the range {@code [0, size)}, an - * {@link IllegalArgumentException} will be thrown. - *

- * The provided {@code underlying} function is guaranteed to be successfully invoked - * at most once per allowed input, even in a multi-threaded environment. Competing - * threads invoking the returned function's - * {@linkplain IntFunction#apply(int) apply()} method when a value is already under - * computation will block until a value is computed or an exception is thrown by - * the computing thread. - *

- * If invoking the provided {@code underlying} function throws an exception, it is - * rethrown to the initial caller and no contents is recorded. - *

- * If the provided {@code underlying} function recursively calls the returned - * function for the same input, an {@linkplain IllegalStateException} will - * be thrown. - * - * @param size the upper bound of the range {@code [0, size)} indicating - * the allowed inputs - * @param underlying {@code IntFunction} used to compute cached values - * @param the type of results delivered by the returned IntFunction - * @throws IllegalArgumentException if the provided {@code size} is negative. - */ - static IntFunction intFunction(int size, - IntFunction underlying) { - StableUtil.assertSizeNonNegative(size); - Objects.requireNonNull(underlying); - return StableIntFunction.of(size, underlying); - } - - /** - * {@return a new stable {@linkplain Function}} - *

- * The returned function is a caching function that, for each allowed - * input in the given set of {@code inputs}, records the values of the provided - * {@code underlying} function upon being first accessed via the returned function's - * {@linkplain Function#apply(Object) apply()} method. If the returned function is - * invoked with an input that is not in {@code inputs}, an {@link IllegalArgumentException} - * will be thrown. - *

- * The provided {@code underlying} function is guaranteed to be successfully invoked - * at most once per allowed input, even in a multi-threaded environment. Competing - * threads invoking the returned function's {@linkplain Function#apply(Object) apply()} - * method when a value is already under computation will block until a value is - * computed or an exception is thrown by the computing thread. - *

- * If invoking the provided {@code underlying} function throws an exception, it is - * rethrown to the initial caller and no contents is recorded. - *

- * If the provided {@code underlying} function recursively calls the returned - * function for the same input, an {@linkplain IllegalStateException} will - * be thrown. - * - * @param inputs the set of (non-null) allowed input values - * @param underlying {@code Function} used to compute cached values - * @param the type of the input to the returned Function - * @param the type of results delivered by the returned Function - * @throws NullPointerException if the provided set of {@code inputs} contains a - * {@code null} element. - */ - static Function function(Set inputs, - Function underlying) { - Objects.requireNonNull(inputs); - // Checking that the Set of inputs does not contain a `null` value is made in the - // implementing classes. - Objects.requireNonNull(underlying); - return inputs instanceof EnumSet && !inputs.isEmpty() - ? StableEnumFunction.of(inputs, underlying) - : StableFunction.of(inputs, underlying); - } - - /** - * {@return a new stable list with the provided {@code size}} - *

- * The returned list is an {@linkplain Collection##unmodifiable unmodifiable} list - * with the provided {@code size}. The list's elements are computed via the - * provided {@code mapper} when they are first accessed - * (e.g. via {@linkplain List#get(int) List::get}). - *

- * The provided {@code mapper} function is guaranteed to be successfully invoked - * at most once per list index, even in a multi-threaded environment. Competing - * threads accessing an element already under computation will block until an element - * is computed or an exception is thrown by the computing thread. - *

- * If invoking the provided {@code mapper} function throws an exception, it - * is rethrown to the initial caller and no value for the element is recorded. - *

- * Any {@link List#subList(int, int) subList} or {@link List#reversed()} views - * of the returned list are also stable. - *

- * The returned list and its {@link List#subList(int, int) subList} or - * {@link List#reversed()} views implement the {@link RandomAccess} interface. - *

- * The returned list is unmodifiable and does not implement the - * {@linkplain Collection##optional-operation optional operations} in the - * {@linkplain List} interface. - *

- * If the provided {@code mapper} recursively calls the returned list for the - * same index, an {@linkplain IllegalStateException} will be thrown. - * - * @param size the size of the returned list - * @param mapper to invoke whenever an element is first accessed - * (may return {@code null}) - * @param the type of elements in the returned list - * @throws IllegalArgumentException if the provided {@code size} is negative. - */ - static List list(int size, - IntFunction mapper) { - StableUtil.assertSizeNonNegative(size); - Objects.requireNonNull(mapper); - return SharedSecrets.getJavaUtilCollectionAccess().stableList(size, mapper); - } - - /** - * {@return a new stable map with the provided {@code keys}} - *

- * The returned map is an {@linkplain Collection##unmodifiable unmodifiable} map whose - * keys are known at construction. The map's values are computed via the provided - * {@code mapper} when they are first accessed - * (e.g. via {@linkplain Map#get(Object) Map::get}). - *

- * The provided {@code mapper} function is guaranteed to be successfully invoked - * at most once per key, even in a multi-threaded environment. Competing - * threads accessing a value already under computation will block until an element - * is computed or an exception is thrown by the computing thread. - *

- * If invoking the provided {@code mapper} function throws an exception, it - * is rethrown to the initial caller and no value associated with the provided key - * is recorded. - *

- * Any {@link Map#values()} or {@link Map#entrySet()} views of the returned map are - * also stable. - *

- * The returned map is unmodifiable and does not implement the - * {@linkplain Collection##optional-operations optional operations} in the - * {@linkplain Map} interface. - *

- * If the provided {@code mapper} recursively calls the returned map for - * the same key, an {@linkplain IllegalStateException} will be thrown. - * - * @param keys the (non-null) keys in the returned map - * @param mapper to invoke whenever an associated value is first accessed - * (may return {@code null}) - * @param the type of keys maintained by the returned map - * @param the type of mapped values in the returned map - * @throws NullPointerException if the provided set of {@code inputs} contains a - * {@code null} element. - */ - static Map map(Set keys, - Function mapper) { - Objects.requireNonNull(keys); - // Checking that the Set of keys does not contain a `null` value is made in the - // implementing class. - Objects.requireNonNull(mapper); - return SharedSecrets.getJavaUtilCollectionAccess().stableMap(keys, mapper); - } - -} diff --git a/src/java.base/share/classes/java/nio/charset/Charset.java b/src/java.base/share/classes/java/nio/charset/Charset.java index 1eb3c9c209411..736ed4f12d548 100644 --- a/src/java.base/share/classes/java/nio/charset/Charset.java +++ b/src/java.base/share/classes/java/nio/charset/Charset.java @@ -25,7 +25,6 @@ package java.nio.charset; -import jdk.internal.misc.ThreadTracker; import jdk.internal.misc.VM; import jdk.internal.util.StaticProperty; import jdk.internal.vm.annotation.Stable; @@ -41,7 +40,6 @@ import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; -import java.util.Objects; import java.util.ServiceLoader; import java.util.Set; import java.util.SortedMap; @@ -426,7 +424,7 @@ public Charset call() { } /* The extended set of charsets */ - private static final Supplier> EXTENDED_PROVIDERS = StableValue.supplier( + private static final LazyConstant> EXTENDED_PROVIDERS = LazyConstant.of( new Supplier<>() { public List get() { return extendedProviders0(); }}); private static List extendedProviders0() { @@ -617,7 +615,7 @@ public static SortedMap availableCharsets() { return Collections.unmodifiableSortedMap(m); } - private static final Supplier defaultCharset = StableValue.supplier( + private static final LazyConstant defaultCharset = LazyConstant.of( new Supplier<>() { public Charset get() { return defaultCharset0(); }}); private static Charset defaultCharset0() { @@ -658,7 +656,7 @@ public static Charset defaultCharset() { @Stable private final String[] aliases; @Stable - private final Supplier> aliasSet = StableValue.supplier( + private final LazyConstant> aliasSet = LazyConstant.of( new Supplier<>() { public Set get() { return Set.of(aliases); }}); /** diff --git a/src/java.base/share/classes/java/util/Currency.java b/src/java.base/share/classes/java/util/Currency.java index febae04a77b6c..b254bae32a123 100644 --- a/src/java.base/share/classes/java/util/Currency.java +++ b/src/java.base/share/classes/java/util/Currency.java @@ -142,8 +142,8 @@ public final class Currency implements Serializable { // class data: instance map private static ConcurrentMap instances = new ConcurrentHashMap<>(7); - private static final Supplier> available = - StableValue.supplier(Currency::computeAllCurrencies); + private static final LazyConstant> available = + LazyConstant.of(Currency::computeAllCurrencies); // Class data: currency data obtained from currency.data file. // Purpose: diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java index 1dd7808da20f7..abc48ff5ed995 100644 --- a/src/java.base/share/classes/java/util/ImmutableCollections.java +++ b/src/java.base/share/classes/java/util/ImmutableCollections.java @@ -36,18 +36,12 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.IntFunction; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.function.UnaryOperator; import jdk.internal.access.JavaUtilCollectionAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.lang.stable.StableUtil; -import jdk.internal.lang.stable.StableValueImpl; import jdk.internal.misc.CDS; -import jdk.internal.util.ArraysSupport; -import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; /** @@ -135,14 +129,6 @@ public List listFromTrustedArray(Object[] array) { public List listFromTrustedArrayNullsAllowed(Object[] array) { return ImmutableCollections.listFromTrustedArrayNullsAllowed(array); } - public List stableList(int size, IntFunction mapper) { - // A stable list is not Serializable, so we cannot return `List.of()` if `size == 0` - return new StableList<>(size, mapper); - } - public Map stableMap(Set keys, Function mapper) { - // A stable map is not Serializable, so we cannot return `Map.of()` if `keys.isEmpty()` - return new StableMap<>(keys, mapper); - } }); } } @@ -450,7 +436,7 @@ public void add(E e) { } } - static sealed class SubList extends AbstractImmutableList + static final class SubList extends AbstractImmutableList implements RandomAccess { @Stable @@ -462,8 +448,10 @@ static sealed class SubList extends AbstractImmutableList @Stable final int size; - private SubList(AbstractImmutableList root, int offset, int size) { - assert root instanceof List12 || root instanceof ListN || root instanceof StableList; + SubList(AbstractImmutableList root, int offset, int size) { + assert root instanceof List12 + || root instanceof ListN + || root instanceof LazyCollections.LazyList; this.root = root; this.offset = offset; this.size = size; @@ -795,187 +783,6 @@ public int lastIndexOf(Object o) { } } - @FunctionalInterface - interface HasStableDelegates { - StableValueImpl[] delegates(); - } - - @jdk.internal.ValueBased - static final class StableList - extends AbstractImmutableList - implements HasStableDelegates { - - @Stable - private final IntFunction mapper; - @Stable - final StableValueImpl[] delegates; - - StableList(int size, IntFunction mapper) { - this.mapper = mapper; - this.delegates = StableUtil.array(size); - } - - @Override public boolean isEmpty() { return delegates.length == 0;} - @Override public int size() { return delegates.length; } - @Override public Object[] toArray() { return copyInto(new Object[size()]); } - - @ForceInline - @Override - public E get(int i) { - final StableValueImpl delegate; - try { - delegate = delegates[i]; - } catch (ArrayIndexOutOfBoundsException aioobe) { - throw new IndexOutOfBoundsException(i); - } - return delegate.orElseSet(new Supplier() { - @Override public E get() { return mapper.apply(i); }}); - } - - @Override - @SuppressWarnings("unchecked") - public T[] toArray(T[] a) { - final int size = delegates.length; - if (a.length < size) { - // Make a new array of a's runtime type, but my contents: - T[] n = (T[])Array.newInstance(a.getClass().getComponentType(), size); - return copyInto(n); - } - copyInto(a); - if (a.length > size) { - a[size] = null; // null-terminate - } - return a; - } - - @Override - public int indexOf(Object o) { - final int size = size(); - for (int i = 0; i < size; i++) { - if (Objects.equals(o, get(i))) { - return i; - } - } - return -1; - } - - @Override - public int lastIndexOf(Object o) { - for (int i = size() - 1; i >= 0; i--) { - if (Objects.equals(o, get(i))) { - return i; - } - } - return -1; - } - - @SuppressWarnings("unchecked") - private T[] copyInto(Object[] a) { - final int len = delegates.length; - for (int i = 0; i < len; i++) { - a[i] = get(i); - } - return (T[]) a; - } - - @Override - public List reversed() { - return new StableReverseOrderListView<>(this); - } - - @Override - public List subList(int fromIndex, int toIndex) { - subListRangeCheck(fromIndex, toIndex, size()); - return StableSubList.fromStableList(this, fromIndex, toIndex); - } - - @Override - public String toString() { - return StableUtil.renderElements(this, "StableCollection", delegates); - } - - @Override - public StableValueImpl[] delegates() { - return delegates; - } - - private static final class StableSubList extends SubList - implements HasStableDelegates { - - private StableSubList(AbstractImmutableList root, int offset, int size) { - super(root, offset, size); - } - - @Override - public List reversed() { - return new StableReverseOrderListView<>(this); - } - - @Override - public List subList(int fromIndex, int toIndex) { - subListRangeCheck(fromIndex, toIndex, size()); - return StableSubList.fromStableSubList(this, fromIndex, toIndex); - } - - @Override - public String toString() { - return StableUtil.renderElements(this, "StableCollection", delegates()); - } - - @Override - boolean allowNulls() { - return true; - } - - @Override - public StableValueImpl[] delegates() { - @SuppressWarnings("unchecked") - final var rootDelegates = ((HasStableDelegates) root).delegates(); - return Arrays.copyOfRange(rootDelegates, offset, offset + size); - } - - static SubList fromStableList(StableList list, int fromIndex, int toIndex) { - return new StableSubList<>(list, fromIndex, toIndex - fromIndex); - } - - static SubList fromStableSubList(StableSubList parent, int fromIndex, int toIndex) { - return new StableSubList<>(parent.root, parent.offset + fromIndex, toIndex - fromIndex); - } - - } - - private static final class StableReverseOrderListView - extends ReverseOrderListView.Rand - implements HasStableDelegates { - - private StableReverseOrderListView(List base) { - super(base, false); - } - - // This method does not evaluate the elements - @Override - public String toString() { - return StableUtil.renderElements(this, "StableCollection", delegates()); - } - - @Override - public List subList(int fromIndex, int toIndex) { - final int size = base.size(); - subListRangeCheck(fromIndex, toIndex, size); - return new StableReverseOrderListView<>(base.subList(size - toIndex, size - fromIndex)); - } - - @Override - public StableValueImpl[] delegates() { - @SuppressWarnings("unchecked") - final var baseDelegates = ((HasStableDelegates) base).delegates(); - return ArraysSupport.reverse( - Arrays.copyOf(baseDelegates, baseDelegates.length)); - } - } - - } - // ---------- Set Implementations ---------- @jdk.internal.ValueBased @@ -1614,187 +1421,6 @@ private Object writeReplace() { } } - static final class StableMap - extends AbstractImmutableMap { - - @Stable - private final Function mapper; - @Stable - private final Map> delegate; - - StableMap(Set keys, Function mapper) { - this.mapper = mapper; - this.delegate = StableUtil.map(keys); - } - - @Override public boolean containsKey(Object o) { return delegate.containsKey(o); } - @Override public int size() { return delegate.size(); } - @Override public Set> entrySet() { return StableMapEntrySet.of(this); } - - @ForceInline - @Override - public V get(Object key) { - return getOrDefault(key, null); - } - - @ForceInline - @Override - public V getOrDefault(Object key, V defaultValue) { - final StableValueImpl stable = delegate.get(key); - if (stable == null) { - return defaultValue; - } - @SuppressWarnings("unchecked") - final K k = (K) key; - return stable.orElseSet(new Supplier() { - @Override public V get() { return mapper.apply(k); }}); - } - - @jdk.internal.ValueBased - static final class StableMapEntrySet extends AbstractImmutableSet> { - - // Use a separate field for the outer class in order to facilitate - // a @Stable annotation. - @Stable - private final StableMap outer; - - @Stable - private final Set>> delegateEntrySet; - - private StableMapEntrySet(StableMap outer) { - this.outer = outer; - this.delegateEntrySet = outer.delegate.entrySet(); - } - - @Override public Iterator> iterator() { return LazyMapIterator.of(this); } - @Override public int size() { return delegateEntrySet.size(); } - @Override public int hashCode() { return outer.hashCode(); } - - @Override - public String toString() { - return StableUtil.renderMappings(this, "StableCollection", delegateEntrySet, false); - } - - // For @ValueBased - private static StableMapEntrySet of(StableMap outer) { - return new StableMapEntrySet<>(outer); - } - - @jdk.internal.ValueBased - static final class LazyMapIterator implements Iterator> { - - // Use a separate field for the outer class in order to facilitate - // a @Stable annotation. - @Stable - private final StableMapEntrySet outer; - - @Stable - private final Iterator>> delegateIterator; - - private LazyMapIterator(StableMapEntrySet outer) { - this.outer = outer; - this.delegateIterator = outer.delegateEntrySet.iterator(); - } - - @Override public boolean hasNext() { return delegateIterator.hasNext(); } - - @Override - public Entry next() { - final Map.Entry> inner = delegateIterator.next(); - final K k = inner.getKey(); - return new StableEntry<>(k, inner.getValue(), new Supplier() { - @Override public V get() { return outer.outer.mapper.apply(k); }}); - } - - @Override - public void forEachRemaining(Consumer> action) { - final Consumer>> innerAction = - new Consumer<>() { - @Override - public void accept(Entry> inner) { - final K k = inner.getKey(); - action.accept(new StableEntry<>(k, inner.getValue(), new Supplier() { - @Override public V get() { return outer.outer.mapper.apply(k); }})); - } - }; - delegateIterator.forEachRemaining(innerAction); - } - - // For @ValueBased - private static LazyMapIterator of(StableMapEntrySet outer) { - return new LazyMapIterator<>(outer); - } - - } - } - - private record StableEntry(K getKey, // trick - StableValueImpl stableValue, - Supplier supplier) implements Map.Entry { - - @Override public V setValue(V value) { throw uoe(); } - @Override public V getValue() { return stableValue.orElseSet(supplier); } - @Override public int hashCode() { return hash(getKey()) ^ hash(getValue()); } - @Override public String toString() { return getKey() + "=" + stableValue.toString(); } - @Override public boolean equals(Object o) { - return o instanceof Map.Entry e - && Objects.equals(getKey(), e.getKey()) - // Invoke `getValue()` as late as possible to avoid evaluation - && Objects.equals(getValue(), e.getValue()); - } - - private int hash(Object obj) { return (obj == null) ? 0 : obj.hashCode(); } - } - - @Override - public Collection values() { - return StableMapValues.of(this); - } - - @jdk.internal.ValueBased - static final class StableMapValues extends AbstractImmutableCollection { - - // Use a separate field for the outer class in order to facilitate - // a @Stable annotation. - @Stable - private final StableMap outer; - - private StableMapValues(StableMap outer) { - this.outer = outer; - } - - @Override public Iterator iterator() { return outer.new ValueIterator(); } - @Override public int size() { return outer.size(); } - @Override public boolean isEmpty() { return outer.isEmpty();} - @Override public boolean contains(Object v) { return outer.containsValue(v); } - - private static final IntFunction[]> GENERATOR = new IntFunction[]>() { - @Override - public StableValueImpl[] apply(int len) { - return new StableValueImpl[len]; - } - }; - - @Override - public String toString() { - final StableValueImpl[] values = outer.delegate.values().toArray(GENERATOR); - return StableUtil.renderElements(this, "StableCollection", values); - } - - // For @ValueBased - private static StableMapValues of(StableMap outer) { - return new StableMapValues<>(outer); - } - - } - - @Override - public String toString() { - return StableUtil.renderMappings(this, "StableMap", delegate.entrySet(), true); - } - - } - } // ---------- Serialization Proxy ---------- diff --git a/src/java.base/share/classes/java/util/LazyCollections.java b/src/java.base/share/classes/java/util/LazyCollections.java new file mode 100644 index 0000000000000..0bbdad87ac45e --- /dev/null +++ b/src/java.base/share/classes/java/util/LazyCollections.java @@ -0,0 +1,584 @@ +/* + * 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 java.util; + +import jdk.internal.misc.Unsafe; +import jdk.internal.util.ImmutableBitSetPredicate; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; + +import java.lang.LazyConstant; +import java.lang.reflect.Array; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.IntPredicate; +import java.util.function.Supplier; + +/** + * Container class for lazy collections implementations. Not part of the public API. + */ +@AOTSafeClassInitializer +final class LazyCollections { + + /** + * No instances. + */ + private LazyCollections() { } + + // Unsafe allows LazyCollection classes to be used early in the boot sequence + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + @jdk.internal.ValueBased + static final class LazyList + extends ImmutableCollections.AbstractImmutableList { + + @Stable + private final E[] elements; + // Keeping track of `size` separately reduces bytecode size compared to + // using `elements.length`. + @Stable + private final int size; + @Stable + final FunctionHolder> functionHolder; + @Stable + private final Mutexes mutexes; + + private LazyList(int size, IntFunction computingFunction) { + this.elements = newGenericArray(size); + this.size = size; + this.functionHolder = new FunctionHolder<>(computingFunction, size); + this.mutexes = new Mutexes(size); + super(); + } + + @Override public boolean isEmpty() { return size == 0; } + @Override public int size() { return size; } + @Override public Object[] toArray() { return copyInto(new Object[size]); } + + @ForceInline + @Override + public E get(int i) { + final E e = contentsAcquire(offsetFor(Objects.checkIndex(i, size))); + return (e != null) ? e : getSlowPath(i); + } + + private E getSlowPath(int i) { + return orElseComputeSlowPath(elements, i, mutexes, i, functionHolder); + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + if (a.length < size) { + // Make a new array of a's runtime type, but my contents: + T[] n = (T[]) Array.newInstance(a.getClass().getComponentType(), size); + return copyInto(n); + } + copyInto(a); + if (a.length > size) { + a[size] = null; // null-terminate + } + return a; + } + + @Override + public int indexOf(Object o) { + for (int i = 0; i < size; i++) { + if (Objects.equals(o, get(i))) { + return i; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object o) { + for (int i = size - 1; i >= 0; i--) { + if (Objects.equals(o, get(i))) { + return i; + } + } + return -1; + } + + @SuppressWarnings("unchecked") + private T[] copyInto(Object[] a) { + for (int i = 0; i < size; i++) { + a[i] = get(i); + } + return (T[]) a; + } + + @SuppressWarnings("unchecked") + @ForceInline + private E contentsAcquire(long offset) { + return (E) UNSAFE.getReferenceAcquire(elements, offset); + } + + } + + static final class LazyEnumMap, V> + extends AbstractLazyMap { + + @Stable + private final Class enumType; + @Stable + // We are using a wrapper class here to be able to use a min value of zero that + // is also stable. + private final Integer min; + @Stable + private final IntPredicate member; + + public LazyEnumMap(Set set, + Class enumType, + int min, + int backingSize, + IntPredicate member, + Function computingFunction) { + this.enumType = enumType; + this.min = min; + this.member = member; + super(set, set.size(), backingSize, computingFunction); + } + + @Override + @ForceInline + public boolean containsKey(Object o) { + return enumType.isAssignableFrom(o.getClass()) + && member.test(((Enum) o).ordinal()); + } + + @ForceInline + @Override + public V getOrDefault(Object key, V defaultValue) { + if (enumType.isAssignableFrom(key.getClass())) { + final int ordinal = ((Enum) key).ordinal(); + if (member.test(ordinal)) { + @SuppressWarnings("unchecked") + final K k = (K) key; + return orElseCompute(k, indexForAsInt(k)); + } + } + return defaultValue; + } + + @Override + Integer indexFor(K key) { + return indexForAsInt(key); + } + + private int indexForAsInt(K key) { + return key.ordinal() - min; + } + + } + + static final class LazyMap + extends AbstractLazyMap { + + // Use an unmodifiable map with known entries that are @Stable. Lookups through this map can be folded because + // it is created using Map.ofEntrie. This allows us to avoid creating a separate hashing function. + @Stable + private final Map indexMapper; + + public LazyMap(Set keys, Function computingFunction) { + @SuppressWarnings("unchecked") + final Entry[] entries = (Entry[]) new Entry[keys.size()]; + int i = 0; + for (K k : keys) { + entries[i] = Map.entry(k, i++); + } + this.indexMapper = Map.ofEntries(entries); + super(keys, i, i, computingFunction); + } + + @ForceInline + @Override + public V getOrDefault(Object key, V defaultValue) { + final Integer index = indexMapper.get(key); + if (index != null) { + @SuppressWarnings("unchecked") + final K k = (K) key; + return orElseCompute(k, index); + } + return defaultValue; + } + + @Override public boolean containsKey(Object o) { return indexMapper.containsKey(o); } + + @Override + Integer indexFor(K key) { + return indexMapper.get(key); + } + } + + static sealed abstract class AbstractLazyMap + extends ImmutableCollections.AbstractImmutableMap { + + // This field shadows AbstractMap.keySet which is not @Stable. + @Stable + Set keySet; + // This field shadows AbstractMap.values which is of another type + @Stable + final V[] values; + @Stable + Mutexes mutexes; + @Stable + private final int size; + @Stable + final FunctionHolder> functionHolder; + @Stable + private final Set> entrySet; + + private AbstractLazyMap(Set keySet, + int size, + int backingSize, + Function computingFunction) { + this.size = size; + this.functionHolder = new FunctionHolder<>(computingFunction, size); + this.values = newGenericArray(backingSize); + this.mutexes = new Mutexes(backingSize); + super(); + this.keySet = keySet; + this.entrySet = LazyMapEntrySet.of(this); + } + + // Abstract methods + @Override public abstract boolean containsKey(Object o); + abstract Integer indexFor(K key); + + // Public methods + @Override public final int size() { return size; } + @Override public final boolean isEmpty() { return size == 0; } + @Override public final Set> entrySet() { return entrySet; } + @Override public Set keySet() { return keySet; } + + @ForceInline + @Override + public final V get(Object key) { + return getOrDefault(key, null); + } + + @SuppressWarnings("unchecked") + @ForceInline + final V orElseCompute(K key, int index) { + final long offset = offsetFor(index); + final V v = (V) UNSAFE.getReferenceAcquire(values, offset); + if (v != null) { + return v; + } + return orElseComputeSlowPath(values, index, mutexes, key, functionHolder); + } + + @jdk.internal.ValueBased + static final class LazyMapEntrySet extends ImmutableCollections.AbstractImmutableSet> { + + // Use a separate field for the outer class in order to facilitate + // a @Stable annotation. + @Stable + private final AbstractLazyMap map; + + private LazyMapEntrySet(AbstractLazyMap map) { + this.map = map; + super(); + } + + @Override public Iterator> iterator() { return LazyMapIterator.of(map); } + @Override public int size() { return map.size(); } + @Override public int hashCode() { return map.hashCode(); } + + // For @ValueBased + private static LazyMapEntrySet of(AbstractLazyMap outer) { + return new LazyMapEntrySet<>(outer); + } + + @jdk.internal.ValueBased + static final class LazyMapIterator implements Iterator> { + + // Use a separate field for the outer class in order to facilitate + // a @Stable annotation. + @Stable + private final AbstractLazyMap map; + @Stable + private final Iterator keyIterator; + + private LazyMapIterator(AbstractLazyMap map) { + this.map = map; + this.keyIterator = map.keySet.iterator(); + super(); + } + + @Override public boolean hasNext() { return keyIterator.hasNext(); } + + @Override + public Entry next() { + final K k = keyIterator.next(); + return new LazyEntry<>(k, map, map.functionHolder); + } + + @Override + public void forEachRemaining(Consumer> action) { + final Consumer innerAction = + new Consumer<>() { + @Override + public void accept(K key) { + action.accept(new LazyEntry<>(key, map, map.functionHolder)); + } + }; + keyIterator.forEachRemaining(innerAction); + } + + // For @ValueBased + private static LazyMapIterator of(AbstractLazyMap map) { + return new LazyMapIterator<>(map); + } + + } + } + + private record LazyEntry(K getKey, // trick + AbstractLazyMap map, + FunctionHolder> functionHolder) implements Entry { + + @Override public V setValue(V value) { throw ImmutableCollections.uoe(); } + @Override public V getValue() { return map.orElseCompute(getKey, map.indexFor(getKey)); } + @Override public int hashCode() { return hash(getKey()) ^ hash(getValue()); } + @Override public String toString() { return getKey() + "=" + getValue(); } + + @Override + public boolean equals(Object o) { + return o instanceof Map.Entry e + && Objects.equals(getKey(), e.getKey()) + // Invoke `getValue()` as late as possible to avoid evaluation + && Objects.equals(getValue(), e.getValue()); + } + + private int hash(Object obj) { + return (obj == null) ? 0 : obj.hashCode(); + } + } + + @Override + public Collection values() { + return LazyMapValues.of(this); + } + + @jdk.internal.ValueBased + static final class LazyMapValues extends ImmutableCollections.AbstractImmutableCollection { + + // Use a separate field for the outer class in order to facilitate + // a @Stable annotation. + @Stable + private final AbstractLazyMap map; + + private LazyMapValues(AbstractLazyMap map) { + this.map = map; + super(); + } + + @Override public Iterator iterator() { return map.new ValueIterator(); } + @Override public int size() { return map.size(); } + @Override public boolean isEmpty() { return map.isEmpty(); } + @Override public boolean contains(Object v) { return map.containsValue(v); } + + // For @ValueBased + private static LazyMapValues of(AbstractLazyMap outer) { + return new LazyMapValues<>(outer); + } + + } + + } + + static final class Mutexes { + + private static final Object TOMB_STONE = new Object(); + + // Filled on demand and then discarded once it is not needed anymore. + // A mutex element can only transition like so: `null` -> `new Object()` -> `TOMB_STONE` + private volatile Object[] mutexes; + // Used to detect we have computed all elements and no longer need the `mutexes` array + private volatile AtomicInteger counter; + + private Mutexes(int length) { + this.mutexes = new Object[length]; + this.counter = new AtomicInteger(length); + } + + @ForceInline + private Object acquireMutex(long offset) { + assert mutexes != null; + // Check if there already is a mutex (Object or TOMB_STONE) + final Object mutex = UNSAFE.getReferenceVolatile(mutexes, offset); + if (mutex != null) { + return mutex; + } + // Protect against racy stores of mutex candidates + final Object candidate = new Object(); + final Object witness = UNSAFE.compareAndExchangeReference(mutexes, offset, null, candidate); + return witness == null ? candidate : witness; + } + + private void releaseMutex(long offset) { + // Replace the old mutex with a tomb stone since now the old mutex can be collected. + UNSAFE.putReference(mutexes, offset, TOMB_STONE); + if (counter != null && counter.decrementAndGet() == 0) { + mutexes = null; + counter = null; + } + } + + } + + @ForceInline + private static long offsetFor(long index) { + return Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * index; + } + + @SuppressWarnings("unchecked") + private static E[] newGenericArray(int length) { + return (E[]) new Object[length]; + } + + public static List ofLazyList(int size, + IntFunction computingFunction) { + return new LazyList<>(size, computingFunction); + } + + public static Map ofLazyMap(Set keys, + Function computingFunction) { + return new LazyMap<>(keys, computingFunction); + } + + @SuppressWarnings("unchecked") + public static , V> + Map ofLazyMapWithEnumKeys(Set keys, + Function computingFunction) { + // The input set is not empty + final Class enumType = ((E) keys.iterator().next()).getDeclaringClass(); + final BitSet bitSet = new BitSet(enumType.getEnumConstants().length); + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (K t : keys) { + final int ordinal = ((E) t).ordinal(); + min = Math.min(min, ordinal); + max = Math.max(max, ordinal); + bitSet.set(ordinal); + } + final int backingSize = max - min + 1; + final IntPredicate member = ImmutableBitSetPredicate.of(bitSet); + return (Map) new LazyEnumMap<>((Set) keys, enumType, min, backingSize, member, (Function) computingFunction); + } + + @SuppressWarnings("unchecked") + static T orElseComputeSlowPath(final T[] array, + final int index, + final Mutexes mutexes, + final Object input, + final FunctionHolder functionHolder) { + final long offset = offsetFor(index); + final Object mutex = mutexes.acquireMutex(offset); + preventReentry(mutex); + synchronized (mutex) { + final T t = array[index]; // Plain semantics suffice here + if (t == null) { + final T newValue = switch (functionHolder.function()) { + case IntFunction iFun -> (T) iFun.apply((int) input); + case Function fun -> ((Function) fun).apply(input); + default -> throw new InternalError("cannot reach here"); + }; + Objects.requireNonNull(newValue); + // Reduce the counter and if it reaches zero, clear the reference + // to the underlying holder. + functionHolder.countDown(); + + // The mutex is not reentrant so we know newValue should be returned + set(array, index, mutex, newValue); + // We do not need the mutex anymore + mutexes.releaseMutex(offset); + return newValue; + } + return t; + } + } + + static void preventReentry(Object mutex) { + if (Thread.holdsLock(mutex)) { + throw new IllegalStateException("Recursive initialization of a lazy collection is illegal"); + } + } + + static void set(T[] array, int index, Object mutex, T newValue) { + assert Thread.holdsLock(mutex) : index + "didn't hold " + mutex; + // We know we hold the monitor here so plain semantic is enough + // This is an extra safety net to emulate a CAS op. + if (array[index] == null) { + UNSAFE.putReferenceRelease(array, Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * (long) index, newValue); + } + } + + /** + * This class is thread safe. Any thread can create and use an instance of this class at + * any time. The `function` field is only accessed if `counter` is positive so the setting + * of function to `null` is safe. + * + * @param the underlying function type + */ + @AOTSafeClassInitializer + static final class FunctionHolder { + + private static final long COUNTER_OFFSET = UNSAFE.objectFieldOffset(FunctionHolder.class, "counter"); + + // This field can only transition at most once from being set to a + // non-null reference to being `null`. Once `null`, it is never read. + private U function; + // Used reflectively via Unsafe + private int counter; + + public FunctionHolder(U function, int counter) { + this.function = (counter == 0) ? null : function; + this.counter = counter; + // Safe publication + UNSAFE.storeStoreFence(); + } + + @ForceInline + public U function() { + return function; + } + + public void countDown() { + if (UNSAFE.getAndAddInt(this, COUNTER_OFFSET, -1) == 1) { + // Do not reference the underlying function anymore so it can be collected. + function = null; + } + } + } + +} diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java index 32430298363ee..6ab80b83ef86a 100644 --- a/src/java.base/share/classes/java/util/List.java +++ b/src/java.base/share/classes/java/util/List.java @@ -25,6 +25,11 @@ package java.util; +import jdk.internal.foreign.Utils; +import jdk.internal.javac.PreviewFeature; + +import java.io.Serializable; +import java.util.function.IntFunction; import java.util.function.UnaryOperator; /** @@ -87,9 +92,9 @@ * interface. * *

Unmodifiable Lists

- *

The {@link List#of(Object...) List.of} and - * {@link List#copyOf List.copyOf} static factory methods - * provide a convenient way to create unmodifiable lists. The {@code List} + *

The {@link List#of(Object...) List.of}, + * {@link List#copyOf List.copyOf}, and {@link List#ofLazy(int, IntFunction)} static + * factory methods provide a convenient way to create unmodifiable lists. The {@code List} * instances created by these methods have the following characteristics: * *

    @@ -100,7 +105,7 @@ * this may cause the List's contents to appear to change. *
  • They disallow {@code null} elements. Attempts to create them with * {@code null} elements result in {@code NullPointerException}. - *
  • They are serializable if all elements are serializable. + *
  • Unless otherwise specified, they are serializable if all elements are serializable. *
  • The order of elements in the list is the same as the order of the * provided arguments, or of the elements in the provided array. *
  • The lists and their {@link #subList(int, int) subList} views implement the @@ -1190,4 +1195,71 @@ static List of(E... elements) { static List copyOf(Collection coll) { return ImmutableCollections.listCopy(coll); } + + /** + * {@return a new lazily computed list of the provided {@code size}} + *

    + * The returned list is an {@linkplain Collection##unmodifiable unmodifiable} list + * with the provided {@code size}. The list's elements are lazily computed via the + * provided {@code computingFunction} when they are first accessed + * (e.g., via {@linkplain List#get(int) List::get}). + *

    + * The provided computing function is guaranteed to be successfully + * invoked at most once per list index, even in a multi-threaded environment. + * Competing threads accessing an element already under computation will block until + * an element is computed or the computing function completes abnormally + *

    + * If invoking the provided computing function throws an exception, it is rethrown + * to the initial caller and no value for the element is recorded. + *

    + * If the provided computing function returns {@code null}, + * a {@linkplain NullPointerException} will be thrown. Hence, just like other + * unmodifiable lists created via the {@code List::of} factories, a lazy list + * cannot contain {@code null} elements. Clients that want to use nullable elements + * can wrap elements into an {@linkplain Optional} holder. + *

    + * The elements of any {@link List#subList(int, int) subList()} or + * {@link List#reversed()} views of the returned list are also lazily computed. + *

    + * The returned list and its {@link List#subList(int, int) subList()} or + * {@link List#reversed()} views implement the {@link RandomAccess} interface. + *

    + * If the provided computing function recursively calls itself or the returned + * lazy list for the same index, an {@linkplain IllegalStateException} + * will be thrown. + *

    + * The returned list's {@linkplain Object Object methods}; + * {@linkplain Object#equals(Object) equals()}, + * {@linkplain Object#hashCode() hashCode()}, and + * {@linkplain Object#toString() toString()} methods may trigger initialization of + * one or more lazy elements. + *

    + * The returned lazy list strongly references its computing + * function used to compute elements at least so long as there are uninitialized + * elements. + *

    + * The returned List is not {@linkplain Serializable}. + * + * @implNote after all elements have been initialized successfully, the computing + * function is no longer strongly referenced and becomes eligible for + * garbage collection. + * + * @param size the size of the returned lazy list + * @param computingFunction to invoke whenever an element is first accessed + * (may not return {@code null}) + * @param the type of elements in the returned list + * @throws IllegalArgumentException if the provided {@code size} is negative. + * + * @see LazyConstant + * @since 26 + */ + @PreviewFeature(feature = PreviewFeature.Feature.LAZY_CONSTANTS) + static List ofLazy(int size, + IntFunction computingFunction) { + Utils.checkNonNegativeArgument(size, "size"); + Objects.requireNonNull(computingFunction); + // A computed list is not Serializable, so we cannot return `List.of()` if `size == 0` + return LazyCollections.ofLazyList(size, computingFunction); + } + } diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 97278cdbafa6e..54863d587827c 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -992,8 +992,8 @@ static Locale getInstance(BaseLocale baseloc, LocaleExtensions extensions) { } } - private static final Supplier> LOCALE_CACHE = - StableValue.supplier(new Supplier<>() { + private static final LazyConstant> LOCALE_CACHE = + LazyConstant.of(new Supplier<>() { @Override public ReferencedKeyMap get() { return ReferencedKeyMap.create(true, ReferencedKeyMap.concurrentHashMapSupplier()); @@ -2330,8 +2330,8 @@ public boolean equals(Object obj) { private static volatile Locale defaultDisplayLocale; private static volatile Locale defaultFormatLocale; - private final transient Supplier languageTag = - StableValue.supplier(new Supplier<>() { + private final transient LazyConstant languageTag = + LazyConstant.of(new Supplier<>() { @Override public String get() { return computeLanguageTag(); diff --git a/src/java.base/share/classes/java/util/LocaleISOData.java b/src/java.base/share/classes/java/util/LocaleISOData.java index 29e0b28be016b..c99e88bd2bd2b 100644 --- a/src/java.base/share/classes/java/util/LocaleISOData.java +++ b/src/java.base/share/classes/java/util/LocaleISOData.java @@ -30,32 +30,32 @@ // Methods and suppliers for producing ISO 639/3166 resources used by Locale. class LocaleISOData { - static final Supplier ISO_639 = - StableValue.supplier(new Supplier<>() { + static final LazyConstant ISO_639 = + LazyConstant.of(new Supplier<>() { @Override public String[] get() { return getISO2Table(isoLanguageTable); } }); - static final Supplier ISO_3166_1_ALPHA2 = - StableValue.supplier(new Supplier<>() { + static final LazyConstant ISO_3166_1_ALPHA2 = + LazyConstant.of(new Supplier<>() { @Override public String[] get() { return getISO2Table(isoCountryTable); } }); - static final Supplier> ISO_3166_1_ALPHA3 = - StableValue.supplier(new Supplier<>() { + static final LazyConstant> ISO_3166_1_ALPHA3 = + LazyConstant.of(new Supplier<>() { @Override public Set get() { return computeISO3166_1Alpha3Countries(); } }); - static final Supplier> ISO_3166_3 = - StableValue.supplier(new Supplier<>() { + static final LazyConstant> ISO_3166_3 = + LazyConstant.of(new Supplier<>() { @Override public Set get() { return Set.of(ISO3166_3); diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java index fbb41be507330..abee819069fc6 100644 --- a/src/java.base/share/classes/java/util/Map.java +++ b/src/java.base/share/classes/java/util/Map.java @@ -25,6 +25,8 @@ package java.util; +import jdk.internal.javac.PreviewFeature; + import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; @@ -114,8 +116,9 @@ * *

    Unmodifiable Maps

    *

    The {@link Map#of() Map.of}, - * {@link Map#ofEntries(Map.Entry...) Map.ofEntries}, and - * {@link Map#copyOf Map.copyOf} + * {@link Map#ofEntries(Map.Entry...) Map.ofEntries}, + * {@link Map#copyOf Map.copyOf}, and + * {@link Map#ofLazy(Set, Function)} * static factory methods provide a convenient way to create unmodifiable maps. * The {@code Map} * instances created by these methods have the following characteristics: @@ -128,7 +131,8 @@ * Map to behave inconsistently or its contents to appear to change. *

  • They disallow {@code null} keys and values. Attempts to create them with * {@code null} keys or values result in {@code NullPointerException}. - *
  • They are serializable if all keys and values are serializable. + *
  • Unless otherwise specified, they are serializable if all keys and values + * are serializable. *
  • They reject duplicate keys at creation time. Duplicate keys * passed to a static factory method result in {@code IllegalArgumentException}. *
  • The iteration order of mappings is unspecified and is subject to change. @@ -1746,4 +1750,79 @@ static Map copyOf(Map map) { return (Map)Map.ofEntries(map.entrySet().toArray(new Entry[0])); } } + + /** + * {@return a new lazily computed map with the provided {@code keys}} + *

    + * The returned map is an {@linkplain Collection##unmodifiable unmodifiable} map whose + * keys are known at construction. The map's values are lazily computed via the + * provided {@code computingFunction} when they are first accessed + * (e.g., via {@linkplain Map#get(Object) Map::get}). + *

    + * The provided computing function is guaranteed to be successfully invoked + * at most once per key, even in a multi-threaded environment. Competing + * threads accessing a value already under computation will block until an element + * is computed or the computing function completes abnormally. + *

    + * If invoking the provided computing function throws an exception, it + * is rethrown to the initial caller and no value associated with the provided key + * is recorded. + *

    + * If the provided computing function returns {@code null}, + * a {@linkplain NullPointerException} will be thrown. Hence, just like other + * unmodifiable maps created via the {@code Map::of} factories, a lazy map + * cannot contain {@code null} values. Clients that want to use nullable values can + * wrap values into an {@linkplain Optional} holder. + *

    + * The values of any {@link Map#values()} or {@link Map#entrySet()} views of + * the returned map are also lazily computed. + *

    + * If the provided computing function recursively calls itself or + * the returned lazy map for the same key, an {@linkplain IllegalStateException} + * will be thrown. + *

    + * The returned map's {@linkplain Object Object methods}; + * {@linkplain Object#equals(Object) equals()}, + * {@linkplain Object#hashCode() hashCode()}, and + * {@linkplain Object#toString() toString()} methods may trigger initialization of + * one or more lazy elements. + *

    + * The returned lazy map strongly references its underlying + * computing function used to compute values at least so long as there are + * uncomputed values. + *

    + * The returned Map is not {@linkplain Serializable}. + * + * @implNote after all values have been initialized successfully, the computing + * function is no longer strongly referenced and becomes eligible for + * garbage collection. + * + * @param keys the (non-null) keys in the returned computed map + * @param computingFunction to invoke whenever an associated value is first accessed + * @param the type of keys maintained by the returned map + * @param the type of mapped values in the returned map + * @throws NullPointerException if the provided set of {@code keys} is {@code null} + * or if the set of {@code keys} contains a {@code null} element. + * + * @see LazyConstant + * @since 26 + */ + @PreviewFeature(feature = PreviewFeature.Feature.LAZY_CONSTANTS) + static Map ofLazy(Set keys, + Function computingFunction) { + // Protect against TOC-TOU attacks. + // Also, implicit null check of `keys` and all its elements + final Set keyCopies = Set.copyOf(keys); + Objects.requireNonNull(computingFunction); + // We need to check the instance type using the original `keys` parameter. + if (keys instanceof EnumSet && !keyCopies.isEmpty()) { + @SuppressWarnings("unchecked") + var enumMap = (Map) LazyCollections.ofLazyMapWithEnumKeys(keyCopies, computingFunction); + return enumMap; + } else { + // A computed map is not Serializable, so we cannot return `Map.of()` if `keys.isEmpty()` + return LazyCollections.ofLazyMap(keyCopies, computingFunction); + } + } + } diff --git a/src/java.base/share/classes/java/util/Optional.java b/src/java.base/share/classes/java/util/Optional.java index e19dde6383ea2..3e577bd379cd5 100644 --- a/src/java.base/share/classes/java/util/Optional.java +++ b/src/java.base/share/classes/java/util/Optional.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -22,8 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.util; +import jdk.internal.vm.annotation.Stable; + import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -68,6 +71,7 @@ public final class Optional { /** * If non-null, the value; if null, indicates no value is present */ + @Stable private final T value; /** diff --git a/src/java.base/share/classes/java/util/ResourceBundle.java b/src/java.base/share/classes/java/util/ResourceBundle.java index b375a8ba94134..db19eda639929 100644 --- a/src/java.base/share/classes/java/util/ResourceBundle.java +++ b/src/java.base/share/classes/java/util/ResourceBundle.java @@ -488,7 +488,7 @@ public String getBaseBundleName() { /** * A Set of the keys contained only in this ResourceBundle. */ - private final Supplier> keySet = StableValue.supplier( + private final LazyConstant> keySet = LazyConstant.of( new Supplier<>() { public Set get() { return keySet0(); }}); private Set keySet0() { diff --git a/src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java index cac8785b158dd..5e7647d6106f4 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java @@ -26,14 +26,8 @@ package jdk.internal.access; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.function.IntFunction; public interface JavaUtilCollectionAccess { List listFromTrustedArray(Object[] array); List listFromTrustedArrayNullsAllowed(Object[] array); - List stableList(int size, IntFunction mapper); - Map stableMap(Set keys, Function mapper); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/CaptureStateUtil.java b/src/java.base/share/classes/jdk/internal/foreign/CaptureStateUtil.java index 2f3ba7d9e05b1..b3a19340fb547 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/CaptureStateUtil.java +++ b/src/java.base/share/classes/jdk/internal/foreign/CaptureStateUtil.java @@ -38,6 +38,8 @@ import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -57,7 +59,7 @@ public final class CaptureStateUtil { // of combinators needed to form an adapted method handle. // The function is lazily computed. // - private static final Function SEGMENT_EXTRACTION_HANDLE_CACHE; + private static final Map SEGMENT_EXTRACTION_HANDLE_CACHE; static { final Set inputs = new HashSet<>(); @@ -77,7 +79,7 @@ public MethodHandle apply(SegmentExtractorKey basicKey) { } }; - SEGMENT_EXTRACTION_HANDLE_CACHE = StableValue.function(inputs, segmentExtractionHandle); + SEGMENT_EXTRACTION_HANDLE_CACHE = Map.ofLazy(inputs, segmentExtractionHandle); } // A key that holds both the `returnType` and the `stateName` needed to look up a @@ -188,7 +190,10 @@ public static MethodHandle adaptSystemCall(MethodHandle target, final SegmentExtractorKey key = new SegmentExtractorKey(target, stateName); // ((int | long), MemorySegment)(int | long) - final MethodHandle segmentExtractor = SEGMENT_EXTRACTION_HANDLE_CACHE.apply(key); + final MethodHandle segmentExtractor = SEGMENT_EXTRACTION_HANDLE_CACHE.get(key); + if (segmentExtractor == null) { + throw new IllegalArgumentException("Input not allowed: " + key); + } // Make `target` specific adaptations of the basic handle @@ -208,7 +213,7 @@ public static MethodHandle adaptSystemCall(MethodHandle target, // Use an `Arena` for the first argument instead and extract a segment from it. // (C0=Arena, C1-Cn)(int|long) - innerAdapted = MethodHandles.collectArguments(innerAdapted, 0, HANDLES_CACHE.apply(ALLOCATE)); + innerAdapted = MethodHandles.collectArguments(innerAdapted, 0, HANDLES_CACHE.get(ALLOCATE)); // Add an identity function for the result of the cleanup action. // ((int|long))(int|long) @@ -221,7 +226,7 @@ public static MethodHandle adaptSystemCall(MethodHandle target, // cleanup action and invoke `Arena::close` when it is run. The `cleanup` handle // does not have to have all parameters. It can have zero or more. // (Throwable, (int|long), Arena)(int|long) - cleanup = MethodHandles.collectArguments(cleanup, 2, HANDLES_CACHE.apply(ARENA_CLOSE)); + cleanup = MethodHandles.collectArguments(cleanup, 2, HANDLES_CACHE.get(ARENA_CLOSE)); // Combine the `innerAdapted` and `cleanup` action into a try/finally block. // (Arena, C1-Cn)(int|long) @@ -230,7 +235,7 @@ public static MethodHandle adaptSystemCall(MethodHandle target, // Acquire the arena from the global pool. // With this, we finally arrive at the intended method handle: // (C1-Cn)(int|long) - return MethodHandles.collectArguments(tryFinally, 0, HANDLES_CACHE.apply(ACQUIRE_ARENA)); + return MethodHandles.collectArguments(tryFinally, 0, HANDLES_CACHE.get(ACQUIRE_ARENA)); } private static MethodHandle makeSegmentExtractionHandle(SegmentExtractorKey segmentExtractorKey) { @@ -260,15 +265,15 @@ private static MethodHandle makeSegmentExtractionHandle(SegmentExtractorKey segm if (segmentExtractorKey.returnType().equals(int.class)) { // (int, MemorySegment)int return MethodHandles.guardWithTest( - HANDLES_CACHE.apply(NON_NEGATIVE_INT), - HANDLES_CACHE.apply(SUCCESS_INT), - HANDLES_CACHE.apply(ERROR_INT).bindTo(intExtractor)); + HANDLES_CACHE.get(NON_NEGATIVE_INT), + HANDLES_CACHE.get(SUCCESS_INT), + HANDLES_CACHE.get(ERROR_INT).bindTo(intExtractor)); } else { // (long, MemorySegment)long return MethodHandles.guardWithTest( - HANDLES_CACHE.apply(NON_NEGATIVE_LONG), - HANDLES_CACHE.apply(SUCCESS_LONG), - HANDLES_CACHE.apply(ERROR_LONG).bindTo(intExtractor)); + HANDLES_CACHE.get(NON_NEGATIVE_LONG), + HANDLES_CACHE.get(SUCCESS_LONG), + HANDLES_CACHE.get(ERROR_LONG).bindTo(intExtractor)); } } @@ -341,8 +346,8 @@ public MethodHandle apply(int value) { } }; - private static final IntFunction HANDLES_CACHE = - StableValue.intFunction(ARENA_CLOSE + 1, UNDERLYING_MAKE_HANDLE); + private static final List HANDLES_CACHE = + List.ofLazy(ARENA_CLOSE + 1, UNDERLYING_MAKE_HANDLE); private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 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 820dfe90073ce..a6f4174324ec8 100644 --- a/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java +++ b/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java @@ -39,6 +39,7 @@ import java.util.Locale; import java.util.Objects; import java.util.Optional; +import java.util.function.Supplier; import jdk.internal.access.SharedSecrets; import jdk.internal.util.StaticProperty; @@ -114,24 +115,29 @@ public char[] readPassword(Locale locale, String format, Object ... args) { // check if `System.console()` returns a Console instance and use it if available. Otherwise, // it should call this method to obtain a JdkConsoleImpl. This ensures only one Console // instance exists in the Java runtime. - private static final StableValue> INSTANCE = StableValue.of(); - public static Optional passwordConsole() { - return INSTANCE.orElseSet(() -> { - // If there's already a proper console, throw an exception - if (System.console() != null) { - throw new IllegalStateException("Can’t create a dedicated password " + - "console since a real console already exists"); + 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(); + } } + ); - // 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() { + return PASSWORD_CONSOLE.get(); } // Dedicated entry for sun.security.util.Password when stdout is redirected. diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index 1316b8b946fe2..5942cefa2a1d9 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -85,8 +85,8 @@ public enum Feature { //--- @JEP(number=525, title="Structured Concurrency", status="Sixth Preview") STRUCTURED_CONCURRENCY, - @JEP(number = 502, title = "Stable Values", status = "Preview") - STABLE_VALUES, + @JEP(number = 526, title = "Lazy Constants", status = "Second Preview") + LAZY_CONSTANTS, @JEP(number=524, title="PEM Encodings of Cryptographic Objects", status="Second Preview") PEM_API, diff --git a/src/java.base/share/classes/jdk/internal/lang/LazyConstantImpl.java b/src/java.base/share/classes/jdk/internal/lang/LazyConstantImpl.java new file mode 100644 index 0000000000000..59d0174c4c956 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/lang/LazyConstantImpl.java @@ -0,0 +1,173 @@ +/* + * 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 jdk.internal.lang; + +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; + +import java.util.Objects; +import java.util.function.Supplier; + +/** + * The sole implementation of the LazyConstant interface. + * + * @param type of the constant + * @implNote This implementation can be used early in the boot sequence as it does not + * rely on reflection, MethodHandles, Streams etc. + */ +@AOTSafeClassInitializer +public final class LazyConstantImpl implements LazyConstant { + + // Unsafe allows `LazyConstant` instances to be used early in the boot sequence + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + // Unsafe offset for access of the `constant` field + private static final long CONSTANT_OFFSET = + UNSAFE.objectFieldOffset(LazyConstantImpl.class, "constant"); + + // Generally, fields annotated with `@Stable` are accessed by the JVM using special + // memory semantics rules (see `parse.hpp` and `parse(1|2|3).cpp`). + // + // This field is used reflectively via Unsafe using explicit memory semantics. + // + // | Value | Meaning | + // | --------------- | -------------- | + // | `null` | Unset | + // | `other` | Set to `other` | + // + @Stable + private T constant; + + // Underlying computing function to be used to compute the `constant` field. + // The field needs to be `volatile` as a lazy constant can be + // created by one thread and computed by another thread. + // After the function is successfully invoked, the field is set to + // `null` to allow the function to be collected. + @Stable + private volatile Supplier computingFunction; + + private LazyConstantImpl(Supplier computingFunction) { + this.computingFunction = computingFunction; + } + + @ForceInline + @Override + public T get() { + final T t = getAcquire(); + return (t != null) ? t : getSlowPath(); + } + + private T getSlowPath() { + preventReentry(); + synchronized (this) { + T t = getAcquire(); + if (t == null) { + t = computingFunction.get(); + Objects.requireNonNull(t); + setRelease(t); + // Allow the underlying supplier to be collected after successful use + computingFunction = null; + } + return t; + } + } + + @ForceInline + @Override + public T orElse(T other) { + final T t = getAcquire(); + return (t == null) ? other : t; + } + + @ForceInline + @Override + public boolean isInitialized() { + return getAcquire() != null; + } + + @Override + public String toString() { + return super.toString() + "[" + toStringSuffix() + "]"; + } + + private String toStringSuffix() { + final T t = getAcquire(); + if (t == this) { + return "(this LazyConstant)"; + } else if (t != null) { + return t.toString(); + } + // Volatile read + final Supplier cf = computingFunction; + // There could be a race here + if (cf != null) { + return "computing function=" + computingFunction.toString(); + } + // As we know `computingFunction` is `null` via a volatile read, we + // can now be sure that this lazy constant is initialized + return getAcquire().toString(); + } + + + // Discussion on the memory semantics used. + // ---------------------------------------- + // Using acquire/release semantics on the `constant` field is the cheapest way to + // establish a happens-before (HB) relation between load and store operations. Every + // implementation of a method defined in the interface `LazyConstant` except + // `equals()` starts with a load of the `constant` field using acquire semantics. + // + // If the underlying supplier was guaranteed to always create a new object, + // a fence after creation and subsequent plain loads would suffice to ensure + // new objects' state are always correctly observed. However, no such restriction is + // imposed on the underlying supplier. Hence, the docs state there should be an + // HB relation meaning we will have to pay a price (on certain platforms) on every + // `get()` operation that is not constant-folded. + + @SuppressWarnings("unchecked") + @ForceInline + private T getAcquire() { + return (T) UNSAFE.getReferenceAcquire(this, CONSTANT_OFFSET); + } + + private void setRelease(T newValue) { + UNSAFE.putReferenceRelease(this, CONSTANT_OFFSET, newValue); + } + + private void preventReentry() { + if (Thread.holdsLock(this)) { + throw new IllegalStateException("Recursive invocation of a LazyConstant's computing function: " + computingFunction); + } + } + + // Factory + + public static LazyConstantImpl ofLazy(Supplier computingFunction) { + return new LazyConstantImpl<>(computingFunction); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableEnumFunction.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableEnumFunction.java deleted file mode 100644 index d6893438be5de..0000000000000 --- a/src/java.base/share/classes/jdk/internal/lang/stable/StableEnumFunction.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 jdk.internal.lang.stable; - -import jdk.internal.util.ImmutableBitSetPredicate; -import jdk.internal.vm.annotation.ForceInline; -import jdk.internal.vm.annotation.Stable; - -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.function.IntPredicate; -import java.util.function.Supplier; - -/** - * Optimized implementation of a stable Function with enums as keys. - * - * @implNote This implementation can be used early in the boot sequence as it does not - * rely on reflection, MethodHandles, Streams etc. - * - * @param enumType the class type of the Enum - * @param firstOrdinal the lowest ordinal used - * @param member an int predicate that can be used to test if an enum is a member - * of the valid inputs (as there might be "holes") - * @param delegates a delegate array of inputs to StableValue mappings - * @param original the original Function - * @param the type of the input to the function - * @param the type of the result of the function - */ -public record StableEnumFunction, R>(Class enumType, - int firstOrdinal, - IntPredicate member, - @Stable StableValueImpl[] delegates, - Function original) implements Function { - @ForceInline - @Override - public R apply(E value) { - if (!member.test(value.ordinal())) { // Implicit null-check of value - throw new IllegalArgumentException("Input not allowed: " + value); - } - final int index = value.ordinal() - firstOrdinal; - // Since we did the member.test above, we know the index is in bounds - return delegates[index].orElseSet(new Supplier() { - @Override public R get() { return original.apply(value); }}); - - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - @Override - public boolean equals(Object obj) { - return obj == this; - } - - @Override - public String toString() { - final Collection>> entries = new ArrayList<>(delegates.length); - final E[] enumElements = enumType.getEnumConstants(); - int ordinal = firstOrdinal; - for (int i = 0; i < delegates.length; i++, ordinal++) { - if (member.test(ordinal)) { - entries.add(new AbstractMap.SimpleImmutableEntry<>(enumElements[ordinal], delegates[i])); - } - } - return StableUtil.renderMappings(this, "StableFunction", entries, true); - } - - @SuppressWarnings("unchecked") - public static , R> Function of(Set inputs, - Function original) { - // The input set is not empty - final Class enumType = ((E) inputs.iterator().next()).getDeclaringClass(); - final BitSet bitSet = new BitSet(enumType.getEnumConstants().length); - int min = Integer.MAX_VALUE; - int max = Integer.MIN_VALUE; - for (T t : inputs) { - final int ordinal = ((E) t).ordinal(); - min = Math.min(min, ordinal); - max = Math.max(max, ordinal); - bitSet.set(ordinal); - } - final int size = max - min + 1; - final IntPredicate member = ImmutableBitSetPredicate.of(bitSet); - return (Function) new StableEnumFunction(enumType, min, member, StableUtil.array(size), (Function) original); - } - -} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableFunction.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableFunction.java deleted file mode 100644 index e36b4e9b25a0a..0000000000000 --- a/src/java.base/share/classes/jdk/internal/lang/stable/StableFunction.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 jdk.internal.lang.stable; - -import jdk.internal.vm.annotation.ForceInline; - -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; - -// Note: It would be possible to just use `StableMap::get` with some additional logic -// instead of this class but explicitly providing a class like this provides better -// debug capability, exception handling, and may provide better performance. -/** - * Implementation of a stable Function. - * - * @implNote This implementation can be used early in the boot sequence as it does not - * rely on reflection, MethodHandles, Streams etc. - * - * @param values a delegate map of inputs to StableValue mappings - * @param original the original Function - * @param the type of the input to the function - * @param the type of the result of the function - */ -public record StableFunction(Map> values, - Function original) implements Function { - - @ForceInline - @Override - public R apply(T value) { - final StableValueImpl stable = values.get(value); - if (stable == null) { - throw new IllegalArgumentException("Input not allowed: " + value); - } - return stable.orElseSet(new Supplier() { - @Override public R get() { return original.apply(value); }}); - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - @Override - public boolean equals(Object obj) { - return obj == this; - } - - @Override - public String toString() { - return StableUtil.renderMappings(this, "StableFunction", values.entrySet(), true); - } - - public static StableFunction of(Set inputs, - Function original) { - return new StableFunction<>(StableUtil.map(inputs), original); - } - -} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableIntFunction.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableIntFunction.java deleted file mode 100644 index a921a4de87b2d..0000000000000 --- a/src/java.base/share/classes/jdk/internal/lang/stable/StableIntFunction.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 jdk.internal.lang.stable; - -import jdk.internal.vm.annotation.ForceInline; -import jdk.internal.vm.annotation.Stable; - -import java.util.function.IntFunction; -import java.util.function.Supplier; - -/** - * Implementation of a stable IntFunction. - *

    - * For performance reasons (~10%), we are not delegating to a StableList but are using - * the more primitive functions in StableValueUtil that are shared with StableList/StableValueImpl. - * - * @implNote This implementation can be used early in the boot sequence as it does not - * rely on reflection, MethodHandles, Streams etc. - * - * @param the return type - */ -public record StableIntFunction(@Stable StableValueImpl[] delegates, - IntFunction original) implements IntFunction { - - @ForceInline - @Override - public R apply(int index) { - final StableValueImpl delegate; - try { - delegate = delegates[index]; - } catch (ArrayIndexOutOfBoundsException ioob) { - throw new IllegalArgumentException("Input not allowed: " + index, ioob); - } - return delegate.orElseSet(new Supplier() { - @Override public R get() { return original.apply(index); }}); - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - @Override - public boolean equals(Object obj) { - return obj == this; - } - - @Override - public String toString() { - return StableUtil.renderElements(this, "StableIntFunction", delegates); - } - - public static StableIntFunction of(int size, IntFunction original) { - return new StableIntFunction<>(StableUtil.array(size), original); - } - -} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableSupplier.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableSupplier.java deleted file mode 100644 index 631a41c5710ef..0000000000000 --- a/src/java.base/share/classes/jdk/internal/lang/stable/StableSupplier.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 jdk.internal.lang.stable; - -import jdk.internal.vm.annotation.ForceInline; - -import java.util.function.Supplier; - -/** - * Implementation of a stable supplier. - *

    - * @implNote This implementation can be used early in the boot sequence as it does not - * rely on reflection, MethodHandles, Streams etc. - * - * @param the return type - */ -public record StableSupplier(StableValueImpl delegate, - Supplier original) implements Supplier { - - @ForceInline - @Override - public T get() { - return delegate.orElseSet(original); - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - @Override - public boolean equals(Object obj) { - return obj == this; - } - - @Override - public String toString() { - final Object t = delegate.wrappedContentsAcquire(); - return t == this ? "(this StableSupplier)" : StableValueImpl.renderWrapped(t); - } - - public static StableSupplier of(Supplier original) { - return new StableSupplier<>(StableValueImpl.of(), original); - } - -} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableUtil.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableUtil.java deleted file mode 100644 index 74104ddbb4951..0000000000000 --- a/src/java.base/share/classes/jdk/internal/lang/stable/StableUtil.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 jdk.internal.lang.stable; - -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; - -public final class StableUtil { - - private StableUtil() {} - - public static String renderElements(Object self, - String selfName, - StableValueImpl[] delegates) { - return renderElements(self, selfName, delegates, 0, delegates.length); - } - - public static String renderElements(Object self, - String selfName, - StableValueImpl[] delegates, - int offset, - int length) { - final StringJoiner sj = new StringJoiner(", ", "[", "]"); - for (int i = 0; i < length; i++) { - final Object value = delegates[i + offset].wrappedContentsAcquire(); - if (value == self) { - sj.add("(this " + selfName + ")"); - } else { - sj.add(StableValueImpl.renderWrapped(value)); - } - } - return sj.toString(); - } - - public static String renderMappings(Object self, - String selfName, - Iterable>> delegates, - boolean curly) { - final StringJoiner sj = new StringJoiner(", ", curly ? "{" : "[", curly ? "}" : "]"); - for (var e : delegates) { - final Object value = e.getValue().wrappedContentsAcquire(); - final String valueString; - if (value == self) { - valueString = "(this " + selfName + ")"; - } else { - valueString = StableValueImpl.renderWrapped(value); - } - sj.add(e.getKey() + "=" + valueString); - } - return sj.toString(); - } - - public static StableValueImpl[] array(int size) { - assertSizeNonNegative(size); - @SuppressWarnings("unchecked") - final var stableValues = (StableValueImpl[]) new StableValueImpl[size]; - for (int i = 0; i < size; i++) { - stableValues[i] = StableValueImpl.of(); - } - return stableValues; - } - - public static Map> map(Set keys) { - Objects.requireNonNull(keys); - @SuppressWarnings("unchecked") - final var entries = (Map.Entry>[]) new Map.Entry[keys.size()]; - int i = 0; - for (K key : keys) { - entries[i++] = Map.entry(key, StableValueImpl.of()); - } - return Map.ofEntries(entries); - } - - public static void assertSizeNonNegative(int size) { - if (size < 0) { - throw new IllegalArgumentException("size can not be negative: " + size); - } - } - -} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableValueImpl.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableValueImpl.java deleted file mode 100644 index 1413fd7446e4f..0000000000000 --- a/src/java.base/share/classes/jdk/internal/lang/stable/StableValueImpl.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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 jdk.internal.lang.stable; - -import jdk.internal.misc.Unsafe; -import jdk.internal.vm.annotation.DontInline; -import jdk.internal.vm.annotation.ForceInline; -import jdk.internal.vm.annotation.Stable; - -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.function.Supplier; - -/** - * The implementation of StableValue. - * - * @implNote This implementation can be used early in the boot sequence as it does not - * rely on reflection, MethodHandles, Streams etc. - * - * @param type of the contents - */ -public final class StableValueImpl implements StableValue { - - static final String UNSET_LABEL = ".unset"; - - // Unsafe allows StableValue to be used early in the boot sequence - static final Unsafe UNSAFE = Unsafe.getUnsafe(); - - // Unsafe offsets for direct field access - - private static final long CONTENTS_OFFSET = - UNSAFE.objectFieldOffset(StableValueImpl.class, "contents"); - - // Used to indicate a holder value is `null` (see field `contents` below) - private static final Object NULL_SENTINEL = new Object(); - - // Generally, fields annotated with `@Stable` are accessed by the JVM using special - // memory semantics rules (see `parse.hpp` and `parse(1|2|3).cpp`). - // - // This field is used directly and reflectively via Unsafe using explicit memory semantics. - // - // | Value | Meaning | - // | -------------- | ------------ | - // | null | Unset | - // | NULL_SENTINEL | Set(null) | - // | other | Set(other) | - // - @Stable - private Object contents; - - // Only allow creation via the factory `StableValueImpl::newInstance` - private StableValueImpl() {} - - @ForceInline - @Override - public boolean trySet(T contents) { - if (wrappedContentsAcquire() != null) { - return false; - } - // Prevent reentry via an orElseSet(supplier) - preventReentry(); - // Mutual exclusion is required here as `orElseSet` might also - // attempt to modify `this.contents` - synchronized (this) { - return wrapAndSet(contents); - } - } - - @ForceInline - @Override - public void setOrThrow(T contents) { - if (!trySet(contents)) { - // Neither the set contents nor the provided contents is revealed in the - // exception message as it might be sensitive. - throw new IllegalStateException("The contents is already set"); - } - } - - @ForceInline - @Override - public T orElseThrow() { - final Object t = wrappedContentsAcquire(); - if (t == null) { - throw new NoSuchElementException("No contents set"); - } - return unwrap(t); - } - - @ForceInline - @Override - public T orElse(T other) { - final Object t = wrappedContentsAcquire(); - return (t == null) ? other : unwrap(t); - } - - @ForceInline - @Override - public boolean isSet() { - return wrappedContentsAcquire() != null; - } - - @ForceInline - @Override - public T orElseSet(Supplier supplier) { - Objects.requireNonNull(supplier); - final Object t = wrappedContentsAcquire(); - return (t == null) ? orElseSetSlowPath(supplier) : unwrap(t); - } - - @DontInline - private T orElseSetSlowPath(Supplier supplier) { - preventReentry(); - synchronized (this) { - final Object t = contents; // Plain semantics suffice here - if (t == null) { - final T newValue = supplier.get(); - // The mutex is not reentrant so we know newValue should be returned - wrapAndSet(newValue); - return newValue; - } - return unwrap(t); - } - } - - // The methods equals() and hashCode() should be based on identity (defaults from Object) - - @Override - public String toString() { - final Object t = wrappedContentsAcquire(); - return t == this - ? "(this StableValue)" - : renderWrapped(t); - } - - // Internal methods shared with other internal classes - - @ForceInline - public Object wrappedContentsAcquire() { - return UNSAFE.getReferenceAcquire(this, CONTENTS_OFFSET); - } - - static String renderWrapped(Object t) { - return (t == null) ? UNSET_LABEL : Objects.toString(unwrap(t)); - } - - // Private methods - - // This method is not annotated with @ForceInline as it is always called - // in a slow path. - private void preventReentry() { - if (Thread.holdsLock(this)) { - throw new IllegalStateException("Recursive initialization of a stable value is illegal"); - } - } - - /** - * Wraps the provided {@code newValue} and tries to set the contents. - *

    - * This method ensures the {@link Stable} field is written to at most once. - * - * @param newValue to wrap and set - * @return if the contents was set - */ - @ForceInline - private boolean wrapAndSet(T newValue) { - assert Thread.holdsLock(this); - // We know we hold the monitor here so plain semantic is enough - if (contents == null) { - UNSAFE.putReferenceRelease(this, CONTENTS_OFFSET, wrap(newValue)); - return true; - } - return false; - } - - - // Wraps `null` values into a sentinel value - @ForceInline - private static Object wrap(Object t) { - return (t == null) ? NULL_SENTINEL : t; - } - - // Unwraps null sentinel values into `null` - @SuppressWarnings("unchecked") - @ForceInline - private static T unwrap(Object t) { - return t != NULL_SENTINEL ? (T) t : null; - } - - // Factory - - public static StableValueImpl of() { - return new StableValueImpl<>(); - } - -} diff --git a/src/java.base/share/classes/sun/nio/ch/Net.java b/src/java.base/share/classes/sun/nio/ch/Net.java index 9ec7975a35cd3..4564579d05465 100644 --- a/src/java.base/share/classes/sun/nio/ch/Net.java +++ b/src/java.base/share/classes/sun/nio/ch/Net.java @@ -48,6 +48,8 @@ import java.nio.channels.UnsupportedAddressTypeException; import java.util.Enumeration; import java.util.Objects; +import java.lang.LazyConstant; +import java.util.function.Supplier; import sun.net.ext.ExtendedSocketOptions; import sun.net.util.IPAddressUtil; @@ -94,13 +96,14 @@ static boolean useExclusiveBind() { return EXCLUSIVE_BIND; } - private static final StableValue SHUTDOWN_WRITE_BEFORE_CLOSE = StableValue.of(); + private static final LazyConstant SHUTDOWN_WRITE_BEFORE_CLOSE = LazyConstant.of(new Supplier() { + @Override public Boolean get() { return shouldShutdownWriteBeforeClose0(); }}); /** * Tells whether a TCP connection should be shutdown for writing before closing. */ static boolean shouldShutdownWriteBeforeClose() { - return SHUTDOWN_WRITE_BEFORE_CLOSE.orElseSet(Net::shouldShutdownWriteBeforeClose0); + return SHUTDOWN_WRITE_BEFORE_CLOSE.get(); } /** diff --git a/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/src/java.base/share/classes/sun/util/locale/BaseLocale.java index 529ca1b0c1310..91efc61d1bfd9 100644 --- a/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -92,8 +92,8 @@ public final class BaseLocale { } // Interned BaseLocale cache - private static final Supplier> CACHE = - StableValue.supplier(new Supplier<>() { + private static final LazyConstant> CACHE = + LazyConstant.of(new Supplier<>() { @Override public ReferencedKeySet get() { return ReferencedKeySet.create(true, ReferencedKeySet.concurrentHashMapSupplier()); diff --git a/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java b/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java index d7c575962f627..49e882c5ca64e 100644 --- a/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java +++ b/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java @@ -50,7 +50,7 @@ public abstract class BreakIteratorResourceBundle extends ResourceBundle { // those keys must be added to NON_DATA_KEYS. private static final Set NON_DATA_KEYS = Set.of("BreakIteratorClasses"); - private final Supplier> keys = StableValue.supplier( + private final LazyConstant> keys = LazyConstant.of( new Supplier<>() { public Set get() { return keys0(); }}); private Set keys0() { diff --git a/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java b/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java index 0c16f50801875..9d0c1c015ce3f 100644 --- a/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java +++ b/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java @@ -119,7 +119,7 @@ protected Set createSet() { return new HashSet<>(); } - private final Supplier> lookup = StableValue.supplier( + private final LazyConstant> lookup = LazyConstant.of( new Supplier<>() { public Map get() { return lookup0(); }}); private Map lookup0() { @@ -134,7 +134,7 @@ private Map lookup0() { return temp; } - private final Supplier> keyset = StableValue.supplier( + private final LazyConstant> keyset = LazyConstant.of( new Supplier<>() { public Set get() { return keyset0(); }}); private Set keyset0() { diff --git a/test/jdk/java/lang/LazyConstant/DemoContainerInjectionTest.java b/test/jdk/java/lang/LazyConstant/DemoContainerInjectionTest.java new file mode 100644 index 0000000000000..df51a56221077 --- /dev/null +++ b/test/jdk/java/lang/LazyConstant/DemoContainerInjectionTest.java @@ -0,0 +1,153 @@ +/* + * 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 + * @summary Basic tests for dependency injection + * @enablePreview + * @run junit DemoContainerInjectionTest + */ + +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +final class DemoContainerInjectionTest { + + interface Foo{} + interface Bar{}; + static class FooImpl implements Foo{}; + static class BarImpl implements Bar{}; + + // Provides a type safe way of associating a type to a supplier + record Provider(Class type, Supplier supplier){} + + @Test + void ComputedComponentsViaLambda() { + Container container = ComputedContainer.of(Set.of(Foo.class, Bar.class), t -> switch (t) { + case Class c when c.equals(Foo.class) -> new FooImpl(); + case Class c when c.equals(Bar.class) -> new BarImpl(); + default -> throw new IllegalArgumentException(); + }); + assertContainerPopulated(container); + } + + @Test + void SettableComponents() { + SettableContainer container = SettableScratchContainer.of(Set.of(Foo.class, Bar.class)); + container.set(Foo.class, new FooImpl()); + container.set(Bar.class, new BarImpl()); + assertContainerPopulated(container); + } + + + @Test + void ProviderComponents() { + Container container = ProviderContainer.of(Map.of( + Foo.class, FooImpl::new, + Bar.class, BarImpl::new)); + assertContainerPopulated(container); + } + + @Test + void ProviderTypedComponents() { + Container container = providerTypedContainer(Set.of( + new Provider<>(Foo.class, FooImpl::new), + new Provider<>(Bar.class, BarImpl::new) + )); + assertContainerPopulated(container); + } + + private static void assertContainerPopulated(Container container) { + assertInstanceOf(FooImpl.class, container.get(Foo.class)); + assertInstanceOf(BarImpl.class, container.get(Bar.class)); + } + + interface Container { + T get(Class type); + } + + interface SettableContainer extends Container { + void set(Class type, T implementation); + } + + record ComputedContainer(Map, ?> components) implements Container { + + @Override + public T get(Class type) { + return type.cast(components.get(type)); + } + + static Container of(Set> components, Function, ?> mapper) { + return new ComputedContainer(Map.ofLazy(components, mapper)); + } + + } + + record SettableScratchContainer(Map, Object> scratch, Map, ?> components) implements SettableContainer { + + @Override + public void set(Class type, T implementation) { + if (scratch.putIfAbsent(type, type.cast(implementation)) != null) { + throw new IllegalStateException("Can only set once for " + type); + } + } + + @Override + public T get(Class type) { + return type.cast(components.get(type)); + } + + static SettableContainer of(Set> components) { + Map, Object> scratch = new ConcurrentHashMap<>(); + return new SettableScratchContainer(scratch, Map.ofLazy(components, scratch::get)); + } + + } + + record ProviderContainer(Map, ?> components) implements Container { + + @Override + public T get(Class type) { + return type.cast(components.get(type)); + } + + static Container of(Map, Supplier> components) { + var map = Map.ofLazy(components.keySet(), t -> components.get(t).get()); + return new ProviderContainer(map); + } + + } + + static Container providerTypedContainer(Set> providers) { + return ProviderContainer.of(providers.stream() + .collect(Collectors.toMap(Provider::type, Provider::supplier))); + } + +} diff --git a/test/jdk/java/lang/LazyConstant/DemoImperativeTest.java b/test/jdk/java/lang/LazyConstant/DemoImperativeTest.java new file mode 100644 index 0000000000000..bc1208e67f881 --- /dev/null +++ b/test/jdk/java/lang/LazyConstant/DemoImperativeTest.java @@ -0,0 +1,83 @@ +/* + * 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 + * @summary Test of a demo of an imperative stable value based on a lazy constant + * @enablePreview + * @run junit DemoImperativeTest + */ + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.*; + +final class DemoImperativeTest { + + interface ImperativeStableValue { + T orElse(T other); + boolean isSet(); + boolean trySet(T t); + T get(); + + static ImperativeStableValue of() { + var scratch = new AtomicReference(); + return new Impl<>(scratch, LazyConstant.of(scratch::get)); + } + + } + + + private record Impl(AtomicReference scratch, + LazyConstant underlying) implements ImperativeStableValue { + + @Override + public boolean trySet(T t) { + final boolean result = scratch.compareAndSet(null, t); + if (result) { + // Actually set the value + get(); + } + return result; + } + + @Override public T orElse(T other) { return underlying.orElse(other); } + @Override public boolean isSet() { return underlying.isInitialized(); } + @Override public T get() { return underlying.get(); } + + } + + @Test + void basic() { + var stableValue = ImperativeStableValue.of(); + assertFalse(stableValue.isSet()); + assertEquals(13, stableValue.orElse(13)); + assertTrue(stableValue.trySet(42)); + assertFalse(stableValue.trySet(13)); + assertTrue(stableValue.isSet()); + assertEquals(42, stableValue.get()); + assertEquals(42, stableValue.orElse(13)); + } + +} diff --git a/test/jdk/java/lang/LazyConstant/DemoMapTest.java b/test/jdk/java/lang/LazyConstant/DemoMapTest.java new file mode 100644 index 0000000000000..25e37494cb9d7 --- /dev/null +++ b/test/jdk/java/lang/LazyConstant/DemoMapTest.java @@ -0,0 +1,119 @@ +/* + * 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 + * @summary Test of a lazy map application + * @enablePreview + * @run junit DemoMapTest + */ + +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +final class DemoMapTest { + + static class OrderController{} + + // NEW: + static final Map ORDERS + = Map.ofLazy( + Set.of("Customers", "Internal", "Testing"), + _ -> new OrderController() + ); + + public static OrderController orders() { + String groupName = Thread.currentThread().getThreadGroup().getName(); + return ORDERS.get(groupName); + } + + @Test + void orderController() throws InterruptedException { + Thread t = Thread.ofPlatform() + .group(new ThreadGroup("Customers")) + .start(() -> { + String groupName = Thread.currentThread().getThreadGroup().getName(); + OrderController orderController = ORDERS.get(groupName); + assertNotNull(orderController); + }); + t.join(); + } + + private static final Map SERVER_ERROR_PAGES = Map.ofLazy( + Set.of(500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511), + e -> { + try { + return Files.readString(Path.of("server_error_" + e + ".html")); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + }); + + private static String htmlServerErrorPage(int errorCode) { + return SERVER_ERROR_PAGES.get(errorCode); // Eligible for constant folding + } + + @Test + void page500() { + String page = htmlServerErrorPage(500); // Constant folds + assertEquals(DEFAULT_FS_MSG, page); + } + + static final String DEFAULT_FS_MSG = """ + + + + + Internal Server Error (500) + + + + + There was a general problem with the server, code 500 + + + """; + + @BeforeAll + public static void setup() throws IOException { + var file = Path.of("server_error_500.html"); + if (Files.notExists(file)) { + Files.createFile(file); + Files.writeString(file, DEFAULT_FS_MSG); + } + assertEquals(DEFAULT_FS_MSG, Files.readString(file)); + } + + @AfterAll + public static void cleanUp() throws IOException { + var file = Path.of("server_error_500.html"); + Files.deleteIfExists(file); + } + +} diff --git a/test/jdk/java/lang/StableValue/StableValuesSafePublicationTest.java b/test/jdk/java/lang/LazyConstant/LazyConstantSafePublicationTest.java similarity index 73% rename from test/jdk/java/lang/StableValue/StableValuesSafePublicationTest.java rename to test/jdk/java/lang/LazyConstant/LazyConstantSafePublicationTest.java index 151d6f9c805f8..4d88169b1551d 100644 --- a/test/jdk/java/lang/StableValue/StableValuesSafePublicationTest.java +++ b/test/jdk/java/lang/LazyConstant/LazyConstantSafePublicationTest.java @@ -22,10 +22,11 @@ */ /* @test - * @summary Basic tests for making sure StableValue publishes values safely + * @summary Basic tests for making sure ComputedConstant publishes values safely * @modules java.base/jdk.internal.misc + * @modules java.base/jdk.internal.lang * @enablePreview - * @run junit StableValuesSafePublicationTest + * @run junit LazyConstantSafePublicationTest */ import org.junit.jupiter.api.Test; @@ -36,30 +37,22 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.lang.LazyConstant; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; -final class StableValuesSafePublicationTest { +final class LazyConstantSafePublicationTest { private static final int SIZE = 100_000; private static final int THREADS = Runtime.getRuntime().availableProcessors(); - private static final StableValue[] STABLES = stables(); - - static StableValue[] stables() { - @SuppressWarnings("unchecked") - StableValue[] stables = (StableValue[]) new StableValue[SIZE]; - for (int i = 0; i < SIZE; i++) { - stables[i] = StableValue.of(); - } - return stables; - } static final class Holder { // These are non-final fields but should be seen - // fully initialized thanks to the HB properties of StableValue. + // fully initialized thanks to the HB properties of ComputedConstants. int a, b, c, d, e; Holder() { @@ -69,17 +62,21 @@ static final class Holder { static final class Consumer implements Runnable { + final LazyConstant[] constants; final int[] observations = new int[SIZE]; - final StableValue[] stables = STABLES; int i = 0; + public Consumer(LazyConstant[] constants) { + this.constants = constants; + } + @Override public void run() { for (; i < SIZE; i++) { - StableValue s = stables[i]; + LazyConstant s = constants[i]; Holder h; - // Wait until the StableValue has a holder value - while ((h = s.orElse(null)) == null) {} + // Wait until the ComputedConstant has a holder value + while ((h = s.orElse(null)) == null) { Thread.onSpinWait();} int a = h.a; int b = h.b; int c = h.c; @@ -92,15 +89,19 @@ public void run() { static final class Producer implements Runnable { - final StableValue[] stables = STABLES; + final LazyConstant[] constants; + + public Producer(LazyConstant[] constants) { + this.constants = constants; + } @Override public void run() { - StableValue s; + LazyConstant s; long deadlineNs = System.nanoTime(); for (int i = 0; i < SIZE; i++) { - s = stables[i]; - s.trySet(new Holder()); + s = constants[i]; + s.get(); deadlineNs += 1000; while (System.nanoTime() < deadlineNs) { Thread.onSpinWait(); @@ -110,9 +111,10 @@ public void run() { } @Test - void main() { + void mainTest() { + final LazyConstant[] constants = constants(); List consumers = IntStream.range(0, THREADS) - .mapToObj(_ -> new Consumer()) + .mapToObj(_ -> new Consumer(constants)) .toList(); List consumersThreads = IntStream.range(0, THREADS) @@ -121,14 +123,14 @@ void main() { .start(consumers.get(i))) .toList(); - Producer producer = new Producer(); + Producer producer = new Producer(constants); Thread producerThread = Thread.ofPlatform() .name("Producer Thread") .start(producer); - join(consumers, producerThread); - join(consumers, consumersThreads.toArray(Thread[]::new)); + join(constants, consumers, producerThread); + join(constants, consumers, consumersThreads.toArray(Thread[]::new)); int[] histogram = new int[64]; for (Consumer consumer : consumers) { @@ -146,7 +148,7 @@ void main() { assertEquals(THREADS * SIZE, histogram[63]); } - static void join(List consumers, Thread... threads) { + static void join(final LazyConstant[] constants, List consumers, Thread... threads) { try { for (Thread t:threads) { long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(1); @@ -163,11 +165,11 @@ static void join(List consumers, Thread... threads) { } if (System.nanoTime() > deadline) { long nonNulls = CompletableFuture.supplyAsync(() -> - Stream.of(STABLES) + Arrays.stream(constants) .map(s -> s.orElse(null)) .filter(Objects::nonNull) .count(), Executors.newSingleThreadExecutor()).join(); - fail("Giving up! Set stables seen by a new thread: " + nonNulls); + fail("Giving up! Set lazy constants seen by a new thread: " + nonNulls); } } } @@ -176,4 +178,13 @@ static void join(List consumers, Thread... threads) { } } + static LazyConstant[] constants() { + @SuppressWarnings("unchecked") + LazyConstant[] constants = (LazyConstant[]) new LazyConstant[SIZE]; + for (int i = 0; i < SIZE; i++) { + constants[i] = LazyConstant.of(Holder::new); + } + return constants; + } + } diff --git a/test/jdk/java/lang/LazyConstant/LazyConstantTest.java b/test/jdk/java/lang/LazyConstant/LazyConstantTest.java new file mode 100644 index 0000000000000..af194cbcc0138 --- /dev/null +++ b/test/jdk/java/lang/LazyConstant/LazyConstantTest.java @@ -0,0 +1,236 @@ +/* + * 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 + * @summary Basic tests for the LazyConstant implementation + * @enablePreview + * @modules java.base/jdk.internal.lang + * @run junit/othervm --add-opens java.base/jdk.internal.lang=ALL-UNNAMED LazyConstantTest + */ + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.lang.LazyConstant; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +final class LazyConstantTest { + + private static final int VALUE = 42; + private static final Supplier SUPPLIER = () -> VALUE; + + @Test + void factoryInvariants() { + assertThrows(NullPointerException.class, () -> LazyConstant.of(null)); + } + + @ParameterizedTest + @MethodSource("factories") + void basic(Function, LazyConstant> factory) { + LazyConstantTestUtil.CountingSupplier cs = new LazyConstantTestUtil.CountingSupplier<>(SUPPLIER); + var lazy = factory.apply(cs); + assertFalse(lazy.isInitialized()); + assertEquals(SUPPLIER.get(), lazy.get()); + assertEquals(1, cs.cnt()); + assertEquals(SUPPLIER.get(), lazy.get()); + assertEquals(1, cs.cnt()); + assertTrue(lazy.toString().contains(Integer.toString(SUPPLIER.get()))); + } + + @ParameterizedTest + @MethodSource("factories") + void exception(Function, LazyConstant> factory) { + LazyConstantTestUtil.CountingSupplier cs = new LazyConstantTestUtil.CountingSupplier<>(() -> { + throw new UnsupportedOperationException(); + }); + var lazy = factory.apply(cs); + assertThrows(UnsupportedOperationException.class, lazy::get); + assertEquals(1, cs.cnt()); + assertThrows(UnsupportedOperationException.class, lazy::get); + assertEquals(2, cs.cnt()); + assertTrue(lazy.toString().contains("computing function")); + } + + @ParameterizedTest + @MethodSource("lazyConstants") + void orElse(LazyConstant constant) { + assertNull(constant.orElse(null)); + constant.get(); + assertEquals(VALUE, constant.orElse(null)); + } + + @ParameterizedTest + @MethodSource("lazyConstants") + void get(LazyConstant constant) { + assertEquals(VALUE, constant.get()); + } + + @ParameterizedTest + @MethodSource("lazyConstants") + void isInitialized(LazyConstant constant) { + assertFalse(constant.isInitialized()); + constant.get(); + assertTrue(constant.isInitialized()); + } + + @ParameterizedTest + @MethodSource("lazyConstants") + void testHashCode(LazyConstant constant) { + assertEquals(System.identityHashCode(constant), constant.hashCode()); + } + + @ParameterizedTest + @MethodSource("lazyConstants") + void testEquals(LazyConstant c0) { + assertNotEquals(null, c0); + LazyConstant different = LazyConstant.of(SUPPLIER); + assertNotEquals(different, c0); + assertNotEquals(c0, different); + assertNotEquals("a", c0); + } + + @ParameterizedTest + @MethodSource("lazyConstants") + void testLazyConstantAsComputingFunction(LazyConstant constant) { + LazyConstant c1 = LazyConstant.of(constant); + assertSame(constant, c1); + } + + @Test + void toStringTest() { + Supplier supplier = () -> "str"; + LazyConstant lazy = LazyConstant.of(supplier); + var expectedSubstring = "computing function=" + supplier; + assertTrue(lazy.toString().contains(expectedSubstring)); + lazy.get(); + assertTrue(lazy.toString().contains("str")); + } + + @ParameterizedTest + @MethodSource("lazyConstants") + void toStringUnset(LazyConstant constant) { + String unInitializedToString = constant.toString(); + int suffixEnd = unInitializedToString.indexOf("["); + String suffix = unInitializedToString.substring(0, suffixEnd); + String expectedUninitialized = suffix+"[computing function="; + assertTrue(unInitializedToString.startsWith(expectedUninitialized)); + constant.get(); + String expectedInitialized = suffix + "[" + VALUE + "]"; + assertEquals(expectedInitialized, constant.toString()); + } + + @Test + void toStringCircular() { + AtomicReference> ref = new AtomicReference<>(); + LazyConstant> constant = LazyConstant.of(ref::get); + ref.set(constant); + constant.get(); + String toString = assertDoesNotThrow(constant::toString); + assertTrue(constant.toString().contains("(this LazyConstant)"), toString); + } + + @Test + void recursiveCall() { + AtomicReference> ref = new AtomicReference<>(); + LazyConstant constant = LazyConstant.of(() -> ref.get().get()); + LazyConstant constant1 = LazyConstant.of(constant); + ref.set(constant1); + assertThrows(IllegalStateException.class, constant::get); + } + + @ParameterizedTest + @MethodSource("factories") + void underlying(Function, LazyConstant> factory) { + LazyConstantTestUtil.CountingSupplier cs = new LazyConstantTestUtil.CountingSupplier<>(SUPPLIER); + var f1 = factory.apply(cs); + + Supplier underlyingBefore = LazyConstantTestUtil.computingFunction(f1); + assertSame(cs, underlyingBefore); + int v = f1.get(); + Supplier underlyingAfter = LazyConstantTestUtil.computingFunction(f1); + assertNull(underlyingAfter); + } + + @ParameterizedTest + @MethodSource("factories") + void functionHolderException(Function, LazyConstant> factory) { + LazyConstantTestUtil.CountingSupplier cs = new LazyConstantTestUtil.CountingSupplier<>(() -> { + throw new UnsupportedOperationException(); + }); + var f1 = factory.apply(cs); + + Supplier underlyingBefore = LazyConstantTestUtil.computingFunction(f1); + assertSame(cs, underlyingBefore); + try { + int v = f1.get(); + } catch (UnsupportedOperationException _) { + // Expected + } + Supplier underlyingAfter = LazyConstantTestUtil.computingFunction(f1); + assertSame(cs, underlyingAfter); + } + + private static Stream> lazyConstants() { + return factories() + .map(f -> f.apply(() -> VALUE)); + } + + private static Stream, LazyConstant>> factories() { + return Stream.of( + supplier("ComputedConstant.of()", LazyConstant::of) + ); + } + + private static Function, LazyConstant> supplier(String name, + Function, LazyConstant> underlying) { + return new Function, LazyConstant>() { + @Override + public LazyConstant apply(Supplier supplier) { + return underlying.apply(supplier); + } + + @Override + public String toString() { + return name; + } + }; + } + + record Lazy(LazyConstant underlying) implements Supplier { + @Override + public T get() { return underlying.get(); } + + static Lazy of(Supplier computingFunction) { + return new Lazy<>(LazyConstant.of(computingFunction)); + } + } + +} diff --git a/test/jdk/java/lang/StableValue/StableTestUtil.java b/test/jdk/java/lang/LazyConstant/LazyConstantTestUtil.java similarity index 64% rename from test/jdk/java/lang/StableValue/StableTestUtil.java rename to test/jdk/java/lang/LazyConstant/LazyConstantTestUtil.java index f71915c28ee7e..502f46b726d9f 100644 --- a/test/jdk/java/lang/StableValue/StableTestUtil.java +++ b/test/jdk/java/lang/LazyConstant/LazyConstantTestUtil.java @@ -21,15 +21,18 @@ * questions. */ +import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; -final class StableTestUtil { +final class LazyConstantTestUtil { - private StableTestUtil() {} + private LazyConstantTestUtil() { } + + public static final String UNINITIALIZED_TAG = ".uninitialized"; public static final class CountingSupplier extends AbstractCounting> @@ -117,4 +120,55 @@ public final String toString() { } } + static Object functionHolder(Object o) { + try { + final Field field = field(o.getClass(), "functionHolder"); + field.setAccessible(true); + return field.get(o); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + static Object functionHolderFunction(Object o) { + try { + final Field field = field(o.getClass(), "function"); + field.setAccessible(true); + return field.get(o); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + static int functionHolderCounter(Object o) { + try { + final Field field = field(o.getClass(), "counter"); + field.setAccessible(true); + return (int)field.get(o); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + static Supplier computingFunction(LazyConstant o) { + try { + final Field field = field(o.getClass(), "computingFunction"); + field.setAccessible(true); + return (Supplier) field.get(o); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + static Field field(Class clazz, String name) { + if (clazz.equals(Object.class)) { + throw new RuntimeException("No " + name); + } + try { + return clazz.getDeclaredField(name); + } catch (NoSuchFieldException e) { + return field(clazz.getSuperclass(), name); + } + } + } diff --git a/test/jdk/java/lang/StableValue/StableListTest.java b/test/jdk/java/lang/LazyConstant/LazyListTest.java similarity index 60% rename from test/jdk/java/lang/StableValue/StableListTest.java rename to test/jdk/java/lang/LazyConstant/LazyListTest.java index 2abe305b0e7ae..046f9107b173e 100644 --- a/test/jdk/java/lang/StableValue/StableListTest.java +++ b/test/jdk/java/lang/LazyConstant/LazyListTest.java @@ -22,23 +22,18 @@ */ /* @test - * @summary Basic tests for StableList methods - * @modules java.base/jdk.internal.lang.stable + * @summary Basic tests for lazy list methods * @enablePreview - * @run junit StableListTest + * @run junit/othervm --add-opens java.base/java.util=ALL-UNNAMED LazyListTest */ -import jdk.internal.lang.stable.StableUtil; -import jdk.internal.lang.stable.StableValueImpl; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.io.Serializable; import java.util.Comparator; -import java.util.IdentityHashMap; import java.util.List; -import java.util.Map; import java.util.NoSuchElementException; import java.util.RandomAccess; import java.util.Set; @@ -54,7 +49,7 @@ import static org.junit.jupiter.api.Assertions.*; -final class StableListTest { +final class LazyListTest { private static final int ZERO = 0; private static final int INDEX = 7; @@ -63,26 +58,26 @@ final class StableListTest { @Test void factoryInvariants() { - assertThrows(NullPointerException.class, () -> StableValue.list(SIZE, null)); - assertThrows(IllegalArgumentException.class, () -> StableValue.list(-1, IDENTITY)); + assertThrows(NullPointerException.class, () -> List.ofLazy(SIZE, null)); + assertThrows(IllegalArgumentException.class, () -> List.ofLazy(-1, IDENTITY)); } @Test void isEmpty() { - assertFalse(newList().isEmpty()); - assertTrue(newEmptyList().isEmpty()); + assertFalse(newLazyList().isEmpty()); + assertTrue(newEmptyLazyList().isEmpty()); } @Test void size() { - assertEquals(SIZE, newList().size()); - assertEquals(ZERO, newEmptyList().size()); + assertEquals(SIZE, newLazyList().size()); + assertEquals(ZERO, newEmptyLazyList().size()); } @Test void get() { - StableTestUtil.CountingIntFunction cif = new StableTestUtil.CountingIntFunction<>(IDENTITY); - var lazy = StableValue.list(SIZE, cif); + LazyConstantTestUtil.CountingIntFunction cif = new LazyConstantTestUtil.CountingIntFunction(IDENTITY); + var lazy = List.ofLazy(SIZE, cif); for (int i = 0; i < SIZE; i++) { assertEquals(i, lazy.get(i)); assertEquals(i + 1, cif.cnt()); @@ -93,10 +88,10 @@ void get() { @Test void getException() { - StableTestUtil.CountingIntFunction cif = new StableTestUtil.CountingIntFunction<>(_ -> { + LazyConstantTestUtil.CountingIntFunction cif = new LazyConstantTestUtil.CountingIntFunction(_ -> { throw new UnsupportedOperationException(); }); - var lazy = StableValue.list(SIZE, cif); + var lazy = List.ofLazy(SIZE, cif); assertThrows(UnsupportedOperationException.class, () -> lazy.get(INDEX)); assertEquals(1, cif.cnt()); assertThrows(UnsupportedOperationException.class, () -> lazy.get(INDEX)); @@ -105,8 +100,8 @@ void getException() { @Test void toArray() { - assertArrayEquals(new Object[ZERO], newEmptyList().toArray()); - assertArrayEquals(newRegularList().toArray(), newList().toArray()); + assertArrayEquals(new Object[ZERO], newEmptyLazyList().toArray()); + assertArrayEquals(newRegularList().toArray(), newLazyList().toArray()); } @Test @@ -115,8 +110,8 @@ void toArrayWithArrayLarger() { for (int i = 0; i < SIZE; i++) { actual[INDEX] = 100 + i; } - var list = StableValue.list(INDEX, IDENTITY); - assertSame(actual, list.toArray(actual)); + var lazy = List.ofLazy(INDEX, IDENTITY); + assertSame(actual, lazy.toArray(actual)); Integer[] expected = IntStream.range(0, SIZE) .mapToObj(i -> i < INDEX ? i : null) .toArray(Integer[]::new); @@ -126,7 +121,7 @@ void toArrayWithArrayLarger() { @Test void toArrayWithArraySmaller() { Integer[] arr = new Integer[INDEX]; - Integer[] actual = newList().toArray(arr); + Integer[] actual = newLazyList().toArray(arr); assertNotSame(arr, actual); Integer[] expected = newRegularList().toArray(new Integer[0]); assertArrayEquals(expected, actual); @@ -135,13 +130,13 @@ void toArrayWithArraySmaller() { @Test void toArrayWithGenerator() { Integer[] expected = newRegularList().toArray(Integer[]::new); - Integer[] actual = newList().toArray(Integer[]::new); + Integer[] actual = newLazyList().toArray(Integer[]::new); assertArrayEquals(expected, actual); } @Test void firstIndex() { - var lazy = newList(); + var lazy = newLazyList(); for (int i = INDEX; i < SIZE; i++) { assertEquals(i, lazy.indexOf(i)); } @@ -150,7 +145,7 @@ void firstIndex() { @Test void lastIndex() { - var lazy = newList(); + var lazy = newLazyList(); for (int i = INDEX; i < SIZE; i++) { assertEquals(i, lazy.lastIndexOf(i)); } @@ -159,42 +154,28 @@ void lastIndex() { @Test void toStringTest() { - assertEquals("[]", newEmptyList().toString()); - var list = StableValue.list(2, IDENTITY); - assertEquals("[.unset, .unset]", list.toString()); - list.get(0); - assertEquals("[0, .unset]", list.toString()); - list.get(1); - assertEquals("[0, 1]", list.toString()); + assertEquals("[]", newEmptyLazyList().toString()); + assertEquals("[0, 1]", List.ofLazy(2, IDENTITY).toString()); } @Test void hashCodeTest() { - assertEquals(List.of().hashCode(), newEmptyList().hashCode()); - assertEquals(newRegularList().hashCode(), newList().hashCode()); + assertEquals(List.of().hashCode(), newEmptyLazyList().hashCode()); + assertEquals(newRegularList().hashCode(), newLazyList().hashCode()); } @Test void equalsTest() { - assertTrue(newEmptyList().equals(List.of())); - assertTrue(List.of().equals(newEmptyList())); - assertTrue(newList().equals(newRegularList())); - assertTrue(newRegularList().equals(newList())); - assertFalse(newList().equals("A")); - } - - @Test - void equalsPartialEvaluationTest() { - var list = StableValue.list(2, IDENTITY); - assertFalse(list.equals(List.of(0))); - assertEquals("[0, .unset]", list.toString()); - assertTrue(list.equals(List.of(0, 1))); - assertEquals("[0, 1]", list.toString()); + assertTrue(newEmptyLazyList().equals(List.of())); + assertTrue(List.of().equals(newEmptyLazyList())); + assertTrue(newLazyList().equals(newRegularList())); + assertTrue(newRegularList().equals(newLazyList())); + assertFalse(newLazyList().equals("A")); } @Test void iteratorTotal() { - var iterator = newList().iterator(); + var iterator = newLazyList().iterator(); for (int i = 0; i < SIZE; i++) { assertTrue(iterator.hasNext()); assertTrue(iterator.hasNext()); @@ -209,7 +190,7 @@ void iteratorTotal() { @Test void iteratorPartial() { - var iterator = newList().iterator(); + var iterator = newLazyList().iterator(); for (int i = 0; i < INDEX; i++) { assertTrue(iterator.hasNext()); assertTrue(iterator.hasNext()); @@ -225,7 +206,7 @@ void iteratorPartial() { @Test void subList() { - var lazy = newList(); + var lazy = newLazyList(); var lazySubList = lazy.subList(1, SIZE); assertInstanceOf(RandomAccess.class, lazySubList); var regularList = newRegularList(); @@ -235,36 +216,33 @@ void subList() { @Test void subList2() { - var lazy = newList(); + var lazy = newLazyList(); var lazySubList = lazy.subList(1, SIZE); lazySubList.get(0); - var eq = newList(); + var eq = newLazyList(); eq.get(1); assertEquals(eq.toString(), lazy.toString()); } - void assertUnevaluated(List subList) { - assertEquals(asString(".unset", subList), subList.toString()); - } - @Test void reversed() { - var reversed = newList().reversed(); - assertInstanceOf(RandomAccess.class, reversed); - assertEquals(SIZE - 1, reversed.getFirst()); - assertEquals(0, reversed.getLast()); - - var reversed2 = reversed.reversed(); - assertInstanceOf(RandomAccess.class, reversed2); - assertEquals(0, reversed2.getFirst()); - assertEquals(SIZE - 1, reversed2.getLast()); + var lazy = newLazyList(); + var reversedLazy = lazy.reversed(); + assertInstanceOf(RandomAccess.class, reversedLazy); + assertEquals(SIZE - 1, reversedLazy.getFirst()); + assertEquals(0, reversedLazy.getLast()); + + var reversed2Lazy = reversedLazy.reversed(); + assertInstanceOf(RandomAccess.class, reversed2Lazy); + assertEquals(0, reversed2Lazy.getFirst()); + assertEquals(SIZE - 1, reversed2Lazy.getLast()); // Make sure we get back a non-reversed implementation - assertEquals("java.util.ImmutableCollections$StableList", reversed2.getClass().getName()); + assertEquals(lazy.getClass().getName(), reversed2Lazy.getClass().getName()); } @Test void sublistReversedToString() { - var actual = StableValue.list(4, IDENTITY); + var actual = List.ofLazy(4, IDENTITY); var expected = List.of(0, 1, 2, 3); for (UnaryOperation op : List.of( new UnaryOperation("subList", l -> l.subList(1, 3)), @@ -276,60 +254,17 @@ void sublistReversedToString() { actual.getLast(); var actualToString = actual.toString(); - var expectedToString = expected.toString().replace("2", ".unset"); + var expectedToString = expected.toString(); assertEquals(expectedToString, actualToString); } - // This test makes sure successive view operations retains the property - // of being a Stable view. - @Test - void viewsStable() { - viewOperations().forEach(op0 -> { - viewOperations().forEach( op1 -> { - viewOperations().forEach(op2 -> { - var list = newList(); - var view1 = op0.apply(list); - var view2 = op1.apply(view1); - var view3 = op2.apply(view2); - var className3 = className(view3); - var transitions = className(list) + ", " + - op0 + " -> " + className(view1) + ", " + - op1 + " -> " + className(view2) + ", " + - op2 + " -> " + className3; - assertTrue(className3.contains("Stable"), transitions); - assertUnevaluated(list); - assertUnevaluated(view1); - assertUnevaluated(view2); - assertUnevaluated(view3); - }); - }); - }); - } - @Test void recursiveCall() { AtomicReference> ref = new AtomicReference<>(); - var lazy = StableValue.list(SIZE, i -> ref.get().apply(i)); + var lazy = List.ofLazy(SIZE, i -> ref.get().apply(i)); ref.set(lazy::get); var x = assertThrows(IllegalStateException.class, () -> lazy.get(INDEX)); - assertEquals("Recursive initialization of a stable value is illegal", x.getMessage()); - } - - @Test - void indexOfNullInViews() { - final int size = 5; - final int middle = 2; - viewOperations().forEach(op0 -> { - viewOperations().forEach( op1 -> { - viewOperations().forEach(op2 -> { - var list = StableValue.list(size, x -> x == middle ? null : x);; - var view1 = op0.apply(list); - var view2 = op1.apply(view1); - var view3 = op2.apply(view2); - assertEquals(middle, view3.indexOf(null)); - }); - }); - }); + assertEquals("Recursive initialization of a lazy collection is illegal", x.getMessage()); } // Immutability @@ -355,7 +290,7 @@ void outOfBounds(Operation operation) { } static void assertThrowsForOperation(Class expectedType, Operation operation) { - var lazy = newList(); + var lazy = newLazyList(); assertThrows(expectedType, () -> operation.accept(lazy)); var sub = lazy.subList(1, SIZE / 2); assertThrows(expectedType, () -> operation.accept(sub)); @@ -367,14 +302,14 @@ static void assertThrowsForOperation(Class expectedType @Test void serializable() { - serializable(newList()); - serializable(newEmptyList()); + serializable(newLazyList()); + serializable(newEmptyLazyList()); } void serializable(List list) { assertFalse(list instanceof Serializable); if (list.size()>INDEX) { - assertFalse(newList().subList(1, INDEX) instanceof Serializable); + assertFalse(newLazyList().subList(1, INDEX) instanceof Serializable); } assertFalse(list.iterator() instanceof Serializable); assertFalse(list.reversed() instanceof Serializable); @@ -383,54 +318,25 @@ void serializable(List list) { @Test void randomAccess() { - assertInstanceOf(RandomAccess.class, newList()); - assertInstanceOf(RandomAccess.class, newEmptyList()); - assertInstanceOf(RandomAccess.class, newList().subList(1, INDEX)); + assertInstanceOf(RandomAccess.class, newLazyList()); + assertInstanceOf(RandomAccess.class, newEmptyLazyList()); + assertInstanceOf(RandomAccess.class, newLazyList().subList(1, INDEX)); } @Test - void distinct() { - StableValueImpl[] array = StableUtil.array(SIZE); - assertEquals(SIZE, array.length); - // Check, every StableValue is distinct - Map, Boolean> idMap = new IdentityHashMap<>(); - for (var e: array) { - idMap.put(e, true); - } - assertEquals(SIZE, idMap.size()); - } + void functionHolder() { + LazyConstantTestUtil.CountingIntFunction cif = new LazyConstantTestUtil.CountingIntFunction<>(IDENTITY); + List f1 = List.ofLazy(SIZE, cif); - @Test - void childObjectOpsLazy() { - viewOperations().forEach(op0 -> { - viewOperations().forEach(op1 -> { - viewOperations().forEach(op2 -> { - childOperations().forEach(co -> { - var list = newList(); - var view1 = op0.apply(list); - var view2 = op1.apply(view1); - var view3 = op2.apply(view2); - var child = co.apply(view3); - var childClassName = className(child); - var transitions = className(list) + ", " + - op0 + " -> " + className(view1) + ", " + - op1 + " -> " + className(view2) + ", " + - op2 + " -> " + className(view3) + ", " + - co + " -> " + childClassName; - - // None of these operations should trigger evaluation - var childToString = child.toString(); - int childHashCode = child.hashCode(); - boolean childEqualToNewObj = child.equals(new Object()); - - assertUnevaluated(list); - assertUnevaluated(view1); - assertUnevaluated(view2); - assertUnevaluated(view3); - }); - }); - }); - }); + Object holder = LazyConstantTestUtil.functionHolder(f1); + for (int i = 0; i < SIZE; i++) { + assertEquals(SIZE - i, LazyConstantTestUtil.functionHolderCounter(holder)); + assertSame(cif, LazyConstantTestUtil.functionHolderFunction(holder)); + int v = f1.get(i); + int v2 = f1.get(i); + } + assertEquals(0, LazyConstantTestUtil.functionHolderCounter(holder)); + assertNull(LazyConstantTestUtil.functionHolderFunction(holder)); } // Support constructs @@ -516,25 +422,15 @@ static Stream unsupportedOperations() { ); } - static List newList() { - return StableValue.list(SIZE, IDENTITY); + static List newLazyList() { + return List.ofLazy(SIZE, IDENTITY); } - static List newEmptyList() { - return StableValue.list(ZERO, IDENTITY); + static List newEmptyLazyList() { + return List.ofLazy(ZERO, IDENTITY); } static List newRegularList() { return IntStream.range(0, SIZE).boxed().toList(); } - - static String asString(String first, List list) { - return "[" + first + ", " + Stream.generate(() -> ".unset") - .limit(list.size() - 1) - .collect(Collectors.joining(", ")) + "]"; - } - - static String className(Object o) { - return o.getClass().getName(); - } } diff --git a/test/jdk/java/lang/LazyConstant/LazyMapTest.java b/test/jdk/java/lang/LazyConstant/LazyMapTest.java new file mode 100644 index 0000000000000..766313c91737a --- /dev/null +++ b/test/jdk/java/lang/LazyConstant/LazyMapTest.java @@ -0,0 +1,573 @@ +/* + * 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 + * @summary Basic tests for lazy map methods + * @enablePreview + * @run junit/othervm --add-opens java.base/java.util=ALL-UNNAMED LazyMapTest + */ + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.Serializable; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.mapping; +import static org.junit.jupiter.api.Assertions.*; + +final class LazyMapTest { + + enum Value { + // Zero is here so that we have enums with ordinals before the first one + // actually used in input sets (i.e. ZERO is not in the input set) + ZERO(0), + ILLEGAL_BEFORE(-1), + // Valid values + THIRTEEN(13) { + @Override + public String toString() { + // getEnumConstants will be `null` for this enum as it is overridden + return super.toString()+" (Overridden)"; + } + }, + ILLEGAL_BETWEEN(-2), + FORTY_TWO(42), + // Illegal values (not in the input set) + ILLEGAL_AFTER(-3); + + final int intValue; + + Value(int intValue) { + this.intValue = intValue; + } + + int asInt() { + return intValue; + } + + } + + private static final Function MAPPER = Value::asInt; + + private static final Value KEY = Value.FORTY_TWO; + private static final Integer VALUE = MAPPER.apply(KEY); + + @ParameterizedTest + @MethodSource("allSets") + void factoryInvariants(Set set) { + assertThrows(NullPointerException.class, () -> Map.ofLazy(set, null), set.getClass().getSimpleName()); + assertThrows(NullPointerException.class, () -> Map.ofLazy(null, MAPPER)); + Set setWithNull = new HashSet<>(); + setWithNull.add(KEY); + setWithNull.add(null); + assertThrows(NullPointerException.class, () -> Map.ofLazy(setWithNull, MAPPER)); + } + + @ParameterizedTest + @MethodSource("emptySets") + void empty(Set set) { + var lazy = newLazyMap(set); + assertTrue(lazy.isEmpty()); + assertEquals("{}", lazy.toString()); + assertThrows(NullPointerException.class, () -> lazy.get(null)); + assertNotEquals(null, lazy); + } + + @ParameterizedTest + @MethodSource("allSets") + void size(Set set) { + assertEquals(newRegularMap(set).size(), newLazyMap(set).size()); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void get(Set set) { + LazyConstantTestUtil.CountingFunction cf = new LazyConstantTestUtil.CountingFunction<>(MAPPER); + var lazy = Map.ofLazy(set, cf); + int cnt = 1; + for (Value v : set) { + assertEquals(MAPPER.apply(v), lazy.get(v)); + assertEquals(cnt, cf.cnt()); + assertEquals(MAPPER.apply(v), lazy.get(v)); + assertEquals(cnt++, cf.cnt()); + } + assertNull(lazy.get(Value.ILLEGAL_BETWEEN)); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void exception(Set set) { + LazyConstantTestUtil.CountingFunction cif = new LazyConstantTestUtil.CountingFunction<>(_ -> { + throw new UnsupportedOperationException(); + }); + var lazy = Map.ofLazy(set, cif); + assertThrows(UnsupportedOperationException.class, () -> lazy.get(KEY)); + assertEquals(1, cif.cnt()); + assertThrows(UnsupportedOperationException.class, () -> lazy.get(KEY)); + assertEquals(2, cif.cnt()); + assertThrows(UnsupportedOperationException.class, lazy::toString); + assertEquals(3, cif.cnt()); + } + + @ParameterizedTest + @MethodSource("allSets") + void containsKey(Set set) { + var lazy = newLazyMap(set); + for (Value v : set) { + assertTrue(lazy.containsKey(v)); + } + assertFalse(lazy.containsKey(Value.ILLEGAL_BETWEEN)); + } + + @ParameterizedTest + @MethodSource("allSets") + void containsValue(Set set) { + var lazy = newLazyMap(set); + for (Value v : set) { + assertTrue(lazy.containsValue(MAPPER.apply(v))); + } + assertFalse(lazy.containsValue(MAPPER.apply(Value.ILLEGAL_BETWEEN))); + } + + @ParameterizedTest + @MethodSource("allSets") + void forEach(Set set) { + var lazy = newLazyMap(set); + var ref = newRegularMap(set); + Set> expected = ref.entrySet(); + Set> actual = new HashSet<>(); + lazy.forEach((k, v) -> actual.add(new AbstractMap.SimpleImmutableEntry<>(k , v))); + assertEquals(expected, actual); + } + + @ParameterizedTest + @MethodSource("emptySets") + void toStringTestEmpty(Set set) { + var lazy = newLazyMap(set); + assertEquals("{}", lazy.toString()); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void toStringTest(Set set) { + var lazy = newLazyMap(set); + var toString = lazy.toString(); + assertTrue(toString.startsWith("{")); + assertTrue(toString.endsWith("}")); + + // Key order is unspecified + for (Value key : set) { + toString = lazy.toString(); + assertTrue(toString.contains(key + "=" + MAPPER.apply(key)), toString); + } + + // One between the values + assertEquals(set.size() - 1, toString.chars().filter(ch -> ch == ',').count()); + } + + @ParameterizedTest + @MethodSource("allSets") + void hashCodeTest(Set set) { + var lazy = newLazyMap(set); + var regular = newRegularMap(set); + assertEquals(regular.hashCode(), lazy.hashCode()); + } + + @ParameterizedTest + @MethodSource("allSets") + void equality(Set set) { + var lazy = newLazyMap(set); + var regular = newRegularMap(set); + assertEquals(regular, lazy); + assertEquals(lazy, regular); + assertNotEquals("A", lazy); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void circular(Set set) { + final AtomicReference> ref = new AtomicReference<>(); + Map> lazy = Map.ofLazy(set, _ -> ref.get()); + ref.set(lazy); + lazy.get(KEY); + var toString = lazy.toString(); + assertTrue(toString.contains("FORTY_TWO=(this Map)"), toString); + assertDoesNotThrow((() -> lazy.equals(lazy))); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void recursiveCall(Set set) { + final AtomicReference> ref = new AtomicReference<>(); + @SuppressWarnings("unchecked") + Map> lazy = Map.ofLazy(set, k -> (Map) ref.get().get(k)); + ref.set(lazy); + var x = assertThrows(IllegalStateException.class, () -> lazy.get(KEY)); + assertEquals("Recursive initialization of a lazy collection is illegal", x.getMessage()); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void entrySet(Set set) { + var lazy = newLazyMap(set).entrySet(); + var regular = newRegularMap(set).entrySet(); + assertTrue(regular.equals(lazy)); + assertTrue(lazy.equals(regular)); + assertTrue(regular.equals(lazy)); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void entrySetToString(Set set) { + var lazy = newLazyMap(set); + var lazyEntrySet = lazy.entrySet(); + var toString = lazyEntrySet.toString(); + for (var key : set) { + assertTrue(toString.contains(key + "=" + MAPPER.apply(key))); + } + assertTrue(toString.startsWith("[")); + assertTrue(toString.endsWith("]")); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void values(Set set) { + var lazy = newLazyMap(set); + var lazyValues = lazy.values(); + // Look at one of the elements + var val = lazyValues.stream().iterator().next(); + assertEquals(lazy.size() - 1, functionCounter(lazy)); + + // Mod ops + assertThrows(UnsupportedOperationException.class, () -> lazyValues.remove(val)); + assertThrows(UnsupportedOperationException.class, () -> lazyValues.add(val)); + assertThrows(UnsupportedOperationException.class, lazyValues::clear); + assertThrows(UnsupportedOperationException.class, () -> lazyValues.addAll(Set.of(VALUE))); + assertThrows(UnsupportedOperationException.class, () -> lazyValues.removeIf(i -> true)); + assertThrows(UnsupportedOperationException.class, () -> lazyValues.retainAll(Set.of(VALUE))); + } + + @ParameterizedTest + @MethodSource("allSets") + void valuesToString(Set set) { + var lazy = newLazyMap(set); + var lazyValues = lazy.values(); + var toString = lazyValues.toString(); + + // Key order is unspecified + for (Value key : set) { + assertTrue(toString.contains(MAPPER.apply(key).toString()), toString); + } + assertTrue(toString.startsWith("["), toString); + assertTrue(toString.endsWith("]"), toString); + } + + @ParameterizedTest + @MethodSource("allSets") + void iteratorNext(Set set) { + Set encountered = new HashSet<>(); + var iterator = newLazyMap(set).entrySet().iterator(); + while (iterator.hasNext()) { + var entry = iterator.next(); + assertEquals(MAPPER.apply(entry.getKey()), entry.getValue()); + encountered.add(entry.getKey()); + } + assertEquals(set, encountered); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void iteratorForEachRemaining(Set set) { + Set encountered = new HashSet<>(); + var iterator = newLazyMap(set).entrySet().iterator(); + var entry = iterator.next(); + assertEquals(MAPPER.apply(entry.getKey()), entry.getValue()); + encountered.add(entry.getKey()); + iterator.forEachRemaining(e -> { + assertEquals(MAPPER.apply(e.getKey()), e.getValue()); + encountered.add(e.getKey()); + }); + assertEquals(set, encountered); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void lazyEntry(Set set) { + var lazy = newLazyMap(set); + var entry = lazy.entrySet().stream() + .filter(e -> e.getKey().equals(KEY)) + .findAny() + .orElseThrow(); + + assertEquals(lazy.size(), functionCounter(lazy)); + var otherDifferent = Map.entry(Value.ZERO, -1); + assertNotEquals(entry, otherDifferent); + assertEquals(lazy.size(), functionCounter(lazy)); + var otherEqual = Map.entry(entry.getKey(), entry.getValue()); + assertEquals(entry, otherEqual); + assertEquals(lazy.size() - 1, functionCounter(lazy)); + assertEquals(entry.hashCode(), otherEqual.hashCode()); + } + + @ParameterizedTest + @MethodSource("nonEmptySets") + void lazyForEachEntry(Set set) { + var lazy = newLazyMap(set); + // Only touch the key. + lazy.entrySet().iterator().forEachRemaining(Map.Entry::getKey); + assertEquals(lazy.size(), functionCounter(lazy)); // No evaluation + // Only touch the value. + lazy.entrySet().iterator().forEachRemaining(Map.Entry::getValue); + assertEquals(0, functionCounter(lazy)); + } + + // Immutability + @ParameterizedTest + @MethodSource("unsupportedOperations") + void unsupported(Operation operation) { + assertThrowsForOperation(UnsupportedOperationException.class, operation); + } + + // Method parameter invariant checking + + @ParameterizedTest + @MethodSource("nullAverseOperations") + void nullAverse(Operation operation) { + assertThrowsForOperation(NullPointerException.class, operation); + } + + static void assertThrowsForOperation(Class expectedType, Operation operation) { + for (Set set : allSets().toList()) { + var lazy = newLazyMap(set); + assertThrows(expectedType, () -> operation.accept(lazy), set.getClass().getSimpleName() + " " + operation); + } + } + + // Implementing interfaces + + @ParameterizedTest + @MethodSource("allSets") + void serializable(Set set) { + var lazy = newLazyMap(set); + assertFalse(lazy instanceof Serializable); + assertFalse(lazy.entrySet() instanceof Serializable); + assertFalse(lazy.values() instanceof Serializable); + } + + @Test + void nullResult() { + var lazy = Map.ofLazy(Set.of(0), _ -> null); + assertThrows(NullPointerException.class, () -> lazy.getOrDefault(0, 1));; + assertTrue(lazy.containsKey(0)); + } + + @ParameterizedTest + @MethodSource("allSets") + void functionHolder(Set set) { + LazyConstantTestUtil.CountingFunction cif = new LazyConstantTestUtil.CountingFunction<>(MAPPER); + Map lazy = Map.ofLazy(set, cif); + + Object holder = LazyConstantTestUtil.functionHolder(lazy); + + int i = 0; + for (Value key : set) { + assertEquals(set.size() - i, LazyConstantTestUtil.functionHolderCounter(holder)); + assertSame(cif, LazyConstantTestUtil.functionHolderFunction(holder)); + int v = lazy.get(key); + int v2 = lazy.get(key); + i++; + } + assertEquals(0, LazyConstantTestUtil.functionHolderCounter(holder)); + assertNull(LazyConstantTestUtil.functionHolderFunction(holder)); + } + + @ParameterizedTest + @MethodSource("allSets") + void functionHolderViaEntrySet(Set set) { + LazyConstantTestUtil.CountingFunction cif = new LazyConstantTestUtil.CountingFunction<>(MAPPER); + Map lazy = Map.ofLazy(set, cif); + + Object holder = LazyConstantTestUtil.functionHolder(lazy); + + int i = 0; + for (Map.Entry e : lazy.entrySet()) { + assertEquals(set.size() - i, LazyConstantTestUtil.functionHolderCounter(holder)); + assertSame(cif, LazyConstantTestUtil.functionHolderFunction(holder)); + int v = e.getValue(); + int v2 = e.getValue(); + i++; + } + assertEquals(0, LazyConstantTestUtil.functionHolderCounter(holder)); + assertNull(LazyConstantTestUtil.functionHolderFunction(holder)); + } + + @ParameterizedTest + @MethodSource("allSets") + void underlyingRefViaEntrySetForEach(Set set) { + LazyConstantTestUtil.CountingFunction cif = new LazyConstantTestUtil.CountingFunction<>(MAPPER); + Map lazy = Map.ofLazy(set, cif); + + Object holder = LazyConstantTestUtil.functionHolder(lazy); + + final AtomicInteger i = new AtomicInteger(); + lazy.entrySet().forEach(e -> { + assertEquals(set.size() - i.get(), LazyConstantTestUtil.functionHolderCounter(holder)); + assertSame(cif, LazyConstantTestUtil.functionHolderFunction(holder)); + Integer val = e.getValue(); + Integer val2 = e.getValue(); + i.incrementAndGet(); + }); + assertEquals(0, LazyConstantTestUtil.functionHolderCounter(holder)); + assertNull(LazyConstantTestUtil.functionHolderFunction(holder)); + } + + @Test + void usesOptimizedVersion() { + Map enumMap = Map.ofLazy(EnumSet.of(KEY), Value::asInt); + assertTrue(enumMap.getClass().getName().contains("Enum"), enumMap.getClass().getName()); + Map emptyMap = Map.ofLazy(EnumSet.noneOf(Value.class), Value::asInt); + assertFalse(emptyMap.getClass().getName().contains("Enum"), emptyMap.getClass().getName()); + Map regularMap = Map.ofLazy(Set.of(KEY), Value::asInt); + assertFalse(regularMap.getClass().getName().contains("Enum"), regularMap.getClass().getName()); + } + + @Test + void overriddenEnum() { + final var overridden = Value.THIRTEEN; + Map enumMap = Map.ofLazy(EnumSet.of(overridden), MAPPER); + assertEquals(MAPPER.apply(overridden), enumMap.get(overridden), enumMap.toString()); + } + + @Test + void enumAliasing() { + enum MyEnum {FOO, BAR} + enum MySecondEnum{BAZ, QUX} + Map mapEnum = Map.ofLazy(EnumSet.allOf(MyEnum.class), MyEnum::ordinal); + assertEquals(MyEnum.BAR.ordinal(), mapEnum.get(MyEnum.BAR)); + // Make sure class is checked, not just `ordinal()` + assertNull(mapEnum.get(MySecondEnum.QUX)); + } + + // Support constructs + + record Operation(String name, + Consumer> consumer) implements Consumer> { + @java.lang.Override + public void accept(Map map) { consumer.accept(map); } + @java.lang.Override + public String toString() { return name; } + } + + static Stream nullAverseOperations() { + return Stream.of( + new Operation("forEach", m -> m.forEach(null)) + ); + } + + static Stream unsupportedOperations() { + return Stream.of( + new Operation("clear", Map::clear), + new Operation("compute", m -> m.compute(KEY, (_, _) -> 1)), + new Operation("computeIfAbsent", m -> m.computeIfAbsent(KEY, _ -> 1)), + new Operation("computeIfPresent", m -> m.computeIfPresent(KEY, (_, _) -> 1)), + new Operation("merge", m -> m.merge(KEY, VALUE, (a, _) -> a)), + new Operation("put", m -> m.put(KEY, 0)), + new Operation("putAll", m -> m.putAll(Map.of())), + new Operation("remove1", m -> m.remove(KEY)), + new Operation("remove2", m -> m.remove(KEY, VALUE)), + new Operation("replace2", m -> m.replace(KEY, 1)), + new Operation("replace3", m -> m.replace(KEY, VALUE, 1)), + new Operation("replaceAll", m -> m.replaceAll((a, _) -> MAPPER.apply(a))) + ); + } + + + static Map newLazyMap(Set set) { + return Map.ofLazy(set, MAPPER); + } + static Map newRegularMap(Set set) { + return set.stream() + .collect(Collectors.toMap(Function.identity(), MAPPER)); + } + + private static Stream> nonEmptySets() { + return Stream.of( + Set.of(KEY, Value.THIRTEEN), + linkedHashSet(Value.THIRTEEN, KEY), + treeSet(KEY, Value.THIRTEEN), + EnumSet.of(KEY, Value.THIRTEEN) + ); + } + + private static Stream> emptySets() { + return Stream.of( + Set.of(), + linkedHashSet(), + treeSet(), + EnumSet.noneOf(Value.class) + ); + } + + private static Stream> allSets() { + return Stream.concat( + nonEmptySets(), + emptySets() + ); + } + + static Set treeSet(Value... values) { + return populate(new TreeSet<>(Comparator.comparingInt(Value::asInt).reversed()),values); + } + + static Set linkedHashSet(Value... values) { + return populate(new LinkedHashSet<>(), values); + } + + static Set populate(Set set, Value... values) { + set.addAll(Arrays.asList(values)); + return set; + } + + private static int functionCounter(Map lazy) { + final Object holder = LazyConstantTestUtil.functionHolder(lazy); + return LazyConstantTestUtil.functionHolderCounter(holder); + } + +} diff --git a/test/jdk/java/lang/StableValue/TrustedFieldTypeTest.java b/test/jdk/java/lang/LazyConstant/TrustedFieldTypeTest.java similarity index 61% rename from test/jdk/java/lang/StableValue/TrustedFieldTypeTest.java rename to test/jdk/java/lang/LazyConstant/TrustedFieldTypeTest.java index 205e5ed3a773a..9e63a9b275c11 100644 --- a/test/jdk/java/lang/StableValue/TrustedFieldTypeTest.java +++ b/test/jdk/java/lang/LazyConstant/TrustedFieldTypeTest.java @@ -24,14 +24,14 @@ /* @test * @summary Basic tests for TrustedFieldType implementations * @modules jdk.unsupported/sun.misc - * @modules java.base/jdk.internal.lang.stable + * @modules java.base/jdk.internal.lang * @modules java.base/jdk.internal.misc * @enablePreview - * @run junit/othervm --add-opens java.base/jdk.internal.lang.stable=ALL-UNNAMED -Dopens=true TrustedFieldTypeTest + * @run junit/othervm --add-opens java.base/jdk.internal.lang=ALL-UNNAMED -Dopens=true TrustedFieldTypeTest * @run junit/othervm -Dopens=false TrustedFieldTypeTest */ -import jdk.internal.lang.stable.StableValueImpl; +import jdk.internal.lang.LazyConstantImpl; import jdk.internal.misc.Unsafe; import org.junit.jupiter.api.Test; @@ -39,84 +39,89 @@ import java.lang.invoke.VarHandle; import java.lang.reflect.Field; import java.lang.reflect.InaccessibleObjectException; +import java.lang.LazyConstant; +import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.*; final class TrustedFieldTypeTest { + private static final int VALUE = 42; + private static final Supplier SUPPLIER = () -> VALUE; + @Test void varHandle() throws NoSuchFieldException, IllegalAccessException { MethodHandles.Lookup lookup = MethodHandles.lookup(); - StableValue originalValue = StableValue.of(); + LazyConstant originalValue = LazyConstant.of(SUPPLIER); @SuppressWarnings("unchecked") - StableValue[] originalArrayValue = new StableValue[10]; + LazyConstant[] originalArrayValue = new LazyConstant[10]; final class Holder { - private final StableValue value = originalValue; + private final LazyConstant value = originalValue; } final class ArrayHolder { - private final StableValue[] array = originalArrayValue; + private final LazyConstant[] array = originalArrayValue; } - VarHandle valueVarHandle = lookup.findVarHandle(Holder.class, "value", StableValue.class); + VarHandle valueVarHandle = lookup.findVarHandle(Holder.class, "value", LazyConstant.class); Holder holder = new Holder(); assertThrows(UnsupportedOperationException.class, () -> - valueVarHandle.set(holder, StableValue.of()) + valueVarHandle.set(holder, LazyConstant.of(SUPPLIER)) ); assertThrows(UnsupportedOperationException.class, () -> - valueVarHandle.compareAndSet(holder, originalValue, StableValue.of()) + valueVarHandle.compareAndSet(holder, originalValue, LazyConstant.of(SUPPLIER)) ); - VarHandle arrayVarHandle = lookup.findVarHandle(ArrayHolder.class, "array", StableValue[].class); + VarHandle arrayVarHandle = lookup.findVarHandle(ArrayHolder.class, "array", LazyConstant[].class); ArrayHolder arrayHolder = new ArrayHolder(); assertThrows(UnsupportedOperationException.class, () -> - arrayVarHandle.set(arrayHolder, new StableValue[1]) + arrayVarHandle.set(arrayHolder, new LazyConstant[1]) ); assertThrows(UnsupportedOperationException.class, () -> - arrayVarHandle.compareAndSet(arrayHolder, originalArrayValue, new StableValue[1]) + arrayVarHandle.compareAndSet(arrayHolder, originalArrayValue, new LazyConstant[1]) ); } @Test - void updateStableValueContentVia_j_i_m_Unsafe() { - StableValue stableValue = StableValue.of(); - stableValue.trySet(42); + void updateComputedConstantContentVia_j_i_m_Unsafe() { + LazyConstant lazyConstant = LazyConstant.of(SUPPLIER); + lazyConstant.get(); jdk.internal.misc.Unsafe unsafe = Unsafe.getUnsafe(); - long offset = unsafe.objectFieldOffset(stableValue.getClass(), "contents"); + long offset = unsafe.objectFieldOffset(lazyConstant.getClass(), "constant"); assertTrue(offset > 0); // Unfortunately, it is possible to update the underlying data via jdk.internal.misc.Unsafe - Object oldData = unsafe.getAndSetReference(stableValue, offset, 13); - assertEquals(42, oldData); - assertEquals(13, stableValue.orElseThrow()); + Object oldData = unsafe.getAndSetReference(lazyConstant, offset, 13); + assertEquals(VALUE, oldData); + assertEquals(13, lazyConstant.get()); } @Test - void updateStableValueContentViaSetAccessible() throws NoSuchFieldException, IllegalAccessException { + void updateComputedConstantContentViaSetAccessible() throws NoSuchFieldException, IllegalAccessException { if (Boolean.getBoolean("opens")) { // Unfortunately, add-opens allows direct access to the `value` field - Field field = StableValueImpl.class.getDeclaredField("contents"); + Field field = LazyConstantImpl.class.getDeclaredField("constant"); field.setAccessible(true); - StableValue stableValue = StableValue.of(); - stableValue.trySet(42); + LazyConstant lazyConstant = LazyConstant.of(SUPPLIER); + lazyConstant.get(); - Object oldData = field.get(stableValue); - assertEquals(42, oldData); + Object oldData = field.get(lazyConstant); + assertEquals(VALUE, oldData); - field.set(stableValue, 13); - assertEquals(13, stableValue.orElseThrow()); + field.set(lazyConstant, 13); + assertEquals(13, lazyConstant.get()); } else { - Field field = StableValueImpl.class.getDeclaredField("contents"); + Field field = LazyConstantImpl.class.getDeclaredField("constant"); assertThrows(InaccessibleObjectException.class, ()-> field.setAccessible(true)); } } diff --git a/test/jdk/java/lang/StableValue/StableFunctionTest.java b/test/jdk/java/lang/StableValue/StableFunctionTest.java deleted file mode 100644 index 07f51470a0b3d..0000000000000 --- a/test/jdk/java/lang/StableValue/StableFunctionTest.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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 - * @summary Basic tests for StableFunction methods - * @enablePreview - * @run junit StableFunctionTest - */ - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.*; - -final class StableFunctionTest { - - enum Value { - // Zero is here so that we have enums with ordinals before the first one - // actually used in input sets (i.e. ZERO is not in the input set) - ZERO(0), - ILLEGAL_BEFORE(-1), - // Valid values - THIRTEEN(13) { - @Override - public String toString() { - // getEnumConstants will be `null` for this enum as it is overridden - return super.toString()+" (Overridden)"; - } - }, - ILLEGAL_BETWEEN(-2), - FORTY_TWO(42), - // Illegal values (not in the input set) - ILLEGAL_AFTER(-3); - - final int intValue; - - Value(int intValue) { - this.intValue = intValue; - } - - int asInt() { - return intValue; - } - - } - - private static final Function MAPPER = Value::asInt; - - @ParameterizedTest - @MethodSource("allSets") - void factoryInvariants(Set inputs) { - assertThrows(NullPointerException.class, () -> StableValue.function(null, MAPPER)); - assertThrows(NullPointerException.class, () -> StableValue.function(inputs, null)); - } - - @ParameterizedTest - @MethodSource("nonEmptySets") - void basic(Set inputs) { - basic(inputs, MAPPER); - toStringTest(inputs, MAPPER); - basic(inputs, _ -> null); - toStringTest(inputs, _ -> null); - } - - void basic(Set inputs, Function mapper) { - StableTestUtil.CountingFunction cif = new StableTestUtil.CountingFunction<>(mapper); - var cached = StableValue.function(inputs, cif); - assertEquals(mapper.apply(Value.FORTY_TWO), cached.apply(Value.FORTY_TWO)); - assertEquals(1, cif.cnt()); - assertEquals(mapper.apply(Value.FORTY_TWO), cached.apply(Value.FORTY_TWO)); - assertEquals(1, cif.cnt()); - var x0 = assertThrows(IllegalArgumentException.class, () -> cached.apply(Value.ILLEGAL_BEFORE)); - assertEquals("Input not allowed: ILLEGAL_BEFORE", x0.getMessage()); - var x1 = assertThrows(IllegalArgumentException.class, () -> cached.apply(Value.ILLEGAL_BETWEEN)); - assertEquals("Input not allowed: ILLEGAL_BETWEEN", x1.getMessage()); - var x2 = assertThrows(IllegalArgumentException.class, () -> cached.apply(Value.ILLEGAL_AFTER)); - assertEquals("Input not allowed: ILLEGAL_AFTER", x2.getMessage()); - } - - void toStringTest(Set inputs, Function mapper) { - var cached = StableValue.function(inputs, mapper); - cached.apply(Value.FORTY_TWO); - var toString = cached.toString(); - assertTrue(toString.startsWith("{")); - // Key order is unspecified - assertTrue(toString.contains(Value.THIRTEEN + "=.unset")); - assertTrue(toString.contains(Value.FORTY_TWO + "=" + mapper.apply(Value.FORTY_TWO))); - assertTrue(toString.endsWith("}")); - // One between the values - assertEquals(1L, toString.chars().filter(ch -> ch == ',').count()); - } - - @ParameterizedTest - @MethodSource("emptySets") - void empty(Set inputs) { - Function f0 = StableValue.function(inputs, Value::asInt); - Function f1 = StableValue.function(inputs, Value::asInt); - assertEquals("{}", f0.toString()); - assertThrows(NullPointerException.class, () -> f0.apply(null)); - assertNotEquals(f0, f1); - assertNotEquals(null, f0); - } - - @ParameterizedTest - @MethodSource("nonEmptySets") - void exception(Set inputs) { - StableTestUtil.CountingFunction cif = new StableTestUtil.CountingFunction<>(_ -> { - throw new UnsupportedOperationException(); - }); - var cached = StableValue.function(inputs, cif); - assertThrows(UnsupportedOperationException.class, () -> cached.apply(Value.FORTY_TWO)); - assertEquals(1, cif.cnt()); - assertThrows(UnsupportedOperationException.class, () -> cached.apply(Value.FORTY_TWO)); - assertEquals(2, cif.cnt()); - var toString = cached.toString(); - assertTrue(toString.startsWith("{")); - // Key order is unspecified - assertTrue(toString.contains(Value.THIRTEEN + "=.unset")); - assertTrue(toString.contains(Value.FORTY_TWO + "=.unset")); - assertTrue(toString.endsWith("}")); - } - - @ParameterizedTest - @MethodSource("nonEmptySets") - void circular(Set inputs) { - final AtomicReference> ref = new AtomicReference<>(); - Function> cached = StableValue.function(inputs, _ -> ref.get()); - ref.set(cached); - cached.apply(Value.FORTY_TWO); - var toString = cached.toString(); - assertTrue(toString.contains("FORTY_TWO=(this StableFunction)"), toString); - assertDoesNotThrow(cached::hashCode); - assertDoesNotThrow((() -> cached.equals(cached))); - } - - @ParameterizedTest - @MethodSource("allSets") - void equality(Set inputs) { - Function mapper = Value::asInt; - Function f0 = StableValue.function(inputs, mapper); - Function f1 = StableValue.function(inputs, mapper); - // No function is equal to another function - assertNotEquals(f0, f1); - } - - @ParameterizedTest - @MethodSource("allSets") - void hashCodeStable(Set inputs) { - Function f0 = StableValue.function(inputs, Value::asInt); - assertEquals(System.identityHashCode(f0), f0.hashCode()); - if (!inputs.isEmpty()) { - f0.apply(Value.FORTY_TWO); - assertEquals(System.identityHashCode(f0), f0.hashCode()); - } - } - - @Test - void nullKeys() { - Set inputs = new HashSet<>(); - inputs.add(Value.FORTY_TWO); - inputs.add(null); - assertThrows(NullPointerException.class, () -> StableValue.function(inputs, MAPPER)); - } - - @Test - void usesOptimizedVersion() { - Function enumFunction = StableValue.function(EnumSet.of(Value.FORTY_TWO), Value::asInt); - assertEquals("jdk.internal.lang.stable.StableEnumFunction", enumFunction.getClass().getName()); - Function emptyFunction = StableValue.function(Set.of(), Value::asInt); - assertEquals("jdk.internal.lang.stable.StableFunction", emptyFunction.getClass().getName()); - } - - @Test - void overriddenEnum() { - final var overridden = Value.THIRTEEN; - Function enumFunction = StableValue.function(EnumSet.of(overridden), Value::asInt); - assertEquals(MAPPER.apply(overridden), enumFunction.apply(overridden)); - } - - private static Stream> nonEmptySets() { - return Stream.of( - Set.of(Value.FORTY_TWO, Value.THIRTEEN), - linkedHashSet(Value.THIRTEEN, Value.FORTY_TWO), - treeSet(Value.FORTY_TWO, Value.THIRTEEN), - EnumSet.of(Value.FORTY_TWO, Value.THIRTEEN) - ); - } - - private static Stream> emptySets() { - return Stream.of( - Set.of(), - linkedHashSet(), - treeSet(), - EnumSet.noneOf(Value.class) - ); - } - - private static Stream> allSets() { - return Stream.concat( - nonEmptySets(), - emptySets() - ); - } - - static Set treeSet(Value... values) { - return populate(new TreeSet<>(Comparator.comparingInt(Value::asInt).reversed()),values); - } - - static Set linkedHashSet(Value... values) { - return populate(new LinkedHashSet<>(), values); - } - - static Set populate(Set set, Value... values) { - set.addAll(Arrays.asList(values)); - return set; - } - -} diff --git a/test/jdk/java/lang/StableValue/StableIntFunctionTest.java b/test/jdk/java/lang/StableValue/StableIntFunctionTest.java deleted file mode 100644 index 7397a688ee68f..0000000000000 --- a/test/jdk/java/lang/StableValue/StableIntFunctionTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 - * @summary Basic tests for StableIntFunction methods - * @enablePreview - * @run junit StableIntFunctionTest - */ - -import org.junit.jupiter.api.Test; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.IntFunction; - -import static org.junit.jupiter.api.Assertions.*; - -final class StableIntFunctionTest { - - private static final int SIZE = 2; - private static final IntFunction MAPPER = i -> i; - - @Test - void factoryInvariants() { - assertThrows(IllegalArgumentException.class, () -> StableValue.intFunction(-1, MAPPER)); - assertThrows(NullPointerException.class, () -> StableValue.intFunction(SIZE, null)); - } - - @Test - void basic() { - basic(MAPPER); - basic(i -> null); - } - - void basic(IntFunction mapper) { - StableTestUtil.CountingIntFunction cif = new StableTestUtil.CountingIntFunction<>(mapper); - var cached = StableValue.intFunction(SIZE, cif); - assertEquals("[.unset, .unset]", cached.toString()); - assertEquals(mapper.apply(1), cached.apply(1)); - assertEquals(1, cif.cnt()); - assertEquals(mapper.apply(1), cached.apply(1)); - assertEquals(1, cif.cnt()); - assertEquals("[.unset, " + mapper.apply(1) + "]", cached.toString()); - assertThrows(IllegalArgumentException.class, () -> cached.apply(SIZE)); - assertThrows(IllegalArgumentException.class, () -> cached.apply(-1)); - assertThrows(IllegalArgumentException.class, () -> cached.apply(1_000_000)); - } - - @Test - void exception() { - StableTestUtil.CountingIntFunction cif = new StableTestUtil.CountingIntFunction<>(_ -> { - throw new UnsupportedOperationException(); - }); - var cached = StableValue.intFunction(SIZE, cif); - assertThrows(UnsupportedOperationException.class, () -> cached.apply(1)); - assertEquals(1, cif.cnt()); - assertThrows(UnsupportedOperationException.class, () -> cached.apply(1)); - assertEquals(2, cif.cnt()); - assertEquals("[.unset, .unset]", cached.toString()); - } - - @Test - void circular() { - final AtomicReference> ref = new AtomicReference<>(); - IntFunction> cached = StableValue.intFunction(SIZE, _ -> ref.get()); - ref.set(cached); - cached.apply(0); - String toString = cached.toString(); - assertEquals("[(this StableIntFunction), .unset]", toString); - assertDoesNotThrow(cached::hashCode); - assertDoesNotThrow((() -> cached.equals(cached))); - } - - @Test - void equality() { - IntFunction f0 = StableValue.intFunction(8, MAPPER); - IntFunction f1 = StableValue.intFunction(8, MAPPER); - // No function is equal to another function - assertNotEquals(f0, f1); - } - - @Test - void hashCodeStable() { - IntFunction f0 = StableValue.intFunction(8, MAPPER); - assertEquals(System.identityHashCode(f0), f0.hashCode()); - f0.apply(4); - assertEquals(System.identityHashCode(f0), f0.hashCode()); - } - -} diff --git a/test/jdk/java/lang/StableValue/StableMapTest.java b/test/jdk/java/lang/StableValue/StableMapTest.java deleted file mode 100644 index 86cf4ab3643ad..0000000000000 --- a/test/jdk/java/lang/StableValue/StableMapTest.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * 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 - * @summary Basic tests for StableMap methods - * @modules java.base/jdk.internal.lang.stable - * @enablePreview - * @run junit StableMapTest - */ - -import jdk.internal.lang.stable.StableUtil; -import jdk.internal.lang.stable.StableValueImpl; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.io.Serializable; -import java.util.AbstractMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.*; - -final class StableMapTest { - - private static final int NOT_PRESENT = 147; - private static final int KEY = 7; - private static final Set KEYS = Set.of(0, KEY, 13); - private static final Set EMPTY = Set.of(); - private static final Function IDENTITY = Function.identity(); - - @Test - void factoryInvariants() { - assertThrows(NullPointerException.class, () -> StableValue.map(KEYS, null)); - assertThrows(NullPointerException.class, () -> StableValue.map(null, IDENTITY)); - } - - @Test - void isEmpty() { - assertFalse(newMap().isEmpty()); - assertTrue(newEmptyMap().isEmpty()); - } - - @Test - void size() { - assertEquals(KEYS.size(), newMap().size()); - assertEquals(EMPTY.size(), newEmptyMap().size()); - } - - @Test - void get() { - StableTestUtil.CountingFunction cf = new StableTestUtil.CountingFunction<>(IDENTITY); - var lazy = StableValue.map(KEYS, cf); - int cnt = 1; - for (int i : KEYS) { - assertEquals(i, lazy.get(i)); - assertEquals(cnt, cf.cnt()); - assertEquals(i, lazy.get(i)); - assertEquals(cnt++, cf.cnt()); - } - assertNull(lazy.get(NOT_PRESENT)); - } - - @Test - void getException() { - StableTestUtil.CountingFunction cf = new StableTestUtil.CountingFunction<>(_ -> { - throw new UnsupportedOperationException(); - }); - var lazy = StableValue.map(KEYS, cf); - assertThrows(UnsupportedOperationException.class, () -> lazy.get(KEY)); - assertEquals(1, cf.cnt()); - assertThrows(UnsupportedOperationException.class, () -> lazy.get(KEY)); - assertEquals(2, cf.cnt()); - } - - @Test - void containsKey() { - var lazy = newMap(); - for (int i : KEYS) { - assertTrue(lazy.containsKey(i)); - } - assertFalse(lazy.containsKey(NOT_PRESENT)); - } - - @Test - void containsValue() { - var lazy = newMap(); - for (int i : KEYS) { - assertTrue(lazy.containsValue(i)); - } - assertFalse(lazy.containsValue(NOT_PRESENT)); - } - - @Test - void forEach() { - var lazy = newMap(); - Set> expected = KEYS.stream() - .map(i -> new AbstractMap.SimpleImmutableEntry<>(i , i)) - .collect(Collectors.toSet()); - Set> actual = new HashSet<>(); - lazy.forEach((k, v) -> actual.add(new AbstractMap.SimpleImmutableEntry<>(k , v))); - assertEquals(expected, actual); - } - - @Test - void toStringTest() { - assertEquals("{}", newEmptyMap().toString()); - var map = StableValue.map(Set.of(KEY), IDENTITY); - assertEquals("{" + KEY + "=.unset}", map.toString()); - map.get(KEY); - assertEquals("{" + KEY + "=" + KEY + "}", map.toString()); - String actual = newMap().toString(); - assertTrue(actual.startsWith("{")); - for (int key : KEYS) { - assertTrue(actual.contains(key + "=.unset")); - } - assertTrue(actual.endsWith("}")); - } - - @Test - void hashCodeTest() { - assertEquals(Map.of().hashCode(), newEmptyMap().hashCode()); - assertEquals(newRegularMap().hashCode(), newMap().hashCode()); - } - - @Test - void equalsTest() { - assertTrue(newEmptyMap().equals(Map.of())); - assertTrue(Map.of().equals(newEmptyMap())); - assertTrue(newMap().equals(newRegularMap())); - assertTrue(newRegularMap().equals(newMap())); - assertFalse(newMap().equals("A")); - } - - @Test - void entrySet() { - var regular = newRegularMap().entrySet(); - var actual = newMap().entrySet(); - assertTrue(regular.equals(actual)); - assertTrue(actual.equals(regular)); - assertTrue(regular.equals(actual)); - } - - @Test - void entrySetToString() { - var map = newMap(); - var entrySet = map.entrySet(); - var toString = entrySet.toString(); - for (var key : KEYS) { - assertTrue(toString.contains(key + "=.unset")); - } - assertTrue(toString.startsWith("[")); - assertTrue(toString.endsWith("]")); - - map.get(KEY); - for (var key : KEYS) { - if (key.equals(KEY)) { - continue; - } - assertTrue(entrySet.toString().contains(key + "=.unset")); - } - assertTrue(entrySet.toString().contains(KEY + "=" + KEY)); - } - - @Test - void values() { - var map = newMap(); - var values = map.values(); - // Look at one of the elements - var val = values.stream().iterator().next(); - var toString = map.toString(); - for (var key : KEYS) { - if (key.equals(val)) { - assertTrue(toString.contains(key + "=" + key)); - } else { - assertTrue(toString.contains(key + "=.unset")); - } - } - - // Mod ops - assertThrows(UnsupportedOperationException.class, () -> values.remove(KEY)); - assertThrows(UnsupportedOperationException.class, () -> values.add(KEY)); - assertThrows(UnsupportedOperationException.class, values::clear); - assertThrows(UnsupportedOperationException.class, () -> values.addAll(Set.of(1))); - assertThrows(UnsupportedOperationException.class, () -> values.removeIf(i -> true)); - assertThrows(UnsupportedOperationException.class, () -> values.retainAll(Set.of(KEY))); - } - - @Test - void valuesToString() { - var map = newMap(); - var values = map.values(); - assertEquals("[.unset, .unset, .unset]", values.toString()); - map.get(KEY); - var afterGet = values.toString(); - assertTrue(afterGet.contains(Integer.toString(KEY)), afterGet); - } - - @Test - void iteratorNext() { - Set encountered = new HashSet<>(); - var iterator = newMap().entrySet().iterator(); - while (iterator.hasNext()) { - var entry = iterator.next(); - assertEquals(entry.getKey(), entry.getValue()); - encountered.add(entry.getValue()); - } - assertEquals(KEYS, encountered); - } - - @Test - void iteratorForEachRemaining() { - Set encountered = new HashSet<>(); - var iterator = newMap().entrySet().iterator(); - var entry = iterator.next(); - assertEquals(entry.getKey(), entry.getValue()); - encountered.add(entry.getValue()); - iterator.forEachRemaining(e -> { - assertEquals(e.getKey(), e.getValue()); - encountered.add(e.getValue()); - }); - assertEquals(KEYS, encountered); - } - - @Test - void stableEntry() { - var map = newMap(); - var entry = map.entrySet().stream() - .filter(e -> e.getKey().equals(KEY)) - .findAny() - .orElseThrow(); - - assertEquals(KEY + "=.unset", entry.toString()); - var otherDifferent = Map.entry(-1, -1); - assertNotEquals(entry, otherDifferent); - assertEquals(KEY + "=.unset", entry.toString()); - var otherEqual = Map.entry(KEY, KEY); - assertEquals(entry, otherEqual); - assertEquals(KEY + "=" + KEY, entry.toString()); - assertEquals(entry.hashCode(), otherEqual.hashCode()); - } - - @Test - void stableForEachEntry() { - var map = newMap(); - // Only touch the key. - map.entrySet().iterator().forEachRemaining(Map.Entry::getKey); - map.entrySet().iterator() - .forEachRemaining(e -> assertTrue(e.toString().contains(".unset"))); - // Only touch the value. - map.entrySet().iterator().forEachRemaining(Map.Entry::getValue); - map.entrySet().iterator() - .forEachRemaining(e -> assertFalse(e.toString().contains(".unset"))); - } - - // Immutability - @ParameterizedTest - @MethodSource("unsupportedOperations") - void unsupported(Operation operation) { - assertThrowsForOperation(UnsupportedOperationException.class, operation); - } - - // Method parameter invariant checking - - @ParameterizedTest - @MethodSource("nullAverseOperations") - void nullAverse(Operation operation) { - assertThrowsForOperation(NullPointerException.class, operation); - } - - static void assertThrowsForOperation(Class expectedType, Operation operation) { - var lazy = newMap(); - assertThrows(expectedType, () -> operation.accept(lazy)); - } - - // Implementing interfaces - - @Test - void serializable() { - serializable(newMap()); - serializable(newEmptyMap()); - } - - void serializable(Map map) { - assertFalse(map instanceof Serializable); - assertFalse(map.entrySet() instanceof Serializable); - assertFalse(map.keySet() instanceof Serializable); - assertFalse(map.values() instanceof Serializable); - } - - @Test - void distinct() { - Map> map = StableUtil.map(Set.of(1, 2, 3)); - assertEquals(3, map.size()); - // Check, every StableValue is distinct - Map, Boolean> idMap = new IdentityHashMap<>(); - map.forEach((k, v) -> idMap.put(v, true)); - assertEquals(3, idMap.size()); - } - - @Test - void nullResult() { - var map = StableValue.map(Set.of(0), _ -> null); - assertNull(map.getOrDefault(0, 1));; - assertTrue(map.containsKey(0)); - assertNull(map.get(0)); - } - - @Test - void nullKeys() { - Set inputs = new HashSet<>(); - inputs.add(0); - inputs.add(null); - assertThrows(NullPointerException.class, () -> StableValue.map(inputs, IDENTITY)); - } - - // Support constructs - - record Operation(String name, - Consumer> consumer) implements Consumer> { - @java.lang.Override - public void accept(Map map) { consumer.accept(map); } - @java.lang.Override - public String toString() { return name; } - } - - static Stream nullAverseOperations() { - return Stream.of( - new Operation("forEach", m -> m.forEach(null)) - ); - } - - static Stream unsupportedOperations() { - return Stream.of( - new Operation("clear", Map::clear), - new Operation("compute", m -> m.compute(KEY, (_, _) -> 1)), - new Operation("computeIfAbsent", m -> m.computeIfAbsent(KEY, _ -> 1)), - new Operation("computeIfPresent", m -> m.computeIfPresent(KEY, (_, _) -> 1)), - new Operation("merge", m -> m.merge(KEY, KEY, (a, _) -> a)), - new Operation("put", m -> m.put(0, 0)), - new Operation("putAll", m -> m.putAll(Map.of())), - new Operation("remove1", m -> m.remove(KEY)), - new Operation("remove2", m -> m.remove(KEY, KEY)), - new Operation("replace2", m -> m.replace(KEY, 1)), - new Operation("replace3", m -> m.replace(KEY, KEY, 1)), - new Operation("replaceAll", m -> m.replaceAll((a, _) -> a)) - ); - } - - static Map newMap() { - return StableValue.map(KEYS, IDENTITY); - } - - static Map newEmptyMap() { - return StableValue.map(EMPTY, IDENTITY); - } - - static Map newRegularMap() { - return KEYS.stream().collect(Collectors.toMap(IDENTITY, IDENTITY)); - } - -} diff --git a/test/jdk/java/lang/StableValue/StableSupplierTest.java b/test/jdk/java/lang/StableValue/StableSupplierTest.java deleted file mode 100644 index 2d542fbf6cacf..0000000000000 --- a/test/jdk/java/lang/StableValue/StableSupplierTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 - * @summary Basic tests for StableSupplier methods - * @enablePreview - * @run junit StableSupplierTest - */ - -import org.junit.jupiter.api.Test; - -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Supplier; - -import static org.junit.jupiter.api.Assertions.*; - -final class StableSupplierTest { - - private static final Supplier SUPPLIER = () -> 42; - - @Test - void factoryInvariants() { - assertThrows(NullPointerException.class, () -> StableValue.supplier(null)); - } - - @Test - void basic() { - basic(SUPPLIER); - basic(() -> null); - } - - void basic(Supplier supplier) { - StableTestUtil.CountingSupplier cs = new StableTestUtil.CountingSupplier<>(supplier); - var cached = StableValue.supplier(cs); - assertEquals(".unset", cached.toString()); - assertEquals(supplier.get(), cached.get()); - assertEquals(1, cs.cnt()); - assertEquals(supplier.get(), cached.get()); - assertEquals(1, cs.cnt()); - assertEquals(Objects.toString(supplier.get()), cached.toString()); - } - - @Test - void exception() { - StableTestUtil.CountingSupplier cs = new StableTestUtil.CountingSupplier<>(() -> { - throw new UnsupportedOperationException(); - }); - var cached = StableValue.supplier(cs); - assertThrows(UnsupportedOperationException.class, cached::get); - assertEquals(1, cs.cnt()); - assertThrows(UnsupportedOperationException.class, cached::get); - assertEquals(2, cs.cnt()); - assertEquals(".unset", cached.toString()); - } - - @Test - void circular() { - final AtomicReference> ref = new AtomicReference<>(); - Supplier> cached = StableValue.supplier(ref::get); - ref.set(cached); - cached.get(); - String toString = cached.toString(); - assertTrue(toString.startsWith("(this StableSupplier)")); - assertDoesNotThrow(cached::hashCode); - } - - @Test - void equality() { - Supplier f0 = StableValue.supplier(SUPPLIER); - Supplier f1 = StableValue.supplier(SUPPLIER); - // No function is equal to another function - assertNotEquals(f0, f1); - } - - @Test - void hashCodeStable() { - Supplier f0 = StableValue.supplier(SUPPLIER); - assertEquals(System.identityHashCode(f0), f0.hashCode()); - f0.get(); - assertEquals(System.identityHashCode(f0), f0.hashCode()); - } - -} diff --git a/test/jdk/java/lang/StableValue/StableValueFactoriesTest.java b/test/jdk/java/lang/StableValue/StableValueFactoriesTest.java deleted file mode 100644 index 85aee0cbeec66..0000000000000 --- a/test/jdk/java/lang/StableValue/StableValueFactoriesTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 - * @summary Basic tests for StableValue factory implementations - * @modules java.base/jdk.internal.lang.stable - * @enablePreview - * @run junit StableValueFactoriesTest - */ - -import jdk.internal.lang.stable.StableUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -final class StableValueFactoriesTest { - - @Test - void array() { - assertThrows(IllegalArgumentException.class, () -> StableUtil.array(-1)); - } - -} diff --git a/test/jdk/java/lang/StableValue/StableValueTest.java b/test/jdk/java/lang/StableValue/StableValueTest.java deleted file mode 100644 index 4290c8716a046..0000000000000 --- a/test/jdk/java/lang/StableValue/StableValueTest.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * 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 - * @summary Basic tests for StableValue implementations - * @enablePreview - * @run junit StableValueTest - */ - -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.LockSupport; -import java.util.function.BiPredicate; -import java.util.function.Function; -import java.util.function.IntFunction; -import java.util.function.UnaryOperator; -import java.util.stream.IntStream; - -import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertEquals; - -final class StableValueTest { - - private static final int VALUE = 42; - private static final int VALUE2 = 13; - - @Test - void trySet() { - trySet(VALUE); - trySet(null); - } - - @Test - void preSet() { - StableValue stable = StableValue.of(VALUE); - assertTrue(stable.isSet()); - assertEquals(VALUE, stable.orElseThrow()); - assertEquals(VALUE, stable.orElse(VALUE2)); - assertEquals(VALUE, stable.orElseSet(() -> VALUE2)); - assertFalse(stable.trySet(VALUE2)); - var e = assertThrows(IllegalStateException.class, () -> stable.setOrThrow(VALUE2)); - assertEquals( - "The contents is already set", - e.getMessage()); - } - - void trySet(Integer initial) { - StableValue stable = StableValue.of(); - assertTrue(stable.trySet(initial)); - assertFalse(stable.trySet(null)); - assertFalse(stable.trySet(VALUE)); - assertFalse(stable.trySet(VALUE2)); - assertEquals(initial, stable.orElseThrow()); - } - - @Test - void setOrThrowValue() { - StableValue stable = StableValue.of(); - stable.setOrThrow(VALUE); - var e = assertThrows(IllegalStateException.class, () -> stable.setOrThrow(VALUE2)); - assertEquals("The contents is already set", e.getMessage()); - } - - @Test - void setOrThrowNull() { - StableValue stable = StableValue.of(); - stable.setOrThrow(null); - var e = assertThrows(IllegalStateException.class, () -> stable.setOrThrow(null)); - assertEquals("The contents is already set", e.getMessage()); - } - - @Test - void orElse() { - StableValue stable = StableValue.of(); - assertEquals(VALUE, stable.orElse(VALUE)); - assertNull(stable.orElse(null)); - stable.trySet(VALUE); - assertEquals(VALUE, stable.orElse(VALUE2)); - } - - @Test - void orElseThrow() { - StableValue stable = StableValue.of(); - var e = assertThrows(NoSuchElementException.class, stable::orElseThrow); - assertEquals("No contents set", e.getMessage()); - stable.trySet(VALUE); - assertEquals(VALUE, stable.orElseThrow()); - } - - @Test - void isSet() { - isSet(VALUE); - isSet(null); - } - - void isSet(Integer initial) { - StableValue stable = StableValue.of(); - assertFalse(stable.isSet()); - stable.trySet(initial); - assertTrue(stable.isSet()); - } - - @Test - void testOrElseSetSupplier() { - StableTestUtil.CountingSupplier cs = new StableTestUtil.CountingSupplier<>(() -> VALUE); - StableValue stable = StableValue.of(); - assertThrows(NullPointerException.class, () -> stable.orElseSet(null)); - assertEquals(VALUE, stable.orElseSet(cs)); - assertEquals(1, cs.cnt()); - assertEquals(VALUE, stable.orElseSet(cs)); - assertEquals(1, cs.cnt()); - } - - @Test - void testHashCode() { - StableValue stableValue = StableValue.of(); - // Should be Object::hashCode - assertEquals(System.identityHashCode(stableValue), stableValue.hashCode()); - } - - @Test - void testEquals() { - StableValue s0 = StableValue.of(); - assertNotEquals(null, s0); - StableValue s1 = StableValue.of(); - assertNotEquals(s0, s1); // Identity based - s0.setOrThrow(42); - s1.setOrThrow(42); - assertNotEquals(s0, s1); - assertNotEquals("a", s0); - StableValue null0 = StableValue.of(); - StableValue null1 = StableValue.of(); - null0.setOrThrow(null); - null1.setOrThrow(null); - assertNotEquals(null0, null1); - } - - @Test - void toStringUnset() { - StableValue stable = StableValue.of(); - assertEquals(".unset", stable.toString()); - } - - @Test - void toStringNull() { - StableValue stable = StableValue.of(); - assertTrue(stable.trySet(null)); - assertEquals("null", stable.toString()); - } - - @Test - void toStringNonNull() { - StableValue stable = StableValue.of(); - assertTrue(stable.trySet(VALUE)); - assertEquals(Objects.toString(VALUE), stable.toString()); - } - - @Test - void toStringCircular() { - StableValue> stable = StableValue.of(); - stable.trySet(stable); - String toString = assertDoesNotThrow(stable::toString); - assertEquals("(this StableValue)", toString); - assertDoesNotThrow(stable::hashCode); - assertDoesNotThrow((() -> stable.equals(stable))); - } - - @Test - void recursiveCall() { - StableValue stable = StableValue.of(); - AtomicReference> ref = new AtomicReference<>(stable); - assertThrows(IllegalStateException.class, () -> - stable.orElseSet(() -> { - ref.get().trySet(1); - return 1; - }) - ); - assertThrows(IllegalStateException.class, () -> - stable.orElseSet(() -> { - ref.get().orElseSet(() -> 1); - return 1; - }) - ); - } - - @Test - void intFunctionExample() { - final class SqrtUtil { - - private SqrtUtil() {} - - private static final int CACHED_SIZE = 10; - - private static final IntFunction SQRT = - // @link substring="intFunction" target="#intFunction(int,IntFunction)" : - StableValue.intFunction(CACHED_SIZE, StrictMath::sqrt); - - public static double sqrt(int a) { - return SQRT.apply(a); - } - } - - double sqrt9 = SqrtUtil.sqrt(9); // May eventually constant fold to 3.0 at runtime - - assertEquals(3, sqrt9); - assertThrows(IllegalArgumentException.class, () -> SqrtUtil.sqrt(16)); - } - - @Test - void intFunctionExample2() { - final class PowerOf2Util { - - private PowerOf2Util() {} - - private static final int SIZE = 6; - private static final IntFunction ORIGINAL_POWER_OF_TWO = - v -> 1 << v; - - private static final IntFunction POWER_OF_TWO = - // @link substring="intFunction" target="#intFunction(int,IntFunction)" : - StableValue.intFunction(SIZE, ORIGINAL_POWER_OF_TWO); - - public static int powerOfTwo(int a) { - return POWER_OF_TWO.apply(a); - } - } - - int pwr4 = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime - - assertEquals(16, pwr4); - assertEquals(1, PowerOf2Util.powerOfTwo(0)); - assertEquals(8, PowerOf2Util.powerOfTwo(3)); - assertEquals(32, PowerOf2Util.powerOfTwo(5)); - assertThrows(IllegalArgumentException.class, () -> PowerOf2Util.powerOfTwo(10)); - } - - @Test - void functionExample() { - - class Log2Util { - - private Log2Util() {} - - private static final Set CACHED_KEYS = - Set.of(1, 2, 4, 8, 16, 32); - private static final UnaryOperator LOG2_ORIGINAL = - i -> 31 - Integer.numberOfLeadingZeros(i); - - private static final Function LOG2_CACHED = - // @link substring="function" target="#function(Set,Function)" : - StableValue.function(CACHED_KEYS, LOG2_ORIGINAL); - - public static double log2(int a) { - if (CACHED_KEYS.contains(a)) { - return LOG2_CACHED.apply(a); - } else { - return LOG2_ORIGINAL.apply(a); - } - } - - } - - double log16 = Log2Util.log2(16); // May eventually constant fold to 4.0 at runtime - double log256 = Log2Util.log2(256); // Will not constant fold - - assertEquals(4, log16); - assertEquals(8, log256); - } - - @Test - void functionExample2() { - - class Log2Util { - - private Log2Util() {} - - private static final Set KEYS = - Set.of(1, 2, 4, 8); - private static final UnaryOperator LOG2_ORIGINAL = - i -> 31 - Integer.numberOfLeadingZeros(i); - - private static final Function LOG2 = - // @link substring="function" target="#function(Set,Function)" : - StableValue.function(KEYS, LOG2_ORIGINAL); - - public static double log2(int a) { - return LOG2.apply(a); - } - - } - - double log16 = Log2Util.log2(8); // May eventually constant fold to 3.0 at runtime - - assertEquals(3, log16); - assertThrows(IllegalArgumentException.class, () -> Log2Util.log2(3)); - } - - private static final BiPredicate, Integer> TRY_SET = StableValue::trySet; - private static final BiPredicate, Integer> SET_OR_THROW = (s, i) -> { - try { - s.setOrThrow(i); - return true; - } catch (IllegalStateException e) { - return false; - } - }; - - @Test - void raceTrySet() { - race(TRY_SET); - } - - @Test - void raceSetOrThrow() { - race(SET_OR_THROW); - } - - @Test - void raceMixed() { - race((s, i) -> switch (i % 2) { - case 0 -> TRY_SET.test(s, i); - case 1 -> SET_OR_THROW.test(s, i); - default -> fail("should not reach here"); - }); - } - - void race(BiPredicate, Integer> winnerPredicate) { - int noThreads = 10; - CountDownLatch starter = new CountDownLatch(noThreads); - StableValue stable = StableValue.of(); - Map winners = new ConcurrentHashMap<>(); - List threads = IntStream.range(0, noThreads).mapToObj(i -> new Thread(() -> { - try { - // Ready ... - starter.countDown(); - // ... set ... - starter.await(); - // Here we go! - winners.put(i, winnerPredicate.test(stable, i)); - } catch (Throwable t) { - fail(t); - } - })) - .toList(); - threads.forEach(Thread::start); - threads.forEach(StableValueTest::join); - // There can only be one winner - assertEquals(1, winners.values().stream().filter(b -> b).count()); - } - - private static void join(Thread thread) { - try { - thread.join(); - } catch (InterruptedException e) { - fail(e); - } - } - -} diff --git a/test/jdk/java/util/Collection/MOAT.java b/test/jdk/java/util/Collection/MOAT.java index ab9e4b4309d0e..d0d27c8f91e14 100644 --- a/test/jdk/java/util/Collection/MOAT.java +++ b/test/jdk/java/util/Collection/MOAT.java @@ -220,15 +220,15 @@ public static void realMain(String[] args) { // Immutable List testEmptyList(List.of()); testEmptyList(List.of().subList(0,0)); - testEmptyList(StableValue.list(0, i -> i)); - testEmptyList(StableValue.list(3, i -> i).subList(0, 0)); + testEmptyList(List.ofLazy(0, i -> i)); + testEmptyList(List.ofLazy(3, i -> i).subList(0, 0)); testListMutatorsAlwaysThrow(List.of()); testListMutatorsAlwaysThrow(List.of().subList(0,0)); - testListMutatorsAlwaysThrow(StableValue.list(0, i -> i)); + testListMutatorsAlwaysThrow(List.ofLazy(0, i -> i)); testEmptyListMutatorsAlwaysThrow(List.of()); testEmptyListMutatorsAlwaysThrow(List.of().subList(0,0)); - testEmptyListMutatorsAlwaysThrow(StableValue.list(0, i -> i)); - testEmptyListMutatorsAlwaysThrow(StableValue.list(3, i -> i).subList(0, 0)); + testEmptyListMutatorsAlwaysThrow(List.ofLazy(0, i -> i)); + testEmptyListMutatorsAlwaysThrow(List.ofLazy(3, i -> i).subList(0, 0)); for (List list : Arrays.asList( List.of(), List.of(1), @@ -251,9 +251,9 @@ public static void realMain(String[] args) { Stream.of(1, null).toList(), Stream.of(1, null, 3).toList(), Stream.of(1, null, 3, 4).toList(), - StableValue.list(0, i -> i), - StableValue.list(3, i -> i), - StableValue.list(10, i -> i))) { + List.ofLazy(0, i -> i), + List.ofLazy(3, i -> i), + List.ofLazy(10, i -> i))) { testCollection(list); testImmutableList(list); testListMutatorsAlwaysThrow(list); @@ -365,9 +365,9 @@ public static void realMain(String[] args) { testEmptyMap(Map.of()); testMapMutatorsAlwaysThrow(Map.of()); testEmptyMapMutatorsAlwaysThrow(Map.of()); - testEmptyMap(StableValue.map(Set.of(), k -> k)); - testMapMutatorsAlwaysThrow(StableValue.map(Set.of(), k -> k)); - testEmptyMapMutatorsAlwaysThrow(StableValue.map(Set.of(), k -> k)); + testEmptyMap(Map.ofLazy(Set.of(), k -> k)); + testMapMutatorsAlwaysThrow(Map.ofLazy(Set.of(), k -> k)); + testEmptyMapMutatorsAlwaysThrow(Map.ofLazy(Set.of(), k -> k)); for (Map map : Arrays.asList( Map.of(), Map.of(1, 101), @@ -381,9 +381,9 @@ public static void realMain(String[] args) { Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707, 8, 808, 9, 909), Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707, 8, 808, 9, 909, 10, 1010), Map.ofEntries(ea), - StableValue.map(Set.of(), k -> k), - StableValue.map(Set.of(1), k -> k), - StableValue.map(Set.of(1, 2, 3), k -> k))) { + Map.ofLazy(Set.of(), k -> k), + Map.ofLazy(Set.of(1), k -> k), + Map.ofLazy(Set.of(1, 2, 3), k -> k))) { testMap(map); testImmutableMap(map); testMapMutatorsAlwaysThrow(map); diff --git a/test/langtools/jdk/jshell/CompletionSuggestionTest.java b/test/langtools/jdk/jshell/CompletionSuggestionTest.java index d31a32b63f84a..7a960258cc1e8 100644 --- a/test/langtools/jdk/jshell/CompletionSuggestionTest.java +++ b/test/langtools/jdk/jshell/CompletionSuggestionTest.java @@ -947,7 +947,7 @@ public void testAnnotation() { public void testMultiSnippet() { assertCompletion("String s = \"\"; s.len|", true, "length()"); assertCompletion("String s() { return \"\"; } s().len|", true, "length()"); - assertCompletion("String s() { return \"\"; } import java.util.List; List.o|", true, "of("); + assertCompletion("String s() { return \"\"; } import java.util.List; List.o|", true, "of(", "ofLazy("); assertCompletion("String s() { return \"\"; } import java.ut| ", true, "util."); assertCompletion("class S { public int length() { return 0; } } new S().len|", true, "length()"); assertSignature("void f() { } f(|", "void f()"); diff --git a/test/micro/org/openjdk/bench/java/lang/stable/StableIntFunctionBenchmark.java b/test/micro/org/openjdk/bench/java/lang/stable/StableListBenchmark.java similarity index 86% rename from test/micro/org/openjdk/bench/java/lang/stable/StableIntFunctionBenchmark.java rename to test/micro/org/openjdk/bench/java/lang/stable/StableListBenchmark.java index 0b8e5d97cac71..67a6075568216 100644 --- a/test/micro/org/openjdk/bench/java/lang/stable/StableIntFunctionBenchmark.java +++ b/test/micro/org/openjdk/bench/java/lang/stable/StableListBenchmark.java @@ -40,7 +40,7 @@ import java.util.function.IntFunction; /** - * Benchmark measuring StableValue performance + * Benchmark measuring stable list performance */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -52,16 +52,16 @@ }) @Threads(Threads.MAX) // Benchmark under contention @OperationsPerInvocation(100) -public class StableIntFunctionBenchmark { +public class StableListBenchmark { private static final int SIZE = 100; private static final IntFunction IDENTITY = i -> i; - private static final List LIST = StableValue.list(SIZE, IDENTITY); - private static final IntFunction INT_FUNCTION = StableValue.intFunction(SIZE, IDENTITY); + private static final List LIST = List.ofLazy(SIZE, IDENTITY); + private static final IntFunction INT_FUNCTION = LIST::get; - private final List list = StableValue.list(SIZE, IDENTITY); - private final IntFunction intFunction = StableValue.intFunction(SIZE, IDENTITY); + private final List list = List.ofLazy(SIZE, IDENTITY); + private final IntFunction intFunction = list::get; @Benchmark public int list() { diff --git a/test/micro/org/openjdk/bench/java/lang/stable/StableIntFunctionSingleBenchmark.java b/test/micro/org/openjdk/bench/java/lang/stable/StableListSingleBenchmark.java similarity index 84% rename from test/micro/org/openjdk/bench/java/lang/stable/StableIntFunctionSingleBenchmark.java rename to test/micro/org/openjdk/bench/java/lang/stable/StableListSingleBenchmark.java index 1e8e250ba8a52..9210d8a86bec3 100644 --- a/test/micro/org/openjdk/bench/java/lang/stable/StableIntFunctionSingleBenchmark.java +++ b/test/micro/org/openjdk/bench/java/lang/stable/StableListSingleBenchmark.java @@ -39,7 +39,7 @@ import java.util.function.IntFunction; /** - * Benchmark measuring StableValue performance + * Benchmark measuring stable list performance */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -50,16 +50,16 @@ "--enable-preview" }) @Threads(Threads.MAX) // Benchmark under contention -public class StableIntFunctionSingleBenchmark { +public class StableListSingleBenchmark { private static final int SIZE = 100; private static final IntFunction IDENTITY = i -> i; - private static final List STABLE = StableValue.list(SIZE, IDENTITY); - private static final IntFunction INT_FUNCTION = StableValue.intFunction(SIZE, IDENTITY); + private static final List STABLE = List.ofLazy(SIZE, IDENTITY); + private static final IntFunction INT_FUNCTION = STABLE::get; - private final List stable = StableValue.list(SIZE, IDENTITY); - private final IntFunction intFunction = StableValue.intFunction(SIZE, IDENTITY); + private final List stable = List.ofLazy(SIZE, IDENTITY); + private final IntFunction intFunction = stable::get; @Benchmark public int list() { diff --git a/test/micro/org/openjdk/bench/java/lang/stable/StableFunctionBenchmark.java b/test/micro/org/openjdk/bench/java/lang/stable/StableMapBenchmark.java similarity index 85% rename from test/micro/org/openjdk/bench/java/lang/stable/StableFunctionBenchmark.java rename to test/micro/org/openjdk/bench/java/lang/stable/StableMapBenchmark.java index 44fd3f2c18e6e..2b8e90eee1729 100644 --- a/test/micro/org/openjdk/bench/java/lang/stable/StableFunctionBenchmark.java +++ b/test/micro/org/openjdk/bench/java/lang/stable/StableMapBenchmark.java @@ -43,7 +43,7 @@ import java.util.stream.IntStream; /** - * Benchmark measuring StableValue performance + * Benchmark measuring stable map performance */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -55,16 +55,16 @@ }) @Threads(Threads.MAX) // Benchmark under contention @OperationsPerInvocation(100) -public class StableFunctionBenchmark { +public class StableMapBenchmark { private static final int SIZE = 100; private static final Set SET = IntStream.range(0, SIZE).boxed().collect(Collectors.toSet()); - private static final Map MAP = StableValue.map(SET, Function.identity()); - private static final Function FUNCTION = StableValue.function(SET, Function.identity()); + private static final Map MAP = Map.ofLazy(SET, Function.identity()); + private static final Function FUNCTION = MAP::get; - private final Map map = StableValue.map(SET, Function.identity()); - private final Function function = StableValue.function(SET, Function.identity()); + private final Map map = Map.ofLazy(SET, Function.identity()); + private final Function function = map::get; @Benchmark public int map() { @@ -85,7 +85,7 @@ public int function() { } @Benchmark - public int staticSMap() { + public int staticMap() { int sum = 0; for (int i = 0; i < SIZE; i++) { sum += MAP.get(i); diff --git a/test/micro/org/openjdk/bench/java/lang/stable/StableFunctionSingleBenchmark.java b/test/micro/org/openjdk/bench/java/lang/stable/StableMapSingleBenchmark.java similarity index 70% rename from test/micro/org/openjdk/bench/java/lang/stable/StableFunctionSingleBenchmark.java rename to test/micro/org/openjdk/bench/java/lang/stable/StableMapSingleBenchmark.java index 1cb1a04582f8f..4965ec649ebac 100644 --- a/test/micro/org/openjdk/bench/java/lang/stable/StableFunctionSingleBenchmark.java +++ b/test/micro/org/openjdk/bench/java/lang/stable/StableMapSingleBenchmark.java @@ -28,14 +28,15 @@ import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OperationsPerInvocation; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; +import java.util.EnumSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -43,7 +44,7 @@ import java.util.stream.IntStream; /** - * Benchmark measuring StableValue performance + * Benchmark measuring lazy map performance */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -54,16 +55,17 @@ "--enable-preview" }) @Threads(Threads.MAX) // Benchmark under contention -public class StableFunctionSingleBenchmark { +public class StableMapSingleBenchmark { private static final int SIZE = 100; private static final Set SET = IntStream.range(0, SIZE).boxed().collect(Collectors.toSet()); - private static final Map MAP = StableValue.map(SET, Function.identity()); - private static final Function FUNCTION = StableValue.function(SET, Function.identity()); + private static final Map MAP = Map.ofLazy(SET, Function.identity()); + private static final Map MAP_ENUM = Map.ofLazy(EnumSet.allOf(MyEnum.class), MyEnum::ordinal); + private static final Map> MAP_ENUM_OPTIONAL = Map.ofLazy(EnumSet.allOf(MyEnum.class), e -> Optional.of(e.ordinal())); - private final Map map = StableValue.map(SET, Function.identity()); - private final Function function = StableValue.function(SET, Function.identity()); + private final Map map = Map.ofLazy(SET, Function.identity()); + private final Map mapEnum = Map.ofLazy(EnumSet.allOf(MyEnum.class), MyEnum::ordinal); @Benchmark public int map() { @@ -71,18 +73,25 @@ public int map() { } @Benchmark - public int function() { - return function.apply(1); + public int mapEnum() { + return mapEnum.get(MyEnum.BAR); } @Benchmark - public int staticSMap() { + public int staticMap() { return MAP.get(1); } @Benchmark - public int staticIntFunction() { - return FUNCTION.apply(1); + public int staticMapEnum() { + return MAP_ENUM.get(MyEnum.BAR); } + @Benchmark + public int staticMapEnumOptional() { + return MAP_ENUM_OPTIONAL.get(MyEnum.BAR).orElseThrow(); + } + + private enum MyEnum {FOO, BAR} + } diff --git a/test/micro/org/openjdk/bench/java/lang/stable/StableMethodHandleBenchmark.java b/test/micro/org/openjdk/bench/java/lang/stable/StableMethodHandleBenchmark.java index 95721d88cec57..c61e7b1861f19 100644 --- a/test/micro/org/openjdk/bench/java/lang/stable/StableMethodHandleBenchmark.java +++ b/test/micro/org/openjdk/bench/java/lang/stable/StableMethodHandleBenchmark.java @@ -35,22 +35,18 @@ import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; -import java.lang.classfile.CodeBuilder; -import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; - -import static java.lang.constant.ConstantDescs.*; +import java.lang.LazyConstant; /** - * Benchmark measuring StableValue performance + * Benchmark measuring lazy value performance */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -64,17 +60,15 @@ public class StableMethodHandleBenchmark { private static final MethodHandle FINAL_MH = identityHandle(); - private static final StableValue STABLE_MH; + private static final LazyConstant STABLE_MH = LazyConstant.of(StableMethodHandleBenchmark::identityHandle); private static /* intentionally not final */ MethodHandle mh = identityHandle(); private static final Dcl DCL = new Dcl<>(StableMethodHandleBenchmark::identityHandle); private static final AtomicReference ATOMIC_REFERENCE = new AtomicReference<>(identityHandle()); private static final Map MAP = new ConcurrentHashMap<>(); - private static final Map STABLE_MAP = StableValue.map(Set.of("identityHandle"), _ -> identityHandle()); + private static final Map STABLE_MAP = Map.ofLazy(Set.of("identityHandle"), _ -> identityHandle()); static { - STABLE_MH = StableValue.of(); - STABLE_MH.setOrThrow(identityHandle()); MAP.put("identityHandle", identityHandle()); } @@ -110,7 +104,7 @@ public int stableMap() throws Throwable { @Benchmark public int stableMh() throws Throwable { - return (int) STABLE_MH.orElseThrow().invokeExact(1); + return (int) STABLE_MH.get().invokeExact(1); } static MethodHandle identityHandle() { diff --git a/test/micro/org/openjdk/bench/java/lang/stable/StableSupplierBenchmark.java b/test/micro/org/openjdk/bench/java/lang/stable/StableSupplierBenchmark.java index 883f13da05aff..59aaa9c014eeb 100644 --- a/test/micro/org/openjdk/bench/java/lang/stable/StableSupplierBenchmark.java +++ b/test/micro/org/openjdk/bench/java/lang/stable/StableSupplierBenchmark.java @@ -36,10 +36,10 @@ import org.openjdk.jmh.annotations.Warmup; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; +import java.lang.LazyConstant; /** - * Benchmark measuring StableValue performance + * Benchmark measuring lazy constant performance */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -56,39 +56,24 @@ public class StableSupplierBenchmark { private static final int VALUE = 42; private static final int VALUE2 = 23; - private static final StableValue STABLE = init(StableValue.of(), VALUE); - private static final StableValue STABLE2 = init(StableValue.of(), VALUE2); - private static final Supplier SUPPLIER = StableValue.supplier(() -> VALUE); - private static final Supplier SUPPLIER2 = StableValue.supplier(() -> VALUE); + private static final LazyConstant STABLE = init(VALUE); + private static final LazyConstant STABLE2 = init(VALUE2); - private final StableValue stable = init(StableValue.of(), VALUE); - private final StableValue stable2 = init(StableValue.of(), VALUE2); - private final Supplier supplier = StableValue.supplier(() -> VALUE); - private final Supplier supplier2 = StableValue.supplier(() -> VALUE2); + private final LazyConstant stable = init(VALUE); + private final LazyConstant stable2 = init(VALUE2); @Benchmark public int stable() { - return stable.orElseThrow() + stable2.orElseThrow(); - } - - @Benchmark - public int supplier() { - return supplier.get() + supplier2.get(); + return stable.get() + stable2.get(); } @Benchmark public int staticStable() { - return STABLE.orElseThrow() + STABLE2.orElseThrow(); - } - - @Benchmark - public int staticSupplier() { - return SUPPLIER.get() + SUPPLIER2.get(); + return STABLE.get() + STABLE2.get(); } - private static StableValue init(StableValue m, Integer value) { - m.trySet(value); - return m; + private static LazyConstant init(Integer value) { + return LazyConstant.of(() -> value); } } diff --git a/test/micro/org/openjdk/bench/java/lang/stable/StableValueBenchmark.java b/test/micro/org/openjdk/bench/java/lang/stable/StableValueBenchmark.java index 505cfffdc2ca4..5323af36c2272 100644 --- a/test/micro/org/openjdk/bench/java/lang/stable/StableValueBenchmark.java +++ b/test/micro/org/openjdk/bench/java/lang/stable/StableValueBenchmark.java @@ -25,12 +25,14 @@ import org.openjdk.jmh.annotations.*; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.lang.LazyConstant; import java.util.function.Supplier; /** - * Benchmark measuring StableValue performance + * Benchmark measuring lazy constant performance */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -47,10 +49,10 @@ public class StableValueBenchmark { private static final int VALUE = 42; private static final int VALUE2 = 23; - private static final StableValue STABLE = init(StableValue.of(), VALUE); - private static final StableValue STABLE2 = init(StableValue.of(), VALUE2); - private static final StableValue DCL = init(StableValue.of(), VALUE); - private static final StableValue DCL2 = init(StableValue.of(), VALUE2); + private static final LazyConstant STABLE = init(VALUE); + private static final LazyConstant STABLE2 = init(VALUE2); + private static final Supplier DCL = new Dcl<>(() -> VALUE); + private static final Supplier DCL2 = new Dcl<>(() -> VALUE2); private static final AtomicReference ATOMIC = new AtomicReference<>(VALUE); private static final AtomicReference ATOMIC2 = new AtomicReference<>(VALUE2); private static final Holder HOLDER = new Holder(VALUE); @@ -58,10 +60,13 @@ public class StableValueBenchmark { private static final RecordHolder RECORD_HOLDER = new RecordHolder(VALUE); private static final RecordHolder RECORD_HOLDER2 = new RecordHolder(VALUE2); - private final StableValue stable = init(StableValue.of(), VALUE); - private final StableValue stable2 = init(StableValue.of(), VALUE2); - private final StableValue stableNull = StableValue.of(); - private final StableValue stableNull2 = StableValue.of(); + private static final LazyConstant> OPTIONAL_42 = LazyConstant.of(() -> Optional.of(42)); + private static final LazyConstant> OPTIONAL_42_2 = LazyConstant.of(() -> Optional.of(42)); + private static final LazyConstant> OPTIONAL_EMPTY = LazyConstant.of(Optional::empty); + private static final LazyConstant> OPTIONAL_EMPTY2 = LazyConstant.of(Optional::empty); + + private final LazyConstant stable = init(VALUE); + private final LazyConstant stable2 = init(VALUE2); private final Supplier dcl = new Dcl<>(() -> VALUE); private final Supplier dcl2 = new Dcl<>(() -> VALUE2); private final AtomicReference atomic = new AtomicReference<>(VALUE); @@ -69,13 +74,6 @@ public class StableValueBenchmark { private final Supplier supplier = () -> VALUE; private final Supplier supplier2 = () -> VALUE2; - - @Setup - public void setup() { - stableNull.trySet(null); - stableNull2.trySet(null); - } - @Benchmark public int atomic() { return atomic.get() + atomic2.get(); @@ -88,12 +86,7 @@ public int dcl() { @Benchmark public int stable() { - return stable.orElseThrow() + stable2.orElseThrow(); - } - - @Benchmark - public int stableNull() { - return (stableNull.orElseThrow() == null ? VALUE : VALUE2) + (stableNull2.orElseThrow() == null ? VALUE : VALUE2); + return stable.get() + stable2.get(); } // Reference case @@ -109,7 +102,7 @@ public int staticAtomic() { @Benchmark public int staticDcl() { - return DCL.orElseThrow() + DCL2.orElseThrow(); + return DCL.get() + DCL2.get(); } @Benchmark @@ -117,6 +110,16 @@ public int staticHolder() { return HOLDER.get() + HOLDER2.get(); } + @Benchmark + public int staticOptional42() { + return OPTIONAL_42.get().orElseThrow() + OPTIONAL_42_2.get().orElseThrow(); + } + + @Benchmark + public boolean staticOptionalEmpty() { + return OPTIONAL_EMPTY.get().isEmpty() ^ OPTIONAL_EMPTY2.get().isEmpty(); + } + @Benchmark public int staticRecordHolder() { return RECORD_HOLDER.get() + RECORD_HOLDER2.get(); @@ -124,50 +127,46 @@ public int staticRecordHolder() { @Benchmark public int staticStable() { - return STABLE.orElseThrow() + STABLE2.orElseThrow(); + return STABLE.get() + STABLE2.get(); } - private static StableValue init(StableValue m, Integer value) { - m.trySet(value); - return m; + private static LazyConstant init(Integer value) { + return LazyConstant.of(() -> value); } private static final class Holder { - private final StableValue delegate = StableValue.of(); + private final LazyConstant delegate; Holder(int value) { - delegate.setOrThrow(value); + delegate = LazyConstant.of(() -> value); } int get() { - return delegate.orElseThrow(); + return delegate.get(); } } - private record RecordHolder(StableValue delegate) { + private record RecordHolder(LazyConstant delegate) { RecordHolder(int value) { - this(StableValue.of()); - delegate.setOrThrow(value); + this(LazyConstant.of(() -> value)); } int get() { - return delegate.orElseThrow(); + return delegate.get(); } } - // Handles null values public static class Dcl implements Supplier { private final Supplier supplier; private volatile V value; - private boolean bound; public Dcl(Supplier supplier) { this.supplier = supplier; @@ -177,15 +176,10 @@ public Dcl(Supplier supplier) { public V get() { V v = value; if (v == null) { - if (!bound) { - synchronized (this) { - v = value; - if (v == null) { - if (!bound) { - value = v = supplier.get(); - bound = true; - } - } + synchronized (this) { + v = value; + if (v == null) { + value = v = supplier.get(); } } } diff --git a/test/micro/org/openjdk/bench/java/lang/stable/VarHandleHolderBenchmark.java b/test/micro/org/openjdk/bench/java/lang/stable/VarHandleHolderBenchmark.java index 65d53a42e88f8..759cb25b59230 100644 --- a/test/micro/org/openjdk/bench/java/lang/stable/VarHandleHolderBenchmark.java +++ b/test/micro/org/openjdk/bench/java/lang/stable/VarHandleHolderBenchmark.java @@ -41,7 +41,6 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import static java.lang.foreign.MemoryLayout.PathElement.groupElement; import static java.util.concurrent.TimeUnit.*; @@ -75,8 +74,8 @@ public VarHandle apply(String name) { private static final VarHandle VH_X = VAR_HANDLE_FUNCTION.apply("x"); private static final VarHandle VH_Y = VAR_HANDLE_FUNCTION.apply("y"); - private static final Supplier SV_X = StableValue.supplier(() -> VAR_HANDLE_FUNCTION.apply("x")); - private static final Supplier SV_Y = StableValue.supplier(() -> VAR_HANDLE_FUNCTION.apply("y")); + private static final LazyConstant SV_X = LazyConstant.of(() -> VAR_HANDLE_FUNCTION.apply("x")); + private static final LazyConstant SV_Y = LazyConstant.of(() -> VAR_HANDLE_FUNCTION.apply("y")); private static final Map U_MAP = Map.of( "x", VH_X, @@ -86,13 +85,11 @@ public VarHandle apply(String name) { "x", LAYOUT.varHandle(groupElement("x")), "y", LAYOUT.varHandle(groupElement("y"))); - private static final Map S_MAP = StableValue.map( + private static final Map S_MAP = Map.ofLazy( Set.of("x", "y"), VAR_HANDLE_FUNCTION); - private static final Function S_FUN = StableValue.function( - Set.of("x", "y"), - VAR_HANDLE_FUNCTION); + private static final Function S_FUN = S_MAP::get; private static final MemorySegment confined; static { From 36b66e13c8eca8e460bfd6d900f139408aff9d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Tue, 18 Nov 2025 13:05:57 +0000 Subject: [PATCH 02/44] 8371778: Make MallocMemorySummary::_snapshot a DeferredStatic Reviewed-by: phubner, azafari --- src/hotspot/share/nmt/mallocTracker.cpp | 4 ++-- src/hotspot/share/nmt/mallocTracker.hpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/nmt/mallocTracker.cpp b/src/hotspot/share/nmt/mallocTracker.cpp index a61a27db25d2f..2cf5034c0bfd2 100644 --- a/src/hotspot/share/nmt/mallocTracker.cpp +++ b/src/hotspot/share/nmt/mallocTracker.cpp @@ -45,7 +45,7 @@ #include "utilities/ostream.hpp" #include "utilities/vmError.hpp" -MallocMemorySnapshot MallocMemorySummary::_snapshot; +DeferredStatic MallocMemorySummary::_snapshot; void MemoryCounter::update_peak(size_t size, size_t cnt) { size_t peak_sz = peak_size(); @@ -101,7 +101,7 @@ void MallocMemorySnapshot::make_adjustment() { } void MallocMemorySummary::initialize() { - // Uses placement new operator to initialize static area. + _snapshot.initialize(); MallocLimitHandler::initialize(MallocLimit); } diff --git a/src/hotspot/share/nmt/mallocTracker.hpp b/src/hotspot/share/nmt/mallocTracker.hpp index 0ead41f24110f..fc03faf721258 100644 --- a/src/hotspot/share/nmt/mallocTracker.hpp +++ b/src/hotspot/share/nmt/mallocTracker.hpp @@ -30,6 +30,7 @@ #include "nmt/memTag.hpp" #include "nmt/nmtCommon.hpp" #include "runtime/atomicAccess.hpp" +#include "utilities/deferredStatic.hpp" #include "utilities/nativeCallStack.hpp" class outputStream; @@ -204,7 +205,7 @@ class MallocMemorySnapshot { class MallocMemorySummary : AllStatic { private: // Reserve memory for placement of MallocMemorySnapshot object - static MallocMemorySnapshot _snapshot; + static DeferredStatic _snapshot; static bool _have_limits; // Called when a total limit break was detected. @@ -251,7 +252,7 @@ class MallocMemorySummary : AllStatic { } static MallocMemorySnapshot* as_snapshot() { - return &_snapshot; + return _snapshot.get(); } // MallocLimit: returns true if allocating s bytes on f would trigger From 2e68b79a3973c8a3dde6b47f19b19c0c7faacc51 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 18 Nov 2025 13:55:42 +0000 Subject: [PATCH 03/44] 8364991: Incorrect not-exhaustive error Reviewed-by: vromero --- .../javac/comp/ExhaustivenessComputer.java | 142 ++++++++++---- .../tools/javac/patterns/Exhaustiveness.java | 184 +++++++++++++++++- 2 files changed, 285 insertions(+), 41 deletions(-) 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 3b66ac010cfaa..036e86dff5e77 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 @@ -39,6 +39,7 @@ 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.Iterator; import java.util.function.Predicate; @@ -60,6 +61,7 @@ public class ExhaustivenessComputer { private final Types types; private final Check chk; private final Infer infer; + private final Map, Boolean> isSubtypeCache = new HashMap<>(); public static ExhaustivenessComputer instance(Context context) { ExhaustivenessComputer instance = context.get(exhaustivenessKey); @@ -120,6 +122,7 @@ public boolean exhausts(JCExpression selector, List cases) { } } Set patterns = patternSet; + Set> seenFallback = new HashSet<>(); boolean useHashes = true; try { boolean repeat = true; @@ -141,7 +144,7 @@ public boolean exhausts(JCExpression selector, List cases) { //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; + repeat = useHashes && seenFallback.add(updatedPatterns); useHashes = false; } else { //if a reduction happened, make sure hashing in reduceNestedPatterns @@ -154,6 +157,8 @@ public boolean exhausts(JCExpression selector, List cases) { } catch (CompletionFailure cf) { chk.completionError(selector.pos(), cf); return true; //error recovery + } finally { + isSubtypeCache.clear(); } } @@ -211,10 +216,9 @@ private Set reduceBindingPatterns(Type selectorType, Set types.isSubtype(clazzErasure, c))) { + .noneMatch(c -> isSubtypeErasure(clazzType, c))) { continue; } @@ -238,18 +242,18 @@ private Set reduceBindingPatterns(Type selectorType, Set types.isSubtype(types.erasure(pending.type), - types.erasure(bpOther.type))); + pendingPermitted.removeIf(pending -> isSubtypeErasure(pending.type, + bpOther.type)); if (bpOther.type.tsym.isAbstract()) { //all types that are in a diamond hierarchy with bpOther's type //i.e. there's a common subtype of the given type and bpOther's type: Predicate check = pending -> permitted.stream() - .filter(perm -> types.isSubtype(types.erasure(perm.type), - types.erasure(bpOther.type))) - .filter(perm -> types.isSubtype(types.erasure(perm.type), - types.erasure(pending.type))) + .filter(perm -> isSubtypeErasure(perm.type, + bpOther.type)) + .filter(perm -> isSubtypeErasure(perm.type, + pending.type)) .findAny() .isPresent(); @@ -374,30 +378,15 @@ private Set reduceNestedPatterns(Set pat join.append(rpOne); - NEXT_PATTERN: for (int nextCandidate = 0; - nextCandidate < candidatesArr.length; - nextCandidate++) { + for (int nextCandidate = 0; nextCandidate < candidatesArr.length; nextCandidate++) { if (firstCandidate == nextCandidate) { continue; } RecordPattern rpOther = candidatesArr[nextCandidate]; - if (rpOne.recordType.tsym == rpOther.recordType.tsym) { - for (int i = 0; i < rpOne.nested.length; i++) { - if (i != mismatchingCandidate) { - if (!rpOne.nested[i].equals(rpOther.nested[i])) { - if (useHashes || - //when not using hashes, - //check if rpOne.nested[i] is - //a subtype of rpOther.nested[i]: - !(rpOne.nested[i] instanceof BindingPattern bpOne) || - !(rpOther.nested[i] instanceof BindingPattern bpOther) || - !types.isSubtype(types.erasure(bpOne.type), types.erasure(bpOther.type))) { - continue NEXT_PATTERN; - } - } - } - } + + if (rpOne.recordType.tsym == rpOther.recordType.tsym && + nestedComponentsEquivalent(rpOne, rpOther, mismatchingCandidate, useHashes)) { join.append(rpOther); } } @@ -418,9 +407,11 @@ private Set reduceNestedPatterns(Set pat PatternDescription[] newNested = Arrays.copyOf(rpOne.nested, rpOne.nested.length); newNested[mismatchingCandidateFin] = nested; - current.add(new RecordPattern(rpOne.recordType(), + RecordPattern nue = new RecordPattern(rpOne.recordType(), rpOne.fullComponentTypes(), - newNested)); + newNested, + new HashSet<>(join)); + current.add(nue); } } } @@ -437,6 +428,70 @@ private Set reduceNestedPatterns(Set pat return patterns; } + /* Returns true if all nested components of existing and candidate are + * equivalent (if useHashes == true), or "substitutable" (if useHashes == false). + * A candidate pattern is "substitutable" if it is a binding pattern, and: + * - 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 + */ + private boolean nestedComponentsEquivalent(RecordPattern existing, + RecordPattern candidate, + int mismatchingCandidate, + boolean useHashes) { + NEXT_NESTED: + for (int i = 0; i < existing.nested.length; i++) { + if (i != mismatchingCandidate) { + if (!existing.nested[i].equals(candidate.nested[i])) { + if (useHashes) { + return false; + } + //when not using hashes, + //check if rpOne.nested[i] is + //a subtype of rpOther.nested[i]: + if (!(candidate.nested[i] instanceof BindingPattern nestedCandidate)) { + return false; + } + if (existing.nested[i] instanceof BindingPattern nestedExisting) { + if (!isSubtypeErasure(nestedExisting.type, nestedCandidate.type)) { + return false; + } + } else if (existing.nested[i] instanceof RecordPattern nestedExisting) { + java.util.List pendingReplacedPatterns = + new ArrayList<>(nestedCandidate.sourcePatterns()); + + 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; + } + + pendingReplacedPatterns.addAll(currentReplaced.sourcePatterns()); + } + + return false; + } else { + return false; + } + } + } + } + + return true; + } + + /*The same as types.isSubtype(types.erasure(t), types.erasure(s)), but cached. + */ + private boolean isSubtypeErasure(Type t, Type s) { + Pair key = Pair.of(t, s); + + return isSubtypeCache.computeIfAbsent(key, _ -> + types.isSubtype(types.erasure(t), types.erasure(s))); + } + /* In the set of patterns, find those for which, given: * $record($nested1, $nested2, ...) * all the $nestedX pattern cover the given record component, @@ -480,9 +535,11 @@ private PatternDescription reduceRecordPattern(PatternDescription pattern) { covered &= checkCovered(componentType[i], List.of(newNested)); } if (covered) { - return new BindingPattern(rpOne.recordType); + PatternDescription pd = new BindingPattern(rpOne.recordType, Set.of(pattern)); + return pd; } else if (reducedNestedPatterns != null) { - return new RecordPattern(rpOne.recordType, rpOne.fullComponentTypes(), reducedNestedPatterns); + PatternDescription pd = new RecordPattern(rpOne.recordType, rpOne.fullComponentTypes(), reducedNestedPatterns, Set.of(pattern)); + return pd; } } return pattern; @@ -517,7 +574,9 @@ private boolean isBpCovered(Type componentType, PatternDescription newNested) { return false; } - sealed interface PatternDescription { } + sealed interface PatternDescription { + public Set sourcePatterns(); + } public PatternDescription makePatternDescription(Type selectorType, JCPattern pattern) { if (pattern instanceof JCBindingPattern binding) { Type type = !selectorType.isPrimitive() && types.isSubtype(selectorType, binding.type) @@ -552,7 +611,12 @@ public PatternDescription makePatternDescription(Type selectorType, JCPattern pa throw Assert.error(); } } - record BindingPattern(Type type) implements PatternDescription { + record BindingPattern(Type type, Set sourcePatterns) implements PatternDescription { + + public BindingPattern(Type type) { + this(type, Set.of()); + } + @Override public int hashCode() { return type.tsym.hashCode(); @@ -567,10 +631,14 @@ public String toString() { return type.tsym + " _"; } } - record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription... nested) implements PatternDescription { + record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription[] nested, Set sourcePatterns) implements PatternDescription { public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) { - this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested); + this(recordType, fullComponentTypes, nested, Set.of()); + } + + public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested, Set sourcePatterns) { + this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested, sourcePatterns); } @Override diff --git a/test/langtools/tools/javac/patterns/Exhaustiveness.java b/test/langtools/tools/javac/patterns/Exhaustiveness.java index f328765b5792b..84e67855b3b18 100644 --- a/test/langtools/tools/javac/patterns/Exhaustiveness.java +++ b/test/langtools/tools/javac/patterns/Exhaustiveness.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8366968 + * @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8366968 8364991 * @summary Check exhaustiveness of switches over sealed types. * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -1543,7 +1544,7 @@ int test(Object o) { private static final int NESTING_CONSTANT = 4; Set createDeeplyNestedVariants() { - Set variants = new HashSet<>(); + Set variants = new LinkedHashSet<>(); variants.add("C _"); variants.add("R(I _, I _, I _)"); for (int n = 0; n < NESTING_CONSTANT; n++) { @@ -1636,10 +1637,11 @@ public void testDeeplyNestedExhaustive(Path base) throws Exception { @Test public void testDeeplyNestedNotExhaustive(Path base) throws Exception { List variants = createDeeplyNestedVariants().stream().collect(Collectors.toCollection(ArrayList::new)); - variants.remove((int) (Math.random() * variants.size())); + int removed = (int) (Math.random() * variants.size()); + variants.remove(removed); String code = testCodeForVariants(variants); - System.err.println("analyzing:"); + System.err.println("analyzing (removed: " + removed + "):"); System.err.println(code); doTest(base, new String[0], @@ -2311,6 +2313,180 @@ static int demo(final Base base) { "1 error"); } + @Test //JDK-8364991 + public void testDifferentReductionPaths(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + 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 _) -> 0; //functionally equivalent to: Root(R2 _, R2(R2 _, R2 _), R2(R2 _, R2 _)) + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2(Base b1, Base b2) implements Base {} + record Root(Base b1, Base b2, Base b3) {} + } + """); + doTest(base, + new String[0], + """ + package test; + 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 _) -> 0; //functionally equivalent to: Root(R2 _, R2(R2 _, R2 _), R2(R2 _, R2 _)) + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2(Base b1, Base b2) implements Base {} + record Root(T b1, T b2, T b3) {} + } + """); + } + + @Test //JDK-8364991 + public void testDifferentReductionPathsSimplified(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test1(Root r) { + return switch (r) { + case Root(R2(R1 _), R2(R1 _)) -> 0; + case Root(R2(R1 _), R2(R2 _)) -> 0; + case Root(R2(R2 _), R2(R1 _)) -> 0; + case Root(R2(R2 _), R2 _) -> 0; + }; + } + private int test2(Root r) { + return switch (r) { + case Root(R2(R1 _), R2(R1 _)) -> 0; + case Root(R2(R2 _), R2(R1 _)) -> 0; + case Root(R2(R1 _), R2(R2 _)) -> 0; + case Root(R2 _, R2(R2 _)) -> 0; + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2(Base b1) implements Base {} + record Root(R2 b2, R2 b3) {} + } + """); + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test1(Root> r) { + return switch (r) { + case Root(R2(R1 _), R2(R1 _)) -> 0; + case Root(R2(R1 _), R2(R2 _)) -> 0; + case Root(R2(R2 _), R2(R1 _)) -> 0; + case Root(R2(R2 _), R2 _) -> 0; + }; + } + private int test2(Root> r) { + return switch (r) { + case Root(R2(R1 _), R2(R1 _)) -> 0; + case Root(R2(R2 _), R2(R1 _)) -> 0; + case Root(R2(R1 _), R2(R2 _)) -> 0; + case Root(R2 _, R2(R2 _)) -> 0; + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2(T b1) implements Base {} + record Root(T b2, T b3) {} + } + """); + } + + @Test //JDK-8364991 + public void testBindingPatternDoesNotStandInPlaceOfRecordPatterns(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test(Root r) { + return switch (r) { + case Root(R2 _, R2(R1 _)) -> 0; + case Root(R2(R1 _), R2(R2 _)) -> 0; + case Root(R2(R2 _), R2 _) -> 0; + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2(Base b) implements Base {} + record Root(R2 b2, R2 b3) {} + } + """, + "Test.java:4:16: compiler.err.not.exhaustive", + "1 error"); + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test(Root> r) { + return switch (r) { + case Root(R2 _, R2(R1 _)) -> 0; + case Root(R2(R1 _), R2(R2 _)) -> 0; + case Root(R2(R2 _), R2 _) -> 0; + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2(T b) implements Base {} + record Root(T b2, T b3) {} + } + """, + "Test.java:4:16: compiler.err.not.exhaustive", + "1 error"); + } + private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException { doTest(base, libraryCode, testCode, false, expectedErrors); } From dcba014ad56eae753c25c579fb30bb8ecfab69af Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Tue, 18 Nov 2025 14:44:14 +0000 Subject: [PATCH 04/44] 8371967: Add Visual Studio 2026 to build toolchain for Windows Reviewed-by: erikj --- doc/building.html | 2 +- doc/building.md | 2 +- make/autoconf/toolchain_microsoft.m4 | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/building.html b/doc/building.html index 99eb3e0c473f1..19313ebf43a82 100644 --- a/doc/building.html +++ b/doc/building.html @@ -668,7 +668,7 @@

    Microsoft Visual Studio

    (Note that this version is often presented as "MSVC 14.28", and reported by cl.exe as 19.28.) Older versions will not be accepted by configure and will not work. The maximum accepted version -of Visual Studio is 2022.

    +of Visual Studio is 2026.

    If you have multiple versions of Visual Studio installed, configure will by default pick the latest. You can request a specific version to be used by setting diff --git a/doc/building.md b/doc/building.md index 047255d184803..1fbd395a9d130 100644 --- a/doc/building.md +++ b/doc/building.md @@ -468,7 +468,7 @@ available for this update. The minimum accepted version is Visual Studio 2019 version 16.8. (Note that this version is often presented as "MSVC 14.28", and reported by cl.exe as 19.28.) Older versions will not be accepted by `configure` and will not work. -The maximum accepted version of Visual Studio is 2022. +The maximum accepted version of Visual Studio is 2026. If you have multiple versions of Visual Studio installed, `configure` will by default pick the latest. You can request a specific version to be used by diff --git a/make/autoconf/toolchain_microsoft.m4 b/make/autoconf/toolchain_microsoft.m4 index 17ad2666b3ab8..f577cf1a2a1d6 100644 --- a/make/autoconf/toolchain_microsoft.m4 +++ b/make/autoconf/toolchain_microsoft.m4 @@ -25,7 +25,7 @@ ################################################################################ # The order of these defines the priority by which we try to find them. -VALID_VS_VERSIONS="2022 2019" +VALID_VS_VERSIONS="2022 2019 2026" VS_DESCRIPTION_2019="Microsoft Visual Studio 2019" VS_VERSION_INTERNAL_2019=142 @@ -57,6 +57,21 @@ VS_SDK_PLATFORM_NAME_2022= VS_SUPPORTED_2022=true VS_TOOLSET_SUPPORTED_2022=true +VS_DESCRIPTION_2026="Microsoft Visual Studio 2026" +VS_VERSION_INTERNAL_2026=145 +VS_MSVCR_2026=vcruntime140.dll +VS_VCRUNTIME_1_2026=vcruntime140_1.dll +VS_MSVCP_2026=msvcp140.dll +VS_ENVVAR_2026="VS180COMNTOOLS" +VS_USE_UCRT_2026="true" +VS_VS_INSTALLDIR_2026="Microsoft Visual Studio/18" +VS_EDITIONS_2026="BuildTools Community Professional Enterprise" +VS_SDK_INSTALLDIR_2026= +VS_VS_PLATFORM_NAME_2026="v145" +VS_SDK_PLATFORM_NAME_2026= +VS_SUPPORTED_2026=true +VS_TOOLSET_SUPPORTED_2026=true + ################################################################################ AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT], From 43040f30a72591a37deb9a54ab7723988c1e4b51 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Tue, 18 Nov 2025 15:11:45 +0000 Subject: [PATCH 05/44] 8372012: java/nio/file/attribute/BasicFileAttributeView/SetTimesNanos.java should check ability to create links Reviewed-by: alanb, jpai --- .../attribute/BasicFileAttributeView/SetTimesNanos.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/SetTimesNanos.java b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/SetTimesNanos.java index acdff69260166..55a7ca10a6dd4 100644 --- a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/SetTimesNanos.java +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/SetTimesNanos.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, 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 @@ -67,8 +67,10 @@ public static void main(String[] args) throws Exception { Path file = Files.createFile(dir.resolve("test.dat")); testNanos(file); - testNanosLink(false); - testNanosLink(true); + if (TestUtil.supportsSymbolicLinks(Path.of(""))) { + testNanosLink(false); + testNanosLink(true); + } } private static void testNanos(Path path) throws IOException { From b6d83eda6bfa76da98274aa3ad294759cb56d3a5 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 18 Nov 2025 15:14:20 +0000 Subject: [PATCH 06/44] 8371960: Missing null check in AnnotatedType annotation accessor methods Reviewed-by: alanb --- .../annotation/AnnotatedTypeFactory.java | 6 +- .../AnnotatedElementNullCheckTest.java | 101 ++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/lang/reflect/AnnotatedElement/AnnotatedElementNullCheckTest.java diff --git a/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java b/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java index aa69e29831ded..b5db897b2186f 100644 --- a/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java +++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, 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 @@ -128,7 +128,7 @@ private static class AnnotatedTypeBaseImpl implements AnnotatedType { private final Type type; private final LocationInfo location; private final TypeAnnotation[] allOnSameTargetTypeAnnotations; - private final Map, Annotation> annotations; + private final Map, Annotation> annotations; AnnotatedTypeBaseImpl(Type type, LocationInfo location, TypeAnnotation[] actualTypeAnnotations, TypeAnnotation[] allOnSameTargetTypeAnnotations) { @@ -162,11 +162,13 @@ public final Annotation[] getDeclaredAnnotations() { @Override @SuppressWarnings("unchecked") public final T getDeclaredAnnotation(Class annotation) { + Objects.requireNonNull(annotation); return (T)annotations.get(annotation); } @Override public final T[] getDeclaredAnnotationsByType(Class annotation) { + Objects.requireNonNull(annotation); return AnnotationSupport.getDirectlyAndIndirectlyPresent(annotations, annotation); } diff --git a/test/jdk/java/lang/reflect/AnnotatedElement/AnnotatedElementNullCheckTest.java b/test/jdk/java/lang/reflect/AnnotatedElement/AnnotatedElementNullCheckTest.java new file mode 100644 index 0000000000000..632dab252a9c8 --- /dev/null +++ b/test/jdk/java/lang/reflect/AnnotatedElement/AnnotatedElementNullCheckTest.java @@ -0,0 +1,101 @@ +/* + * 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 8371953 8371960 + * @summary Null checks for AnnotatedElement APIs. + * @run junit AnnotatedElementNullCheckTest + */ + +import java.lang.reflect.AnnotatedArrayType; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.AnnotatedParameterizedType; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +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; +import static org.junit.jupiter.api.Assertions.fail; + +class AnnotatedElementNullCheckTest { + + // Return a stream of instances, each of a different implementation class + static AnnotatedElement[] implementations() throws Exception { + var objectHashCodeMethod = Object.class.getMethod("hashCode"); + var annotatedParameterizedType = (AnnotatedParameterizedType) Optional.class + .getMethod("ifPresent", Consumer.class) + .getAnnotatedParameterTypes()[0]; + var annotatedGenericArrayType = (AnnotatedArrayType) List.class + .getMethod("of", Object[].class) + .getAnnotatedParameterTypes()[0]; + record Rec(int a) {} + return new AnnotatedElement[] { + Object.class, + objectHashCodeMethod, + System.class.getField("out"), + Object.class.getConstructor(), + Object.class.getPackage(), + Optional.class.getTypeParameters()[0], + // AnnotatedType (direct) + objectHashCodeMethod.getAnnotatedReturnType(), + // AnnotatedParameterizedType + annotatedParameterizedType, + // AnnotatedArrayType + annotatedGenericArrayType, + // AnnotatedTypeVariable + annotatedGenericArrayType.getAnnotatedGenericComponentType(), + // AnnotatedWildcardType + annotatedParameterizedType.getAnnotatedActualTypeArguments()[0], + Rec.class.getRecordComponents()[0], + Object.class.getMethod("equals", Object.class).getParameters()[0], + Object.class.getModule(), + }; + } + + @Test + void ensureImplementationsDistinct() throws Throwable { + var set = new HashSet>(); + for (var impl : implementations()) { + var clazz = impl.getClass(); + if (!set.add(clazz)) { + fail("Duplicate implementation class %s in %s".formatted(clazz, impl)); + } + } + } + + @ParameterizedTest + @MethodSource("implementations") + void nullChecks(AnnotatedElement impl) { + assertThrows(NullPointerException.class, () -> impl.isAnnotationPresent(null)); + assertThrows(NullPointerException.class, () -> impl.getAnnotation(null)); + assertThrows(NullPointerException.class, () -> impl.getAnnotationsByType(null)); + assertThrows(NullPointerException.class, () -> impl.getDeclaredAnnotation(null)); + assertThrows(NullPointerException.class, () -> impl.getDeclaredAnnotationsByType(null)); + } +} From 1f99cf942449728cdeb9918b93fd9a97a51eb0b6 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 18 Nov 2025 15:14:49 +0000 Subject: [PATCH 07/44] 8372002: VarHandle for receiver's superclass instance fields fails describeConstable Reviewed-by: psandoz, jvernee --- .../classes/java/lang/invoke/VarHandles.java | 15 ++++--- .../DescribeConstableTest.java | 45 +++++++++++++++---- .../VarHandles/describeConstable/p/C.java | 4 +- .../VarHandles/describeConstable/p/q/Q.java | 4 +- 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java index c97d44ba5d3e1..8c3e123f39a69 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java @@ -166,12 +166,15 @@ else if (type == double.class) { static Field getFieldFromReceiverAndOffset(Class receiverType, long offset, Class fieldType) { - for (Field f : receiverType.getDeclaredFields()) { - if (Modifier.isStatic(f.getModifiers())) continue; - - if (offset == UNSAFE.objectFieldOffset(f)) { - assert f.getType() == fieldType; - return f; + // The receiver may be a referenced class different from the declaring class + for (var declaringClass = receiverType; declaringClass != null; declaringClass = declaringClass.getSuperclass()) { + for (Field f : declaringClass.getDeclaredFields()) { + if (Modifier.isStatic(f.getModifiers())) continue; + + if (offset == UNSAFE.objectFieldOffset(f)) { + assert f.getType() == fieldType; + return f; + } } } throw new InternalError("Field not found at offset"); diff --git a/test/jdk/java/lang/invoke/VarHandles/describeConstable/DescribeConstableTest.java b/test/jdk/java/lang/invoke/VarHandles/describeConstable/DescribeConstableTest.java index ecda61cac4884..0eecbdc379839 100644 --- a/test/jdk/java/lang/invoke/VarHandles/describeConstable/DescribeConstableTest.java +++ b/test/jdk/java/lang/invoke/VarHandles/describeConstable/DescribeConstableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -21,18 +21,16 @@ * questions. */ -/** +/* * @test - * @bug 8302260 + * @bug 8302260 8372002 * @build p.C p.D p.I p.q.Q * @run junit DescribeConstableTest * @summary Test VarHandle::describeConstable on static fields */ -import java.lang.constant.ClassDesc; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; -import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle.VarHandleDesc; import java.util.stream.Stream; @@ -43,7 +41,7 @@ public class DescribeConstableTest { private static final Lookup LOOKUP = MethodHandles.lookup(); - private static Stream testCases() { + private static Stream staticTestCases() { return Stream.of( // static field defined in p.C only Arguments.of(p.C.class, "cString", String.class, p.C.class, "CClass"), @@ -68,8 +66,8 @@ private static Stream testCases() { } @ParameterizedTest - @MethodSource("testCases") - void test(Class refc, String name, Class type, Class declaringClass, Object value) throws Throwable { + @MethodSource("staticTestCases") + void testStatic(Class refc, String name, Class type, Class declaringClass, Object value) throws Throwable { var vh = LOOKUP.findStaticVarHandle(refc, name, type); assertEquals(value, vh.get()); @@ -84,6 +82,37 @@ void test(Class refc, String name, Class type, Class declaringClass, Ob assertEquals(vhd.toString(), varHandleDescString(declaringClass, name, type, true)); } + private static Arguments[] instanceTestCases() { + return new Arguments[] { + // Basic instance field in p.q.Q + Arguments.of(p.q.Q.class, "instanceIntField", int.class, new p.q.Q(), 42), + // p.C.instanceIntField hides the superclass instanceIntField, but it still exists + Arguments.of(p.C.class, "instanceIntField", int.class, new p.C(), 76), + Arguments.of(p.q.Q.class, "instanceIntField", int.class, new p.C(), 42), + // p.D.instanceIntField points to that of p.q.Q + Arguments.of(p.D.class, "instanceIntField", int.class, new p.D(), 42), + }; + } + + @ParameterizedTest + @MethodSource("instanceTestCases") + void testInstance(Class refc, String name, Class type, Object instance, Object value) throws Throwable { + var vh = LOOKUP.findVarHandle(refc, name, type).withInvokeBehavior(); + assertEquals(value, vh.get(instance)); + + var refcDesc = refc.describeConstable().orElseThrow(); + var typeDesc = type.describeConstable().orElseThrow(); + var vhd = vh.describeConstable().orElseThrow(); + var vhd2 = VarHandleDesc.ofField(refcDesc, name, typeDesc); + + assertEquals(value, vhd.resolveConstantDesc(LOOKUP).get(instance)); + assertEquals(value, vhd2.resolveConstantDesc(LOOKUP).get(instance)); + + // The string does not use the declaring class because + // receiver is restricted on the handle + assertEquals(vhd.toString(), varHandleDescString(refc, name, type, false)); + } + static String varHandleDescString(Class declaringClass, String name, Class type, boolean staticField) { return String.format("VarHandleDesc[%s%s.%s:%s]", staticField ? "static " : "", diff --git a/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/C.java b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/C.java index 353bd26dcf5b2..5e56451ea2534 100644 --- a/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/C.java +++ b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/C.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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,4 +25,6 @@ public class C extends p.q.Q implements I { public static String cString = "CClass"; + + public int instanceIntField = 76; // Hides the field from Q } diff --git a/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/q/Q.java b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/q/Q.java index b3bbb8adafc45..0461d43b533f0 100644 --- a/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/q/Q.java +++ b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/q/Q.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -29,4 +29,6 @@ public class Q { public static String stringField2 = "QClass2"; public static long longField2 = 102L; + + public int instanceIntField = 42; } From 713de231a61234632e2f9858b222b5f7fd0bdaf1 Mon Sep 17 00:00:00 2001 From: Nityanand Rai Date: Tue, 18 Nov 2025 15:47:54 +0000 Subject: [PATCH 08/44] 8371854: Shenandoah: Simplify WALK_FORWARD_IN_BLOCK_START use Reviewed-by: shade, ysr, xpeng --- .../share/gc/shenandoah/shenandoahScanRemembered.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp index 3a99023eca42a..34713898fc637 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -378,24 +378,20 @@ HeapWord* ShenandoahCardCluster::first_object_start(const size_t card_index, con // evacuation phase) of young collections. This is never called // during global collections during marking or update refs.. // 4. Every allocation under TAMS updates the object start array. +#ifdef ASSERT oop obj = cast_to_oop(p); assert(oopDesc::is_oop(obj), "Should be an object"); -#ifdef ASSERT -#define WALK_FORWARD_IN_BLOCK_START true -#else -#define WALK_FORWARD_IN_BLOCK_START false -#endif // ASSERT - while (WALK_FORWARD_IN_BLOCK_START && p + obj->size() < left) { + while (p + obj->size() < left) { p += obj->size(); obj = cast_to_oop(p); assert(oopDesc::is_oop(obj), "Should be an object"); assert(Klass::is_valid(obj->klass()), "Not a valid klass ptr"); // Check assumptions in previous block comment if this assert fires - guarantee(false, "Should never need forward walk in block start"); + fatal("Should never need forward walk in block start"); } -#undef WALK_FORWARD_IN_BLOCK_START assert(p <= left, "p should start at or before left end of card"); assert(p + obj->size() > left, "obj should end after left end of card"); +#endif // ASSERT return p; } From ac6f5e96512a7f003ac536611c53f2564ea912a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Tue, 18 Nov 2025 16:52:12 +0000 Subject: [PATCH 09/44] 8366094: Sealed graph for nested types creates broken links Reviewed-by: liach --- .../src/classes/build/tools/taglet/SealedGraph.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/make/jdk/src/classes/build/tools/taglet/SealedGraph.java b/make/jdk/src/classes/build/tools/taglet/SealedGraph.java index 17867b99595bc..3e93826c1804d 100644 --- a/make/jdk/src/classes/build/tools/taglet/SealedGraph.java +++ b/make/jdk/src/classes/build/tools/taglet/SealedGraph.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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 @@ -219,13 +219,13 @@ public void addNode(TypeElement node) { // This implies the module is always the same. private String relativeLink(TypeElement node) { var util = SealedGraph.this.docletEnvironment.getElementUtils(); - var rootPackage = util.getPackageOf(rootNode); var nodePackage = util.getPackageOf(node); - var backNavigator = rootPackage.getQualifiedName().toString().chars() + // Note: SVG files for nested types use the simple names of containing types as parent directories. + // We therefore need to convert all dots in the qualified name to "../" below. + var backNavigator = rootNode.getQualifiedName().toString().chars() .filter(c -> c == '.') .mapToObj(c -> "../") - .collect(joining()) + - "../"; + .collect(joining()); var forwardNavigator = nodePackage.getQualifiedName().toString() .replace(".", "/"); From 0e6c7e8664fdddd8b789851263613852fc2c55f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Tue, 18 Nov 2025 17:55:43 +0000 Subject: [PATCH 10/44] 8371896: Links in snippets can not be highlighted Reviewed-by: liach --- .../formats/html/taglets/SnippetTaglet.java | 12 +-- .../testSnippetTag/TestSnippetMarkup.java | 82 ++++++++++++++++--- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SnippetTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SnippetTaglet.java index d16c47dd59f6c..38baa7b2826d6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SnippetTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SnippetTaglet.java @@ -162,10 +162,11 @@ private Content snippetTagOutput(Element element, SnippetTree tag, StyledText co case Style.Markup m -> markupEncountered = true; } } - Content c; if (markupEncountered) { return; - } else if (linkTarget != null) { + } + Content c = Text.of(text); + if (linkTarget != null) { //disable preview tagging inside the snippets: Utils.PreviewFlagProvider prevPreviewProvider = utils.setPreviewFlagProvider(el -> false); try { @@ -174,15 +175,16 @@ private Content snippetTagOutput(Element element, SnippetTree tag, StyledText co null, linkTarget, ref, - false, // TODO: for now + true, Text.of(sequence.toString()), (key, args) -> { /* Error has already been reported above */ }, tagletWriter); } finally { utils.setPreviewFlagProvider(prevPreviewProvider); } - } else { - c = HtmlTree.SPAN(Text.of(text)); + } + if (!classes.isEmpty()) { + c = HtmlTree.SPAN(c); classes.forEach(((HtmlTree) c)::addStyle); } code.add(c); diff --git a/test/langtools/jdk/javadoc/doclet/testSnippetTag/TestSnippetMarkup.java b/test/langtools/jdk/javadoc/doclet/testSnippetTag/TestSnippetMarkup.java index a3e92584ae2cf..be60a11404029 100644 --- a/test/langtools/jdk/javadoc/doclet/testSnippetTag/TestSnippetMarkup.java +++ b/test/langtools/jdk/javadoc/doclet/testSnippetTag/TestSnippetMarkup.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8266666 8281969 8319339 8338833 + * @bug 8266666 8281969 8319339 8338833 8371896 * @summary Implementation for snippets * @library /tools/lib ../../lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -180,7 +180,7 @@ public void testLink(Path base) throws Exception { replace(""" link(First) link(line) Second line - """, "link\\((.+?)\\)", r -> link(true, "java.lang.Object#Object", r.group(1))) + """, "link\\((.+?)\\)", r -> link("java.lang.Object#Object", r.group(1))) ), new TestCase( """ @@ -190,7 +190,7 @@ public void testLink(Path base) throws Exception { replace(""" First line link( )Secondlink( )line - """, "link\\((.+?)\\)", r -> link(true, "java.lang.System#out", r.group(1))) + """, "link\\((.+?)\\)", r -> link("java.lang.System#out", r.group(1))) ), new TestCase( """ @@ -200,7 +200,68 @@ public void testLink(Path base) throws Exception { replace(""" First line link( )Secondlink( )line - """, "link\\((.+?)\\)", r -> link(true, "java.lang.System#in", r.group(1))) + """, "link\\((.+?)\\)", r -> link("java.lang.System#in", r.group(1))) + ) + ); + testPositive(base, testCases); + } + + // Test combinations of @link, @highlight and @replace on the same or overlapping substrings. + @Test + public void testLinkHighlightReplace(Path base) throws Exception { + var testCases = List.of( + new TestCase( + """ + void method(Object o); // @link substring=Object target=Object @highlight substring=Object + """, + replace(""" + void method(link(Object) o); + """, "link\\((.+?)\\)", r -> link("Object", r.group(1))) + ), + new TestCase( + """ + void method(Object o); // @replace substring=Object replacement=String \ + @highlight substring=String @link substring=String target=String + """, + replace(""" + void method(link(String) o); + """, "link\\((.+?)\\)", r -> link("String", r.group(1))) + ), + new TestCase( + """ + void method(Object o); // @highlight substring="(Object o)" @link substring=Object target=Object + """, + replace(""" + void method(link(Object) o); + """, "link\\((.+?)\\)", r -> link("Object", r.group(1))) + ), + new TestCase( + """ + void method(Object o); // @replace substring=Object replacement=String \ + @link substring=String target=String @highlight substring="(String o)" + """, + replace(""" + void method(link(String) o); + """, "link\\((.+?)\\)", r -> link("String", r.group(1))) + ), + new TestCase( + """ + void method(Object o); // @replace substring=Object replacement=String \ + @link substring="String o" target=String @highlight substring=String + """, + replace(""" + void method(link(String)link( o)); + """, "link\\((.+?)\\)", r -> link("String", r.group(1))) + ), + // replacement subtext does not retain links and highlights of the replaced subtext + new TestCase( + """ + void method(Object o); // @link substring=Object target=Object \ + @highlight substring=Object @replace substring=Object replacement=String + """, + """ + void method(String o); + """ ) ); testPositive(base, testCases); @@ -245,7 +306,7 @@ public void testLinkReferenceNotFound(Path base) throws Exception { """

    invalid reference -
    Second
    +
    Second
    """); checkNoCrashes(); } @@ -646,7 +707,7 @@ public void testPositiveInlineTagMarkup_BlankLinesFromNextLineMarkup(Path base) replace(""" First line link( Third line) - """, "link\\((.+?)\\)", r -> link(true, "java.lang.Object#equals(Object)", r.group(1))) + """, "link\\((.+?)\\)", r -> link("java.lang.Object#equals(Object)", r.group(1))) ), new TestCase(""" First line @@ -910,8 +971,7 @@ public class A { checkNoCrashes(); } - private static String link(boolean linkPlain, - String targetReference, + private static String link(String targetReference, String content) throws UncheckedIOException { @@ -935,7 +995,7 @@ private static String link(boolean linkPlain, var LABEL_PLACEHOLDER = "label"; var source = """ - /** {@link %s %s} */ + /** {@linkplain %s %s} */ public interface A { } """.formatted(targetReference, LABEL_PLACEHOLDER); @@ -1063,8 +1123,8 @@ public FileObject getFileForOutput(Location location, } String output = fileManager.getFileString(DOCUMENTATION_OUTPUT, "A.html"); // use the [^<>] regex to select HTML elements that immediately enclose "content" - Matcher m = Pattern.compile("(?is)(]*\" title=\"[^<>]*\" class=\"[^<>]*\">)" - + LABEL_PLACEHOLDER + "()").matcher(output); + Matcher m = Pattern.compile("(?is)(]*\" title=\"[^<>]*\" class=\"[^<>]*\">)" + + LABEL_PLACEHOLDER + "()").matcher(output); if (!m.find()) { throw new IOException(output); } From b3e408c07891b58a312a58ffd756d6a1d18c0f6d Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 18 Nov 2025 18:12:07 +0000 Subject: [PATCH 11/44] 8372045: AOT assembly phase asserts with old class if AOT class linking is disabled Reviewed-by: shade, mgronlun --- src/hotspot/share/oops/instanceKlass.cpp | 2 +- .../cds/appcds/aotCache/OldClassSupport2.java | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 2d03b69ee92e9..24358f662bc6c 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -2870,7 +2870,7 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl } bool InstanceKlass::can_be_verified_at_dumptime() const { - if (AOTMetaspace::in_aot_cache(this)) { + if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(this)) { // This is a class that was dumped into the base archive, so we know // it was verified at dump time. return true; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java new file mode 100644 index 0000000000000..16a4d258e4f2e --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java @@ -0,0 +1,104 @@ +/* + * 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 + * @summary AOT cache should exclude old classes when AOT class linking is disabled. + * @bug 8372045 + * @requires vm.cds.supports.aot.class.linking + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @build OldClass + * @build OldClassSupport2 + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * AppUsesOldClass2 OldClass + * @run driver OldClassSupport2 + */ + +import jdk.jfr.Event; +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class OldClassSupport2 { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = "AppUsesOldClass2"; + + public static void main(String[] args) throws Exception { + // Explicitly disable + Tester tester1 = new Tester("-XX:-AOTClassLinking"); + tester1.run(new String[] {"AOT", "--two-step-training"} ); + + // Full module graph caching is disabled with -Djdk.module.showModuleResolution=true. + // This will disable AOT class linking. + Tester tester2 = new Tester("-Djdk.module.showModuleResolution=true"); + tester2.run(new String[] {"AOT", "--two-step-training"} ); + + // Heap archiving is disable with -XX:-UseCompressedClassPointers. + // This will disable AOT class linking. + Tester tester3 = new Tester("-XX:-UseCompressedClassPointers"); + tester3.run(new String[] {"AOT", "--two-step-training"} ); + } + + static class Tester extends CDSAppTester { + String extraArg; + public Tester(String extraArg) { + super(mainClass); + this.extraArg = extraArg; + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + return new String[] { + "-Xlog:aot", + "-Xlog:aot+class=debug", + extraArg, + }; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] {mainClass}; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) { + String prefix = "aot,class.* = 0x.* app *"; + if (runMode == RunMode.ASSEMBLY) { + out.shouldMatch(prefix + "AppUsesOldClass2"); + out.shouldNotMatch(prefix + "OldClass"); + } + } + } +} + +class AppUsesOldClass2 { + public static void main(String args[]) { + System.out.println("Old Class Instance: " + new OldClass()); + } +} From 4a975637a144fa8aa449a1419e656721833513b5 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Tue, 18 Nov 2025 18:35:01 +0000 Subject: [PATCH 12/44] 8346944: Update Unicode Data Files to 17.0.0 8346947: Update ICU4J to Version 78.1 Reviewed-by: joehw --- .../share/classes/java/lang/Character.java | 459 +++-- .../java/text/CollationElementIterator.java | 6 +- .../icu/impl/CharacterIteratorWrapper.java | 6 +- .../jdk/internal/icu/impl/Norm2AllModes.java | 4 +- .../impl/ReplaceableUCharacterIterator.java | 8 +- .../jdk/internal/icu/impl/UBiDiProps.java | 4 +- .../internal/icu/impl/UCharacterProperty.java | 4 +- .../impl/data/{icudt76b => icudata}/nfc.nrm | Bin 36224 -> 36336 bytes .../impl/data/{icudt76b => icudata}/nfkc.nrm | Bin 56032 -> 56128 bytes .../impl/data/{icudt76b => icudata}/ubidi.icu | Bin 28464 -> 28768 bytes .../internal/icu/impl/data/icudata/uprops.icu | Bin 0 -> 170864 bytes .../icu/impl/data/icudt76b/uprops.icu | Bin 146880 -> 0 bytes .../jdk/internal/icu/text/NormalizerBase.java | 6 +- .../internal/icu/text/UCharacterIterator.java | 6 +- .../jdk/internal/icu/text/UnicodeSet.java | 16 +- .../jdk/internal/icu/util/VersionInfo.java | 4 +- .../jdk/internal/util/regex/Grapheme.java | 7 +- .../regex/IndicConjunctBreak.java.template | 7 +- .../share/data/unicodedata/Blocks.txt | 14 +- .../share/data/unicodedata/CaseFolding.txt | 42 +- .../unicodedata/DerivedCoreProperties.txt | 599 ++++-- .../data/unicodedata/NormalizationTest.txt | 75 +- .../share/data/unicodedata/PropList.txt | 72 +- .../data/unicodedata/PropertyValueAliases.txt | 35 +- .../share/data/unicodedata/ReadMe.txt | 19 +- .../share/data/unicodedata/Scripts.txt | 142 +- .../share/data/unicodedata/SpecialCasing.txt | 14 +- .../share/data/unicodedata/UnicodeData.txt | 489 ++++- .../auxiliary/GraphemeBreakProperty.txt | 28 +- .../auxiliary/GraphemeBreakTest.txt | 1737 +++++++---------- .../data/unicodedata/emoji/emoji-data.txt | 148 +- src/java.base/share/legal/icu.md | 4 +- src/java.base/share/legal/unicode.md | 4 +- 33 files changed, 2327 insertions(+), 1632 deletions(-) rename src/java.base/share/classes/jdk/internal/icu/impl/data/{icudt76b => icudata}/nfc.nrm (64%) rename src/java.base/share/classes/jdk/internal/icu/impl/data/{icudt76b => icudata}/nfkc.nrm (90%) rename src/java.base/share/classes/jdk/internal/icu/impl/data/{icudt76b => icudata}/ubidi.icu (74%) create mode 100644 src/java.base/share/classes/jdk/internal/icu/impl/data/icudata/uprops.icu delete mode 100644 src/java.base/share/classes/jdk/internal/icu/impl/data/icudt76b/uprops.icu diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index 72ff33651f961..d866202909cae 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -63,7 +63,7 @@ * from the Unicode Consortium at * http://www.unicode.org. *

    - * Character information is based on the Unicode Standard, version 16.0. + * Character information is based on the Unicode Standard, version 17.0. *

    * The Java platform has supported different versions of the Unicode * Standard over time. Upgrades to newer versions of the Unicode Standard @@ -75,6 +75,8 @@ * Unicode version * * + * Java SE 26 + * Unicode 17.0 * Java SE 24 * Unicode 16.0 * Java SE 22 @@ -745,7 +747,7 @@ public static final class UnicodeBlock extends Subset { * It should be adjusted whenever the Unicode Character Database * is upgraded. */ - private static final int NUM_ENTITIES = 782; + private static final int NUM_ENTITIES = 804; private static Map map = HashMap.newHashMap(NUM_ENTITIES); /** @@ -3715,6 +3717,85 @@ private UnicodeBlock(String idName, String... aliases) { "OL ONAL", "OLONAL"); + /** + * Constant for the "Sidetic" Unicode + * character block. + * @since 26 + */ + public static final UnicodeBlock SIDETIC = + new UnicodeBlock("SIDETIC"); + + /** + * Constant for the "Sharada Supplement" Unicode + * character block. + * @since 26 + */ + public static final UnicodeBlock SHARADA_SUPPLEMENT = + new UnicodeBlock("SHARADA_SUPPLEMENT", + "SHARADA SUPPLEMENT", + "SHARADASUPPLEMENT"); + + /** + * Constant for the "Tolong Siki" Unicode + * character block. + * @since 26 + */ + public static final UnicodeBlock TOLONG_SIKI = + new UnicodeBlock("TOLONG_SIKI", + "TOLONG SIKI", + "TOLONGSIKI"); + + /** + * Constant for the "Beria Erfe" Unicode + * character block. + * @since 26 + */ + public static final UnicodeBlock BERIA_ERFE = + new UnicodeBlock("BERIA_ERFE", + "BERIA ERFE", + "BERIAERFE"); + + /** + * Constant for the "Tangut Components Supplement" Unicode + * character block. + * @since 26 + */ + public static final UnicodeBlock TANGUT_COMPONENTS_SUPPLEMENT = + new UnicodeBlock("TANGUT_COMPONENTS_SUPPLEMENT", + "TANGUT COMPONENTS SUPPLEMENT", + "TANGUTCOMPONENTSSUPPLEMENT"); + + /** + * Constant for the "Miscellaneous Symbols Supplement" Unicode + * character block. + * @since 26 + */ + public static final UnicodeBlock MISCELLANEOUS_SYMBOLS_SUPPLEMENT = + new UnicodeBlock("MISCELLANEOUS_SYMBOLS_SUPPLEMENT", + "MISCELLANEOUS SYMBOLS SUPPLEMENT", + "MISCELLANEOUSSYMBOLSSUPPLEMENT"); + + /** + * Constant for the "Tai Yo" Unicode + * character block. + * @since 26 + */ + public static final UnicodeBlock TAI_YO = + new UnicodeBlock("TAI_YO", + "TAI YO", + "TAIYO"); + + /** + * Constant for the "CJK Unified Ideographs Extension J" Unicode + * character block. + * @since 26 + */ + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_J = + new UnicodeBlock("CJK_UNIFIED_IDEOGRAPHS_EXTENSION_J", + "CJK UNIFIED IDEOGRAPHS EXTENSION J", + "CJKUNIFIEDIDEOGRAPHSEXTENSIONJ"); + + private static final int[] blockStarts = { 0x0000, // 0000..007F; Basic Latin 0x0080, // 0080..00FF; Latin-1 Supplement @@ -3916,7 +3997,8 @@ private UnicodeBlock(String idName, String... aliases) { 0x108E0, // 108E0..108FF; Hatran 0x10900, // 10900..1091F; Phoenician 0x10920, // 10920..1093F; Lydian - 0x10940, // unassigned + 0x10940, // 10940..1095F; Sidetic + 0x10960, // unassigned 0x10980, // 10980..1099F; Meroitic Hieroglyphs 0x109A0, // 109A0..109FF; Meroitic Cursive 0x10A00, // 10A00..10A5F; Kharoshthi @@ -3977,14 +4059,16 @@ private UnicodeBlock(String idName, String... aliases) { 0x11AB0, // 11AB0..11ABF; Unified Canadian Aboriginal Syllabics Extended-A 0x11AC0, // 11AC0..11AFF; Pau Cin Hau 0x11B00, // 11B00..11B5F; Devanagari Extended-A - 0x11B60, // unassigned + 0x11B60, // 11B60..11B7F; Sharada Supplement + 0x11B80, // unassigned 0x11BC0, // 11BC0..11BFF; Sunuwar 0x11C00, // 11C00..11C6F; Bhaiksuki 0x11C70, // 11C70..11CBF; Marchen 0x11CC0, // unassigned 0x11D00, // 11D00..11D5F; Masaram Gondi 0x11D60, // 11D60..11DAF; Gunjala Gondi - 0x11DB0, // unassigned + 0x11DB0, // 11DB0..11DEF; Tolong Siki + 0x11DF0, // unassigned 0x11EE0, // 11EE0..11EFF; Makasar 0x11F00, // 11F00..11F5F; Kawi 0x11F60, // unassigned @@ -4011,7 +4095,8 @@ private UnicodeBlock(String idName, String... aliases) { 0x16D40, // 16D40..16D7F; Kirat Rai 0x16D80, // unassigned 0x16E40, // 16E40..16E9F; Medefaidrin - 0x16EA0, // unassigned + 0x16EA0, // 16EA0..16EDF; Beria Erfe + 0x16EE0, // unassigned 0x16F00, // 16F00..16F9F; Miao 0x16FA0, // unassigned 0x16FE0, // 16FE0..16FFF; Ideographic Symbols and Punctuation @@ -4019,7 +4104,8 @@ private UnicodeBlock(String idName, String... aliases) { 0x18800, // 18800..18AFF; Tangut Components 0x18B00, // 18B00..18CFF; Khitan Small Script 0x18D00, // 18D00..18D7F; Tangut Supplement - 0x18D80, // unassigned + 0x18D80, // 18D80..18DFF; Tangut Components Supplement + 0x18E00, // unassigned 0x1AFF0, // 1AFF0..1AFFF; Kana Extended-B 0x1B000, // 1B000..1B0FF; Kana Supplement 0x1B100, // 1B100..1B12F; Kana Extended-A @@ -4030,7 +4116,7 @@ private UnicodeBlock(String idName, String... aliases) { 0x1BCA0, // 1BCA0..1BCAF; Shorthand Format Controls 0x1BCB0, // unassigned 0x1CC00, // 1CC00..1CEBF; Symbols for Legacy Computing Supplement - 0x1CEC0, // unassigned + 0x1CEC0, // 1CEC0..1CEFF; Miscellaneous Symbols Supplement 0x1CF00, // 1CF00..1CFCF; Znamenny Musical Notation 0x1CFD0, // unassigned 0x1D000, // 1D000..1D0FF; Byzantine Musical Symbols @@ -4058,6 +4144,8 @@ private UnicodeBlock(String idName, String... aliases) { 0x1E500, // unassigned 0x1E5D0, // 1E5D0..1E5FF; Ol Onal 0x1E600, // unassigned + 0x1E6C0, // 1E6C0..1E6FF; Tai Yo + 0x1E700, // unassigned 0x1E7E0, // 1E7E0..1E7FF; Ethiopic Extended-B 0x1E800, // 1E800..1E8DF; Mende Kikakui 0x1E8E0, // unassigned @@ -4098,7 +4186,8 @@ private UnicodeBlock(String idName, String... aliases) { 0x2FA20, // unassigned 0x30000, // 30000..3134F; CJK Unified Ideographs Extension G 0x31350, // 31350..323AF; CJK Unified Ideographs Extension H - 0x323B0, // unassigned + 0x323B0, // 323B0..3347F; CJK Unified Ideographs Extension J + 0x33480, // unassigned 0xE0000, // E0000..E007F; Tags 0xE0080, // unassigned 0xE0100, // E0100..E01EF; Variation Selectors Supplement @@ -4308,6 +4397,7 @@ private UnicodeBlock(String idName, String... aliases) { HATRAN, PHOENICIAN, LYDIAN, + SIDETIC, null, MEROITIC_HIEROGLYPHS, MEROITIC_CURSIVE, @@ -4369,6 +4459,7 @@ private UnicodeBlock(String idName, String... aliases) { UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED_A, PAU_CIN_HAU, DEVANAGARI_EXTENDED_A, + SHARADA_SUPPLEMENT, null, SUNUWAR, BHAIKSUKI, @@ -4376,6 +4467,7 @@ private UnicodeBlock(String idName, String... aliases) { null, MASARAM_GONDI, GUNJALA_GONDI, + TOLONG_SIKI, null, MAKASAR, KAWI, @@ -4403,6 +4495,7 @@ private UnicodeBlock(String idName, String... aliases) { KIRAT_RAI, null, MEDEFAIDRIN, + BERIA_ERFE, null, MIAO, null, @@ -4411,6 +4504,7 @@ private UnicodeBlock(String idName, String... aliases) { TANGUT_COMPONENTS, KHITAN_SMALL_SCRIPT, TANGUT_SUPPLEMENT, + TANGUT_COMPONENTS_SUPPLEMENT, null, KANA_EXTENDED_B, KANA_SUPPLEMENT, @@ -4422,7 +4516,7 @@ private UnicodeBlock(String idName, String... aliases) { SHORTHAND_FORMAT_CONTROLS, null, SYMBOLS_FOR_LEGACY_COMPUTING_SUPPLEMENT, - null, + MISCELLANEOUS_SYMBOLS_SUPPLEMENT, ZNAMENNY_MUSICAL_NOTATION, null, BYZANTINE_MUSICAL_SYMBOLS, @@ -4450,6 +4544,8 @@ private UnicodeBlock(String idName, String... aliases) { null, OL_ONAL, null, + TAI_YO, + null, ETHIOPIC_EXTENDED_B, MENDE_KIKAKUI, null, @@ -4490,6 +4586,7 @@ private UnicodeBlock(String idName, String... aliases) { null, CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G, CJK_UNIFIED_IDEOGRAPHS_EXTENSION_H, + CJK_UNIFIED_IDEOGRAPHS_EXTENSION_J, null, TAGS, null, @@ -5547,6 +5644,30 @@ public static enum UnicodeScript { */ OL_ONAL, + /** + * Unicode script "Sidetic". + * @since 26 + */ + SIDETIC, + + /** + * Unicode script "Tolong Siki". + * @since 26 + */ + TOLONG_SIKI, + + /** + * Unicode script "Beria Erfe". + * @since 26 + */ + BERIA_ERFE, + + /** + * Unicode script "Tai Yo". + * @since 26 + */ + TAI_YO, + /** * Unicode script "Unknown". */ @@ -5648,9 +5769,7 @@ public static enum UnicodeScript { 0x085F, // 085F ; UNKNOWN 0x0860, // 0860..086A; SYRIAC 0x086B, // 086B..086F; UNKNOWN - 0x0870, // 0870..088E; ARABIC - 0x088F, // 088F ; UNKNOWN - 0x0890, // 0890..0891; ARABIC + 0x0870, // 0870..0891; ARABIC 0x0892, // 0892..0896; UNKNOWN 0x0897, // 0897..08E1; ARABIC 0x08E2, // 08E2 ; COMMON @@ -5825,8 +5944,8 @@ public static enum UnicodeScript { 0x0C55, // 0C55..0C56; TELUGU 0x0C57, // 0C57 ; UNKNOWN 0x0C58, // 0C58..0C5A; TELUGU - 0x0C5B, // 0C5B..0C5C; UNKNOWN - 0x0C5D, // 0C5D ; TELUGU + 0x0C5B, // 0C5B ; UNKNOWN + 0x0C5C, // 0C5C..0C5D; TELUGU 0x0C5E, // 0C5E..0C5F; UNKNOWN 0x0C60, // 0C60..0C63; TELUGU 0x0C64, // 0C64..0C65; UNKNOWN @@ -5850,8 +5969,8 @@ public static enum UnicodeScript { 0x0CCA, // 0CCA..0CCD; KANNADA 0x0CCE, // 0CCE..0CD4; UNKNOWN 0x0CD5, // 0CD5..0CD6; KANNADA - 0x0CD7, // 0CD7..0CDC; UNKNOWN - 0x0CDD, // 0CDD..0CDE; KANNADA + 0x0CD7, // 0CD7..0CDB; UNKNOWN + 0x0CDC, // 0CDC..0CDE; KANNADA 0x0CDF, // 0CDF ; UNKNOWN 0x0CE0, // 0CE0..0CE3; KANNADA 0x0CE4, // 0CE4..0CE5; UNKNOWN @@ -6062,8 +6181,10 @@ public static enum UnicodeScript { 0x1A9A, // 1A9A..1A9F; UNKNOWN 0x1AA0, // 1AA0..1AAD; TAI_THAM 0x1AAE, // 1AAE..1AAF; UNKNOWN - 0x1AB0, // 1AB0..1ACE; INHERITED - 0x1ACF, // 1ACF..1AFF; UNKNOWN + 0x1AB0, // 1AB0..1ADD; INHERITED + 0x1ADE, // 1ADE..1ADF; UNKNOWN + 0x1AE0, // 1AE0..1AEB; INHERITED + 0x1AEC, // 1AEC..1AFF; UNKNOWN 0x1B00, // 1B00..1B4C; BALINESE 0x1B4D, // 1B4D ; UNKNOWN 0x1B4E, // 1B4E..1B7F; BALINESE @@ -6155,8 +6276,8 @@ public static enum UnicodeScript { 0x208F, // 208F ; UNKNOWN 0x2090, // 2090..209C; LATIN 0x209D, // 209D..209F; UNKNOWN - 0x20A0, // 20A0..20C0; COMMON - 0x20C1, // 20C1..20CF; UNKNOWN + 0x20A0, // 20A0..20C1; COMMON + 0x20C2, // 20C2..20CF; UNKNOWN 0x20D0, // 20D0..20F0; INHERITED 0x20F1, // 20F1..20FF; UNKNOWN 0x2100, // 2100..2125; COMMON @@ -6179,9 +6300,7 @@ public static enum UnicodeScript { 0x2800, // 2800..28FF; BRAILLE 0x2900, // 2900..2B73; COMMON 0x2B74, // 2B74..2B75; UNKNOWN - 0x2B76, // 2B76..2B95; COMMON - 0x2B96, // 2B96 ; UNKNOWN - 0x2B97, // 2B97..2BFF; COMMON + 0x2B76, // 2B76..2BFF; COMMON 0x2C00, // 2C00..2C5F; GLAGOLITIC 0x2C60, // 2C60..2C7F; LATIN 0x2C80, // 2C80..2CF3; COPTIC @@ -6282,15 +6401,9 @@ public static enum UnicodeScript { 0xA700, // A700..A721; COMMON 0xA722, // A722..A787; LATIN 0xA788, // A788..A78A; COMMON - 0xA78B, // A78B..A7CD; LATIN - 0xA7CE, // A7CE..A7CF; UNKNOWN - 0xA7D0, // A7D0..A7D1; LATIN - 0xA7D2, // A7D2 ; UNKNOWN - 0xA7D3, // A7D3 ; LATIN - 0xA7D4, // A7D4 ; UNKNOWN - 0xA7D5, // A7D5..A7DC; LATIN - 0xA7DD, // A7DD..A7F1; UNKNOWN - 0xA7F2, // A7F2..A7FF; LATIN + 0xA78B, // A78B..A7DC; LATIN + 0xA7DD, // A7DD..A7F0; UNKNOWN + 0xA7F1, // A7F1..A7FF; LATIN 0xA800, // A800..A82C; SYLOTI_NAGRI 0xA82D, // A82D..A82F; UNKNOWN 0xA830, // A830..A839; COMMON @@ -6378,15 +6491,9 @@ public static enum UnicodeScript { 0xFB43, // FB43..FB44; HEBREW 0xFB45, // FB45 ; UNKNOWN 0xFB46, // FB46..FB4F; HEBREW - 0xFB50, // FB50..FBC2; ARABIC - 0xFBC3, // FBC3..FBD2; UNKNOWN - 0xFBD3, // FBD3..FD3D; ARABIC + 0xFB50, // FB50..FD3D; ARABIC 0xFD3E, // FD3E..FD3F; COMMON - 0xFD40, // FD40..FD8F; ARABIC - 0xFD90, // FD90..FD91; UNKNOWN - 0xFD92, // FD92..FDC7; ARABIC - 0xFDC8, // FDC8..FDCE; UNKNOWN - 0xFDCF, // FDCF ; ARABIC + 0xFD40, // FD40..FDCF; ARABIC 0xFDD0, // FDD0..FDEF; UNKNOWN 0xFDF0, // FDF0..FDFF; ARABIC 0xFE00, // FE00..FE0F; INHERITED @@ -6555,7 +6662,8 @@ public static enum UnicodeScript { 0x10920, // 10920..10939; LYDIAN 0x1093A, // 1093A..1093E; UNKNOWN 0x1093F, // 1093F ; LYDIAN - 0x10940, // 10940..1097F; UNKNOWN + 0x10940, // 10940..10959; SIDETIC + 0x1095A, // 1095A..1097F; UNKNOWN 0x10980, // 10980..1099F; MEROITIC_HIEROGLYPHS 0x109A0, // 109A0..109B7; MEROITIC_CURSIVE 0x109B8, // 109B8..109BB; UNKNOWN @@ -6625,9 +6733,11 @@ public static enum UnicodeScript { 0x10EAE, // 10EAE..10EAF; UNKNOWN 0x10EB0, // 10EB0..10EB1; YEZIDI 0x10EB2, // 10EB2..10EC1; UNKNOWN - 0x10EC2, // 10EC2..10EC4; ARABIC - 0x10EC5, // 10EC5..10EFB; UNKNOWN - 0x10EFC, // 10EFC..10EFF; ARABIC + 0x10EC2, // 10EC2..10EC7; ARABIC + 0x10EC8, // 10EC8..10ECF; UNKNOWN + 0x10ED0, // 10ED0..10ED8; ARABIC + 0x10ED9, // 10ED9..10EF9; UNKNOWN + 0x10EFA, // 10EFA..10EFF; ARABIC 0x10F00, // 10F00..10F27; OLD_SOGDIAN 0x10F28, // 10F28..10F2F; UNKNOWN 0x10F30, // 10F30..10F59; SOGDIAN @@ -6797,7 +6907,9 @@ public static enum UnicodeScript { 0x11AC0, // 11AC0..11AF8; PAU_CIN_HAU 0x11AF9, // 11AF9..11AFF; UNKNOWN 0x11B00, // 11B00..11B09; DEVANAGARI - 0x11B0A, // 11B0A..11BBF; UNKNOWN + 0x11B0A, // 11B0A..11B5F; UNKNOWN + 0x11B60, // 11B60..11B67; SHARADA + 0x11B68, // 11B68..11BBF; UNKNOWN 0x11BC0, // 11BC0..11BE1; SUNUWAR 0x11BE2, // 11BE2..11BEF; UNKNOWN 0x11BF0, // 11BF0..11BF9; SUNUWAR @@ -6841,7 +6953,11 @@ public static enum UnicodeScript { 0x11D93, // 11D93..11D98; GUNJALA_GONDI 0x11D99, // 11D99..11D9F; UNKNOWN 0x11DA0, // 11DA0..11DA9; GUNJALA_GONDI - 0x11DAA, // 11DAA..11EDF; UNKNOWN + 0x11DAA, // 11DAA..11DAF; UNKNOWN + 0x11DB0, // 11DB0..11DDB; TOLONG_SIKI + 0x11DDC, // 11DDC..11DDF; UNKNOWN + 0x11DE0, // 11DE0..11DE9; TOLONG_SIKI + 0x11DEA, // 11DEA..11EDF; UNKNOWN 0x11EE0, // 11EE0..11EF8; MAKASAR 0x11EF9, // 11EF9..11EFF; UNKNOWN 0x11F00, // 11F00..11F10; KAWI @@ -6901,7 +7017,11 @@ public static enum UnicodeScript { 0x16D40, // 16D40..16D79; KIRAT_RAI 0x16D7A, // 16D7A..16E3F; UNKNOWN 0x16E40, // 16E40..16E9A; MEDEFAIDRIN - 0x16E9B, // 16E9B..16EFF; UNKNOWN + 0x16E9B, // 16E9B..16E9F; UNKNOWN + 0x16EA0, // 16EA0..16EB8; BERIA_ERFE + 0x16EB9, // 16EB9..16EBA; UNKNOWN + 0x16EBB, // 16EBB..16ED3; BERIA_ERFE + 0x16ED4, // 16ED4..16EFF; UNKNOWN 0x16F00, // 16F00..16F4A; MIAO 0x16F4B, // 16F4B..16F4E; UNKNOWN 0x16F4F, // 16F4F..16F87; MIAO @@ -6913,16 +7033,16 @@ public static enum UnicodeScript { 0x16FE2, // 16FE2..16FE3; HAN 0x16FE4, // 16FE4 ; KHITAN_SMALL_SCRIPT 0x16FE5, // 16FE5..16FEF; UNKNOWN - 0x16FF0, // 16FF0..16FF1; HAN - 0x16FF2, // 16FF2..16FFF; UNKNOWN - 0x17000, // 17000..187F7; TANGUT - 0x187F8, // 187F8..187FF; UNKNOWN - 0x18800, // 18800..18AFF; TANGUT + 0x16FF0, // 16FF0..16FF6; HAN + 0x16FF7, // 16FF7..16FFF; UNKNOWN + 0x17000, // 17000..18AFF; TANGUT 0x18B00, // 18B00..18CD5; KHITAN_SMALL_SCRIPT 0x18CD6, // 18CD6..18CFE; UNKNOWN 0x18CFF, // 18CFF ; KHITAN_SMALL_SCRIPT - 0x18D00, // 18D00..18D08; TANGUT - 0x18D09, // 18D09..1AFEF; UNKNOWN + 0x18D00, // 18D00..18D1E; TANGUT + 0x18D1F, // 18D1F..18D7F; UNKNOWN + 0x18D80, // 18D80..18DF2; TANGUT + 0x18DF3, // 18DF3..1AFEF; UNKNOWN 0x1AFF0, // 1AFF0..1AFF3; KATAKANA 0x1AFF4, // 1AFF4 ; UNKNOWN 0x1AFF5, // 1AFF5..1AFFB; KATAKANA @@ -6954,10 +7074,14 @@ public static enum UnicodeScript { 0x1BC9C, // 1BC9C..1BC9F; DUPLOYAN 0x1BCA0, // 1BCA0..1BCA3; COMMON 0x1BCA4, // 1BCA4..1CBFF; UNKNOWN - 0x1CC00, // 1CC00..1CCF9; COMMON - 0x1CCFA, // 1CCFA..1CCFF; UNKNOWN + 0x1CC00, // 1CC00..1CCFC; COMMON + 0x1CCFD, // 1CCFD..1CCFF; UNKNOWN 0x1CD00, // 1CD00..1CEB3; COMMON - 0x1CEB4, // 1CEB4..1CEFF; UNKNOWN + 0x1CEB4, // 1CEB4..1CEB9; UNKNOWN + 0x1CEBA, // 1CEBA..1CED0; COMMON + 0x1CED1, // 1CED1..1CEDF; UNKNOWN + 0x1CEE0, // 1CEE0..1CEF0; COMMON + 0x1CEF1, // 1CEF1..1CEFF; UNKNOWN 0x1CF00, // 1CF00..1CF2D; INHERITED 0x1CF2E, // 1CF2E..1CF2F; UNKNOWN 0x1CF30, // 1CF30..1CF46; INHERITED @@ -7072,7 +7196,13 @@ public static enum UnicodeScript { 0x1E5D0, // 1E5D0..1E5FA; OL_ONAL 0x1E5FB, // 1E5FB..1E5FE; UNKNOWN 0x1E5FF, // 1E5FF ; OL_ONAL - 0x1E600, // 1E600..1E7DF; UNKNOWN + 0x1E600, // 1E600..1E6BF; UNKNOWN + 0x1E6C0, // 1E6C0..1E6DE; TAI_YO + 0x1E6DF, // 1E6DF ; UNKNOWN + 0x1E6E0, // 1E6E0..1E6F5; TAI_YO + 0x1E6F6, // 1E6F6..1E6FD; UNKNOWN + 0x1E6FE, // 1E6FE..1E6FF; TAI_YO + 0x1E700, // 1E700..1E7DF; UNKNOWN 0x1E7E0, // 1E7E0..1E7E6; ETHIOPIC 0x1E7E7, // 1E7E7 ; UNKNOWN 0x1E7E8, // 1E7E8..1E7EB; ETHIOPIC @@ -7189,15 +7319,13 @@ public static enum UnicodeScript { 0x1F252, // 1F252..1F25F; UNKNOWN 0x1F260, // 1F260..1F265; COMMON 0x1F266, // 1F266..1F2FF; UNKNOWN - 0x1F300, // 1F300..1F6D7; COMMON - 0x1F6D8, // 1F6D8..1F6DB; UNKNOWN + 0x1F300, // 1F300..1F6D8; COMMON + 0x1F6D9, // 1F6D9..1F6DB; UNKNOWN 0x1F6DC, // 1F6DC..1F6EC; COMMON 0x1F6ED, // 1F6ED..1F6EF; UNKNOWN 0x1F6F0, // 1F6F0..1F6FC; COMMON 0x1F6FD, // 1F6FD..1F6FF; UNKNOWN - 0x1F700, // 1F700..1F776; COMMON - 0x1F777, // 1F777..1F77A; UNKNOWN - 0x1F77B, // 1F77B..1F7D9; COMMON + 0x1F700, // 1F700..1F7D9; COMMON 0x1F7DA, // 1F7DA..1F7DF; UNKNOWN 0x1F7E0, // 1F7E0..1F7EB; COMMON 0x1F7EC, // 1F7EC..1F7EF; UNKNOWN @@ -7216,35 +7344,37 @@ public static enum UnicodeScript { 0x1F8B0, // 1F8B0..1F8BB; COMMON 0x1F8BC, // 1F8BC..1F8BF; UNKNOWN 0x1F8C0, // 1F8C0..1F8C1; COMMON - 0x1F8C2, // 1F8C2..1F8FF; UNKNOWN - 0x1F900, // 1F900..1FA53; COMMON - 0x1FA54, // 1FA54..1FA5F; UNKNOWN + 0x1F8C2, // 1F8C2..1F8CF; UNKNOWN + 0x1F8D0, // 1F8D0..1F8D8; COMMON + 0x1F8D9, // 1F8D9..1F8FF; UNKNOWN + 0x1F900, // 1F900..1FA57; COMMON + 0x1FA58, // 1FA58..1FA5F; UNKNOWN 0x1FA60, // 1FA60..1FA6D; COMMON 0x1FA6E, // 1FA6E..1FA6F; UNKNOWN 0x1FA70, // 1FA70..1FA7C; COMMON 0x1FA7D, // 1FA7D..1FA7F; UNKNOWN - 0x1FA80, // 1FA80..1FA89; COMMON - 0x1FA8A, // 1FA8A..1FA8E; UNKNOWN - 0x1FA8F, // 1FA8F..1FAC6; COMMON - 0x1FAC7, // 1FAC7..1FACD; UNKNOWN - 0x1FACE, // 1FACE..1FADC; COMMON + 0x1FA80, // 1FA80..1FA8A; COMMON + 0x1FA8B, // 1FA8B..1FA8D; UNKNOWN + 0x1FA8E, // 1FA8E..1FAC6; COMMON + 0x1FAC7, // 1FAC7 ; UNKNOWN + 0x1FAC8, // 1FAC8 ; COMMON + 0x1FAC9, // 1FAC9..1FACC; UNKNOWN + 0x1FACD, // 1FACD..1FADC; COMMON 0x1FADD, // 1FADD..1FADE; UNKNOWN - 0x1FADF, // 1FADF..1FAE9; COMMON - 0x1FAEA, // 1FAEA..1FAEF; UNKNOWN - 0x1FAF0, // 1FAF0..1FAF8; COMMON + 0x1FADF, // 1FADF..1FAEA; COMMON + 0x1FAEB, // 1FAEB..1FAEE; UNKNOWN + 0x1FAEF, // 1FAEF..1FAF8; COMMON 0x1FAF9, // 1FAF9..1FAFF; UNKNOWN 0x1FB00, // 1FB00..1FB92; COMMON 0x1FB93, // 1FB93 ; UNKNOWN - 0x1FB94, // 1FB94..1FBF9; COMMON - 0x1FBFA, // 1FBFA..1FFFF; UNKNOWN + 0x1FB94, // 1FB94..1FBFA; COMMON + 0x1FBFB, // 1FBFB..1FFFF; UNKNOWN 0x20000, // 20000..2A6DF; HAN 0x2A6E0, // 2A6E0..2A6FF; UNKNOWN - 0x2A700, // 2A700..2B739; HAN - 0x2B73A, // 2B73A..2B73F; UNKNOWN - 0x2B740, // 2B740..2B81D; HAN + 0x2A700, // 2A700..2B81D; HAN 0x2B81E, // 2B81E..2B81F; UNKNOWN - 0x2B820, // 2B820..2CEA1; HAN - 0x2CEA2, // 2CEA2..2CEAF; UNKNOWN + 0x2B820, // 2B820..2CEAD; HAN + 0x2CEAE, // 2CEAE..2CEAF; UNKNOWN 0x2CEB0, // 2CEB0..2EBE0; HAN 0x2EBE1, // 2EBE1..2EBEF; UNKNOWN 0x2EBF0, // 2EBF0..2EE5D; HAN @@ -7253,8 +7383,8 @@ public static enum UnicodeScript { 0x2FA1E, // 2FA1E..2FFFF; UNKNOWN 0x30000, // 30000..3134A; HAN 0x3134B, // 3134B..3134F; UNKNOWN - 0x31350, // 31350..323AF; HAN - 0x323B0, // 323B0..E0000; UNKNOWN + 0x31350, // 31350..33479; HAN + 0x3347A, // 3347A..E0000; UNKNOWN 0xE0001, // E0001 ; COMMON 0xE0002, // E0002..E001F; UNKNOWN 0xE0020, // E0020..E007F; COMMON @@ -7359,9 +7489,7 @@ public static enum UnicodeScript { UNKNOWN, // 085F SYRIAC, // 0860..086A UNKNOWN, // 086B..086F - ARABIC, // 0870..088E - UNKNOWN, // 088F - ARABIC, // 0890..0891 + ARABIC, // 0870..0891 UNKNOWN, // 0892..0896 ARABIC, // 0897..08E1 COMMON, // 08E2 @@ -7536,8 +7664,8 @@ public static enum UnicodeScript { TELUGU, // 0C55..0C56 UNKNOWN, // 0C57 TELUGU, // 0C58..0C5A - UNKNOWN, // 0C5B..0C5C - TELUGU, // 0C5D + UNKNOWN, // 0C5B + TELUGU, // 0C5C..0C5D UNKNOWN, // 0C5E..0C5F TELUGU, // 0C60..0C63 UNKNOWN, // 0C64..0C65 @@ -7561,8 +7689,8 @@ public static enum UnicodeScript { KANNADA, // 0CCA..0CCD UNKNOWN, // 0CCE..0CD4 KANNADA, // 0CD5..0CD6 - UNKNOWN, // 0CD7..0CDC - KANNADA, // 0CDD..0CDE + UNKNOWN, // 0CD7..0CDB + KANNADA, // 0CDC..0CDE UNKNOWN, // 0CDF KANNADA, // 0CE0..0CE3 UNKNOWN, // 0CE4..0CE5 @@ -7773,8 +7901,10 @@ public static enum UnicodeScript { UNKNOWN, // 1A9A..1A9F TAI_THAM, // 1AA0..1AAD UNKNOWN, // 1AAE..1AAF - INHERITED, // 1AB0..1ACE - UNKNOWN, // 1ACF..1AFF + INHERITED, // 1AB0..1ADD + UNKNOWN, // 1ADE..1ADF + INHERITED, // 1AE0..1AEB + UNKNOWN, // 1AEC..1AFF BALINESE, // 1B00..1B4C UNKNOWN, // 1B4D BALINESE, // 1B4E..1B7F @@ -7866,8 +7996,8 @@ public static enum UnicodeScript { UNKNOWN, // 208F LATIN, // 2090..209C UNKNOWN, // 209D..209F - COMMON, // 20A0..20C0 - UNKNOWN, // 20C1..20CF + COMMON, // 20A0..20C1 + UNKNOWN, // 20C2..20CF INHERITED, // 20D0..20F0 UNKNOWN, // 20F1..20FF COMMON, // 2100..2125 @@ -7890,9 +8020,7 @@ public static enum UnicodeScript { BRAILLE, // 2800..28FF COMMON, // 2900..2B73 UNKNOWN, // 2B74..2B75 - COMMON, // 2B76..2B95 - UNKNOWN, // 2B96 - COMMON, // 2B97..2BFF + COMMON, // 2B76..2BFF GLAGOLITIC, // 2C00..2C5F LATIN, // 2C60..2C7F COPTIC, // 2C80..2CF3 @@ -7993,15 +8121,9 @@ public static enum UnicodeScript { COMMON, // A700..A721 LATIN, // A722..A787 COMMON, // A788..A78A - LATIN, // A78B..A7CD - UNKNOWN, // A7CE..A7CF - LATIN, // A7D0..A7D1 - UNKNOWN, // A7D2 - LATIN, // A7D3 - UNKNOWN, // A7D4 - LATIN, // A7D5..A7DC - UNKNOWN, // A7DD..A7F1 - LATIN, // A7F2..A7FF + LATIN, // A78B..A7DC + UNKNOWN, // A7DD..A7F0 + LATIN, // A7F1..A7FF SYLOTI_NAGRI, // A800..A82C UNKNOWN, // A82D..A82F COMMON, // A830..A839 @@ -8089,15 +8211,9 @@ public static enum UnicodeScript { HEBREW, // FB43..FB44 UNKNOWN, // FB45 HEBREW, // FB46..FB4F - ARABIC, // FB50..FBC2 - UNKNOWN, // FBC3..FBD2 - ARABIC, // FBD3..FD3D + ARABIC, // FB50..FD3D COMMON, // FD3E..FD3F - ARABIC, // FD40..FD8F - UNKNOWN, // FD90..FD91 - ARABIC, // FD92..FDC7 - UNKNOWN, // FDC8..FDCE - ARABIC, // FDCF + ARABIC, // FD40..FDCF UNKNOWN, // FDD0..FDEF ARABIC, // FDF0..FDFF INHERITED, // FE00..FE0F @@ -8266,7 +8382,8 @@ public static enum UnicodeScript { LYDIAN, // 10920..10939 UNKNOWN, // 1093A..1093E LYDIAN, // 1093F - UNKNOWN, // 10940..1097F + SIDETIC, // 10940..10959 + UNKNOWN, // 1095A..1097F MEROITIC_HIEROGLYPHS, // 10980..1099F MEROITIC_CURSIVE, // 109A0..109B7 UNKNOWN, // 109B8..109BB @@ -8336,9 +8453,11 @@ public static enum UnicodeScript { UNKNOWN, // 10EAE..10EAF YEZIDI, // 10EB0..10EB1 UNKNOWN, // 10EB2..10EC1 - ARABIC, // 10EC2..10EC4 - UNKNOWN, // 10EC5..10EFB - ARABIC, // 10EFC..10EFF + ARABIC, // 10EC2..10EC7 + UNKNOWN, // 10EC8..10ECF + ARABIC, // 10ED0..10ED8 + UNKNOWN, // 10ED9..10EF9 + ARABIC, // 10EFA..10EFF OLD_SOGDIAN, // 10F00..10F27 UNKNOWN, // 10F28..10F2F SOGDIAN, // 10F30..10F59 @@ -8508,7 +8627,9 @@ public static enum UnicodeScript { PAU_CIN_HAU, // 11AC0..11AF8 UNKNOWN, // 11AF9..11AFF DEVANAGARI, // 11B00..11B09 - UNKNOWN, // 11B0A..11BBF + UNKNOWN, // 11B0A..11B5F + SHARADA, // 11B60..11B67 + UNKNOWN, // 11B68..11BBF SUNUWAR, // 11BC0..11BE1 UNKNOWN, // 11BE2..11BEF SUNUWAR, // 11BF0..11BF9 @@ -8552,7 +8673,11 @@ public static enum UnicodeScript { GUNJALA_GONDI, // 11D93..11D98 UNKNOWN, // 11D99..11D9F GUNJALA_GONDI, // 11DA0..11DA9 - UNKNOWN, // 11DAA..11EDF + UNKNOWN, // 11DAA..11DAF + TOLONG_SIKI, // 11DB0..11DDB + UNKNOWN, // 11DDC..11DDF + TOLONG_SIKI, // 11DE0..11DE9 + UNKNOWN, // 11DEA..11EDF MAKASAR, // 11EE0..11EF8 UNKNOWN, // 11EF9..11EFF KAWI, // 11F00..11F10 @@ -8612,7 +8737,11 @@ public static enum UnicodeScript { KIRAT_RAI, // 16D40..16D79 UNKNOWN, // 16D7A..16E3F MEDEFAIDRIN, // 16E40..16E9A - UNKNOWN, // 16E9B..16EFF + UNKNOWN, // 16E9B..16E9F + BERIA_ERFE, // 16EA0..16EB8 + UNKNOWN, // 16EB9..16EBA + BERIA_ERFE, // 16EBB..16ED3 + UNKNOWN, // 16ED4..16EFF MIAO, // 16F00..16F4A UNKNOWN, // 16F4B..16F4E MIAO, // 16F4F..16F87 @@ -8624,16 +8753,16 @@ public static enum UnicodeScript { HAN, // 16FE2..16FE3 KHITAN_SMALL_SCRIPT, // 16FE4 UNKNOWN, // 16FE5..16FEF - HAN, // 16FF0..16FF1 - UNKNOWN, // 16FF2..16FFF - TANGUT, // 17000..187F7 - UNKNOWN, // 187F8..187FF - TANGUT, // 18800..18AFF + HAN, // 16FF0..16FF6 + UNKNOWN, // 16FF7..16FFF + TANGUT, // 17000..18AFF KHITAN_SMALL_SCRIPT, // 18B00..18CD5 UNKNOWN, // 18CD6..18CFE KHITAN_SMALL_SCRIPT, // 18CFF - TANGUT, // 18D00..18D08 - UNKNOWN, // 18D09..1AFEF + TANGUT, // 18D00..18D1E + UNKNOWN, // 18D1F..18D7F + TANGUT, // 18D80..18DF2 + UNKNOWN, // 18DF3..1AFEF KATAKANA, // 1AFF0..1AFF3 UNKNOWN, // 1AFF4 KATAKANA, // 1AFF5..1AFFB @@ -8665,10 +8794,14 @@ public static enum UnicodeScript { DUPLOYAN, // 1BC9C..1BC9F COMMON, // 1BCA0..1BCA3 UNKNOWN, // 1BCA4..1CBFF - COMMON, // 1CC00..1CCF9 - UNKNOWN, // 1CCFA..1CCFF + COMMON, // 1CC00..1CCFC + UNKNOWN, // 1CCFD..1CCFF COMMON, // 1CD00..1CEB3 - UNKNOWN, // 1CEB4..1CEFF + UNKNOWN, // 1CEB4..1CEB9 + COMMON, // 1CEBA..1CED0 + UNKNOWN, // 1CED1..1CEDF + COMMON, // 1CEE0..1CEF0 + UNKNOWN, // 1CEF1..1CEFF INHERITED, // 1CF00..1CF2D UNKNOWN, // 1CF2E..1CF2F INHERITED, // 1CF30..1CF46 @@ -8783,7 +8916,13 @@ public static enum UnicodeScript { OL_ONAL, // 1E5D0..1E5FA UNKNOWN, // 1E5FB..1E5FE OL_ONAL, // 1E5FF - UNKNOWN, // 1E600..1E7DF + UNKNOWN, // 1E600..1E6BF + TAI_YO, // 1E6C0..1E6DE + UNKNOWN, // 1E6DF + TAI_YO, // 1E6E0..1E6F5 + UNKNOWN, // 1E6F6..1E6FD + TAI_YO, // 1E6FE..1E6FF + UNKNOWN, // 1E700..1E7DF ETHIOPIC, // 1E7E0..1E7E6 UNKNOWN, // 1E7E7 ETHIOPIC, // 1E7E8..1E7EB @@ -8900,15 +9039,13 @@ public static enum UnicodeScript { UNKNOWN, // 1F252..1F25F COMMON, // 1F260..1F265 UNKNOWN, // 1F266..1F2FF - COMMON, // 1F300..1F6D7 - UNKNOWN, // 1F6D8..1F6DB + COMMON, // 1F300..1F6D8 + UNKNOWN, // 1F6D9..1F6DB COMMON, // 1F6DC..1F6EC UNKNOWN, // 1F6ED..1F6EF COMMON, // 1F6F0..1F6FC UNKNOWN, // 1F6FD..1F6FF - COMMON, // 1F700..1F776 - UNKNOWN, // 1F777..1F77A - COMMON, // 1F77B..1F7D9 + COMMON, // 1F700..1F7D9 UNKNOWN, // 1F7DA..1F7DF COMMON, // 1F7E0..1F7EB UNKNOWN, // 1F7EC..1F7EF @@ -8927,35 +9064,37 @@ public static enum UnicodeScript { COMMON, // 1F8B0..1F8BB UNKNOWN, // 1F8BC..1F8BF COMMON, // 1F8C0..1F8C1 - UNKNOWN, // 1F8C2..1F8FF - COMMON, // 1F900..1FA53 - UNKNOWN, // 1FA54..1FA5F + UNKNOWN, // 1F8C2..1F8CF + COMMON, // 1F8D0..1F8D8 + UNKNOWN, // 1F8D9..1F8FF + COMMON, // 1F900..1FA57 + UNKNOWN, // 1FA58..1FA5F COMMON, // 1FA60..1FA6D UNKNOWN, // 1FA6E..1FA6F COMMON, // 1FA70..1FA7C UNKNOWN, // 1FA7D..1FA7F - COMMON, // 1FA80..1FA89 - UNKNOWN, // 1FA8A..1FA8E - COMMON, // 1FA8F..1FAC6 - UNKNOWN, // 1FAC7..1FACD - COMMON, // 1FACE..1FADC + COMMON, // 1FA80..1FA8A + UNKNOWN, // 1FA8B..1FA8D + COMMON, // 1FA8E..1FAC6 + UNKNOWN, // 1FAC7 + COMMON, // 1FAC8 + UNKNOWN, // 1FAC9..1FACC + COMMON, // 1FACD..1FADC UNKNOWN, // 1FADD..1FADE - COMMON, // 1FADF..1FAE9 - UNKNOWN, // 1FAEA..1FAEF - COMMON, // 1FAF0..1FAF8 + COMMON, // 1FADF..1FAEA + UNKNOWN, // 1FAEB..1FAEE + COMMON, // 1FAEF..1FAF8 UNKNOWN, // 1FAF9..1FAFF COMMON, // 1FB00..1FB92 UNKNOWN, // 1FB93 - COMMON, // 1FB94..1FBF9 - UNKNOWN, // 1FBFA..1FFFF + COMMON, // 1FB94..1FBFA + UNKNOWN, // 1FBFB..1FFFF HAN, // 20000..2A6DF UNKNOWN, // 2A6E0..2A6FF - HAN, // 2A700..2B739 - UNKNOWN, // 2B73A..2B73F - HAN, // 2B740..2B81D + HAN, // 2A700..2B81D UNKNOWN, // 2B81E..2B81F - HAN, // 2B820..2CEA1 - UNKNOWN, // 2CEA2..2CEAF + HAN, // 2B820..2CEAD + UNKNOWN, // 2CEAE..2CEAF HAN, // 2CEB0..2EBE0 UNKNOWN, // 2EBE1..2EBEF HAN, // 2EBF0..2EE5D @@ -8964,8 +9103,8 @@ public static enum UnicodeScript { UNKNOWN, // 2FA1E..2FFFF HAN, // 30000..3134A UNKNOWN, // 3134B..3134F - HAN, // 31350..323AF - UNKNOWN, // 323B0..E0000 + HAN, // 31350..33479 + UNKNOWN, // 3347A..E0000 COMMON, // E0001 UNKNOWN, // E0002..E001F COMMON, // E0020..E007F @@ -8989,6 +9128,7 @@ public static enum UnicodeScript { aliases.put("BASS", BASSA_VAH); aliases.put("BATK", BATAK); aliases.put("BENG", BENGALI); + aliases.put("BERF", BERIA_ERFE); aliases.put("BHKS", BHAIKSUKI); aliases.put("BOPO", BOPOMOFO); aliases.put("BRAH", BRAHMI); @@ -9107,6 +9247,7 @@ public static enum UnicodeScript { aliases.put("SHAW", SHAVIAN); aliases.put("SHRD", SHARADA); aliases.put("SIDD", SIDDHAM); + aliases.put("SIDT", SIDETIC); aliases.put("SIND", KHUDAWADI); aliases.put("SINH", SINHALA); aliases.put("SOGD", SOGDIAN); @@ -9124,6 +9265,7 @@ public static enum UnicodeScript { aliases.put("TAML", TAMIL); aliases.put("TANG", TANGUT); aliases.put("TAVT", TAI_VIET); + aliases.put("TAYO", TAI_YO); aliases.put("TELU", TELUGU); aliases.put("TFNG", TIFINAGH); aliases.put("TGLG", TAGALOG); @@ -9133,6 +9275,7 @@ public static enum UnicodeScript { aliases.put("TIRH", TIRHUTA); aliases.put("TNSA", TANGSA); aliases.put("TODR", TODHRI); + aliases.put("TOLS", TOLONG_SIKI); aliases.put("TOTO", TOTO); aliases.put("TUTG", TULU_TIGALARI); aliases.put("UGAR", UGARITIC); diff --git a/src/java.base/share/classes/java/text/CollationElementIterator.java b/src/java.base/share/classes/java/text/CollationElementIterator.java index 5469b95b11e8f..46fe80e1bda4d 100644 --- a/src/java.base/share/classes/java/text/CollationElementIterator.java +++ b/src/java.base/share/classes/java/text/CollationElementIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -661,7 +661,7 @@ private int nextContractChar(int ch) // (the Normalizer is cloned here so that the seeking we do in the next loop // won't affect our real position in the text) - NormalizerBase tempText = (NormalizerBase)text.clone(); + NormalizerBase tempText = text.clone(); // extract the next maxLength characters in the string (we have to do this using the // Normalizer to ensure that our offsets correspond to those the rest of the @@ -732,7 +732,7 @@ private int prevContractChar(int ch) pair = list.lastElement(); int maxLength = pair.entryName.length(); - NormalizerBase tempText = (NormalizerBase)text.clone(); + NormalizerBase tempText = text.clone(); tempText.next(); key.setLength(0); diff --git a/src/java.base/share/classes/jdk/internal/icu/impl/CharacterIteratorWrapper.java b/src/java.base/share/classes/jdk/internal/icu/impl/CharacterIteratorWrapper.java index 79a6264d16dc8..698f28ea2be11 100644 --- a/src/java.base/share/classes/jdk/internal/icu/impl/CharacterIteratorWrapper.java +++ b/src/java.base/share/classes/jdk/internal/icu/impl/CharacterIteratorWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -47,7 +47,7 @@ * @author ram */ -public class CharacterIteratorWrapper extends UCharacterIterator { +public class CharacterIteratorWrapper extends UCharacterIterator implements Cloneable { private CharacterIterator iterator; @@ -135,7 +135,7 @@ public int getText(char[] fillIn, int offset){ * Creates a clone of this iterator. Clones the underlying character iterator. * @see UCharacterIterator#clone() */ - public Object clone(){ + public CharacterIteratorWrapper clone(){ try { CharacterIteratorWrapper result = (CharacterIteratorWrapper) super.clone(); result.iterator = (CharacterIterator)this.iterator.clone(); diff --git a/src/java.base/share/classes/jdk/internal/icu/impl/Norm2AllModes.java b/src/java.base/share/classes/jdk/internal/icu/impl/Norm2AllModes.java index 7922bb4682921..67c9eff6f3449 100644 --- a/src/java.base/share/classes/jdk/internal/icu/impl/Norm2AllModes.java +++ b/src/java.base/share/classes/jdk/internal/icu/impl/Norm2AllModes.java @@ -269,8 +269,8 @@ private static final class Norm2AllModesSingleton { private Norm2AllModesSingleton(String name) { try { @SuppressWarnings("deprecation") - String DATA_FILE_NAME = "/jdk/internal/icu/impl/data/icudt" + - VersionInfo.ICU_DATA_VERSION_PATH + "/" + name + ".nrm"; + String DATA_FILE_NAME = "/jdk/internal/icu/impl/data/icudata/" + + name + ".nrm"; NormalizerImpl impl=new NormalizerImpl().load(DATA_FILE_NAME); allModes=new Norm2AllModes(impl); } catch (RuntimeException e) { diff --git a/src/java.base/share/classes/jdk/internal/icu/impl/ReplaceableUCharacterIterator.java b/src/java.base/share/classes/jdk/internal/icu/impl/ReplaceableUCharacterIterator.java index cf58f614155ec..b44a0d7675c1e 100644 --- a/src/java.base/share/classes/jdk/internal/icu/impl/ReplaceableUCharacterIterator.java +++ b/src/java.base/share/classes/jdk/internal/icu/impl/ReplaceableUCharacterIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -51,7 +51,7 @@ * * What are first, last, and getBeginIndex doing here?!?!?! */ -public class ReplaceableUCharacterIterator extends UCharacterIterator { +public class ReplaceableUCharacterIterator extends UCharacterIterator implements Cloneable { // public constructor ------------------------------------------------------ @@ -86,9 +86,9 @@ public ReplaceableUCharacterIterator(StringBuffer buf){ * Replaceableobject * @return copy of this iterator */ - public Object clone(){ + public ReplaceableUCharacterIterator clone(){ try { - return super.clone(); + return (ReplaceableUCharacterIterator) super.clone(); } catch (CloneNotSupportedException e) { return null; // never invoked } diff --git a/src/java.base/share/classes/jdk/internal/icu/impl/UBiDiProps.java b/src/java.base/share/classes/jdk/internal/icu/impl/UBiDiProps.java index 287ac38c87b39..e7e1e65c2a029 100644 --- a/src/java.base/share/classes/jdk/internal/icu/impl/UBiDiProps.java +++ b/src/java.base/share/classes/jdk/internal/icu/impl/UBiDiProps.java @@ -201,9 +201,7 @@ public final int getPairedBracket(int c) { // data format constants ----------------------------------------------- *** @SuppressWarnings("deprecation") private static final String DATA_FILE_NAME = - "/jdk/internal/icu/impl/data/icudt" + - VersionInfo.ICU_DATA_VERSION_PATH + - "/ubidi.icu"; + "/jdk/internal/icu/impl/data/icudata/ubidi.icu"; /* format "BiDi" */ private static final int FMT=0x42694469; diff --git a/src/java.base/share/classes/jdk/internal/icu/impl/UCharacterProperty.java b/src/java.base/share/classes/jdk/internal/icu/impl/UCharacterProperty.java index f439ae23d0f34..073ffe933dfb2 100644 --- a/src/java.base/share/classes/jdk/internal/icu/impl/UCharacterProperty.java +++ b/src/java.base/share/classes/jdk/internal/icu/impl/UCharacterProperty.java @@ -330,9 +330,7 @@ public int digit(int c) { */ @SuppressWarnings("deprecation") private static final String DATA_FILE_NAME_ = - "/jdk/internal/icu/impl/data/icudt" + - VersionInfo.ICU_DATA_VERSION_PATH + - "/uprops.icu"; + "/jdk/internal/icu/impl/data/icudata/uprops.icu"; /** * Shift value for lead surrogate to form a supplementary character. diff --git a/src/java.base/share/classes/jdk/internal/icu/impl/data/icudt76b/nfc.nrm b/src/java.base/share/classes/jdk/internal/icu/impl/data/icudata/nfc.nrm similarity index 64% rename from src/java.base/share/classes/jdk/internal/icu/impl/data/icudt76b/nfc.nrm rename to src/java.base/share/classes/jdk/internal/icu/impl/data/icudata/nfc.nrm index 114da46498df3ff787c0e42d58a4b309863f91c4..fa1d2917ef760977c7474f739ab8016201b9d61c 100644 GIT binary patch delta 564 zcmZpe&Gcb5(*zM6K?W2M!NB16hJm5y1Or1a2A=3(&zvCtXk!yQ2md3sPwZUmn(TI9 z=rmb?$!_vBjyo~Z0ZeKTD4oFS4`N9dz(gQ4hb&mM0Vp#;dam?Jm@JD3P-Khr5vVF5 zn7H&62uu3@R?^Sl5+7w4I5lMWScGI`WOSI+WK3k7IO<`tGC}O8U~HgVl1w2` zyA&HIhfJkR=VU!@F(#SGlc#WLGESI0fm@z&@#K447nwK790c(=FG6(k>jL%YOm^cD znXJI1BPR0>VIYhp^8utpK<2+JzpSjR7L%H+nXHR!$mAJ3j;txNIkHug@A0HFwgMH& zO-|vJVFN0d%6=KhRp6Ih3Nr*m%TC_NEk1b_uLFzxLHUco@U)q%#va&lE7#SG=5wxbEOH=SAbkiDA_C!W*7B-i*}TZ_hb`mF$+LT&a;$;^0K^}d*8l(j delta 452 zcmew`o2g+o(*zM60R|Kh!NB0x!objzz`)RpfhRiHGyBRP+}Om$VM(8#yn#`~O8OdH;-T~hP7UdQEJ89oGBQkRGAc4g9Q80+87KBrFg8#wNG1`e zU5bs9Lnc$Ea5Qd7MRJorZWeKiW7#~(tAb@SkN-DY#>~mgy-xvh$7^~3 diff --git a/src/java.base/share/classes/jdk/internal/icu/impl/data/icudt76b/nfkc.nrm b/src/java.base/share/classes/jdk/internal/icu/impl/data/icudata/nfkc.nrm similarity index 90% rename from src/java.base/share/classes/jdk/internal/icu/impl/data/icudt76b/nfkc.nrm rename to src/java.base/share/classes/jdk/internal/icu/impl/data/icudata/nfkc.nrm index 66421d6f29f65ae3e96ca6a5db716eb86da56a29..c78a9d9cac5e80594bcd617998d54e8e47a8f2ab 100644 GIT binary patch delta 832 zcmZXQUr19?9LMjyyY616b2Gt%ZgjCv^4 zkP5Lnfmu)wioW%SyKIEjf+!G?6{56!@F7e^Xb;gJd#H20ZYrH0=bZ2PeSY5`oZtL2 zTYAiP)5FT8Eh{ET@g7OaR!Y+6|Msm&gKd8(_OwgWU5~Zb+N}0N_pF7N?@{WF8QmwJ z3}h8*MPRw8&s2fce8#%gbvwQ_gqK7O^df=3jF$c3%OHSY1aQbyaM5$%wv zgg(yQ?l8}}R8@;>{XFCDG!B-Z_VZM=-=C6XN&Y*zDN+SJElRQB2;K85)x*Zfk;Mu= rMRlG&s_Md%GQ;R<>(Hvq9%h$~)5=(HW3l~&X4J)#=j0{+?bFgffQ0KN delta 730 zcmZXSUr19?9LMjy+wQ&2&0TSvjjxP{`PeMn3F8tk#1)L{UUR z1}0&DIFVpM4@#Cu%Fl>|usjjUI%;)}IHVAnB=dH)l$}!{L0-_jA4n&W|%c zV_SJ{yX|F$w5_@&sXr)5AF`5E{BPcgAFwqww@hEvbk75AN?XuA>2i5EcvNXKm-T?$ zqdrk+WrT~4VM~R-Rh`PJA1}Lq6pEeLhnuR@o)pe*+_rHlQT1_WMd4#SBYXGZ)FwEM zFV^(o>-Ge`haZXXQ#_}?+@yJ3uWG?Q;_o6JXsl_+KZ&gZobc~}rHGrqbrRXh*J^jj zAs(z3ygR}T*B-uDM|j5NQEFbF4OWSYDZ7v;!%QsSz+ z{R&w}w?GF!x`FNkh8P@%ZZv>68iQUqiylG>uEAaKqCqr*CMv|r2vW&xBQOqGSb!z? z46~4fB4l753a|n*@EVq33g#dWzo?x$sY(NMCv`_@oF-_Bo>8cy#cCk`;qH(x3)RSz zbwPvP+}Z;=Dgg4+Njgh&v`9;Io$1WaFpIGc784LN*2PY+^Jst#DwK^_X*RL-4)%h* zVei-%_B*0k|3W205nm+C`#f>6ZRM+;ERqW9s#K5)nyMjfucd@;IP$-0mkH+Ykon2e};Xmi(-tRoVbI+MO zXLg-;UAW*{pO_b?-Sxz3TJvO0duls+vo+21X&O7Kxx^vO<>`g*)6-*?x_hctEFGt5 zo+`eoyK%Q`gxO>6G#bnUMyq*5&ohsaka+^v84_~DykK5|sqWm$KJ!7IJztIV_n3j_etuTYO&;_)fHqmG4O1hQ?>0`8$Zo=pmy3L6r<8}HT?jhLc&PduH z^`htFaQ+wcYq;r73H(5RqPOX{j4;U(n3rX+9F|Y}9X^{7!Aa3B9b^wjaE60AVRP6# zwvdEaLpY<|$%|w+u@?3sd%@v2Ap*YQTaj6WC2 zSjpFtw|S6vMh@R(HOF@7qu;V$-wg6D`(BL9XvGLA{C?QoUf4yZLvvDG?qS_OSomlB z%c#xI@e{hAU$Sj}o!^G_-o$_)yk8^;86`O=Qgy$`61n;w#QQ~&m@1}=nW9Sf8#!XW zSR@+tJh8%PwXaG%PeLMq_DiD8ZcB6+IZk_QhS;d*8Qn&k*n<9d#7?6WalYsY^A3n3 zB!CG~5ub~%!htiGa1n`D!qyFOC&~!h{evwMl5Qy+kBl+eWRgsg8R*4tHz0ECAbq^8 z%ThVnMog7{@iXp^!fIDyykY5ie6QH!@MW!>i#J=U%O!F-Dn&XwC|gl^#*{d&at*3e zl~|4HE8$aLg|S0txHj1CPQsY|NcLjlF`Fb$ zItA@LY@f#?3KQjJa#miKx8+rGO5T(|%6|D5w&_zkQ52U)C8=~|Rwb&D%KmRcb}x=# zbE{p+rr1Y9Y8LO!pPZkchY8W&u z;c<_SIga*S)2I8*pju|XklC(UR4Z#z0dfi(dKp)6pgMg6ys70GojBz-HzI1IdehEU zJ8*TY9(4eGsrRI|sUr$6O`TBZ{$XCSHFXWIVv1YcLRb}0w_;rCcUxlIj_72t#E7W> zf5EIs(f>N!ickMjxGJpJ>;w-4YKFpQUncZr=Ru&_v_=e;y`eLU1QFql`Wptd|70>z`CZ(6cdu%J`#m}Nr2bOsMj@Ib-69{4aP84f@B z4IIwIvc5EkSv&^X>xaR*h7s`4qFz{D&Y-5k3w?_+p}I5{&r^1OmK}%gf)+h@0g`5W64G0 U^1_wINml05rcSk``Ca(@4RK>6)&Kwi delta 1792 zcmcIieN2^A7=JJ4ea|HyHzcNpsELT1UQAQ(mbjUs1Hr+fvP`YeXS?RE>;Uot`qfcoO+2*vS>pAax@5Ddqzusr(`8^-M=bY!9 z_qlS#(>?5I^9EClyFPHAqK2{Q1&9U=1N#kw_8A_2((ojGroPM0POM8f8h)m}*f5g9 zY<17(4?I&XiK135?yW>G&GbNMtt3$;aO#JhBn&W&M(izcE=X_!8N zqI8{88FA}lyVTMK`Xb$;xpWVH2yn=)Ls7pr;DA;%3dsiA2KpfFh^Og0b{9$|%?-H4 zdOIGxgY-RZ({9?U(YgfZ?Q$X~T@F1%&*N2ez{wt=mx22;9Rp@E{S$Nm{J$Bp3Q&|0 zE1xC1RV+P*SuUceT_UcU%Zu1l7BD~jcVQ*0%+0bzYzbSzs@Pgq6N}Wdr%@-{#+(M> zz`b@;O1t@ByS_edVQ=Vnv2x=l#yWT_hFz)~EmlqGsnfd6&_rRq?31|9&au;Gh+WV= z8)74>EyLT)uCY;`!2XJpT;$vg@id;xGXM+t!~79GpD*N}Aj;f#_F8mZf>m~*dl)3zS@GBtc zO|xt>$h!FZvD6B&6kf616jh=cj8c>Kih3{_uxNyuXTdDP9Ih<3?a#0A|=LsZ7zux)oQTx1FAQG4DZ9nGWXUG?^XGxhQk> zUN9h%%K|ebi$P|?{|~5+47wpUwR&)1dU@W2S-5MO<$?(r%?{xfxkO(AN8~cOlGe&K zXaJg653A-*2c%tPWGK z35S0guK#F)>;rhYS@y|6+3(1Zbi_mxMgHJM|0jKa?9l((-cHND#XX63O5mO(d*7^4 zp-ybdRmnBk_MY-zi8>J4uBJbL?fI3jVU-xlQaO1(wKk7hiAJIkQ8I6i%~t+G)a9U0 zt*esuyftM=&0Z$e@o<{@eP)t6U!7!6e{#F2u5S2NrB)WH96z;hZY=Srn;Q$YQnjt$!hSi42W}exRk0R^ZaVxrXa+1Omow~-wdY()p)koo>@17 SJcjZ$%(24_mPy}@eg6PY&nM>q diff --git a/src/java.base/share/classes/jdk/internal/icu/impl/data/icudata/uprops.icu b/src/java.base/share/classes/jdk/internal/icu/impl/data/icudata/uprops.icu new file mode 100644 index 0000000000000000000000000000000000000000..b72fcd903e009ddba8f3c750bd152cd0f3aa3db5 GIT binary patch literal 170864 zcmeFa2b|PY_dh%>nUd)(Gqas-tb!E~6%-3T%3{Z^*s!43zygYj1q%z@j-d0nn6!S$+sF4uLhuw|Q9y#8I+giO2gSeI+e#PMsn zT*-7w4UI^i?`iZ+_s#NM;Jdf3UyKKhodn-`+pmzpsD1 z{|NtX{*(Nt`Dgp5_^nD<68|ee-|;W?f8qbe|4YCX2>9kI zD+2LAF3=e0uN)s38dw9WwIMJ9yfzPv4vYc1D=d2j4h$R)JwHA$IWW!cZ`TB-2WFu* za6#aTz}&#CfqQ^|IIuAAY~WqsJ`Q{m_&V?^aNh>iz^_41FsiHwW`niCmf!&3h6Ohb zjt&kA?j9TyoDe)Lcua6y@but`V3`fe6~POGbAz`A=LPQ%E>K#7i-JpnF9qKWE`e<| z8n&l_;0HzeP4JhHI}{8hLYYuqaCvBOXn3eKG%z$Wv}I_A&>o?qLi>dd2^}Ar9y&jC zNoZzhjs-V{<^@|qcZVJdJrQ~)^aAj&h29H&75WBhZ3!*IHG}5T0->Lae1x<`Ii=ME zTeN0v6|){P6x-9*3eC~h)kcs^6rwiQw$`?%IAi{`YP+%CY5OXz+QG^S?Fj88ZHhKU z8LgeEouggio2$*i2<--nLmGNnyH$HwyGLCUA{OG7)SlH|)!xJ{rM(9u+oFA}eX0GR z{RZ3*VXqiVd{|>!JDjp_^~yBid}V&zD_qa{|8dSEfZHhC79JR0U0D%cCpVt4T zWk#eqva0XZ$oi4BQ6JeP(iwsG>d0P^10#nqmk#q_Il($kxzhr9r-EpSJWSkMN`olr4_Iu+H7v^f71U(;Qz`9L|2Wj z6J6amw{M7U*hl;;`ak?XGy-h)Mz@A@h9{#tMt3X9##w6M8{gdMl<4^A5vYxx6qTM& zMrQ~6gY`mKE{|Rvy&-x>^uB2EtS7oK`mXPS=*Q77qwq;H`fT*oKF*#Eljj3oNu^U` zQZrLa(&6-`=~L5pq*r9t$c)HL%iNv$KHHKVm%TQ7clJ}gQQu3yT7N&6&W*@T%gxWd zl23=Gg&u=XiX*gX+RfVM?zh9y&w9$c*O)sN#Or{FSR&RG%g2VsI$~?YMrgOj zHm4Q>%fPx#Y-d>S6+19CA$BBiQz7nn<@DI8z@HI2J9c&KV&HCo<@VU4vHO5N&GL$C z?WDlBvBj~sVn*L%pT$0meHr^9sK);A&5bMZNIZkbd1XbsH9jz27atzqG(I>!Iz9%h zrziR)R!OXp*gP>bF(NS<y5>zAb89u1O>{<*X{Zi_!)Q7QWoU2NG zoBB+&;w(@7nhvEsY~M{M(|WoNJ<_e^+ppXs@NIh4pvqciQR%hCI{tn>J=9)(dPI6; zdbC}Sn!bNFvi7k!4%?-7O7Cg4RV&l=e@4>>m}OB)AC^8LeY7$8AFVo+pf6I6?TKeNmDpQkb z&h$_JYFL)0zsszV{xGvPSk}*MlIhHBm)Rh*S!S!u4w;QId!*kqw{Mq}(V0VFIWlux zWyneZabw zXhS~Ol067VUArNDX?9}hG#+1s)(r#M0{ zW$(wIXy&m^_J>M+yZ*>yDz;7_zrQK z{I~h=Wk&n(&7U3my81?TZC^Ai>zcH_d0(oiJXVM64=WRT@%y%wnX^>|XgdQ-OuUEcAK_QV_~%^=H83g#SB*HCuQTkL5a+mCKZaG#=RE7>7`baKpWYXcyNgJ1@=_F^cCzPOPM| zcrjNsJnJ1>Q>z$Htvb~r#{D;V9Pi%b^|{KU0DCpU$&>6d%45t{pOMe3lSy#v=RXRX z8(1&$`0^D;8sAgsg|)kTj&pbeE-oyZ%?al<^EBJ5+mYlrTl7ynTB6Xl=5Bqwo)0Z-=3wi$!xMYSbj_6?DxT}=jiSB<8gRK zhp!qy9_W8>|4%_~-N?_^M$^KI-p8t=lz^geeB`e_)di~*J5R|{;`U2>nO#oz~{fn zxnL{xbp*r;oq3#Ff6q8#EAl!N`2;U&oyV~+b=xb!t%#pb5I&=A)%esxo@^Vd#cj>B z=q7GAJ73-CTH8Xo`d*5)IkjILTp;E07c3rl%;7Jp*i7RNTiWa;m+bY>$jkk$T8u{> zZVTDm!L4g=gKJp6KuI6&E$38Mz}Rx|Qy+sf*T}T!ft+=(kY&Vpe98KQ{_yE~o$kTi z8+H%s9@ZV~gn66?ExtG5_GRtZw=`zNXri)~ncZx`DE1;fN;p%^{Y5Qmh@yw}#jL2M zmP8L(e{n2WySR;IFZ95?R2|FAJc$==R^A+mHM1yjjs0c4!|lkn4z@r%QqmGdJHDtr zj+1bcoezu*lzpT$pX0x37!0Tli)(RhCl*w1# zU-Yn#6XnHdY@MA2Qy-90KZuGpk(b*=Ew;t>XBu7VFQd4oK2Tk3t8#V*ql$@=z8Z?z?e9z29v!vV%;I|-%N|&wE!Mg_7imK}7j+KCE7Iai z(oe&TS>^9ZXlDJatT(K+N_#5Hi8_3lbFjSp9f^$ujuJbX)4t90xHiYjB(`U=9JFTb zxQz1<9%Da~b2>*8M-k&$Z+IqazqrN9StGe!RyWpf9XGsE!H)r~hw^hV>^0BNj_CH! z1fm8%aWFs3%ijZPWaoPDjY#LFoufO)bnf0cu5&`?k(~qZor8VyM2g>S;Ue2^Vr9I> zQXKXMU$jkSl)pRD6%n3M=FeuC+)}BQUEvYO$~=Eah!Fx6Q9@X3J(|n!2E?>8z`MX1%;qF*xqC4H0?ap`B;JIcOrhu0`KiAS;BT^R0 z*5dXr$76&zA5cz}t$^1e=kVAmw}{$s1wNub;Bz6SAn(Qy97*UV$PA}2V%+nlVGqU=4b2iooHWIYv$27+A;~jZ$%Jv8P zP64lfus%7Rs}<{|&l_qdJo}~FufbDYXb)i?<|jR8@#AkSXZY9(hlVk-)qpL1Xq@w|)9+}YhI ztfg}2ZgM-uG7GPiaV#rev;NUL9`#nFd_FKO^~Zb+VG{FYM{L%Hy?;!@N1-pAQ+7|! z=OAZ~QWjp5?4AlIoi!u+S;4m#yDJJuiTN-}b}!s_ROqqsEMzCjEo|119x2bwy+k|m z+`_n0cFZT*W%=1>O?H)-Jh$7&*JF$^-2rU3;})QJs)hRR zD#m7u$Mc+VWyiX=F&XbuCO@-BxaS@b{MMH47ZIqZ8)h=R#Ltaz9~S~Y1NmHGDR_ny ze3Bzz+qJGbh^+0Q=6ZBj#1$3lpu4k0FU&x4*3)hOTr1Rv*XEpn-NGx7eFw`VUyDaF zGfzKJSBpP&H(R+R&r4c*e|N(>J73r=se#!;^M&6z>oKk);=Do|+m^WI`wHB1UG%9L z+%*KQaS!Sm+BFy+dpKO%NE_TavU70v@a~b_TXrj5h99=ez&L7^+Q7`LI?PNJ8NSL4 zWw9DkN2xQ^#r}|glz)bQaUc{Jl~`Oq>f+GkQRVEDM_oK>@(iq9U7T2a@r=of74~fz zjSuf1)rd8){&9^n$K$OW#;`orY*yJRJJo^yPBGS2tCg*7(=}kL-btn!xg9fbsanpd zpOxjQy!VymvwMjx*saxRXbx6F<(VsqWImF3Pj^5IbOyQt-9atb8SDynhqO?q{dno` zmK{a?6-Ucjsd|Qp<8GgGlHQqGkj*NYu3R3gy?hSZpCnW^(v|8=y%VeA*86!VyWE_|2ToNg}0*h<1Bc(Y>lt-AadyyW`cR8;^JV|znTajrg zJ5heOy11n);pH<=9%&yjvql~zXzgcc#&rsuiFnN-C7gdllz(w(RCqDy@H-OBLTC6j zs8dGS#u`Th{b*E(rVLHSxF$qfzdXghVP*Kq3iqh;5poawo|DD%(UN)n#%5-HR&gn|$ z3LQbJ>w(!Dx1bYnCRB>P)}mC=7S`mSUTI}=zw-OI7&ooVI80e6dY9rWnXT4%e21tM zeC}}09$W3`*^j;UoTW9YFhbRhJadJxsa$^hucMAKVTc;rz~rC*tcGluGv|> zxVGlA%jAtcW#6~*YwwEj$)nU5;OFvEcCyiZmi+TPS@0@>JqJE#fIVpdt?YRk$dzu7 zU2$OfMBbj?YL(Ss*0GVp`ASy_h;mK2*-Uo&c0ADY`Jnhsx#gDy_{%rClZdxZSxQlw zQ`YTG-`L~4v%WA!w!evf7+yoUDDa*{@%rg<7E&L~0)emdiZ;EDIYm zS^JoUMvK=X=gfU#?>g+B8?o>5SXKx1h)H6l*fTbF?ENaO!CdSeOx`lvR=9oGHpXKt z>SQl51KP{U<{YB8eS5O+==m|XQjS04#vZ|r%v6QlZq~5+*zDzN+;7-uIL!~rPyHj= zO37X=r99?1xz_AsmXfZ%Apw6&*%z3rDN5F>0{jzBsWW2J%v02bm45~{M>2`GDYF_n z9Bpa+CT-3nnf=9h4Rm00jsRptD! z%~C$rn)o=#Ij3BBeuEI-b!0rfUoeVS)y?1J$YMktepix$yNa^5EAw=P9y7DNtPS*~ zyO8+%6}yfv#^BQzQ^jxI(2qT{hDL$+Tb!$0TRDdr*|RFd7}QoKqbIw6CJV(!uWZQ?flGtzJ&F2fGt&ZZmwNr$7w4J<2 zU#U0qE9Z?{hce4(IlC-|>3X*N9ol?ta_6+pGdj=iytwn~&Ko-K=$sF~vYOU)M%US0 z7k6FVbwk%3UGwQTC2Zdn*KBqa^U1z#vHyl^ns$bEwsx_0wRVH%UrY$Y{ZS0{qs|Cj z+ylIWN}Z8dJg9R}=g=;Arl#1siB~!Yk+sw}j7F`477v1Zw=gfXH0omD{LnhPrwiNT z(&QNzg9M-D5sMkr84mkS&$yOlOqDzLNn&B=QO?_zETv0OtKU{QS175{rFQwd;rlB4 zi`p+kYZhDQ_WY2LtW2~imLKy~Bx?tIx?!opj1;)sw$9pS9oQN7QP!%H(IJPER}L_hn-pTwhTq!bb7|n$!e)XB8|Z}hd;@>s}$a?gnO{JPbAM+UmrcZz2e<}(_)pOIz+WHrCo`HQ$V+v#eO z^;g35oB4OTPWyO~;FU1ive==jf zLPbWd(w{D^-b`kh%q_`14+HSXn;*JIXN+r+zQ%e`cEQD5-W zqjdx=G98CXKi$H59D~n1a6BE_1;tTYB4tMnb{{6{d;8)eD1NqJpNXwG?Z4|adx-T_ z#nXPk-dV%&tPkTIz?@w^4%T+W{`Bzl?g+<5f|wg~kgv=x`pWp88mJ!?o*xu*2~Rlb zl8aE-r>O6Y(g-kGtg+M_HszAFfZqk!ztO_{^h9U&yifbs z+OL7h@>@qmw!ezG%{h4Qd5xr#ACqW=*t3tUt(TvtEW5di>LqqBfxUHn4jN^nN~u5W zyBOK{uy0qHKSh_O=B-LC;B4%9=N0d~wOd58h|lcIQ@B0p`FLk4KKZxUQ(M5VW&+Ykla5 zI-*uP$<|SzD6(S)Y=!N4tTn1wq^u9-eS_(Y5{z0X7t{1+&8y-ZR*WxPb8UGJy|D-8 zV-W7k_`)@_6334E?0YDj_v~8>^yRPet&)emW!jc(n`2ev%l9_fe4<*&pI1{oM=d&0 zd(~JcjT7qun=S0Rp6iNU=IYTx z$5yKa2JfORch8yd^O=@!VRDV=eRctX#Qxv{S2g%Jxv!25v_@7o@!wY4ux`MoOgC znoG%8MX8r$|1!C=Ua;0phN2*er%#}z&%BHAn7Q$!bMC?u%q+ zl*w#u@43BAvj!u~`Ro$TBlM#rzHU^m9;(}k^1P)u96cNpoXzGJ8zph?HS@Ci#94uv*}Iri`_+vT z+j2AqSgmZ!n(MYo?487_cZu|@IE*XbSt zPtsY`xncKU4ewLOe>8p8{tH+3d?KgOanQr>zNSm(x`RLWwZ{z_N!H5mc4QHl%X;r5 z&{7A&Rrsrwo~&IV9%7`wq-jt}!g8 zrxg`#hVHQ$3ygbMAe#9wItsFnGM;V}GM;cW3Z8LffVb1r0^Av+8ttP7dpgqS?_ho} z2k%mKT-og|*|)_O%Ej|1e#trWk(c&QIThO_UgnAWHNC6Jjx!XCv{e0Q=lQKiIJB0I z$J{&~IX)ZdctPLkh39&hmXxx$l#}jxXMhym!sB%O|IvSnq{lGVO1r^OSqf#%NX><7C;W zcB_r_r74ec_TxUt$^1~y?%2j}YZy@1n-?8#oTzL+XTGd8r#P!#tj%hZ$>qKQmc^KL zVvBa3eC?LXG_+4DPVDAL)( zY+?TL_dWZFnKYHYZNsKi3N%TmQgxNkQhA(gpXXYt972NoR+Asvn76vw#KOCIHG*Q>m8_-{5BAO2~hI4gEjbz1aWKlyU&JS|4oD6XAJ5bT)YF!Uc1=8a`nl3C1}gt z<13p#DZf>3mCJoTEo1jcpD_-f?mBJf3)YsIOm!ladsLzovzYlx zqGdjm#cm@Dqi8Sn65~txIoZVc%6b0egvw*clT|{JotCy!KT3~^eOpr;QG2`Qj8;3v z5w+T7GWyE&sH2z2lTM`Klf)S>TAlM#UZ?h{0%yD2Vs723edZ<4Ak$(Vddcjf4*f(~ z3>BGw<9I#mLS-cwgX1+qb7t_U0Ll@UCQ_FR}3b>vYP~duo0bE9NQpJl0Zn zbzvoy^!9t>-o^DUiuzG1y5g;8p4V2%mm~|L3x8V3)w&f$@PO z0#gFh0%rtf2QCa;8MrQROW^LnLxCr=j|7$kUJ1MtSQ_{u@O|L-peq-fO|t1WDg8o5}E_+8$>!7eL?Eo(2MEm z=~?Ny*;_(yrXNl}nO>6FKC@e9-^{_8Ntt6aCue4)UrE1{{!jXY#@c=l_FK>rNdG7G zW9rZBbJ>@(uVvp!Uy)s!U7B8=z9;=<`iJx%86^|RWHNP`)_#kc(=A7~KARbsSv#|S zW|Q>sna+MM_Iso8w8qm#^n0g>r9eOL_ievl4f#)8H~V0zZHc#xORMc8+dJF0Z~v|N zvF5v*=eOjVry@chG@lw;-uy;rMd)YErMX&z%}2IONJrCIEzo#T<3(D$@hT22r`79; z>{D7>!z^vJhMODiYIsl^qOIR>Lc?@zMB`b_E40nEZ5p0vSghw8-fDQS;ev)s8s-45 z(L3}t0c$n9)ObDMw#J(cdVS+PMS4@?eT|PcKHb>e_;OK$ewE-3!|QEh{eI)8jVP~Z z{8@Sg^P5ICZHbzu?R)fWWQ?pm zx}wxHuxYoZeGUHu$kudd^X|>N>s#wPG)&NU)A!L2(ht>-(vR0q(Yy4s^z-%0^=tK8 z^j-A3^@sE)^ltrS{cZgteYw6&|3?2M=gtLliCn&60z^;DHRf6yCgcX>2IYq3HqDLB zjnN;^?Vj5=cW`b}Zi>FEzJq?LJ{vsu$xVgz_}nCngNRdeUAgmem*nQ;Zp_Wg&CflW zdph@g?)BV%a-ZbB%Kv6VSEwh54cR4fO+| zj&-5sS^3TKTjf5_@0cG8*8TH`=H>xC3cMy`4f$#L)Ah&mGxO)?FN0oh4Vr5p)7H6p zpuJJQB!7od*>Cyz`N#6lfb|7^pZpv7_WV)#_wvi~U*&%U-JbOidkF4sMuG(^^ ze^$+~nvKD}dCfLZ^Px36)$9q{<@p!#&w=&;eOApu`2jT(^OFlFgFL0?_?qc8Gxgbp zeD2Gd^T2XRuDP&Beu@5h%~dtm*W8Bvthw7*KbZScp9s00fEsf(kLd^3JYDm=zP#oY z@OVc*5bLN}TJr^r+kBW+mw>xb4pTKhEUpRawj_MO_L`i$DoYrn)%f;qh)cPz#r3Il3?0I%Qm zo$I_%$E9@|jND~)@jAWmcEg0a2HogwT^qz5ST_(VUr@-w2p@_w2}bu3*kfRpK_1vQ z@=NR10zYV@Zr%Ivd1(X4gFreR#PrzYh8UI@mh;Wia25(%;m#t~;RaeSLY|r#0_EhDmk%6mrn^ zv2|b9{e=5h-5)h;8!-pgYy$hsrS)EYmcG1Rt550+>I?PtxheS<>f7p9%N<)k1h(80 zI<#NELH%a+TLRsoeh(O*uj}`tY8b7bkiV|}$a**yfX}J5C)UrbKfnHR;IFB_x&F@j z2Vk_;tADKi8U2aE+x5@azgGV)jO74md#Ta(=NN}uqyD@4U-adL%kwXQ##Q%KL!cp^ zdq3ZutE-PfAExO0G(cY(8XNj!gn`u?hCtuP=(F{0^v4S;8a6;J?6I3QK)w3Y4Lj(U zHSCUiSi`thcmJhLCp4Y9GNA3;bhNft>lH1}S+Gbuu;tX2sV%3qUeJ1hc6jrI7FWyc zmUCNnZ~ac2tW9fvMmwWruy%IKvXrWSg!^S~%FN5$pIMMulwBpe zO6J_mrI{DAP1%0gwK8vJ-pee@e3khz^JiAg{5!mg-V|Odyk0h%O=t7rjl-jKw;s%X zpZz7gU3k~;o8E|nwFYzHCNZX zU)xx_SMB+=FTh#GHg#v$EvXOJkB0ZeB@N-ww9sSlNo|BSO}kn9JUb^G4UIsAHw&MZ zIUaCA_^G1i$HC4mig7jW&N&3h1AMtQzcwEmsf{1S!pB8^C^JnJIge$v2cJJ&5i4mZw z3W;&7w%)FzzxOth#r-e-|L@}$n;M(h`!R_Ao>7+z zPwl5Qj|ndee--{V{B!#$5m)=m5kLH6V`>#OQqz906_Mu1s*$xL>qj=k>LdS(jEZd6 zepzJK$UYz)6qy(~HgdAEyzS-4jL5nD*Na>lnG?COb#D84%JTL}k$J7-BlkxZL>5I} zjJ%2Qt-G}D68S(`-ai#t-d~G+(=j;mOUJUPI||o4Iim57o2-3*-(H-yg1@RO5Z(IWZVz&5c&Lh&;yBIOXjFn(qHx{jOUH^uLa z-`Box`)=_^<4?z*cdn)5$N1}_wPRpMYy3a)PvT#}KK@bLNAVxq-j4r?9tpMo^;O~S zj`N(6h$hkr*#8i4iU>$Fh;{t^e4?f8iT>Z4@IzuiV$H*fg<4 zV%xsv?s!?iYl&S1Nwff;*gJ7x;_!~U5|a}rc3g%?z?^ToRzzZU)3u2Uo8~rM-gIlz zT}=-ru53QP`TWFntjfe~iF@%{cH)u5Q&e};7fs(Ko@@Fs@mk`&#ImLpiLVkrCjM;t zq3I8_G=1IlW7D5aUp9T;v?8fAJ<-(N^itECO;0sF*Ys-BJIP4X(*Dh5a9ICU`ybYS ztz;%yM{V|nCR>vO#rWjlVJCj7PD7*DS3BCCV4;lB^M+g7yXlqlP@LT zN`9F9Ecq>1e-UlSr`+vrDL*W+R5}IwY5%SJAJhND{;%}ksbggSJ^R1Yv1P}WsoPTb zrXJ}yCG}M5xsF2^QY}Cyr3R$-?Gs+L*7NtuskK|SORe8Bre#cO!_>c0Tc>tvy{9$N z`f%&Q)>~71LeCCLO-vn~Iw5r$^|EbH+b~!*ZX4B>Y1^)CSK#+)JE-l5wkd6Oz@O08 z+BTr=w6>Y;tF;eq*W1o(yR7Y+wwv1 zj}wvliuC^!?YHXRh?~5hLE{hCW7z2$$k zz8yNcsz-W{jwe#me` zNAw%nZ#Bp?8kY5pjNA3(-PPd6_8Zr4Lcg*74h89men%U+V_|)=!AbXTIvc(yG|rfd6U>I>P z^O?`^{sHNYwg)->$+JtN?dqUmVqn5_YMI!`6|N^(BARB%Y6I!F819j;Cf$WIKp?X?@-@a(8GWF z)ia`QPbK4R^Xc>+1|xrr_b3>VgT4EEcXG}3E^#gKo#kEIJKndCccS+y??}KNu2aGK znRi#GcQA2vt6+JGb2*U^b zJtqTqGE#@-M96?Xhjhxv&9(Bd23S1H!}7;#9^!lEc6)V;JvQ*5&*uc-#1 zUaMc}K8Za&+_RnMM9)E96N{R$KpCch^+fO?X`JDYnuCnqQ2ar~Ok+J`iyXFv zu_t;k2WB|PGa9UW8`4+&(WU4Vc~0|7dS4(v9&wpsY!mD|E|{HqMmm%a zDCPv6{~5sh{&!7ryLoo-RIy;Zu{`5n=zq)qO7P~;G5**6FB%KTsFN+BBN1Acf!W$@ zpll5^d|dd1@TstzU~niM2l7csJ==MofNeAdz}LuaM=07!W}YN5%Du-@6l?>t50_X= zTxPA|b`o{SOLO?U3D{%LbSqpJ0xtJ_YS0T^=K&_TCV0od`acGGr7U3lN{UbF-+ z44zkNA-~9uk!?yX(NCiv#~+VBn!GA`xwya{w0-q3_RbyMn;IAe>wjerc5ky1ux0jO zZ_(c?zS?llPw|J` zL*485U?@lxAhHqQU%2-CD)U$OFeC6&;Mc&fP{|je<>4BR#Ma)79d6y7b|>7?;H=;* zbys&Yw6GB@9Ys`7A9y#)>T3^;xJcnYqNrcrn{y9U z{v*O2a7UF70Y$BQ1NR2V7qy@dtsh$7U9bad037T;IJACXgn{(}W~>A9I`CHxn*@dj zK7^$g2%Ulmd|Cz{2PD9`$>OzjwDkb%7$C|dt|Jem=u`BAnZ>YaL=KpDfz4JTJ`YF` zoM+zC%u@IU32qMe9=C-NKiV)aJTLrjz})bC;k&U0-Itx2of$uvA$Fdbf^;By#m*Km zD|R7BxF9Z!UFdQpB30v%1ORgnqEh(Qfpq8}^bRYLV z>Z{ra)QKZ=;|43tT(D+{;7zg}x3!ZLob?>_}V}GCqUw4C#-v9of}k8GwK` z*YI59xdtBkdb^0XY#Gn0kMFmK)`J!VkX_S~qhfo6PID^Ds32GsuKBA-AC+YO)HxHv5|BDQdC(&<~+QJ*%lTYCW!@ z#Z{q4AgT30VGVo}uM;^nr{=h(uw8C~2Wwi>vu5~{@F!^T4z$DN3Qq`6$Om|&nai$~ z`oS~XGh2VtbEyfipD~%JvOosUY;_ex^bGKWJf7J=&jEC)@5SHE^z4V3H4y45@O%&f z>FJ*HJ?BTyz-Wj&!;Z*V94s<&ZscMPm^m%*^q5vSXB=e1YJoKYf}ZugN3&`VC-~TP zvfQY{Y-aCb{{Juv)_1TrSOZXk1?B$6(jVxX6#p4dfHjV^I;F(@%#s#7sS!2ZsmEw^O^)NdtUCz;PPzZ*#x#NEc%+r0sC;zCTa`N_0$#){W|2a6^Pn`0Q_dK7q$1GCBG%;*0IBg8{rv| zxG`~~zs^9RNbBJ9IMdYmdvs+u@+VpdY#mt`S%|IR%Rdw_*jNVpM*%m=(4cmxKWgYV zg(ofjOI_h#ACP8HS0~h!TQOtl5EXUx$Hwc(_qXAn8 z_+H?*fcKn-0$XU8YL`YHjXWNC2!1zYApTMOBi1uqyKm)yGw*A-h4Jn?O2dD#2<-s> z#n8i|4s#LDD9T)Cw`=1{+s)ayR4%S;yHK|XUZbexe7W|}UI8w^5lHNi*dbi6?d?Ac zp3F6~h|7RK*M>9w5?~IOOB+`PduukZNVfJLTPf0$Fd}_82g-qLAm0n50&$Fn_*yCU zh9`~f%`C~M%wTb}4&LpA+$^%dbG?*MAZIyG!3^MYUwMS!i-J%z_*n2UYL(OA1LYA) zgc3Z$%mp8Ig3HA$a88mArOi=X^54%9xCi;bf*w2=co6tUg5JP<|KotW{Ezx?^<+7?MMNVNra#4^l7djxPz zH3$z34}_=GTpc#fU$rZ=E8usf6(Tk=z6~xS;Ng;2#x@51#<(^kHj=}z>=-uz`w-tg zwxJ!YcWiC9#*EK}{{)>p;9afxx( zYT{ooM7}6udBlY1b!MP7dQ0?1mM8Kx;3EUeA~qC#jLgxL1n^J~LZ-Z`y4&5&EYK>}!m57-=)I8jUcgtN*#4 zz7*rZ+hk`sg#7O^drutpoV8E+?}vK^T*0k^+akihyMoC*7+m!NHVoc|IlLoX8}?u) z*Lc@Xu6jyc;vPhV@{KYk$`m*Iuq&;rM$FA^y4 zfae*st+mm{GFsb& zBl;mc=Kz-Y`tc3o8ze4G7*`irPHhKmJHTe(y#=ff;(&ANE2v%K;g65YA~8QPpT{Am zJsV$c)|jQlZe`B8EobHCXq3$H)ywb<-l_p-+Y~dkha-)gPPJFgG=Zs=!zqWT51a!y zAAnRq{$Rj?`2%65uBQyLErXPGKwHT&2>i_POha0d)(I{R8lM!@=hf$xlL5n(5kcWv zwi4+bqFZMZS(sJIs^tM*vjLYnBf0}a{^z`L9xsk1yz?F${G>FLe!WHLt`TRop?6fd8cN_xqCg&yY zN!$~Eg^sb2L$wDD%-80JhlGb{^R;^za_uZr>^%b?z!O?Nh%E(t5;J}N4L@8V9tb}W zek}Z$wZ7is{$VdCd;o+0L1Irb^44#g| z(H>hHP?TZzeHL5A4ypqEQm^;m=j3Clx2#A$U?*+A*d?*cll3wro=vWuTs!eB;5EPt ziI)M-Ci!)1p_zC$2dcRg@0Q6zsKp!o#b}h1x0ObEDg$`k0DJLpcnbAv@QmG_vmJhw zOU}i1GO%rI+xRZAo^@>3*qHe4SZQo4JH#IN2QY{_k&pgV2`D1^eQ!Y-;NEcoq_tA_ zmIQ9`U#n(34W3pT@}PA9T;K1pAkSoR&Fu!)&%Ks=fU&IZ9xS7wLTPB90)EoI(Z14t z1$?i4tbNY=0Du1dknerpySM~b1XqM!40Zb+E#e0s;v?TOAH2KxzVI#g{Rw#8*X_H% zXL;QBxbJJgx5oNw@cY{Litk0=OM7OL)m zIyN63f^5tNW=_~bSe&xKeXR=mE zSI$m;c8Zbg6}lJjP>*z1s7e&G+#j+)nCDQ`mUjHSB{vgYkoJ%$Jj`s2- z8&1um#I(kR`p^bV zMhb^i4;~L!J3p=t6q~*}eKkfREd}&1Wrr^h)g*)z$;sCK)6JGDDC<33pKll+AMU$Vpf=j@eS(Ssk8Kc=2Z!W!@$ z**T3(s2nURwtsBAfElrhf$_1^EjTq+0fc%_ikXn|ln0MSF8LROS<9v5?;^Nf&c%jU zA@_6Are{|Ir)ZedU6sHbpFV>l+YNXw`@t(0m^vTcRVERI&5N97|^XI}d z{VyvIF(2~z3-ZPtMpnHjN8juV;62J~2WW=?jAqa(wkPBB$07<_07hHwg-r@0%&>Y? zE*H)(oKx`S|AolDWx~7GEZJAeLvN5F`(hEVXN$1rmdUvrD!_^y!_7GZvOG>Y{qwT; zR2Sfs$Et^ab*lo_Xn4OmK)^u>f5IRCnLyt0AIjjjxDC0_;9uV^fq!}XBzFnr;WX`z z9Tz(;_a)bGE_*KW$+F-P$R+-;L-a^CnJ^?-MB+cdtqWlCPD!}iBk_&|ZzeuOWPiw> zl|3uhb3YEXkvg{$q(`J?rp`(kJ)}sY$wifc{LM6VM0vnnERJ6Jy8MpjjMYi$7ev7E z`~_SXI7b>Yb+DU0f5KC8OJma%0RAC{qcz>1_aU{B9oz~P{F>f5rKjMEb*j+}^g<0$ zvz1HMNQx3Y^nU5xQg!KUO2>u4$P2td3#mf77P++W4LV3!)Uj8pE!7ObDvMF+ya?vS zH56y2`9Bc_H(Qtl?^wqDZUr}TVEe5)K-98Azx+q}j|%-b;NGTw*mePagHUJ%fCqS@ z*J^+@^UDjX=D)~)orkNn`STCL^e%wd3sGxq@w%RG>oSj178qcY<& z`@=uGPOwA&Ff$&mMC_~k^>O;g`p4zkC}mHtlR7>d5Q>?zJW9<-q;0f3z zklrG7N9vCB77Q4Na_X+sU8n)e*6Goy>r?jwZUWqD;QsWRCGc9>Kqi<8V!reysX2fP zQdgp6f2A(AL-c}wyLEu*<;*98QjCNj<>P{Dj?U!f`qwK~a^{>>LMNnFZ(}D?k_5 zBiYNSh18Z3j7nKR-d390K35|G?K${v1=nUUja_f{0%@TBJpq1Q^S%C)k!@gZU~cW) zhWf9dfhD(w{)hgR{s-F3FZfSf8$=KNL^IH4)|8c|>WJGJjCkakcB+0S#7}``s@Q7f zJp3QpFIgq4WPZu~p7}9rd#}$nW;f!`k6_Hr{yBXw=HFt?Rg z>@a(jmI@caKeAn90IBiaMCk~#HUE14MYF~%6BpOObM{a%ubnIGkcW>dS~u-23Ys7Seb0BsY*`a-~0qX zG)QbM>Pzw2p&Tr4b|_pA04-Ysw>~TzWH-osfI9Ol^R^wLNA@NT$OjTzb8Q9O_1Ple zm;onskO5>p1xpt5i7)scdmBW1;pnO{1tTa&VUitWtAu9nssMNB@HgMtTLJfG?=@%5 z?*iz^x8*l=0-Pl}+9Z}X$b~r#u(m*hy4~~p;D0mc8>MK@+fWd`hcj2rCH)=F%Q$@t zeUoyK#W9_}S@O$jkv>Pv7k(b$lmo8oE@4PI9P88PFo-#;;d67#;@`Ov_e15kfvLl> zeZB-_b>+|M4S1&X?IO6oQVu@gWi3eMZ$*G5RmkUWvO~F-fDe){S#2mM->?I;FNpvdvs47$ zMz#JtQsnaY8+fRPKAJ~74!A475OA*@Vtn@gs_;;DA^P(Sg@dXE#ug5!6uc!fmsmj1 zcjF)*{YJ1pQVROcW>6%f$$<=sC!LTvJ6oq;q+gmFW{xB&w{C9T!pVRW5v1ibm#G{) zs<&MJhg{N%!WosLe7m_N46x@GIZ+1fB1Gvt`3*|j(W+m8KrX)#uB|WqY6h#`da$ij zraZErou#5=h2xRrXK75weUOv+8G0=pPp49e)I$vBsLG}M5oK^>-iE>;1ZyLIXk`#9 zV)5nHVuYP97hPD-9wTaUuQ~($oUNio%*goM$kMP!RHg~WDu*niIT}480kf{Ols>=; z?5C_HxlTJ;J38Ei(0V>I$UWuS+%J_u9u1$2cBu?b@u6p`fjvw3rSizW-vg-DoFiCY z0yRMaaI38#_3#)k)0flLLq3SbYG}PHK7h{pUakNO7%~y-Kd3HWL1&G zDAp;$vBlCox3t4EVt%%5xDL6}?PysoE34L=Ln5&%Gva84$Aq^va^-?$kv_kOpA33Y z`qK18fb&@ooYv`|2yjN+!^=3Q45ZH;CBbBgPUd$MWkH(PI7EGEK6AMUxB_I7WfJBy z>zOQSd7F7pY2^NrfyMAF<+%K)avu65C$LEU78AJl(YkRw)(^LX+Jwx95bs2DcUJ(( z{a4m}`WX(mM`xI>+rV?^Cv#rT<<}~MJ@X8!=WUQ@G;8zEl}Dkr&;VZDc2KVTGlj$Z z0{Fzl;x9EQTl0*U7DSElOh0NFDaPc+i-7Z?qRcohlip0>ymbHpGp0JMNH>T;AM-0S ztcaU4h`E^?5c=Nam08!09@~)25aOL_ne%}?i*}-x_hX({Z|y5ES7J5;=SWi-WpTJ> z+2nb+mU%L7>I;{U;|o^6BAHgSi9AIxUmnGKAhS>g?nNB;CXJm^?c_@oJw@Ig$uvA( z&LyTNrpql%o7`UxijiBnmi&lv3+Kp&lxrC$+GKXCKg#H*Kd3(_`{YLX&_=wB7S^-E z?1lNugViDPlhr2b^aXa%XHs(|l}r9+e{;kmhs(<#x7xMjZ>8~TO98!0*^}c-+Y_y& z?eS}q<12@?c9b(GIRSn-Rve?g()dliKyzZ%#H!9MS919qGnkQ?K^g5o4j>DoiA@rl zm^EezPXBvgwp1o1`X}Iz-WkL#n0k>(>31@x0-ne`p`Vk3N7@#qvb_1hxrY&A4ChnN zrJiGcq%*RdYSj7>%T%;G9OvxEm+bBgx!=W}>F3BnKDmd9lhaDyW9hn(H49ZGqv%W3 z<<$IO1e0;(Sf-+k4+{nuKHt(97pCUFrFf*wN4kD22grZx@=`2vn9bsCY%|shwx9o& zZAHEYTTlC#1M_m^hvtV`N1%EckB7O93+uS}BhhV~^0FgdfM*lE;0gxA=HLDHPSX6P zQ8w#bR}udG{QKpPLAb(IpeLU47_{OH$rCCARwVU2J@t5UM&^V}CbNW)-7mXO_CQ?p z)$}J3xorWP=Q>TxR#o)Uu|L80WQfC09pVL7%oPiJs|UvQ6^ZqF4{SUAVfw?ubUDzT zO_xLI9_07yJuq`{-QJ^gW#z5L{*Molw1LOw*DnqE$d=af?5-I6XXgIi@$jFTH+YW& zoZ$KqkRb#P@lJHz?Y-PP*Yyc-j{)Yo@ZUA}cdc-J>7DF4$~6+Oc@H{^T+Xd2@3H?JdG~U)fLAB1Pb%8bqXn>`cU^dP99SXpKG#5?+j=H>pEtZtVDX-< zK|0+vgTcS2f5%XxmJRM3+&{R#YZVjPT^oCUbgiO(;@`%<3nI8va3?L!5M3O7Hu`M* z`sA+3!;%w|6VqcePkFbp!?i~6smc&q7~CtkM-lr&MGRwtduWkxCp>dbiN1iq|3Y=G z%~AY!)D2zhx{fQ3=-aM&t{Z}<`ws-b5gZYm5PSlb3Bk#s)kAG?d9XEjl-3G(4VHe| zn;`FM%paUXW#Ia^ZOgGEe(^bX^t?Op;+mI=61_Ko#XBBntI3P|GY+4gXFNX-=G*hQ zn1J~yALW@_%sH2O&S`M694_!R<$2HbstXq(H|!;X5JmeKMVcw*;koEoA5Ry(?sFN4-xa?LG&ci^ zBOja?Jj`kF4za`Bs$JqW*lk!L8&y~rk1&kmvT?-u%=LkIL! zwG=c<0md9AJ*u=gC|)L2EcQ=dV%1O z(!lDxYglT8HiI)lkLSkZ#`HWXi=LQOEft=q1+B1WjUg?UNAwYSiUVEz{?f>eDQs36 zMnyu#VujeAxS)sK%j(0bWb46IZmZQHzZvCcO}W`%&NmqPAOfoQ2EAe2#-#vN;kM#H z30p=Dr_77Vv?e?ErC9Wk@hl6=Tx=1yUr+1iD3+h)D!=CSnp;{CdW&cQQUbuWu0NTxnk3&K&m~0RB{N%new59uc6XuvCuqpE$aHmE8pdky|#CGw8 z%CGJ@?kf@49^m^Tg|->gH=Jnv=az+u;eUB=K1WrT*-wyr~{4sEmSM?675bMJA zqP}8VZ+S?r3z@DImAT%6p8`wb6K|goT;$VzQJ>~Z_|kA$BVa=Tw0(rSNL>WaJKa;j zF22gp49`lf^Zm;=3Z9s{yMP^im4U@?6kOsPpd6qaq#W+M3x4Q&1^iI=8s%!`M&&wX zoU*sEj8%4nZXT{66_=xxsmh7U6s5Iy2)!G6HyrfctGo&CF|b4@MGucoN_~-9&X@G} z>2G|`_}&8i40sNn(Q8|jbIQTDkR$XQgK`mp+j`Go&I*1WRQ?A|DGUXm-J)*Kik)5&w0JyDWv0kaox!LFCLV%3`C%#v6=S%``@mXj<* ztu3MKWaD~SpPl2(ZD1CjiQ8m4=4rK9WuAj^^6Jp$9EldP61C)lkda2Q_jn>g^U5>7?g*t(?3a@?!Za&1(1KmD+9C zhFNQtSPe2Aug-3@Ysgzpd4+PjTw84`<}57Q+H$oyOZi)Ko7*=_@;q{_xW&vKqQrU5 zQJbi-YsqS+RzIuEyqUI?jc1j6ak~>&K2GLg*V#4Z7SYapWLwZFTGWd?<)(}zS4zcP zoadZ1535;jmusyVP_}!D8j-h0%d9e;J(6oge~~8-Q779<tvd$W-rznejrIoT6T$`hLf9!g@hIoH@!YiE*WPiXvKRJ=~vj@^+ZLoR=6&zC=Y^b$n$RL@$vS+p_Advglii zw{A7jw|q{shglLcS@&F-r%cCkI9sR9&#=B zklF29%l-k0d{$%&RhAim_5zH0oq}c`SJnB`+p( zE$7I?PR%}C;+#Fkt|{eR%4W}G)nGI&R+~&N)tcRA*T{0(bz(*49A?QL$ux2=xmKRT z?#ncGy*$2@)_H&7QRIWv-lko{BRMDcl52Ssa+Ug2>SxX=iy|FSb3Jy+*$*vbCCWAS zOy=6;cC%L8BD@9BhqOo;FKT3-q7HfTC%rj~%!51`wZ=PJOM9B*Na8V^BbyVg#IyX! zI%T)(SuUQ@seMr|^7f4Otz!1Uwn}A?*4%I0>RC3BpZLF9Qn)5>jDFRG}V zH^teUW0#)k${<5FlWX$g=`L6j3r@c!)E$0)v7g zh;Ts!4j~xufgp(B2n2a4>NO~W3TV)XNtB18<6LyE?cj_v&Miwb$NfpZz+gPP$`uDY&g?cQ&_;{cBS=FSm`o>)FEb+7gFjB&Kd2q1NA7 zT)O%(!cj*qoy%AE>m6-$q%x<*Dd&1ltw(U@R2`{$-}yD$hBSekKP|OZca+=aN7b{^ zr*bcNsJ-6NIKRNNdp3&L$R1*o>ctPX#x30WxNT)r#?>S7&sv{GC77*T)sd?GnQP;c z)uyhs)Jt8@R+}=;d9EqqD4J&Wl=VF-t=y}$#5wU;3~+cu9>9e1w#A-blh-lw%$ zDMLOkrQ3!hfq1O)c;ncOkA!$JRmOEk>Ra7Wx2^Wl{a_8GDXNdVIF)Bduj-V+vmQ0_ zu8tJ@1rAg%wzJL6%X!cB>IZ)%=E5r&a&zRi)hU|qH%e7$uiSGHX{~?6dDPp^o?$l#+G9)1>RWx@l^!8>r9JEgs?3}A_*jS(+ntBo#$L)SUpN{l z_H@?jZR``X^NVe+HY#m#S*wreeq6dn9f+AT+xfX| z{j=fG+46M}YPa>yvDPcLXkLwTYV zjra73J6^r5ZV5c@eB#p8v&F~jr)p#kDMQ>q;b<*2_iBDLjut#g&8~3pjMF=Ae$;uz z+c>Yerw*xV)6`nx{A$_xyRGwZc~slo@!IX&J>#@YKIyIl|GewE=8p^?CF_=edoU|+s3JRr14SBLyynqRau_K z-r>qL_6f0?`BWaO+zSz6s`iQZV~*2QawxsC$LV6$-zwmbyT1c(=?%KIzyR^OOPObjU#B);*J1bR@ zy62P{|BB&qopcjkilOSYcKJ&`}j>Z<%zxzFZ4n?H#^q0c++yDQh3d(x^q_hcTP@O?7( z_qoolj{J3fclX^bwv%%6Th!`YBOQ}Fq54?=K#lsVeYSJ|Z*nL7S#(}*n^fG78tZk+ zUC^T5pKDEPa(@n8zRgj#JwnUF_NA(f6UT_T(rYezaq5`olD67Mdqbl>QSHrrm!sR(^QpDXHHYR=-BGu#&8NPF zxd+wvnX5I#AqPxs)N3MjRsB;x{#4)AezFW9=MZ~ae2?JIq2V>Pl=rfZ(Z4zvGd`dK_0m7DAaY$G5?vdrS0Ob z3nenQvwbR4&UMd4ovVMfAJbRc;{CNvkH((LYrCjg`&=!T+O&25-fsMx;F~jA-j8b9 z%uAK+sP|2M#ab0cp&ZgTR2;YWO|kMv<&V;c-S3TbT({H8?o{+XyVbq$GuYqK{g&>F ztlr%1^xmE;8t88-?G55L&s~o)=eKm9-J;_2-yXiXyEV;Ism|ARv-}0` zsuVx(snlRk6ZIai>dQOzEN&wHjo4TVdVbcoW8YBUu6@OxoqKj>3+_3(=ctxGr}wm` z3wq|Nb9(COyq?xH*W4TVUDwl!)>QW9Iq!u$KiktpW7qcF*^0)lZWet`#~$c;p!bzM z_=)YSbQ>&gzsYt}PixxR)0&>_X-V7u*W#;E^fv#OU)@~22SUTpFJj?u*x;|&cShgYjX!p!=4a3R-|OF$E$&(P0#)3*u%WP_xc9)t zvG50CR9oDvTRVSS%aXpP>OH^rTzsf=g)Z#9xK}52QGy!?whC5)odXLxj(;_ zLm%TlEjb)lyYo=LT;W@VZ}uJ6x2o>v*!-sa`>DRy5A0T{{F?llz9aiitedV;ygK%Q zTLwNQpHB~bdgwm~c5614%<0&b=+Bi3R~RI3Y+TiMm%ew=x54TQeP765lRp7_x7Mn^ z9WsR@`fl#K8E*c=x&S&yT-ycQweYU}x?`%;c`EAp9o|-Q|ch2ve-xJ!q1u=a+ zy*0mo{;O8|TYg~RuLhP5EX}_a+OJl{tphty#jVAyl5&6Xr1!SVzOP)^u~GwLL;qB* zk)c}UFKJEv&(+oM`l0-jBPSN;7ut&R>xuS4TVds_x*`9Y{%7*PvHJHs^_!vdhAtR6 zbL8d1lIH5~)#LirTmB`gu%y3Z@Q(uz4L&vaRL%QVwQpcGx9rg0nkM?GokT9uPBtd( z{5bozZ8d7sG~@F|kEzh=(WA$Uq9Jr2d#d>7Mc&e!dZe-|AqY*(hh6A7>#X=?sB-c zf6gkL0i9Vmv;SHl?w!$pOKU2e-G58rY^wh={hzVhTgYO6Z~r|l>g)a6ulxHS>i_n3 zYhyp8ouTrldo#Ycb8)c$Mf|MM@TE?5N5|&|P98XU^Z}~=@nGf1=|)hu?SIj!I8s%z zKQ-UCN6#sqGrCywuk`0$KVtO%i9fl;3N?rO!K~gD3T6G8)va9RiLF@a-D8jADK1ns zFKzq&7`|1Z#t)cxc~D`W7IkZ(C5^pE&0D6@J^ti=GJ{&q{ZG( zk2qgPd4$HFv!`$7 z!Sdt7^7EYKJZgT{sg0_b_0v^bHBP8yJ??U?%!OXTv+`KeJ-G_vq4i%pOU1V76|T!E zRcBHYyU}%XSD43#*ERIAS2;Q!N`dwhavvB7T%Vi=xABMG?wF<=z&{s5JuCO!vC8e> zV}p-Lk8sz8b>Xj}ue-SX5!+a^U$Z!RT-*B>TT{7s(8%catN5XTYl|PM(iKH$;A*dH zhu$^xuCe2XcktS27#iI=y0!Q~i~2^f1&y9ie7H3Y99sNs@%PYgi+_OrwfOHtFAW=N z@YKZRpOS&ko%-^!cIB4;(UZNbxsF-I{)b7{6hxO2Ot|i``Coe;IO%A+-VgZUOUG}xM!47R3y z2Devx4N?bhlMWcH))9j(XlTLUsup$JU~4*iu)fY5gobvfEG-+_b7=3u_d;jL&$kbM zdVI&h%bIIwzrl+JFB;k}h+c4kp~EVrHrQW1OX}va zdGz)cb>Co1sy8OmHgvkG(Q&np?ml!@i~7(|Yr1NvHC;2*nywpaO}`)77P-8drExxP z`}?{jTtC#BzBtsnzB2Ts*`f?@tgo@I(QBaV!kxaQpAWAK8p(&_!)qI9q$kqTBNZC@ z8TTbG=4Un5G4{~tbre!I{-O|ZtG^T5=KW?@dJe}b&UxMN+UQK>tcTaJ{GrB#)(-cp zW%Qbg^!;HEaipGtr5=s+3?Bj=*n*h86}@M8u6p+{b$^xSn!1tSM}}L}$bUJF-WD|P zib~Bbm$89H;=MHVU8}8Qe?2xm_S0&OJU@J6eT|e_Qi9H|=q(@2}=7pvQ~-}k+F^qR2_*SE*I*n`=xoyGg> zSm0CnVIzZNBi<^L+v-^+h}Rw8BBP3Gxo^HycT8Kfol)jyj|$7 zX~lMKt%F`Cwy0Z2TGKa2TGO{iTGRJNTGKy{Y_Fagp`@RncXhbAl3VVpCF)QAGV(9j z!%uiz=e?&!Thn2q&2`o2Rq)`aTCW|Y&S_CsjJ78EmR7$j-Tt5BXU-IY>BnO&X}oPVjs2;a#t&#w zr;fL#Ik)sCJ>u-~T77i9HQg{?tDDDFcVT<$EZQ)>-I_NzZ*cr;E$S!Zt?8-pYW-u4 zrP?Ff4{u&sMX-aN|AHtE z+1fKEQ_9%7${5&f8HA;r9jP&e(tPm&7dDp);seHS!Bu{gU2;1@qU#evj-VpIj;OvTs zZJVKg_UvG1z3^OUN4?28lDDk#+}4SG>(jA$qh4$HP1jG7+cL0cN>XOw>^|2SXV30)LtwoY zZLIs3ti98qjV;XN=#O?~a@rn*ZJQA;MRxe}9K{aKMBEv%j|rbHXMZt@1LieC9N#|M zI-f|)M)hge^OCk|xxjN#-bK#0on^LtMEFH*^U712Hj1B#>Ppy-X_<~RF)v;}RYrWr zE67ou2-|uPw&&7GpH%Zz)_il!%Z`mXiK9M_dG5;AUwo#dPQ+e{{K4M86#0X_-JZLo z1qXZN|DyVLNA=JhoqKn5zQVQ*2;1}RjeL|fzOePlV`OyWp<}igSyX}L@QC_wWiruy!B4*QCi1LyO3#} zkL3jR$tYj2Z&$*$T}?*$3fp{zZOlc{xsVH)vb3v3QQKP-wY^1*Y1eg`>3;=$!uD|`-=if=GK$SxG%ui z;u-e~o^fB`8S8e>X#dK(2D3cv;$WN~$k;gGVaM(o(I4}X&zH=x-P&c%Suki1CB(s4 zN5*;>qa~SjuIpjTCDya$nZVg{#H%8n4y@}*O}qJ7EkENwU*;Gt_O>Xmw#jc!{^a5V#b@B%|Mne@WLrmP0v9uC7ovyY4NRC1_WhpZz@R&8;z* zmt)3MmT}B8?K0=;T&%2Ppk?)!aW5z1c|lmmaAh5Pm2r(&2Jr#2Z72_OKJ^@YWKOqs znbVbR86+*(r`2&7K2KmRs%bF}=s(^wbfmrJ`2w%k&}e8^L!+|Jlr}Sjm?(*GN!q{O zFDRo8bZ3n)=VG$V^_!p0C}XbQq&&hgYV*^*vbS6Zoih4n zW{bq1x}AN0^V*a?53+4*oTXAW#3^Qt<(a^U>Fwxufu}w5e%#qRHvh`obDKjP@Dk^P zvJH9OvP&UiZ!d+k=%+qT#@jjOY@V4fX~E^q|8#?}oMZa=!1tzPZ5#iT3H;BVkv3wn zmvyWtv2T`ft&Mq6?$d=g>U~pL$Al95pk*18mGxfIvWx?kr-OfXL*Nb>!^NlL`JZ?@ z`f}OD&%JQ=fsPA0r-*;yg-*6Z;Zi1)r=)YA+O_SGIUc(Bn7u5sU+dgdmOlQ1`Mktj ziTlj#?I)039|W}Lld}d5vWcU7P zpWX)??5iUCwC6UoPv=i|{_IDdqd07<&c6_)Euh_c`<7k258J1+FLM5DKWM9RUX~>< z+g8P2_})~QbAeu$RocdEy-i|D-FPX-ZP>vNuMp01d?-K7d1_U$b7Q+HJS}xDy!mPI z5q{CzvCfgT2mCj+o07AGt9fk(XN7Z_V7JejR^JW(LS~($y+``X+LUp=#&Tw6#Qrsj8MrO7w`EJ-$Hk|= zi!a=(`99c*-m|6Di~3xRvAW9?uI0H{mUAcf+4Ef;tq+y;+IdDDdPW_3#=N=u8*DRr zjaGkC%CBV*wsrE|$S$n*G{ZXAb+59n!&7-yfb}`*T-o|W&k)oOfBHoC&S`XSlDhRI zeWLq|RMz~u{#TZK6O$NIGDCbO25w7BrGn>4Q^Ai|pDT=c%_wSGL6}CQL?^B?UwS!~(u95u|$AfL@lqG-Xv2e6MbM*o*SAm;_1bUZIA&(2XyVH*?d%ejy~w*G}}od{chu+Mig ziYaV;9J8E@=wr(Uo-`9$PT^ob)%xUarq7tvL+)Elt1-z_jvu9uUhCY?0{7k!@uxzZ z0@|2sSC*w+DeE~ZZ@zvKWmeXF$97~|%Tp2W5%FFT?;Y_gBVIw)yvB}*>_~)!XupPW* z(d6mbWYqwD^qQXdAX&?K;u9flnhEVHMIUr+EJgqFjP~pq?bb8en=&53%X)_ED6p<4 zalZiGCBtJF+dY>f#@f%@F|K;X+Rrh^Qpfy`!}0SWP9YQg3z?1T&$R^CqV$*NEw7#` zD`#-+lsP{0IRouvK8JM7G1)Q4Pq5tMBv(s(aKZ3j&J_H;PtL9ZUV1L`u;Z88Gr!0s z2IGv@zp{=$;9c0)*!~^!`YLN*bIkVUnAcHx$}IM5uLWaXJz+LpKl!#UluzpaEMqWB z`fG&sl>E!!J592?Mvi$MmAM|y2(y0h>@G{Y+`FMp$TIhp)PD&oWy@sR274Q0KCgEF z{Rr)h!}GJwmoZ?=UrsB zjhwYhd@WB;RJI=Y6*7}4)5^U$@4FrIz8mbG{W4$N=W085Z0k_$y=5r_*_A;!t7RB< zZIx-+R$uzSRGG~3bX}%t$rF}wtdLnLWs|;U%ciV#B5cbkJQmq)eTtpW4G>Q`_R;zj zJMaCS55Efl@4ybR>0xOWvRpYOl#|EM=2-98Q6p`$Jk;yS-bKC;iwFy zh$o_aCz$Wj>m|N0pTQ!I;}vA4<=Enw>oLbmBes13Ey|$%OL@oz$Ba`}{{={2SUD;6 zupp)X#O2elP0`23@#8J*%4LZuvqH{&^`|o?wei7;%S+e%nr|BQJ7w*6%6uN)YruH+ z^|11M=Cy0Y)<^8%q&G3hN!Q+B^qi9E#kdU~b?Y?cNz?TKXP-31h{q%5`c3?=D4Fjl z@4z_gHRcZO8z)R^c>q5A9vuGj>9ec)3)?tgUAw|(f<9aiJLY_(tn&+a0e#A92Tz(+ zo~N80Yc^ds!e8dNlKKeSnBYm9m##TA-?0AdwQEAoeV!j7r>M76WbFz@`IY-{{bgLj zye4el+aa@Uusu88Tr!De`M!^g_F3Ap&kb2Jt|`Ck3_K;=#cQ3Shr&l0V}X?P z|2?T^JW~bl#4@ZcNf~yE{QX*@FSK)%Ej)j4%=^$jv?NYmyryK_$H`h$S=XiDgmue$ z%fp>@@*TX(WVy6yLYd{slu%%9@ySkmmiLIPy%hOKUD1y6OZ|98{g5Z|;j?tlrHC&L z%ypgf;dd66eO`!D$^?IT|HS$5+C?mBmtf4{+5`4Bo(p2;$gF3M!;ZPe1It>1^QLE9 zN5`BWJ*&N!aX5yH@{TX42ul~ADP=7z4!e1C|sUPgeYVdAM%lQtxd&Em3-Xm~! ze;HR-rtr^RF7+&RtLwTUsq>|B9XI27b>1lBc^z1vb7rJIm9_p?ssD^=&-1yNKQE5A zi786i{Md^+*Jo20hYODRoC++@n!53P$uXYE)5nj=c{~eZnTKZRzxqMcVcQEO`mg>W zSjJ?s&o?m%iX+b}@?h7_EtjNiCuTQ}MI7s32cP12_s9{*&CinQf=SB5uU6o9mvaXHEoYzWt22Mu#rC*SFzU(`e+ai{B4!g8fYZvCTYuZ^4 zoYToE;_$sDaE5laXRx$kuJOQrT=I-|2zJk!$l5;DUs-*GZ5&~K7q9;E-FiOaesDjp z7sm>6R387F3U+zNgYQ3p{W}j}zfJ~Azp(jgf6=mm@!e3w=sPTf*x8T33z#q0MPU5& z^(<`5wikWazu>c!J}XO7=B2z|E3oFIool(WmUC$ob1D1U%6n7#>>b6~hiSQ9g?*pM z{)&i~kyl_n0H1x4udJ8I!KY(_6jEQ)4xO3Y}T$U_VE33!Q|zg1kPmzh-sCz zFUO2=0N=v-KHle;&+y5UNULoxJWGe&J<~=T@Z4Uo&lpQmpW%69A$m5btoOOfdY=oH zXLr(%aGTA3$Mpd0c%J6{F{e)&u5(~tN*}Hz^<%iP~M<+X>aI9^btmXC%bvXv&MJ#*Yr_V ze_{4J*!}%AIV!WTEt|}hIn7s?_dD-k)O;)3Q!BuT2BrtIYCnjG~Y5jIg^OOknrl(}CUC)n2sU<7C8Nzfa-X zuV~U&`EyTnUBPFhMP*r6fZN;{1@3WU6gbayi1n9edVKx{yL^uj^HR0%=hhhTc~vD( z%31~X{oPone2R>9#UzL4x-Kt{dnH-V7iHW7b9_t2PNkh=X)#F=U)MQ9tTTQ}m>Yz3 ztwP4R%UI_b>vm<#xu_qqt8>dTj)DF7;2G`QF~8#mYoAs2^FHj-?>HuS*0f^hT<`3K z$j)`Rv*R5?&o(bf%Xd-W!)Fm@Wm(QwdB$_W+1Ymv{oxJb@A?|kT3$hB+c@;g8q;z@ zJKN{Vl_}F6FL{+DKDlr>)@-!9zDZ_U_Fu=Z@r<_xJa-vqm-rZm;r}Ma=QA`iWjdfBqbJs;r!i ze6oCIMSoduaJ&V}`rnpqQm%#CbsbWYe3i=)Yunf*Y6FMz99PbW&!KLtLwvB_VmC-02x?~3w$SClVU$7jR=_qlxE73F(o z6!T1;FV}?dKQoH|?kGOk*U7u1yxtwfJUj9SJOAwUlPrIk#$~ zm-JqbYo1}ZUMdOuXY8>1_0n*(MjAG$Jg?@nZpVC%4UXDmdbTfrqH8DK(+ykZeVJpf zk(4K7y`-#jhi9x|99w_C?tf%UU;M*PV#9>V_(?8{tdDr;XRGbY!TWY_n>Cq#Mqbsg-dGydU-MRwzZGRmpv z3p-ir_H_B4OUr*c;~%~@W!iJ1v|wKza0cy7(w@UI*g82UD%*Qm29BSILzZb3V(!F%xmlk9{mBw6hFrS4#QMPmM8`KU!e`&<_JTo(CU&X~6RmxuCa%Vh0S*+V%;Xur$8nb%kQUG}Y!&nXd~7O~8O zHqH%`$ct;*5tIE1^Uaime--iX0(T^29dkO+f4j`I^pX5fh_BBo`Q6ZneQwGBLlH5? zBK6TR%5$IE6O$Qc-=Q7vq6}xmKFPA#n8Gs}pUieY;+|2=Ft1feYxwRi(%R< z1NLPD>s$t(i#bnOAF%fadw;y==F1@OyxH=s3FYZ3lew-KNxD`wf-Zz6t2;5^n6F5eV&sP~~GNs78UMd}A%CrkTbU+_E=m~*AGcbv6vN|rWh{e`VR znfH&@r(|Qcv0V+Re;eBg@9E&<@7+9Oo(0R?%J)ymk-vX73%kD;^Q>vb&igNC=RK5T z-Zz2m{ZV!r?Efz$co&v4vr)>w3-jf;0sDcpuPlrG5aE)tn|I)I2>p5A0bW6$wKL*F zE;wf2U4uE4K5o7sGwmA8p`O33?B)yD-^e&CHQzVVpW`j;e(W8y;|#|Q#~5?^ly#hO z%yDMSjvMN)V~Aca%Tj)2mfQJroFPvd9X~x=f7-3TU!TLrug}3cem*TRN2H$de*0K> zN4SH#u7W8JTf^A_UmeJH~S#p!2tVbn_zi2gZ&PCU|TvZYfHHKE7tZQ2I1zVEN91eZ;YiFLw43 z*ya0Bwg>PS^PRX%So#;ASHP~U{=zmbSiggSkAH3tK8|rxEkEXQwBhTg%CNtg_Oi9R zdsW!a=DD!VlOrF$-hr&Z2Q)5rB?@C{Mg8zTQ3BRkl~yfOG>rLNd7#!dEo+Lc3} z9gpUEVf$X$xC!^U;Hcix^B70!U;32ZhmH$R$-E5q_p0DG^F1i`9k9Q@1N(Uve4guH z5x-Sg=U?zAT>nzmITw6mWC!a!t9Iwp^*%SRjVCF};9{nJeuo`pQ2RVwL)R~M0K508 zb}+qPbUfesa6I%}ig+e)HnMYl==|C5JYybo%~`^^yNK zL{9Nxe+SE$z&7BReHq-&^R@9auARW{=UmU|zslNY!SdauO{>pB(cfv8IO;zxtafG1 zSK0gcn7Gf?XN-sg*85yC*VTL<80_C41D6<|V+D93I+uwk&QkiY-NNT}^kF}>Eca_< z*Qdx)Ot8L-OS|iv;A1176C*wq@m{mth zk4E;7M)s>C`_+;Cn&=vVaosVNq72tWJ|BzHek}6;nD}TrRMz&OtnKjQ^uHBjHDZ38 z{_EA}<59i&aT`85R!^q%zbVR>?5@{MQ8{ml%6U^1^X4e+%~6@b{u~|iy4)Paxg|<_ zOJrXk*_CztSs(e_7Wv#39{$TI3KC<5y`GdXB zosk{v&5B^Bx@7K4mD{J}v*hqU6=TlN1Ex+<*5>uGZli`0W<6oXi z`EPY&5$){Dm*RPE;Hfg1F)zh>fE?xZ>8PBaj`F=d=E&cVKZtW@u!nJ@6pb5Te}7uC z|2J8$uZ|VM(R#RK*Taj^u9Wqif6wU4;Imxc1pEF7#<~}|Xj-tgXV`CLTSc8PE%+42 zzWtXHdp~~lo&zc*NU+DcI%k;l3>3k1pBogc$js|a|cUX z<#*X&|GhIg^4TGSy!eg>?01n5FDvu9^PY=#`m?`y)|j86KifI%%9@reZJ2h~ev}hg zCy*I4)pCBuU2E7sn_)~X4_W3f+TEN*j?&&u|CL$>Wi10)o^`|j?&w^=IG5|E%CyUQ zX<j8CI@Lc`l!e&R1E^H}gpxGo0`6bX3M7-gn@CohZ))W`54jXD{GS@>y>BwAjI2 z=2GUHglDZ_%uOY^cT=8lyxfhYu;Y8W3Av90`>`E-S7Zn4vk|o~asHo);(&D=hRQ0?YAC;Ow-r)Rpyd^#=b;ls3b9=9~_@zmEVfaMuO=Mc01F zw6o0w&Q60jFy`8_W7rXB}7DsKvG0z8_W!l#~&2tIk7}!5&17GjjjIy?w#U{1oXWBHBAqj11v3<|E zgz-vwQreR8u8#fp)Ubn3k@*+=Mb{pbb*xj?HUr)e`G8Rp&FdaFPQnK~5!qeZE_vS0 zwb|k%MOtT1;jg^Jgf`}RM&3)3w#PL9?b5DF5tls|*iYfp&U)r^GqB9*Y;RzFZl+vt zZ4&lD`tx@!g7Ga6%AtAbSgLXSdH_Dk7$3m?T^;bNTw4MkPd0{F|Wk0`^A@r@k$+#996SL}yLT4CNl!vEf0j<+jQ z@>imK$JcmnAP@_EY#wijOj1eK2b; zy`luWGXC$jnsD;39x_NwNG*s zAFR)!VSj-BT!)Zd-yugaAEXcaHS7;YF&_*uGd~Uf;s4GojoPharUw@pGh0%Y=WKG0 zIxlqN|G2?tgfaPl+`zs)FU5aLVEf^{Dj3gub)5kIx|_$r54d)vtnCf_p!3-~e5cd5 z3}s@?YpZ{UrH z)3y$IkVE`17A>>amwABwSOoTC5!hcl@Hb_yUr7Hj7J;=4c-FTp3C}vfJGZ4vr0;_# zcuhHyS2YVlXzZQKanXfJ9 zVlwAG*monR%FBf{jXnW@T zrL6CqIJS1&6X_fW`ws5^)`2nqdi!o}-h_RHYs1Rgw!wJTG$Cys{4J(UHQ!Bvb18Wh z><6O0S+^Y1JkJQ{?DwR3*d6zSyTu=U0qlS0HdxvZ+ZEW)eV#G*Ip*9)cGto&pUHsz z^J?%Ta^1B&kGOG+b~lcZ8HZyR*^OOb`8~sI1LTlbxV{HQ*B9)sFIcWG=OD13gAT;V zAbFv!9w=)ky)Iz?{UaE2Q%U9~@DXIzzp`Enu)m)LKjN;JvR+5~@l!I!G1f>~GV73Y8F>;P>m%b2`!ejf)`|3e&*&FmxxO3|JgeP* zcLuxf*I<8r!CO!snMW*_*vH8osrFN{TqBLItmRSGyue*Dwy1xf%`{!J^Hh4&@TCEUY4_^V9!n{XI)i&nBR|Lem{!&Xyo%~ zZ&3`U#O_6*)q$y3ti)s8z3w14nc*Ulq;tFpFv@b{xQV4wE! zD6M0r1^Y69V}BPjguiTcY=Cb&Ro%1;S{oL)C{m3!r zWw6Z4oa@0lFQYv>W}oux?YcgMeUjJOruAz=*q@L#plOvg?GIdAg3k{le$ur)*q@B; z(q^P=p`EPcID_YS{N1g}J^0-emhC$mu={o0$|O9K@r>upV9aZ}W>cP$_5=27Ht-MK z^);RNZ>fFw6%|FiLh=esqbHiTn|GRX%{p_1S)YCx16$5?^2GE}?7O|snMc#dWPdz85XToF^>XoG3g$-Jf#w$TIrw$LG6Ku;^a>l{ z!1P2|2gPd{{7y9M;MZ=`M)q_aeejcckEfr*c065%&!eU%9T(eUlG}+=9_E64#b48n zrwb$kOHb}VS?IIK+>$<;vYfZDgrL0nn8~4BIdeU>%d!0=w&N&OFZSBadeeq7B-96XFt z%riTih3F_(nopRU&E4q(4UXQN-W?rbstHNAKT=*}ZbaJk=8NV_=F8@R^!s%^mzp}h zW-GkA&G~2%UozdvFjDTHo@X-24yKUaZVCyW2&6Ze{$$cDOs_Nxlf@|Y1L@;tI9Y

    Fgv7gO1q^L!$`&Roiz3gFwAtDPuR1W#I`&A1T4c^ z6F8bd%5L%QcJ)CIo5H13{|TwNo25kE$cb^TOdn6#wzUPIB&hehP{QTrD06~&dwK@0 z_)+G#^zHB-N19Gt>l4y*;c=AhA?K$T<2oIMRK@g@CW{_4fd67Tf-gTk;|weI%&y4C z9!*D5{HJr53cqb`mnz>~;C&f-ToFD)cp#|d!!>1nyW`D{ z@|aJ@>nW|QDh2Gg9*l@z>(ret#C8DfaDIBU*)M$q>gsrW(7xMtG<%u7(_`i5!)C8^ zZ`eD{1!&b5wC!%LL`yJle9t>xVan}?_RecNtMj)QR}7~-gHaY!T$*fbGi~Lzv~6R% zX)i}QrG4!jwWGUuTIt{8^W_wEedvT7}$>;X=d$)VLsr^Uozij_4 z>Y)dJmt=R;#_KXCWzIm2d?@pGna|C-O*2<#)@L?kHfEm4%w(QLofWdB>{8U*s_b<3 ztT}Jb%BI9`%-|^QS@9May<0Bn6b$p@Y z{*Lc<{G{Vo9l!7F>>TZ!>fE>U@Xpnpr*@v(d3oo@JMZYcw{uhHkF0*#`CpyCt24tiVeJ)OVJU7Y)1?vuH@XVKSl)K=)JI8GzZ z{W|xDuI{d4?eVUOTANzB_J;QDI=pLj*Qs6Sc3s}}ajQGJ?&!MPrRw^6*VeA5x_;65 z+pgbqC*8&FiSE6-SMX@}k=<|VJ{{Zlb$_t?lihcVzTUmH`>F0)E!hww>Az zZaWEMXKT8;D*XnG!m*yfh#bjaXs-~4@(N+-t`HtiHxU0&n^T8z>NK%(SZP!WuemnW zTMflEj#H0`)r*x#GxcF5t4cAerC80VK8AHH^?9pD%p=KYvM*(R7^xqtf>``TT(P(+ zi8X-LPpm<#-(wA7Z4_%5Yp_@&xCe+eYAQ8`{}K`l_aPOUhm~Qh9k8NmrukTxR%=HS zw3BJBow2sw7Ja~c04t5439QWjoLYdpkA;!mX5MB_hE9P_g(j;sg?p7+eZe@r+c=$t zbTeZ;6P zGpa?nM{A~COmi*9D-J=s8frJu?zlU_{fXBeMzyDjwHH>r&9oG&%4XWzsP-}Y;J&+7 z%Wzj6YhSakd1a*iOmi*AolHIbt)WgcubM?~$6ZdWcbH0@Zkp>H(_B~JKEUZZbDddR zrFDk7+Us%?bTjUGuBy`hxNnVhfN3l~W>b*vF1im}?OX@Y|FRRxY)dt-4ZNL+$xoJJ_5}ocbK_4_9G#_uGj~ePr zc!s39)dYPRPh~zWy3J7cLSKpWpXNW!?UC-|)1yc~#!73m=vPMdC*$;0qdL<3W%V`V z^g8qZS4ZKwO{~|OzpUP1{<1pSw5C;fVxsyh)0*Ci=TXO)W1%-ft7~*To~bt0ad=i; zsq;*w{+Cf*Vw~@&&|*AG>|$G`ijK>MTwvZu zCH+q3pG5!Hh>}CR{wfife{HVhRVrW13-RQb`a1Mm=)XjAIB84T@Ko>A=TA@j#bRi|g3uhM@QucR;OGe2`mUQd~qys9H5 z?kaP`EV>cTvLhu`|3b|Al)N&Q`Brl!lcLuqO=Vu5MO8&fUZ<)uhuy8Fxt=jla$u18 zN6~YpS|KJVIZ9Pwqw!YnE#&9xLy+MDSWNu^FqDpY+QmHWwZ z^S$Ou21UCjs$o2}Y9jNCI{JOc6VJPvYjvV3I8;hX z=KYaAiElNl|NXUTs-&a2LbzoAS%gQ_&|yI#Eh*W>snYKDl7mD`5|OjkZj&RZTD!9( zZ=l>>^)@*{Rc(*=ll_wY%)d7goRiL=IIZJkCns-NoLD(a)%Ho#Tz4Dla~1l&3Hr8a zu1)xMx|tp|&GndZ`fo!$UZp4Sty`?8O{M-|nkvc8qVXiwZb`NFNt(+1%`D>O_|7c) zfvKy?htYNR29oA_P100VEk$?VVw$UNA~hy^&Z0w;=6Yk&R5e*o6-l;^$pf1y9>B+gNP7qYmmDi{;MET`8|` zsfi_0H>GY*O-((VdL=b4wIH=9^?mAhAudRQDYOVn3cZAth1G@igpGxv!ZyM#!rsEc z!coFu!g0c>!r8(&;bP$`;U?i0;V$6;;c?*^;RT^vctvDsx%pTED)ZlesB#J90BqGY@BG zWV$o206j0WAhRg*yQoP?NszLc?}0L;o>EC#Lh1u_KWQy#kTeviU8KFGgQabx6H_y! zGo(?{dD2nRHPWRZxf$lE(iG`oX@>NS^n&z;Fj#tD`cV2z`da!B*44qVJ{?FulT=R2 zx?Gfd$jiyS<<+FW#a+0;QmIDq5LdnsUqeUdpP{AZ1NueK#LGl*>~# zksnaDRCb_wnu68t%6`g0%HhP)+5QGA$CKJArwN0V(W0iDr(C04s$41@tV~qyP^P73 zD37APGLtV)UZ~7g-cnu?w*-s%xF(f_%D2joxTcifpk)WCaaB@HwFs1{_TX(Ptu9B_ zwz{%^t=Gm;``4z&wA8g2{T~(H0=SOULF%Szm#C@RsynNDsRyZtp}q=xvXhQbPjlO= zXM^-S^

    Iol~zOdG#hYzgkY6SXE<6HI2sP>QrK8p+V;4RfR z`P|~6)pw))sPm%bwMx~GYt=+Ob&(23_xhp!TE9(W7TH*1cA}OQ8ns-!c3GMDQ#V?i ze$`KF^_De)HM6B`kL=Rr*xnD;`S-ZlzKf^q>{`;pUT@j;vKyzq&+eFob1ZBt zdt?vD!nrzoa`vq3*zASb!?RapZvg(S*$1z z)B$DtaGiocFNw+KFfZ@{v*G9{=od?{Pg@U zg+7Jhg{uqi6x)k^ihC9(6yGeBN;{R#Eln@|(y~g+W-aH+BjqQQn6kbyQn_7O6rZPR z^7;sMBlYa~Jjg$t6$vN1GMp_gkk`*n%l@eK*N)YuX+P@y^<(uZsHeYZq~i08K1d^s zohen$9}2vQ#>cSyhH%Vf<@L?|kv1p6Je!pA-}1i}_A4ZX6yUX65xtd(}(NF!184MEPaeV9;gYa z8SwmRqJBNlH|taMNA&xEnhA5aK2LuI(gnmXzf`V~4E+=RC;cn*ZHW3`hGdvhPoo8L zOB%h5Rgo`h#sW1MlZag}is(F4xy7>6-c7;M1x zw=u(b+vql4rja|PdT(8`!kZzD$Gq-7OuiVzTon3kdksP$-;M|e9({rP9Cm<(xdG4y* z1-YrYn{x2%H1}i<>Lv-f*)H3ca-ZhD$i0=DpZg>C14=B#(ygSmoK>`XFilEXy{)ya z^{v$$%}uPGtgZMpQxA>jV#mvY))CeSYfs+R8cEChr&FT?EDE1ej>!z%g)(FyOZZIy7{{? zZ*7ThyQkgDc8=;^c|Cdh#(|{vn(jQGQ)Z%H;b41Hd#kec2Jq&n@@57pd7(7O-Vx^A z?fvcHkpBU>_Aw+P`(*oMCx4cGE{)N?+P=x2Y~N|$Z$D-~ZC`IcZ@+H8XD_g)Ig;x@ zvdF&1{t6^N*?;B5eAfOoAJ0p91MUb)_9gD}{W;;_{PHlboF9;1f|l7#njezioVU*p z%x^D!nBOnI8{vvt{@DDXyau6-j{GSg8|9ZU4(Oc6a&Ttu$Y09m{dE2sww;0ZiFhQ) zPs`uo*No=mAI(3PpM^U4ck{3Dy7@2jKj!}|2!(7R57Li$8Pd|ALI<|GGSl9^u#CL0 zup(Ut@}ks?LjS_R!Uly+3tLk=DkW)QXLmk2R~S^-t88}=rt)WnBe3+s2$)9}Mirp- zf;=cQ3zrtI0iBx*Q>eUfA5!+3G_FR|!Xvl_3gs)kLO0NGh4#-ueZVQdd?)TMx3Hi9 zPdXPT7S~ho!gq@^s&HS0#r&qD*zo&|@ED^c^bR6QXK~9Uo%g82w*dbpikaq0FNVB7 zaeLKo4&VQ&X3?gUJB_jp_gcHeU0RK~aW(4Iux_KSLu;=^u_rvO15gY6iiDxqhw}ed z>A(9ty4WvuBO<0Z2{l%9&{9tASRSYn%$CF1G~fyP(?!#)~vgsd8K6 zIfYIbd%SX-!w-&eCe~z|2wxISx4gbJNw+{!BHGfpYfWi+sE>~!7~-+85bi9svn6M*N#wL3vB!6Dg4!>*%sg< z45xA1`e)h^O9_rc2;RWU-0*g6CT@QuxEAsL1d(#e7M;5ms!7(dSYFqJi)#FO^V9XU zuD2}A*I!FMwy^TcjSERN|E(?!+%oy!plHPI)CgJAZmr;)KOWlh!TMe<+9MCwMbNf` zYu8@};}CzDNFAmfOo_3Kv*t38BIO3*A|EO5UJ>LG?QsgK59-6+_3GXIyVvXP*S$`+ z)CJpd3vlrah3l7;<6qP05v^$&R5FpfC1^z^%EugTsqXqB7dbRV4evyx$fYIm8bS5N zwjkx=Iu4pp1O3wIh-acnXkVWtcTu5bv}MOuh9bb)sd_)LT=x7L^ih-UdteuV1VeEgRP}si%J(^RW}JXoXYI z`h{~NdGWY3}Nhj>N}8ZDI&Dg;(5Fcqp3AZi4EDlX=@{o?B6sETHkDcrasVX)cU4+ zBrOVE@HcC>sE{SJ1Suca0*jAO{`z}0+$(km$#F~&iO-X$nWoJQZMa!V; z8uQWhMGb#DQNL)75s<53;+>Y^PI(zm({|x`bZej<`0bmoFVb|qd+SSNG)__qSw_^C zY~QXu8n1pC&=x&24y9;4XiE9@r0Cs$ULrMS+p)myam0IOt};jI?wYL(=$hTtAD>9` zQ^0=gh|zEGISIvdPpTz;Qd%vVs1wve$ivC)!(l-rbRsQ*t%N+B35PA+_CYzaXA&QD zN&Vw>2!sDhF63U&-2H4Hv?VXc{eazyvv z6Yw0o;~;wImz@LJg3gWbMx<-QuFbl(>l)UzZ`bgyW4ij_nStzS!dygybp&vnyGCpdn$ zc5n_Y$=1VfYYv@M@I7j>JEf$$QsYwHf|Bl*yVP#I%j~wh3f(PTZFsFY4qd<$?4NPz zUc;xvk}Uc4UyVi!etW>YRW$+@OE87U&R_}8Rcp}U^$CqfQ8M{u$Z=X+8Y|6_WuO1D zVWhp=D_NsK*Mx4pupBu8U`+lg%!cQqeb~h7xoNr$ku9=*d$%`I2htLB$&P7+V#hnG z!BVn6(02-W-9hRUcCA*9m)>uPUC{SUcUpnoZ0H_BG~7{Qj$R>z$=wztF0v$FoFGl& z#^+>J;wx`m(a)^hC3MJPWa5vxjncspgmLNmBI|?SW(iIA zE0V(~$@&gnra>B3lAlku3%4iqPq!DhBxoCQYmn#y+LttLd09K%v^-2nS!_w$fTk!xnoP9RTCPy|QUtiZW4WtV=3_ywxmZbe@cI;7t6oq`!xtB%roKB>w`F48L*jbC2g?v(CvkN`I>O9DR8?& z8CjROWcvzyGhNnc>pyM)JfrS6ZjEvMp&Q46$_7gRt|49hy9ag;>E66s80Y9=xdeow zrP4CcGl>p8(})~hVXQDml*OUqSaD8TP7h6wP0xXTJBONcI)_e>M+~hdA2D>o&=F%X zc5#k5XTsPKa|H6Xi?$l8AB_=XAoXLMaEjS`DYPMeyxzR|Fz+-D)DN@qmRfJ#TQ+@$ z>*a^#X^c#c9$2nkDy*Kh`Ki73wfXaF@g?}B^>OGnSW6o0xfV;J6JVd{&M29#%(zUq zq)1)TIH_A!la6c6?NpgZ5BjV_lj1$R*;u)9`2Rl z(qKuD1)e*}8Izn%F-K?0M-Jg(N`O~AZaH5j^+B#MFvq6ywN<|^=PHZ(!xXPj*+gi1 zHx`yMeWdU?Pt@eTr{mwD5dZvhG~`svhebvC{#dZ~c!kNXG6}`<^x6|s^28OFRE{a# zhH1(y+|RivAFq!YT?BiY^QX8L372}ODf>E|U(>bFLEBxJoxjE0oG`lz-+x8tdfsDk z*Xv-Do+A%c=Kv4iiJ%X9N6>~mVW_XIu{BVSwhB!xhm!|C^#JRJ`v~5N;T~0wEw793 zHHkfIEnZ$b?cL&oEuA$=zHt)WUw>8B1GJHUa(_D$~pyN_F2_mo`r8))l==AE$R4iqQrAZgrcA z*=3G1yV)8SM=`oyN|;rz1+QOU$?2fA=Hm%YZK&=D$ca83J_aw{T#e9M+KIsHm*&0x zu?@r_RO(Mpiu6YHPZ*V+Md?P;5xxlOYc(vhwzb*mE6E$Lw}rfNf4+W;m~g@vgY=@dOsY@zIGTfV*CTFq z%1MusLt94}CCtKYy4G!l9zp7>fm<8bU>I;qs1?n|BGge9#*`jaYh=NC)%S7UZe*2q zIAIpA9Yxo&wR-LG3{flCw!n zt~%ZR{a13;{L?#;YII<-hZQ`v)w8 zzsvI0g1jZGbxE%B`K8xiP@d>xP09ON_N-oT?mVK}0PoAA z0n)PoN=g42NJW=No;VPHJni@Im7)z!Bqh%ph#zW8S`N2Y`jmjCj8koErgHi=JyW?q zD8E_v{7Ha+y1DO@sE2AP-KI#?Y)Rkn<96qr&?abq%@TD~{Mc&dT-OX`Pr z<;Z1gg5=zuP?xt2r`>imhp`4vMUc>z_NVx~DXKl|i3?-k<3SpEtS)Q|>yEvkv)2hp&6EEs2BHh~)T4v1Vl3 z@z*PwgTBZaEVyK{u5kU3b&N+?9b>QtrY9i#&1E6%rpwW z+|42Jk*$|4alaw05w?9$|FnKI?@g(^-jw=r+XZvoI__L9)*Q@Ob8tsf&Uv2-usfWn zD`MA{Cy29ZzXx_(k{l~DNb2ygwFBdKaZ9e8Tc5W_n&kPVTfchFz4g-V!J9HICh?wy z!po|DHOIULQ{ldAk>Foc)%393C>?iP?rRjcjO4<6;q?uIKkEe1^n5{5e5&q#lM`ga z^YC4X1z#Z*d{5@-6MFPa{E{+ImwvUx&R66)K5v7)En4L_b*M-BsG)7Z>K)ylOfQT= zkECx3UIV$c>DSe-YmITxhbCXTix#^2QEAj0Mkm&O zbNa!zUg#G}8ae@}G?Y%h0mJfGZp7FLkb`@9#2f-~Y{f%wI@~B6e zr)d$l27FuGIh3ln(rU*z>Zg$u3#xV;eZxf`+ z%Y*2!dIa(5*$+#{Yk2Qjj&Qu(AWv={?6>5ohHve$9~$^O*sW152S;UgTtqKUbd^O# z27gzQGAg1lqFRYW&bJ77B1c!OLp|{e<(RO)nQ}PnI96L6G#Xl)V)zxoi0+ZyW4gz8 zPw2kBdvf>P-P5}VboYldaKCXIL0{KfbmO^tNL}$ebuE{?JNUqSrNt|Mv%0^?K=sZp z{))nVMMJ-Tq+0}dbx-8_g_NEh&~r{L!-cr&hIi;B*B1 zilnji?sy?v5x3o8WaWw5F8LO7-pp$?g%UFFv1^om}W3P`26MN2HPUndmazYyX@(;qSG@Nji2s6 zE=|bPb6rDhsV5Qc%KdMhg5vX*iC^7Tn}Xt^b-}wwZwq?z^f(mVn}zo{2KPL0JY8cP z1V_u_Q*zWG-(e!ZnTfU{`XFUA=8cc9E}HHK4|K3&!+FyC!ZXc}nEJn%;J|@u?B7ahQ zOQ-vgyA(c0yz`t!X!0$AzjW;PjQXRI()y5hF+uG^-mVgTY8{=r*D5Umx5mnum;dtC z|11dliVU8@%H>NWTNGJiw3e9CeI?j)ZQQW(d4Bb^j2cXfQM?yM2g}`8S4=yY;@gL< z4_4=(_0FUu0`!3svqy?dj8|rO5|k5rZ+xhTJeqptH1BN%YDMMf0ZSo!9x07R%%`Ld z?tO#soDiH?s4v24WXYmp6h@2_E*ZBvh1ysH_b~|fWt?zHq?95@eexa(w|jD}jJj;9 zUMkh_mrU0sS?3rP)n$8I(Dp=QVfxh0pZZJ1a%eqC1Y~WKqZ6wcp@Pcib^Imr9Cztx z=P#MnwHAtc$Cz=V`LNU-0cmMcf3kMz*JAGN*m3PFs?|R{X67S`s^&jP&hbL(PwIws zr!~fuY+MRc)P{?dmW5Yt|Pe(rwssIo7`;}OnuEA%M@UaRt2?&wiMkBwdl=4;(wNbSkqiay!agU1|v`oSOR zQKQz$9aV6wULAMdy=Jgn5*^{Yb;EM*y~2NILicKVR%W$hy0lc{iGV-2I*tolCT zx95*^*ve!Zr0wmM5)ChxUTSV7|T<;-ew1Y5QQr zZvP}l_FP<&I+5cJJwCg_t9m?iYx{G2`J`U;)sEO>9Wh?qy4(|qI(WZAZczL4YY9D* zvjQI}PuCYOM#h}sQz-$yln|Zrk%KiI;?%c4d$%80yBe7M78OkUw{6~Sunps~9;1RO_mO~Yp+OX1 zE~SI?aJ!~kd(eJHZ82q$hEu4=%JJ^$D6Q^?*(1faO1j_BcfI&Ge9#nF4`-BwaVIDz zj5|>|G3@DeaJ^U=!E(aIs7S1Vjr?a?Y=`9}6JZJC&1TDcLTX zTT+iCrE8V0z2Fr8SK^QDybp{fY6`AbI5)~yby>BeJ^NyTQKZf!Pxpi9lB$=&`w7;j zE#NJQ@}pB`6|Tp0kP6a6KKa5HpRN-iuaSu!2g1^7`yaJyq-?aITXIlZP0nomrPH{g zQ?!fL@Jrb-p2+z9nyw9+vIq%<7H*#3e>jI)hHJPbv`l~NROQrqTnu&Oj-*=7=cbl> zET)5RpxrJKn5JJobqgU&UuY$^W(r#o;MA;ek44i$<`E7?if)c z+&<{(p11UHds=Q({qXOm;PTnXY|-}N<)dGWA$SA;S`Y8DTjq~YEFT;vFUQ7o#)@=} z-9p=eq_kX?(yiXDM@D=`t*H9rUXI68>Z6gAMG`{!>xA#u==-M;-6wRN-sRVcwUM3? zVl5~eF>XY6uzNZVl=sWITK;LmFPJ0$d1}Kisrj{$?j8?+Esccl%KLToFMqk>kDaey zwfKT-#c`{Bn@G1SeoLafT2f+%OPp{SWW~5t$5AgY*qt!3r#k3~SP~B9dsAvfbeR>4 zBuGNNFfB?kZOZYdEPf*6ZFt&mN%e^bdN%c@)W4VS%~!j>tRhbY_N5GWXI;MYy|-kN zr*UE$tWgV>_u{5&u@2IqUi>mDAt}nECf+_uFHFYU*G{uPzQb)uiH*yHENLx*WQ4~w zC>5M~J$SXea?JCSSI*0j_R53uQF_#$w_M7vH4Q44@}kP8wWYdz%F5=sQ8crNE)#u( zYw%wDrSa0*+8@jE`ym{mx!&&@=@X8gnVPY!wLeT+R&eU>`w?}j_5TU~!W7{?;Sph`Fjsh0ct`k1_(J$W_(M#Is%VStVo$M;*iT$r91K_8+lsr2`-q2# zM~frGk>Y6a0`YS3I`J0qF7W~Jaq(&KdGU4eJ@FIqEAc1sue6xf(uH(q+Y;$z(<`M1 zq}OR5*12x`_4#e{JLmVxccnLkv0k=G?~>j-ePH^C^oaDx^x5h2)0d^MP2ZfJlD;qf zNP1>^Zu*tbRU)rte8wOgN%){{1twv=|1hP50Z?JFHpdPO?A^%vz|kGo)vv7o;~z^GXkwx=S-!2Dhx;azV@d z{G>82bJEGu`}q^31=1o|elPtlC#`F(%dFR7+z-ooEcb`pX5DGsZ#|#CR$kir#QM_u z(LUKe%Rbk>#J<|T$-dp5YW-;ocE*+qzm&Eq?bLFeow4#(NB)WYto*b2=dGvnujgO4 zbJlxy!S1w|wO6tS*z4LG*;|zMEZyF!v<>qKNsyjV zI;V7@lfTqiUIojkEw{A%ZY^W=ZhyV~y^gb6c5LZt*|O!X77HQwZfTQOYdKtAOCBT- zm4~)o-=ef8t<|ixQd|D!E%QR(~A_k|7q z!+$OCUkm)#0{^vurv)}?ozQt)@y6oh#R2)o;+67kZR@n|=fR%xy{&DnR%=IFzqWqz zLoLac%UhRe?bY&q+gb8c@|>1Eg`>!Sb zU)c(jp~}|EcEZ5sPyIVQ8U@4a{R+nv?k;>&T%~wK@$TX` zrBzBtlhqYea`c7M^ZKt*gZEv=h+INEcttXV2vc57>xm{Ug z_f<7{eT2G^dbZWq>Z?AT6$vN1a`y1-1@ijYY1tpO{@StHH2qlZ$J|l1!4Puy8Uu|} za^D$atfLUdW5&1Us@A>Mz2=eDcjhI=xAnukm&DfSQt#iWC9(U@{{Qdor%%&oHQok# z|6Jc@V1Cz(b*``ZU2|u@=B?qkp;wf7!obo3gt9Q?G@>Mk9cx_8I!>OtzE>ajh>sHe35 z;?ZP?KX zyuY$aTPAB|+p6qnB6tIH;o+A?7^LX zW{=9Am_51k@$6YJ^4&$*E3?;TCuZ+xyQJ+hIHEoR$7CZrGdrjKB(^t}0)7M0azJWeO-(25b-&Nm- zX;Ml*L_b;|p^tPlM|ZxVU(oVuLwHNSocJWx`gI+X81!3cdH-~(-`-=H9?R%=wLV(| z=77%2F1^>d+&lUMJ@#w{`r{qXMc`#3)1L`%qaH`-FN8}MzohNjYS7=%-`79wv7)|6 z|6c#4{j2s*;B`{QFv4T$F~TVE($2d&Z!wl2oc3edk1>{SA8xFId1H;vzW*kS_3JEs zKo7X641=+Wv4yb%U-N&SHg;*>uJddcMjLw@2N;KSUJwKW-VZHabchJ!MB_ALbhDm& zY|mjQ;{qA15Be(SiuA|NPo-?&Yjp4e&A#M=jq(g zmW8=(S{Ah|%I%QbJ-2`Eu(s`5C$|l2+qZ4A+;LE|({tzK&dXhzyN1@XZC2ZJFu&3^ zuWeG>g0@9Kf8X|dd!k)#yR&UpyWVzx+oSEp_Kx<)+n;Wq+}^XjPkX=ib=vO+*@n4^ zxjWiUM&zcoKLBn1Xl|C3Dh)+g0zv6_9#)p}|E;vN-am*z))sBY{L`>@Xy1&%+P&ku ze-qaJou@Vf>#!c1R|h=l*y7(sY4vDWC;m_4JEKRoepq9B3|U;T&i|j*ulm|OqwwLs zinssO(xLAE;BhVR82V2i=e*_pn@2e;yUzQH#eWyn)N!_%V>Gd?|M;_*Ef(9ctifYq zQ_m5ac?Lmjg?6DmU=+abV}Hkf1^f{p^NkspS~s;usv{+&%#;SpC7@sMBBU#)mQJ;% z)=t@}L;#irX)gzBfbLKZ8>VW50_7D`{gZ#E`ldGKutBOeD5*ZFOsW^u@ZX*)EgDbM z(%wFuuA~kvZzl85B0^G3{1%&-+&?xq)hqFOQcC`j%q7=Ld=T$}^z|ryY1}RiwORDMz1h(3{sDZpO+yB6XKwfV77rZ%-JBmNHgNteWTzGg~^X zjx`CHm!fUzq!jP~a#tBhx#mP6xhJff|$vFlJykT^f_TXHlUJAR8D1@rLq8_YY^i47M&1-vHC6vTvNd1I`B2T2hxu6+HNQ(E5|uFL0(thP+nK~Q1~#j zskB=d)UoPloD&x&?!^>OGx2McD-p3R%xmarCq5x|c>Mm@i1_^o&~avZ^M3KY<6$$}-xj|) zb42FQ%!lxe{&?vMT*_1B`w`+#$?f8Dx6peaz|Pnb(??Zb9Hj$emyx{_XO;p2EU zATNHC+#-H_{6v@$GzCih#>pKSKO|o3d`$eXK-~j{x8R?VIl`Rm8|lB&e`Vguyd{1w zeu*=*#qs~+c_h>WO2JaSsd5Rl{fW+uag;SHyofyfyYh`o4|L^A<%^J~Tn=B_l;_Or z@Q+BuO!d^0)Dcm0_OI;k`V;!2=7r|@<~VbVGXo8K(B}2S**mw2uj^n#SZYnmpiOZ!C(y`L9@)8X2w!MtR zCFW+u&E#VeSL*l0`^NiM&=+zwK;54raZQlE@S+=;!3c)ILIdp5hWpPVt8;g}bhqu8ln4^T_;ycH8hV#uHZlD!%Xew+J4CtK0 zCBZx?k@7JooFJV}N)nDEaQr$YLpj6Z!^AH6cw)zv!f})q1`C71%V3NM?UA2&(hn>% zz5`&}3V_ajXFq8_mj4v31sT zD37GVHo<_XT@sS#xmc1g3cNp#!14%yOUnknxHalwsZmq>DQTkIOYVg_iD&#Uu9a?d z+goqsOZiTi+YnIZU5V`z+k^G4Za5x+M^j7ab{Al#eSRk#bw!liIm z_72DI_KBUGbf?5l;*LnMshCAQniqBwKvQsH%-&J9-X`J4mU4&w9s{-KQ(;r1IpKOpVF~n2 zG1ml~%2NOo6t|E6LcFRlAt&*oAF-HnjdD%#E0hOK`6Y9oCN@fJRG5|6%nk78aA*qs z8U!G*5%g>T$ld^Y;3om+BI^3V&@LW=m!tVB{6CZjjTlXFTfVG)Uq;VOG6*`c{C84_z{`*cMUNMf=H_ zf#PI{k603)gm$D3-$i8xA}achF61W?K5TbW0e3TG)@O7Yj;!ju^xg9F^7HC@>U?!x z_FV#fzCNGU4chkt2Dsub?;$8Bbo`=dq@kZB2Kg%Jbty*k=!McG#XRHr<<;!n-dL`h zT`lD@Z+whAm(kVALwPO07BIFlwo7=bFD_#1WZ4!vN z*BXWSjQI@gpZyWi%LI(3B_N;4o!lBSpdz13DCteW3jz34stWYLw5VryD*x_>)?0%s zBQkN)zhngJlcmp++F&aG;)hngx{`kcyipI7y_CJ=SLIigy&T^!$uDWU){LF>T{M_^ z#A5nB+V;TT9+z%tJ25ztJ#`l_0)01a8$VD&C%;tNhRQJ2wsc{4ZLczbkMi7`5qP;M z10D;6^A`kqh@O-ul_ynQ&8cn5OdiFP;VIcsrK6OG126}MADgC3hvgjo8wO*T{-v?A zv9taq0#nQZ_As2MXM803=LA`$jLJc)eE51K>jui7%EI#-oU$5#TtEt0AIdt!bKk5V z{j(L=h8Ly%OZyNHxXka?2$-euh#Sl^q4cr(Xnm~qJ}l1zjCbaXAvYc{8dL2$U z_L=sv_Kx7#D52*6Ts^Pz@;RHiX3G)U+RFwe@msTvOQ?K z&0D7Is4 zpV)5j<<>fhj|pPyRIpua|JZi1J!9}hMV>{Wzvc!;2KP;S3HzphPya5hBMkzqN1$w` zCJC~y61}h`d^rS2ud7_IUQD2?*0$ERR3E+&^C9*!-~|HtZ23%QK2tuCA^S`AN1XLR z@R#Zk<09h%>JMsku6z#Q6wn?C%WE0nn)p(tM`$Hp9mXAo^VE^qDk=R_mh)4_4ppw2 zJy+F!4{m#?mCK@fKK*X}fU6y9^+AQAoZ$2Wf8>Od{-Ab*`QZ8K?*KmnFy&C%8n8ua z3mER~rBt_X?oK@q+*;;t&~w`p4&~C^^HNW#Cq^a?5r+sZLQCq^)Em;0EY{R5jUjtv zUd+#fdOTGfuwO0%ST!8k;|WTimIAia($>;)(MUbmDDaX2wxln@Kw1j=8m$6pMWTTz zYh6ldFGeG42cR|EO6{wq+B4D6&(hD*p48@OGadNu{fc`PcP;cPEDJN@@xmiGmxcol z!m?2gOJ_zJXOxwOSBs3vi#s8pN6o3`O~!4;ZTidf7^_|^f9K$9`D^7+TZtTLW zY!Eigdfwbo57`Z%AA<%#eCRuBt8-XO+XztRz*Y61+CV>O>8NMkOOgBBoNi9@VT$QT zMGKy>eeXqfDueo^`X%OQN=s&``V9g!oRlrm2DQ}B{c!l7sy`vL>H6uK^Jal<9gJPD{~eGa!%U^w~jqYDZFD8}+?;^ZIC5 zhY^6*m9Q5tTX+z9JieviT<2j4>?u_+r5)+tK70b)5Wux?MS?WR^Ghaos4wDu z;>$X7ulUMAseX@sPjN7dDwuLvQjdKc?y}&2*?6zYvKP>pBWW-6N9l?VZ$oKfwXD1R z;^52)(U5kDhSCGDlCr$AjIt`Ax6-aG#g~!VA$4Nvc$}qGq*Y*Z$fR~I+aK$txNMsU>fZoZ9kb+b1CMdCB*j8q5IOi<6g?adGlu(2=|lXE=B?@m69?VvIB| z7+@G83F6nPP4ud1W6gp}p}cBxs^?G9l1$LM`uEbWmE1Q{ohW#MAysO82Vyp0Mu*dXabsNsXY?` zqYTslW@Wx#J958LFnj6C#sPc5=g_;Z1L~3HD`~ny!P{v6B+YLe=uLXh555%p;MzcK z>;oDDwa>vLUmlcCQ|Koo5>(Hf=1wi%6a=`Fa`8zXme&U%j8ApFa*`*Jl%6PKMunmr zSL%+N<;-{L0kfl2bJp;!iKl$(x7M_{Z}DJ&Ggb$*OD$t|cyxlSSn1Mi2INY!ajp-< zg7`#x%tPOKH3RPNj?<2IBezO3AhEDzZbdJcESRz$uLW>l0(#7Y^$3OXLml`^aiDFc zZAN@4-$Jjkb%Vu`?PI)KAE>Q$k#!MTVj2b1kCMxuK!3jgc(UOvsei&%_LV;&fG4XC zMN`VJmgYy~N1#vT07S+6!SV-gER+|@AGqbv6IB_xKXQLinfX}-UzuO! zo-knv@I8frE%}VvLA+}xYo~Fz9(tOr5ooVnT#JY%Q?*=FZBE&W+_GqqO;CZJ_$@Gx1k(H zQI0zek*N6NsDhzrac{SBYx=?ax4y83=JXsqiONBDqdZUx^EBmUJngll zR!j$uvYNGdE^lRWI?e=+U!a{dm&;iiQWp0HA95Mwktx@iYXe}6Wh+bJL9`f$+PTxx zYYyIoCC(1uO!OLRmEJ@O_%GB4u$tSFDa|g;ZpkpjVl7VeEecQy05m`owQNAK^m2;{ zqYJ)QdJniym!5XV2{u}b(;{AeZFzflz!ZBDV0Zggd#^I~g|Py6vbVRlv3K>O@RGd~ z{1LfL{`_%>lRF7;c+Pj84;p2H_n5mg3f9`W$+^ka+5~8a z`P}Wf+mQp34Xkx@*X8a3+z6QH;2x_x0?%3mzgxefKdXQ43cz`}%P{Aka^wBrwd|$B zz-xumCj2-afVGUjOqg-WVSRI8VdLT`-kb5eAH2MDcrN49 z2K-nC<_UhGF<5s5;5O?v>p=&%702+PR<)=3i$gFzkNC-l@%caUzvkh;5HjV41Yl6k z1FG#!b6tgnJWyU(NR~Vwd6*|lx5S+*wiFgnfcApIC+Mx%0_ag(3P$=`fOD}OM*DiV z@HNWZQ{e+Yc#Xn=ZlKJ~shZobfS5qw*dtB2`xUMN`@Lay{tDnru8p=ovlqb_KEJ{8 zEBmwjdR{C0{roBn_B-}FBxk>0#_RT5_FG;nmWPpfB9XhgUNGNC6&Ly8)`-rP9xXi( zrBQmw4_@D1-wQZH_;^)01MO>gfDb-D(7ql#3TD*&t75g?W@R_>SLgqL4s;{FJ}U4_;o4&QGiXHx`6~ zkiQNvIX~I$(dn}`S5ixQ7~m=_R2Cpn!B?esT2dBpkT<9_qy)dmm!K=M07%fozZcGz zIPQ48xk9&>m1xzNhg*NlL1$hZUG#8)IK>3 z`S}!ua~Zroitx*1t?=5`pD%q>gzk{tsLxAr8UgHKWs1)O_g+M_&yw%z#jjsD8-e`N z3VtcvbDTrqmF4xuK>g&PV^Ke-6pjZ0Qo>_8NCoLR{G^qzgq)PSkHBqJd#SU$^pqKT>>AG;F^Es;b;b0oz$d4!f2 zU2{)4%p>|I>xS`=iY`aqO5Q3cTDOmY9Eox#zO|Gwiu3F=vukx&XP2?iNzbv)wax)J zXC_jMpry5t0-keL=pZV{n&-hELVoNX!1HSX$K~nUVg)=unzqOBfF~NRd{7Skx%ni| zb6J_i&#K_#Vh|iF=A{}{TBVvsVOSW5W#Lj6nD!opJy4^tvmdlf@Vo=1!_wk4H6Y@- zB=uW3G6dHa2o5XwKy9HGhcG&rD#1Xvk=Ud}Q1VZ~9^Ks1eboU&5eT4lj~~=m>CWOd zJm7kyoF^2^Q@%Te>$jd1=rgyZ7naF`mdjGRcNtgOh)e9t5rv^t!(QHxifkEs8A^wz zRnEsgGD9WiVC3d(Z-h%xwF3}{~v@R@%6q_*uVoA8XjHjsu z(PdVw20;?)foV~SX;Y3rrLj|8nxZC8`z;BF*#}d`NMmHMgmBGmfQhjf#UmXS1j*^?(N6U?+qviV5=JvJ28$0IhBf|kI*;K!=0SqvD z8NI?w)_Yb5dT_%&fqK-J^nyxAYHVO^;O4k<`51#+Qd`bg)>xW?UxT^XBxgNu9}T$I zzPE5<;g`ZM)N84;I0?bqFgo{i?rEY)d5)K(omgMvJ?U4}1{iM{Kuae%Tm~J7f_eE( z2$&c22C)R9Qqm+!1*f&1ql2wuNg7p9iZ+$#ekpY#NFH0Bcr43d%^)0|`K+wc{E9u+ zvaID#i?BYTvk$e?_QMqUt@B&tx5K&6TDSvI90=%FT-`OVRY$9Y)?exjg1#amrpI(; zt@?p>%|c(LRurcYlzQ@I8f!ET^kcnhy~_LZ(s`*GycX_!sjG3Ir_9NX!yQTeIp=r- zgUI;7e3g1egJ}j+Ft($Zlwj<~O_FVZ&e%_Y%Bem5QZg62FS$-~NbFOfo&ZdY&5g}X zim`8EUnWbjV`D=An^e$Mrb=-|I5aNAh2#pcr(-jc%O!gzmrM?bJ(^r4xms*CcvvyH zBBrq=Ys5sO55je{!DG+>!1%{)3H)SSeCdF28VY%3# zh3qE?9J4dF8UyAX-kPzK$}KuCc28__<{5Dw0L}=A%oHh^ znF5$D9WL!JZ!7PSnI`Wc@1bO2IZQDif2Fhi;5JkRu^WABj+{Zt{kzeA-aw0SUM}b8 zxdtRKHYseaE-j`{sF!KKaj?C;fU^tepZcSICYF6pq%~)0W_mDUpzHGUUhK6P&YW-9 zOSoKYV(Qw|wG|FV<(A%@y1~t9JO}G;I;lh0dZE6<sWabRVOZ z?iBqnU-Ve7FIU!jAm%{7UB4YTw*$&8pSd@4SJ(_arug74)t_TA_+=O&X;oM{JySZZ z4;0Hg^yjhpPFmg?(FXGbgpR$8Hk1eHpx8&i`3T@xQ3>6mF<+)hpP1z(dMSIXZ2J|> z3Ws`uDAptvy#KQAmqE5BJ$Cw1>I;spjy{(%?>t4LpedDk^5keuYD3HLr5+ofocfMP zgUVsNH_?(E8~haX{nR)*pa&!h(*IN4%jjOAGACekiB~)^LYbL4(%;3w#le*xmT1pP z^`^9ECU08J59(7Lp5}PiCs!DtMx;M4YRlQVWK^%0!==lWoj$l~Z|z={zPl(@eXUpX zPCb!yW$5XO2h=VxC2<$7qbNXJl|IIt7+D`=v@bHE!T1;#iLqE^WF*eNi%=c6u5~d_Jf_t>vybw1j zV~{uqK3nTiMtf>!A(dW$klLg*r2*+*61OJauMzLSbBAW}L!vqYo-goE9Pn6%KyrTaBj~a5Cl22v zYXh;LC#lJ&Q-8tZuyktk)KsBg>TY2zVRd1BVPNX_)GyBbW9qxqwIIion_qIXP2I5=aDVE4@Iq!>`r(zMMMcxhn%SVI`8?0jCsqwFm4US*dkjJ9 z@#evL%uYR9y$|{-8ndd%!+NB~x^=@)0Jyc!=!4h(0P+M2%6uaAE~yqD1LcvZXl?<-_zCzT_*z&hwJ`OC zw1T%j_2%_8%Sh@j$V&7JpnA)3>&uGJyIDlX2jev!RK`9|daAP=As#E9RKcm@KH}jp zBW?!}iY!kT{ov)4#?c}7appV4qr~CPJX#zrj&Bl|iH!r^mp5Te%Dsa2fL>?XsGalX z!~4{@TAn>Gs&$mZFg^FEj&-F?~oG$D|pKOdpx)k?EnnK#;vT zdoxPa7s@)vQ(ApGeHdyY&;~WLH$!eI;4hQCO`WaIMoz_&l~FGhJT7MK+(*)3crg9A zR~9fv;!<9n2J=s)o5S1bx?uE26#k;9ah#-gL-jtWeVn8~%}>%z!~dN@F^*&JNGsAY zc*4i(3Dx)_Z2TV||7#zocH4?(X8Q_8WkzMjIUxV~jma=?e?M3gj<&-_$MO#DdqYnivK?BD~?;HRbG(oygoPymz*3H&yhDYD4` z9)`nr2mSm$)B)K2j=Wz5hsXy~uf{3r*2XFNo%)?zw4pVVa*|Hx?19<+)E(9BYO$+9 z=dsMg=s5#v)j2f2$a1LX#;m71QAOF#j4a)6IWvRs^K0ciNo@INV-3;6VH@&ncS=9^ zcBGuI4)8-?0nmoqT^M-3%K1F9hh+~V9;JIYTqaSRB|YlkfpSW6Y{~f7N!Ljam2tI1 zagY2+2rh8*L;OdVPNZ1!b^g)S>*VnDZ-lO6bRz#aK)|VT{-`Fer?-JX-74SPM}Lhk zZ{Y2)Ti!eh$~XdbH41s}2<(AhWpWs%Sn42kO?3-(fV{K3Gj73^@pI`oy7i{q__=H( z-8!f{Z3PT(V#B|8ciGcf5Y|1$6zHAbR6HXR5=>3sj{gm@_=`J z@U|~|0W3#m&jg$u2IUOp4E09!y3l#NI$pgjd@f(9RnDH-FUU8XMjw45!dO|KtWP#p zcK?@Sr^=X4&$|BecbT)b8VTiAWfIJYNe(qdxl3_oh#H_7BzEO`-J(MfZ#%z^L6Y8;LC zvrs*!pGMMKW8=%PejXX$y`AM=i@>}*PJ{CK;yd;k#>V=3&K6w$Z#jF2|6k;WINoh{ zHFXuhWObtcrv9>Zr2c|F*SN*F*%=LX5-dG4xAoyQCbun;Et*~@!d=XAv>Q==TUR#$ z3{lr{&0Q+NZt5=CJ+!Zk(e5yh8+VqF?2h?NL!vp7|vTtWEMX1Bg7B{k2Wv@~XQcn%E3-cGiH^-S{ocR*- z66*uACF)pGlk85|pWR?<7%6zh4$sG*Ec<~yUC7>F!Gl@E-s<13(X`!T zYm=OkZ4Sk3FgoC)iKVkkWMS-eK(J0d+^XYNRvFi_f3`lXkwvWSEZ5Dh>x^o?Rkm?# zUKttPO%2QLoPEBGoy+t~=2B)4vqyFqUUogGcot{>&^NW6cAng;%<=|<3> znb`@=K^Fr74|H@XyD&e*Xw~yZYY>nRojrC{#bsJrk7?;L;u<1t9cUer-zERK)?NiY zTyjf8v}-N7FU`AfZ1;dST+7tOTS;pU{WQd?4ydP9%RZ5hGv(Kp60eaqrC4EY7uGGT zQT*A#Z^a*qFzZ_*wDq;Y%|PD{JgiTAIo=!^dEQal>dx<~H3u@@d*jPG#_04$5h0GHs{RLBQzkdJU4dZ#7as?`jA1mGn7B<)s*!rTaLT654( z)u!pEBDBY}$I9t6q#ge0+A~e!MXhl>r@f-R)i7Sy07lUd`o)ydVVJrOOCMMtH-Sf2 z(cIG9lJr^>EN5Ii^3o%4CDrL<*zV}`Ty(xHJIAj!|o$K6*aE{`VXaK$v{ZK~sKqqhvdEmX z790)I{>C8$u0E{TdfCBK6QA#}^+wk)%I)JqwaROmlk@!$a@(&dSB<2sftvh`c5Z8Y zEls@?4=Zgj76F+jhSQD(PtV^wHt!vGGWs?nTvV7OLksa6N=5t2?|1p8*c=s(T^YWwedzpxx97O5WBBSMp zrOtT{YnPi%`#9w5Y9Gz#>Y%T8o>hj$!ig^XILpzNVw@lr#NE$?#_ty zlelGdyJ(Hj@%X~V=?mh!N66!Icu&itxKVM_;*(zFKeES!pghiWVXMMc_J;&)JZdim zqf@VzA7H;?e{6r8A8Wr3pyO0Gn*Lzdfc%>I_44Zw*x%YeSLm`| zFW6B4`wyo%IO_aToP@^%%JJBHjvf`QaDMRb+wbMe@cfYx=kiW}$U*PG>*6*1B!+*2 zQ@pFOQ4Tq)8pm@B9cAX?d~>)b-yH7CH-|^_&EbW7qnMjVI9sUGLq0z)0ORkiYK7@Z|( zToyNvACV=RF8tUe$eQJrbSwjBl!H$9SP5=fX+DgC!5@hlp=%yxHe@u~Rinq{tSYTO zUU#%cQ{zFySke*WmDVJ4Mx2Wrt1f7qx(YA4*Y+BuP4sK;^oU>Mc(0LrLC~194UL1& zjh%jK$&a&21m{6Ix(Y5UH4Yppos56o`0~+Gb9kcU#S9Q30NM03o>m*Y(I#_1j-s8Tf(71f98c_D)g);M>2YTnO};RVKMuVF zqXihj6MZ6I*ptyJr}L~4OHv)6#R|PdEWyz{U*q^XrDyM1(T_j7;ZE@~A8G0*{_M<{ zQsx`8(>B#v;{Pk|T)^zAsyx5Xt#j($dL=hd6%!KVRB|f`AxQC(fB|oXil`t|0}mhH*Mau@uQga3SNtmTzSY8(!Fy1jxeRY&J+Mi(P11D~5UMha7 zqe%|Vc)h~&jlORJdz;MF=V{xjqu}|5&nS6!)OSUj2I>egAne;CyjI&Lyv6W%$=QHm zA2YPiE-dxz7hVhX=r_4W@{Xa%m9!acTE|B9Uc;tl{MU@nJluEKkfoo6%QolfuIMw* zTz66yt=>SJ2p3i zKAU;h=Ey(v_GbJRD4!j%X&5$1<(k}W+t?siQs~oMu1h#O;(8bK*%3FEtoNeAzJK29 zs}3r!7eIo30SK3-6ZuiE%CVU1SAk)8ZTa{d>e`WPwqfejf zYSLF~6PVYi?Cr&-Pp)0`zRiK7{cd9qroP>@zumOC-L$h){A(X7s~=&1Ez^*pLS72Y zd#XGnV`N$tJ?#5-nr}1vTzHmVqcCjPw!q?t_G8*+(fex*9PRyeQO`UU%H}9bo^Gyj zI(_=~*Nwg|b!jXKdz<~JZ=avB&B{k+x$s!xwkz*%`cwGXTw+XF`;Z)R4!NG{m?AR| zFt6^ZD}S+7^v-(O{;&yAnl_~|!#e;=i+kAcAK6JZ|@ zf!S}$#;1=B=p_z0w;o{n1RTd~-~rn3dVL=rWbW@lrYty?4a~9!nX>bZO<>y0w>F;H z{)X`nj{Y0Qet~HZx$ft(1*Yr*bFUY$tiPw=cz+jE$D&UfioI@zAB1^EHir7@ZQT=avY|3k^yVdb@CpA#OZ&+OR- z+R`m^t8$Ob`<)xuo@aB~;Pa`dD@!>ab$+kB_RjB@ZRpys)OWqY@EpVDo^~Nh!+h6; z`xWN0u0@7-vp+E(VQxa7)m*x5fNR}4T86Sv1;!c`v8sAqyGF(uH8R$akrAKDx*qU6 z9Q>f({Zg!fsR#E24)=(9oImw8Ru*Cl1av8>Mfuz3b+AuWqBuK#gu+FEqFXY0H#-LEKP zru!8f;~E>?@EJIl*w%HaGVXPI=7c$ak!5}y+nFk3e%z)!Q^w2APU?jpKZD%2HpS?{ zUMVv6M1kvL>ys_>sAoPI8=os>!OgLs-zY5CSbVwUtp!pCwx#4& z$oOkfJ^<5(GN$iAU=aVIoNX?MK*pItHHi*%GMFLQNLqhv|)S({eoc6_(817 zb$OP$e5{JS@YX_@IbZL~kj9wrw+80M(Y4Ia(1RZx5YCt%cJJhTG&G|++T!XvrO$=O zc8ZPg?x@EaM%I(C-`0Y%V!t2q3ECLd*)8_<+&C0dt^Ypo#b*)@0 zeJkTsS;whxEiLu_9Ao8r*w@qbQuaC-BO?Xly|C&F*^guWhQ3wSb$%iHU#~ZNVb#Ni_GbTBm_0%vYkptWtpAI0-JZC_ zx!iU0Tm@Vy^1L3o;U@5QZ*z@v+wXjH4*N=_=ykc;gByWU>UXOi+z8xm^x&wUZuH=& zuNyts>JJV+pxq94F)cidA{BP4o1HaIc{#`9`$O#%J%9=O83pu_R z^MM%y%6cyXGsXg!HhOJrDe_;6XHw*FF9P#^ zDRZ5f3QM2h`3Uv0CqkbDX8(|7uF19R5_HgxMp1#jLjOFkAcy`0w?L15u9VGCQ>d$z zHqAcK*Tf!YVz3XKM7B0KBZa=hTno9*81s5LBO)^fYF=;J(}CaB(?&si+0WLrUSaNA zp%3SftP6Wajp4w)-$dWplzPdbUg1pZjpr~d%Q$@g>b@qK?P*)5Wyw6V5 z^>k3$DPzjFQ(5~&*tc1DmeKR<5k5rEv&X>PJ4e?3oWsE1_Nc@O&cU1;(e{&DM~0+r zg zgSpg8t_P-lQ|;%We0{JX*FLYHeeYGn(Dl*A`-wRddgZ40j5!MWMW(Jr#{VMY|5Xk6 z@6K5^JA&=edzT%v2g`lTj$v87FWI|bgXh9?F;=<`4Y{;aatp@q5Mya5lQ!=Zo{?5? zAIDkOkTBP`WUgJYuTl2<7w8X$e>bT12TS|a54g<@NMEhIB6WrL2~L84T^mbXiGx03h4CzGIymM- z@M4zbJOVz%@Djs^mYf|ad2FzNefDnYXX#sA7j;XY_se}8!#V0Sr;KwXu%5rA(x1xO z|3hj&?pmhtJSSck^IPI(NZVqrL!ayU3G!Y&FwauJavsu-GmyYzJ3DC;^Wij{SFp|9 z5Xh)Gsl5 z?7u=;?AMsHz;D_&ZfK#KF80H!Y`k0F%BM!mw3wV=)6#?KKf%e5c$ zM;QH)hL0i-V0{alqfxJ{Nyw#5_5#~8BJF8SgqyT)mG@7)URm_zbIiKy+D&`TS#?Os zI(}8}$1={4>ln+l!CIv&Fwc|84d^xA%5z=l!&xitd*qh7(=#!NLvwyr7uNltvhD}L z*kAX`91X@D&!H)-Vmn^$ED2He3%VH$3b43CDOn%Ejw6#bS8<6g+vq(1aRLJ7w;xslBlGBkZq*bE3o*$_o2_(>$ZstGPwnAnel!cLg?FLmkRYWh}L>pIqyRo44*tSNh} z>Hp(sKbT9s$Az4Ox{jkg^BDLwv{_Mz&1*uAqMkO)C&J^AmzlD{IOpL#Jsy4}=a9?x zWUCwS-`S*}?0mx)f?HG7-^a5Hy)L~ido4LFGS=bBndBLA=x5K8ufQ=6L`Iy0ul+KZlZchJ|3o@X^d&$Alv3A~oUrd*5ic;tG}b1W?X>l?*Bj2)KsJV0ig zFTY=XdQPZkybTT(EPIyJm5YCJeFfH6)Q7P~W?7EMz^{*tC(e=ET+oYu|k=azbZCLm?YIPXp*&Ly^$8xlWY#I4T7;PAX= z7<@vAL&Ik)>zo^Si18-&tKIV73!laD!a2GJ*NVNQ>>tUzF2-c!@sgQOg1$Sl#x3<+ z?{V%RH}Dnw2sgE?@LI#;C1=Kl^K9_LxjQiPo@ZHmdzSc*T>6(irt0xFns3_@+^{i9n2Vp zd?pKM40J_n(mIRR%}^cl}rm9wa4|MM&w`nWa#H%uEEwDB>iYld9x zm79h&1{NCo<@7T+E;h?U?t*`??ls7?Z=NXEF1-AQ${}CD=CqKP$YQVe=rm(>YcA6L7uP2}VQrHOXY|SUWT200n;x^C>2dnL zL5=;H=z50d4}rNS3e0tja<8mmlyxqMjI~5yZy(n}(8sf7GUo_i7g+aq(91rK>ku%W znfA(>1RT$VmHXsuSy|77!SdYSm&J3{Sax}sk6?4!MD}w{;!Zbuu+<-`^HJh1piQA| z1M9jAKJlD-b+Eb6_yGrf8|-tLH+z!K_^dxL+dR{ib6jBNsK~=5<9*cDpvU=S3z;#) z{06QtZat^uY*_TVcSHV!jWTj0Smp)J1(8)RpZnn43cY;pqqecHNoHIzX2ANc)cmaE zBeNX(nCHPUmy#Lt%%94dGs*PHoT;quoP+s{rZU(R{UX|MjRbw%zmTct{snvznfW2| zx{}wPD10jWg!vOTWXW%2j!muum33^A>62?eav00tx0t%(x(oXA>3;=$k;8abMmzP| zp(jg!o^RR@#0FTN|mXA$8%ISL}as-QSBVXz%+LoEeUNenoXW+FVILgSqqxImF48^yB;a zO4HAXv}m)=*sL=)SJ9_$|5auCvt!9RrZVi)b&ovLW!Qt@eG2L4O!gq&=Jm$@jfO97 zz(3cbD7%$)rMd7n!&nJK{ZGl7XR_Zj_J(DQbp3PbvyRrlzD_^Nd>`I0@=w$wFGcQB zPF&iv*T$U~O^3c!l&q)z`<$MR$9EEo^ z!QCnRtDpFOAlhH2X$TpSINxzfj21QJs+K4pa<*wUEnxRflm!{fwImi%DUeH zpHCa)1o{U@8{tyFI@nxfe1dg;LD@^G_w5A7HiK6OJ>H4R4M%%Y!n6n62=!iB zwyh0k5#f53deoG}ls@LqR&W;}Df4ZX~F+`mM|d>5GWU}V+D&pbjebD#GU&n=;k`<=*IR`lHC z1U>gDfw?yU`#nfD3_gf$_U+fbz^GiyLeu_bwBeix`_~HR%3)rhp7vbxg9m6cGA=gc zdSH&TD>2{ECggK6%U+53F7o%3Lq3Q8O!^trde5Xi^C9#xH_q~TfO#V@@EzHSk(&rqujjhXb0zJMEMo_Hj-ANt6UIFC z;CyU;nvo0m;;AB0aZu5n{ulWfZ>XT#KoF$=!Y zl)cf|Z!&st^tq|DDc2seUAB^Xu0v*rF+0xdE$3fh|DMup^ZZU(&kVtF4+wrc&k`k` z6BqYT(8oO#IL>{_I`@Gu3FFA{*~-!8hA@tx2kV>&#yh>eGQN~`-c$}YZE_~bz3Kr@ z#~R~&4miB?Gskt_6L_w-VIGT|8y+t?GkUJUf<665#=I7o_eojLHpvZqalAxs8Xk{a zNlU$a7K8Q$zBqot9M^JB19QBCTNsDlzdSeOeTP2Ip^-5j19O~4j(Q!Z(91JB?^DkY z)m~ZcXG?ojudMYdN1Nyq`$|2dgMYB@E6Mc3cSHj-pD4EleS9Yh`keO68{l4Z?Y;Eh zhH(ddKW&&pz$ep&y^5$>Dz8WZHR?Y3EJG=gp?<&8E%Z zc#VO1Uv4&jZZTzVG5XC$udMlMv$46&*xY971;=`Cv-YOVcNqPh;z#|H>myqvw!!+2 zG}?Tp@fn!ca;NoE`cL0c=I8VsOXzcu{ha+}1FX5RwUza9T{PEC20uX9Cn5JJeliEt|wu$D$IT0%Z&bN!|TJI5jNo7Q1;r19DPEs zYgE{O-1zyFu?I(cu+FovzscBuqYe1rP%k*z-)!u`(HcdzAYi>tA2YRjb}FwEpTa!Lh5fDce^nvvzcu6>>N#fD z;;gmgz9yMI*J2GoHg$c$wDSw5-rFr3`G}Oq82R+)_8{gR>ms&gk{021IPH!6TiRS z&=_twjp1}$*O>A%E|%YgoBF-L&s;-7CvnSlBRH-P!EwC@?qPp=8;MUo^MyV>pCucc zIVt?}od@W@NM6xYX1{W8MLq2~Mk1@vyJ*kYhF)3Ak|l1b4>6;hpx^qMWcn=hTJ8$> z7y2)y^r`J3%iKhLn2*S&?A^2<)HWz<8_05=4EwvywSaLg+J?L3y0i^PH;v) zwVk}iGN*y#85($Fh$Ur>rA0{@OW^qKuCmUX%KG_nu&i@9w<@oe`4{}P5D&_l-;_0G zz*~$Bc#-5o)km9qL!N{l+#77b!KMx868bD)i7VqO^0;$>IoFdVmU6?*$g<9aO$+;w zXTo5ayBT9(JqJ^+hd6|Of7>jZGj$xBS`1*eQ#jtj7y zIdfeKmNmcEd!S~aKXK~7!SHW9N*$2aU9rO>Ce%gA; zY(u{*$4g+IOOqS?9s0zxj0>FaEd=kvv z$l7ai=#}xg-N0832*-0{=-(b<1H38RU$Cw{PzyND*Wj49!QU|U-((xO9)SLvA@<0| zKUmL}p?{e6TyK!WxF8#!-=Yo2FZACsKEGA^%-&Gim!I|MH+eoV%Y$doXO=6oJ#BK0 z<=?b{%}n~_-=YD>SnW^TEap${L&38$Dx${s3O@Ec637=0zXpz5{4UEueCkfLyn?8TbG&KW@s59Q0rwC6qg)6#spD7fTwtt)GBW#t^B1{+ zFK;7xCdW8}zQ+FH*pG~{0G9jf^~$Q3XJNeHypwIi7WQmO*O8bAnKJoSbz|{ML5D&2ToaKgl zm9tRS4?6X_3n%dZw3Ho}zIxhR*VAIZQ?5&x@lSi| zuSEPu)@xV&4^#2U_s1G6kRwgGJIqfhAd8G)^yq z^7HIs8Gb(xrI(5YORzTTmbqKpJ+P}nGZUJn#Wm`oSOwkwVsjL%R=M@CYVjq5epRua z7WhBwon736@3V`e@b#qYC}xZ9DXD1{O3%jfq*ZFs5?Ye~XS?;cUGTTS-BLVRuzk0% zWuR&9I#)xBYVOncz7*d#;`?m0rxQQ5xX-%^uBFeN$qsDxV~Z(|HoJHAih6_9&dNkaeUZ=Rvh3Ca`Tatu5~vQH}Cy76ZEw2 zl@iCH%%|KYceC53pFV#L<$Vky%7Vt0sJQXJ~$ z6mKosr4>CWm4e%g8&FS=UMT{%y?9uEFPdJt_M63XJ4(><+T!VgQOUbDn;y|~Uqnqy z-6`%Z?!4l?xM!!hHy7u@dN#^bakt)5Tnvj-d~9D@T#lYT1*K*bpLH2xe?NS1Zzdjb zMTXxAKe>ZZkN<5kv%tq|Ln*w5vT%~^LRhcESeXHvZoKk311$*m^Z@kfr`!#=GdIb- zxefP*{T6;d)o=N`SdRBa;$%_@=n+BmnEV62&&T(FLO;zdPIJc;r=qXU!WSYm`B!(C zJG?kU{{N^utT-I{s=Ewvd0D0Au1$K+{EPP->6$Hvcl^5YuIeu21BaAYxY5KV&7??1 zE3VS46qV5y*U~hyx@F{#lx;2lmcAza@Lr@yNAVZsn?}}TcT|2>d9LzYOIypqXi44W z*z__*54$yJ)ds}*7L1~2+_>9?7S@wI=|?+yw*ihMA*-{y;JX0C3?7|h*Tk@8ET-8v^aLbxKzOQciAg=Lx`MSMj>!196 zTg%_H{G#Pw&<`E>yZwitH%?C9mcAD~^0D-D={@`WUZfk+&FPkOG<_x=Pj{iu>iD-V z`q6Jg*>JYzkN&Q&XxRPU&6sg72k@iJchb#>Ps z@`qa0OLy&ny~^U+;@S~=l-twOBu#C(EmJ!k^3K`?)m^m@)vm5>tlhZ}d9X&=4*9Y5 z^I}~4Y3(<)7uvEtf8W-#r%p9(eUO9Oj%|BG+p4zrv|ZMAt(Q%0o7!#;rP{vQ_DI`P zZ9l5+YWro|@2b1nv-Y0$KK{1-RqZFVpNjAAY`>uW^7iXQZf^f-`y=g7wg0sJH^D>u z3olJNTt|H$GPh%K$C4@J@Q!8sl;b;2fxJ1iy8Pbp=a3JYpGP|W82Mz!^)DiuN?(5( zb$qGg8-H3!E!DB(CCcA+yd-fe+__kNTnqV(6_d)}Qkf{*D;1SbV|7XSvX|{{JN{A7qbcsMjZDXk zZ>5fT&)O!S6ks9N3b&KpJT>~d7`|+O_W)9N#6=~2NUD~tm3T9!D?wL znTxgKWI524@=7<#7{dAEDFI|s5FaxSDXN&2vp z*i*jZf_%UQS%X#28uu|H7rG1GTF6)Tka4VuRQ}3Ml+QVp?>m*xV?U#E6V|6UC2h$Z ziu^|f?>|m_C$4!*; z-PCfCn_8~HDnH2e?s_*eN!H_BU_I8_L9TM8+>HIqb(7>c>@BUl#=W@kF!6XVC%95x z>q=>2#ou%TMwYu1AyfTy}a@Ur?Lfmx-B>- z`r~9X_JBd|b)|gNebjws3i+g?+=r7Lm0MjY_v7T`3nI5U%2vn&Mt1v#`I-9} zPWmXTApb0KDbCeC?uBLF8s#$VdM^`&V46ZaGMV~=x;-#>WymQ(q*`?h-o z@_Ui!&V46}=#q4j9GIxQG4WCs6&l64-ceo#5|w!gWxki!Br1m{l)eQ2K*uC`)CKu3 zj`BBA{->k-t&!*4bMBW>w9PFE#QlAgU$_a9oIXjWB@@K`n~_PE=boJ;|I0;5x{@yU z--0A>qPXXyO!lO0Js|O7e!bg8Ne(L|SR@OK%u0gHOiDSy{Z1trEG3lZ_vAD$wZuwi z;w9K7-A3jnUcztp_B}ZW^14z=8=MjxqT+7chit<6w2@@ef2Gg8Q}Svn?z>Y<(hzw= zGNrf|_92s+lANqENnQT@4qN}r(Q6qN}|>U&F>@Vo!)_7rcB zbU^C+kQvF;(vS1=spQCHf=p=J9zP^Y-4CXgWPg!^6O|sEPE8^1@Ae{pD67S})|3(~ z@r2+-#9O@0O)YPCQ_JNpNT^c2Co3R%lDoe&@>%!U2}bLv?Fr?? zQp&QVWMPnr`tVb7yvUM7Bxv{io}5J4Qy(o%;~pj>q;P|Hhod5s; diff --git a/src/java.base/share/classes/jdk/internal/icu/text/NormalizerBase.java b/src/java.base/share/classes/jdk/internal/icu/text/NormalizerBase.java index f2566d9d419b7..afa09a3f5377f 100644 --- a/src/java.base/share/classes/jdk/internal/icu/text/NormalizerBase.java +++ b/src/java.base/share/classes/jdk/internal/icu/text/NormalizerBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -420,10 +420,10 @@ public NormalizerBase(CharacterIterator iter, Mode mode) { * iterator's {@code clone} method does so. * @stable ICU 2.8 */ - public Object clone() { + public NormalizerBase clone() { try { NormalizerBase copy = (NormalizerBase) super.clone(); - copy.text = (UCharacterIterator) text.clone(); + copy.text = text.clone(); copy.mode = mode; copy.options = options; copy.norm2 = norm2; diff --git a/src/java.base/share/classes/jdk/internal/icu/text/UCharacterIterator.java b/src/java.base/share/classes/jdk/internal/icu/text/UCharacterIterator.java index 93978372c3a03..24c547955086b 100644 --- a/src/java.base/share/classes/jdk/internal/icu/text/UCharacterIterator.java +++ b/src/java.base/share/classes/jdk/internal/icu/text/UCharacterIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, 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 @@ -310,8 +310,8 @@ public int moveCodePointIndex(int delta){ * @return copy of this iterator * @stable ICU 2.4 */ - public Object clone() throws CloneNotSupportedException{ - return super.clone(); + public UCharacterIterator clone() throws CloneNotSupportedException{ + return (UCharacterIterator) super.clone(); } } diff --git a/src/java.base/share/classes/jdk/internal/icu/text/UnicodeSet.java b/src/java.base/share/classes/jdk/internal/icu/text/UnicodeSet.java index 6f5919e016bdc..0bc68080e8360 100644 --- a/src/java.base/share/classes/jdk/internal/icu/text/UnicodeSet.java +++ b/src/java.base/share/classes/jdk/internal/icu/text/UnicodeSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -293,7 +293,7 @@ * @author Alan Liu * @stable ICU 2.0 */ -public class UnicodeSet { +public class UnicodeSet implements Cloneable { private static final int LOW = 0x000000; // LOW <= all valid values. ZERO for codepoints private static final int HIGH = 0x110000; // HIGH > all valid values. 10000 for code units. @@ -385,6 +385,18 @@ public UnicodeSet(String pattern) { applyPattern(pattern, null); } + /** + * Return a new set that is equivalent to this one. + * @stable ICU 2.0 + */ + @Override + public UnicodeSet clone() { + if (isFrozen()) { + return this; + } + return new UnicodeSet(this); + } + /** * Make this object represent the same set as other. * @param other a UnicodeSet whose value will be diff --git a/src/java.base/share/classes/jdk/internal/icu/util/VersionInfo.java b/src/java.base/share/classes/jdk/internal/icu/util/VersionInfo.java index b7a52b74ae17a..fa55b9df5af39 100644 --- a/src/java.base/share/classes/jdk/internal/icu/util/VersionInfo.java +++ b/src/java.base/share/classes/jdk/internal/icu/util/VersionInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -54,7 +54,7 @@ public final class VersionInfo * @deprecated This API is ICU internal only. */ @Deprecated - public static final String ICU_DATA_VERSION_PATH = "76b"; + public static final String ICU_DATA_VERSION_PATH = "78b"; // public methods ------------------------------------------------------ diff --git a/src/java.base/share/classes/jdk/internal/util/regex/Grapheme.java b/src/java.base/share/classes/jdk/internal/util/regex/Grapheme.java index 34a69c3045ffe..303a76cb5453c 100644 --- a/src/java.base/share/classes/jdk/internal/util/regex/Grapheme.java +++ b/src/java.base/share/classes/jdk/internal/util/regex/Grapheme.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -35,9 +35,9 @@ public final class Grapheme { *

    * See Unicode Standard Annex #29 Unicode Text Segmentation for the specification * for the extended grapheme cluster boundary rules. The following implementation - * is based on the annex for Unicode version 16.0. + * is based on the annex for Unicode version 17.0. * - * @spec http://www.unicode.org/reports/tr29/tr29-45.html + * @spec http://www.unicode.org/reports/tr29/tr29-47.html * @param src the {@code CharSequence} to be scanned * @param off offset to start looking for the next boundary in the src * @param limit limit offset in the src (exclusive) @@ -283,7 +283,6 @@ static int getType(int cp) { case 0x113D1: case 0x1193F: case 0x11941: - case 0x11A3A: case 0x11A84: case 0x11A85: case 0x11A86: diff --git a/src/java.base/share/classes/jdk/internal/util/regex/IndicConjunctBreak.java.template b/src/java.base/share/classes/jdk/internal/util/regex/IndicConjunctBreak.java.template index fc0e6605ecaa6..149b1dce660e3 100644 --- a/src/java.base/share/classes/jdk/internal/util/regex/IndicConjunctBreak.java.template +++ b/src/java.base/share/classes/jdk/internal/util/regex/IndicConjunctBreak.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -49,8 +49,9 @@ final class IndicConjunctBreak { } static boolean isConsonant(int cp) { - // fast check - Devanagari to Malayalam - if (cp < 0x0900 || cp > 0x0D7F) { + // fast check - return false for code points below + // the Devanagari range (lowest among Indic scripts) + if (cp < 0x0900) { return false; } diff --git a/src/java.base/share/data/unicodedata/Blocks.txt b/src/java.base/share/data/unicodedata/Blocks.txt index 19460657ac915..5c24ab60cb172 100644 --- a/src/java.base/share/data/unicodedata/Blocks.txt +++ b/src/java.base/share/data/unicodedata/Blocks.txt @@ -1,6 +1,6 @@ -# Blocks-16.0.0.txt -# Date: 2024-02-02 -# Copyright (c) 2024 Unicode, Inc. +# Blocks-17.0.0.txt +# Date: 2025-08-01 +# © 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 # @@ -228,6 +228,7 @@ FFF0..FFFF; Specials 108E0..108FF; Hatran 10900..1091F; Phoenician 10920..1093F; Lydian +10940..1095F; Sidetic 10980..1099F; Meroitic Hieroglyphs 109A0..109FF; Meroitic Cursive 10A00..10A5F; Kharoshthi @@ -279,11 +280,13 @@ FFF0..FFFF; Specials 11AB0..11ABF; Unified Canadian Aboriginal Syllabics Extended-A 11AC0..11AFF; Pau Cin Hau 11B00..11B5F; Devanagari Extended-A +11B60..11B7F; Sharada Supplement 11BC0..11BFF; Sunuwar 11C00..11C6F; Bhaiksuki 11C70..11CBF; Marchen 11D00..11D5F; Masaram Gondi 11D60..11DAF; Gunjala Gondi +11DB0..11DEF; Tolong Siki 11EE0..11EFF; Makasar 11F00..11F5F; Kawi 11FB0..11FBF; Lisu Supplement @@ -304,12 +307,14 @@ FFF0..FFFF; Specials 16B00..16B8F; Pahawh Hmong 16D40..16D7F; Kirat Rai 16E40..16E9F; Medefaidrin +16EA0..16EDF; Beria Erfe 16F00..16F9F; Miao 16FE0..16FFF; Ideographic Symbols and Punctuation 17000..187FF; Tangut 18800..18AFF; Tangut Components 18B00..18CFF; Khitan Small Script 18D00..18D7F; Tangut Supplement +18D80..18DFF; Tangut Components Supplement 1AFF0..1AFFF; Kana Extended-B 1B000..1B0FF; Kana Supplement 1B100..1B12F; Kana Extended-A @@ -318,6 +323,7 @@ FFF0..FFFF; Specials 1BC00..1BC9F; Duployan 1BCA0..1BCAF; Shorthand Format Controls 1CC00..1CEBF; Symbols for Legacy Computing Supplement +1CEC0..1CEFF; Miscellaneous Symbols Supplement 1CF00..1CFCF; Znamenny Musical Notation 1D000..1D0FF; Byzantine Musical Symbols 1D100..1D1FF; Musical Symbols @@ -336,6 +342,7 @@ FFF0..FFFF; Specials 1E2C0..1E2FF; Wancho 1E4D0..1E4FF; Nag Mundari 1E5D0..1E5FF; Ol Onal +1E6C0..1E6FF; Tai Yo 1E7E0..1E7FF; Ethiopic Extended-B 1E800..1E8DF; Mende Kikakui 1E900..1E95F; Adlam @@ -367,6 +374,7 @@ FFF0..FFFF; Specials 2F800..2FA1F; CJK Compatibility Ideographs Supplement 30000..3134F; CJK Unified Ideographs Extension G 31350..323AF; CJK Unified Ideographs Extension H +323B0..3347F; CJK Unified Ideographs Extension J E0000..E007F; Tags E0100..E01EF; Variation Selectors Supplement F0000..FFFFF; Supplementary Private Use Area-A diff --git a/src/java.base/share/data/unicodedata/CaseFolding.txt b/src/java.base/share/data/unicodedata/CaseFolding.txt index 345c4f3dd037d..a0b0f07fd6459 100644 --- a/src/java.base/share/data/unicodedata/CaseFolding.txt +++ b/src/java.base/share/data/unicodedata/CaseFolding.txt @@ -1,6 +1,6 @@ -# CaseFolding-16.0.0.txt -# Date: 2024-04-30, 21:48:11 GMT -# © 2024 Unicode®, Inc. +# CaseFolding-17.0.0.txt +# Date: 2025-07-30, 23:54:36 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 # @@ -18,15 +18,15 @@ # The data supports both implementations that require simple case foldings # (where string lengths don't change), and implementations that allow full case folding # (where string lengths may grow). Note that where they can be supported, the -# full case foldings are superior: for example, they allow "MASSE" and "Maße" to match. +# full case foldings are superior: for example, they allow "FUSS" and "Fuß" to match. # # All code points not listed in this file map to themselves. # # NOTE: case folding does not preserve normalization formats! # # For information on case folding, including how to have case folding -# preserve normalization formats, see Section 3.13 Default Case Algorithms in -# The Unicode Standard. +# preserve normalization formats, see the +# "Conformance" / "Default Case Algorithms" section of the core specification. # # ================================================================================ # Format @@ -1243,7 +1243,10 @@ A7C7; C; A7C8; # LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY A7C9; C; A7CA; # LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY A7CB; C; 0264; # LATIN CAPITAL LETTER RAMS HORN A7CC; C; A7CD; # LATIN CAPITAL LETTER S WITH DIAGONAL STROKE +A7CE; C; A7CF; # LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE A7D0; C; A7D1; # LATIN CAPITAL LETTER CLOSED INSULAR G +A7D2; C; A7D3; # LATIN CAPITAL LETTER DOUBLE THORN +A7D4; C; A7D5; # LATIN CAPITAL LETTER DOUBLE WYNN A7D6; C; A7D7; # LATIN CAPITAL LETTER MIDDLE SCOTS S A7D8; C; A7D9; # LATIN CAPITAL LETTER SIGMOID S A7DA; C; A7DB; # LATIN CAPITAL LETTER LAMBDA @@ -1616,6 +1619,31 @@ FF3A; C; FF5A; # FULLWIDTH LATIN CAPITAL LETTER Z 16E5D; C; 16E7D; # MEDEFAIDRIN CAPITAL LETTER O 16E5E; C; 16E7E; # MEDEFAIDRIN CAPITAL LETTER AI 16E5F; C; 16E7F; # MEDEFAIDRIN CAPITAL LETTER Y +16EA0; C; 16EBB; # BERIA ERFE CAPITAL LETTER ARKAB +16EA1; C; 16EBC; # BERIA ERFE CAPITAL LETTER BASIGNA +16EA2; C; 16EBD; # BERIA ERFE CAPITAL LETTER DARBAI +16EA3; C; 16EBE; # BERIA ERFE CAPITAL LETTER EH +16EA4; C; 16EBF; # BERIA ERFE CAPITAL LETTER FITKO +16EA5; C; 16EC0; # BERIA ERFE CAPITAL LETTER GOWAY +16EA6; C; 16EC1; # BERIA ERFE CAPITAL LETTER HIRDEABO +16EA7; C; 16EC2; # BERIA ERFE CAPITAL LETTER I +16EA8; C; 16EC3; # BERIA ERFE CAPITAL LETTER DJAI +16EA9; C; 16EC4; # BERIA ERFE CAPITAL LETTER KOBO +16EAA; C; 16EC5; # BERIA ERFE CAPITAL LETTER LAKKO +16EAB; C; 16EC6; # BERIA ERFE CAPITAL LETTER MERI +16EAC; C; 16EC7; # BERIA ERFE CAPITAL LETTER NINI +16EAD; C; 16EC8; # BERIA ERFE CAPITAL LETTER GNA +16EAE; C; 16EC9; # BERIA ERFE CAPITAL LETTER NGAY +16EAF; C; 16ECA; # BERIA ERFE CAPITAL LETTER OI +16EB0; C; 16ECB; # BERIA ERFE CAPITAL LETTER PI +16EB1; C; 16ECC; # BERIA ERFE CAPITAL LETTER ERIGO +16EB2; C; 16ECD; # BERIA ERFE CAPITAL LETTER ERIGO TAMURA +16EB3; C; 16ECE; # BERIA ERFE CAPITAL LETTER SERI +16EB4; C; 16ECF; # BERIA ERFE CAPITAL LETTER SHEP +16EB5; C; 16ED0; # BERIA ERFE CAPITAL LETTER TATASOUE +16EB6; C; 16ED1; # BERIA ERFE CAPITAL LETTER UI +16EB7; C; 16ED2; # BERIA ERFE CAPITAL LETTER WASSE +16EB8; C; 16ED3; # BERIA ERFE CAPITAL LETTER AY 1E900; C; 1E922; # ADLAM CAPITAL LETTER ALIF 1E901; C; 1E923; # ADLAM CAPITAL LETTER DAALI 1E902; C; 1E924; # ADLAM CAPITAL LETTER LAAM @@ -1651,4 +1679,4 @@ FF3A; C; FF5A; # FULLWIDTH LATIN CAPITAL LETTER Z 1E920; C; 1E942; # ADLAM CAPITAL LETTER KPO 1E921; C; 1E943; # ADLAM CAPITAL LETTER SHA # -# EOF \ No newline at end of file +# EOF diff --git a/src/java.base/share/data/unicodedata/DerivedCoreProperties.txt b/src/java.base/share/data/unicodedata/DerivedCoreProperties.txt index 43d7af85c29f0..f327784bf3956 100644 --- a/src/java.base/share/data/unicodedata/DerivedCoreProperties.txt +++ b/src/java.base/share/data/unicodedata/DerivedCoreProperties.txt @@ -1,6 +1,6 @@ -# DerivedCoreProperties-16.0.0.txt -# Date: 2024-05-31, 18:09:32 GMT -# Copyright (c) 2024 Unicode, Inc. +# DerivedCoreProperties-17.0.0.txt +# Date: 2025-07-30, 23:55:08 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 # @@ -178,6 +178,7 @@ FF5E ; Math # Sm FULLWIDTH TILDE FFE2 ; Math # Sm FULLWIDTH NOT SIGN FFE9..FFEC ; Math # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW 10D8E..10D8F ; Math # Sm [2] GARAY PLUS SIGN..GARAY MINUS SIGN +1CEF0 ; Math # Sm MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR 1D400..1D454 ; Math # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G 1D456..1D49C ; Math # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A 1D49E..1D49F ; Math # L& [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D @@ -253,8 +254,9 @@ FFE9..FFEC ; Math # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A 1EEA5..1EEA9 ; Math # Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH 1EEAB..1EEBB ; Math # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN 1EEF0..1EEF1 ; Math # Sm [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL +1F8D0..1F8D8 ; Math # Sm [9] LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW..LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE -# Total code points: 2312 +# Total code points: 2322 # ================================================ @@ -273,8 +275,8 @@ FFE9..FFEC ; Math # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A 01BC..01BF ; Alphabetic # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3 ; Alphabetic # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..0293 ; Alphabetic # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL -0294 ; Alphabetic # Lo LATIN LETTER GLOTTAL STOP -0295..02AF ; Alphabetic # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0294..0295 ; Alphabetic # Lo [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE +0296..02AF ; Alphabetic # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02C1 ; Alphabetic # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C6..02D1 ; Alphabetic # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON 02E0..02E4 ; Alphabetic # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP @@ -344,7 +346,7 @@ FFE9..FFEC ; Math # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A 0840..0858 ; Alphabetic # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN 0860..086A ; Alphabetic # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA 0870..0887 ; Alphabetic # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT -0889..088E ; Alphabetic # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL +0889..088F ; Alphabetic # Lo [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE 0897 ; Alphabetic # Mn ARABIC PEPET 08A0..08C8 ; Alphabetic # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF 08C9 ; Alphabetic # Lm ARABIC SMALL FARSI YEH @@ -477,7 +479,7 @@ FFE9..FFEC ; Math # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A 0C4A..0C4C ; Alphabetic # Mn [3] TELUGU VOWEL SIGN O..TELUGU VOWEL SIGN AU 0C55..0C56 ; Alphabetic # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK 0C58..0C5A ; Alphabetic # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA -0C5D ; Alphabetic # Lo TELUGU LETTER NAKAARA POLLU +0C5C..0C5D ; Alphabetic # Lo [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU 0C60..0C61 ; Alphabetic # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C62..0C63 ; Alphabetic # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL 0C80 ; Alphabetic # Lo KANNADA SIGN SPACING CANDRABINDU @@ -497,7 +499,7 @@ FFE9..FFEC ; Math # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A 0CCA..0CCB ; Alphabetic # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO 0CCC ; Alphabetic # Mn KANNADA VOWEL SIGN AU 0CD5..0CD6 ; Alphabetic # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK -0CDD..0CDE ; Alphabetic # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA +0CDC..0CDE ; Alphabetic # Lo [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA 0CE0..0CE1 ; Alphabetic # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CE2..0CE3 ; Alphabetic # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL 0CF1..0CF2 ; Alphabetic # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA @@ -833,11 +835,8 @@ A771..A787 ; Alphabetic # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER A788 ; Alphabetic # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A78B..A78E ; Alphabetic # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F ; Alphabetic # Lo LATIN LETTER SINOLOGICAL DOT -A790..A7CD ; Alphabetic # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; Alphabetic # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3 ; Alphabetic # L& LATIN SMALL LETTER DOUBLE THORN -A7D5..A7DC ; Alphabetic # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE -A7F2..A7F4 ; Alphabetic # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A790..A7DC ; Alphabetic # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7F1..A7F4 ; Alphabetic # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F5..A7F6 ; Alphabetic # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H A7F7 ; Alphabetic # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9 ; Alphabetic # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE @@ -1020,6 +1019,7 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 108F4..108F5 ; Alphabetic # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW 10900..10915 ; Alphabetic # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU 10920..10939 ; Alphabetic # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C +10940..10959 ; Alphabetic # Lo [26] SIDETIC LETTER N01..SIDETIC LETTER N26 10980..109B7 ; Alphabetic # Lo [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA 109BE..109BF ; Alphabetic # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN 10A00 ; Alphabetic # Lo KHAROSHTHI LETTER A @@ -1053,7 +1053,9 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 10EAB..10EAC ; Alphabetic # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK 10EB0..10EB1 ; Alphabetic # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE 10EC2..10EC4 ; Alphabetic # Lo [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW -10EFC ; Alphabetic # Mn ARABIC COMBINING ALEF OVERLAY +10EC5 ; Alphabetic # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW +10EC6..10EC7 ; Alphabetic # Lo [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW +10EFA..10EFC ; Alphabetic # Mn [3] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC COMBINING ALEF OVERLAY 10F00..10F1C ; Alphabetic # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL 10F27 ; Alphabetic # Lo OLD SOGDIAN LIGATURE AYIN-DALETH 10F30..10F45 ; Alphabetic # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN @@ -1239,6 +1241,12 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 11A97 ; Alphabetic # Mc SOYOMBO SIGN VISARGA 11A9D ; Alphabetic # Lo SOYOMBO MARK PLUTA 11AB0..11AF8 ; Alphabetic # Lo [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL +11B60 ; Alphabetic # Mn SHARADA VOWEL SIGN OE +11B61 ; Alphabetic # Mc SHARADA VOWEL SIGN OOE +11B62..11B64 ; Alphabetic # Mn [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E +11B65 ; Alphabetic # Mc SHARADA VOWEL SIGN SHORT O +11B66 ; Alphabetic # Mn SHARADA VOWEL SIGN CANDRA E +11B67 ; Alphabetic # Mc SHARADA VOWEL SIGN CANDRA O 11BC0..11BE0 ; Alphabetic # Lo [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO 11C00..11C08 ; Alphabetic # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L 11C0A..11C2E ; Alphabetic # Lo [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA @@ -1274,6 +1282,9 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 11D95 ; Alphabetic # Mn GUNJALA GONDI SIGN ANUSVARA 11D96 ; Alphabetic # Mc GUNJALA GONDI SIGN VISARGA 11D98 ; Alphabetic # Lo GUNJALA GONDI OM +11DB0..11DD8 ; Alphabetic # Lo [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH +11DD9 ; Alphabetic # Lm TOLONG SIKI SIGN SELA +11DDA..11DDB ; Alphabetic # Lo [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA 11EE0..11EF2 ; Alphabetic # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA 11EF3..11EF4 ; Alphabetic # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U 11EF5..11EF6 ; Alphabetic # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O @@ -1311,6 +1322,8 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 16D43..16D6A ; Alphabetic # Lo [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU 16D6B..16D6C ; Alphabetic # Lm [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT 16E40..16E7F ; Alphabetic # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EA0..16EB8 ; Alphabetic # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; Alphabetic # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 16F00..16F4A ; Alphabetic # Lo [75] MIAO LETTER PA..MIAO LETTER RTE 16F4F ; Alphabetic # Mn MIAO SIGN CONSONANT MODIFIER BAR 16F50 ; Alphabetic # Lo MIAO LETTER NASALIZATION @@ -1320,9 +1333,11 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 16FE0..16FE1 ; Alphabetic # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK 16FE3 ; Alphabetic # Lm OLD CHINESE ITERATION MARK 16FF0..16FF1 ; Alphabetic # Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY -17000..187F7 ; Alphabetic # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7 -18800..18CD5 ; Alphabetic # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5 -18CFF..18D08 ; Alphabetic # Lo [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08 +16FF2..16FF3 ; Alphabetic # Lm [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER +16FF4..16FF6 ; Alphabetic # Nl [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS +17000..18CD5 ; Alphabetic # Lo [7382] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CD5 +18CFF..18D1E ; Alphabetic # Lo [32] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D1E +18D80..18DF2 ; Alphabetic # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883 1AFF0..1AFF3 ; Alphabetic # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5 1AFF5..1AFFB ; Alphabetic # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5 1AFFD..1AFFE ; Alphabetic # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8 @@ -1387,6 +1402,17 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 1E4EB ; Alphabetic # Lm NAG MUNDARI SIGN OJOD 1E5D0..1E5ED ; Alphabetic # Lo [30] OL ONAL LETTER O..OL ONAL LETTER EG 1E5F0 ; Alphabetic # Lo OL ONAL SIGN HODDOND +1E6C0..1E6DE ; Alphabetic # Lo [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO +1E6E0..1E6E2 ; Alphabetic # Lo [3] TAI YO LETTER AA..TAI YO LETTER UE +1E6E3 ; Alphabetic # Mn TAI YO SIGN UE +1E6E4..1E6E5 ; Alphabetic # Lo [2] TAI YO LETTER U..TAI YO LETTER AE +1E6E6 ; Alphabetic # Mn TAI YO SIGN AU +1E6E7..1E6ED ; Alphabetic # Lo [7] TAI YO LETTER O..TAI YO LETTER AUE +1E6EE..1E6EF ; Alphabetic # Mn [2] TAI YO SIGN AY..TAI YO SIGN ANG +1E6F0..1E6F4 ; Alphabetic # Lo [5] TAI YO LETTER AN..TAI YO LETTER AP +1E6F5 ; Alphabetic # Mn TAI YO SIGN OM +1E6FE ; Alphabetic # Lo TAI YO SYMBOL MUEANG +1E6FF ; Alphabetic # Lm TAI YO XAM LAI 1E7E0..1E7E6 ; Alphabetic # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO 1E7E8..1E7EB ; Alphabetic # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE 1E7ED..1E7EE ; Alphabetic # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE @@ -1432,16 +1458,15 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 1F150..1F169 ; Alphabetic # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z 1F170..1F189 ; Alphabetic # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z 20000..2A6DF ; Alphabetic # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF -2A700..2B739 ; Alphabetic # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739 -2B740..2B81D ; Alphabetic # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D -2B820..2CEA1 ; Alphabetic # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 +2A700..2B81D ; Alphabetic # Lo [4382] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81D +2B820..2CEAD ; Alphabetic # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD 2CEB0..2EBE0 ; Alphabetic # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 2EBF0..2EE5D ; Alphabetic # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D 2F800..2FA1D ; Alphabetic # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D 30000..3134A ; Alphabetic # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A -31350..323AF ; Alphabetic # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF +31350..33479 ; Alphabetic # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479 -# Total code points: 142759 +# Total code points: 147421 # ================================================ @@ -1595,7 +1620,7 @@ FFDA..FFDC ; Alphabetic # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG 024B ; Lowercase # L& LATIN SMALL LETTER Q WITH HOOK TAIL 024D ; Lowercase # L& LATIN SMALL LETTER R WITH STROKE 024F..0293 ; Lowercase # L& [69] LATIN SMALL LETTER Y WITH STROKE..LATIN SMALL LETTER EZH WITH CURL -0295..02AF ; Lowercase # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0296..02AF ; Lowercase # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02B8 ; Lowercase # Lm [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y 02C0..02C1 ; Lowercase # Lm [2] MODIFIER LETTER GLOTTAL STOP..MODIFIER LETTER REVERSED GLOTTAL STOP 02E0..02E4 ; Lowercase # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP @@ -2073,13 +2098,14 @@ A7C3 ; Lowercase # L& LATIN SMALL LETTER ANGLICANA W A7C8 ; Lowercase # L& LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY A7CA ; Lowercase # L& LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY A7CD ; Lowercase # L& LATIN SMALL LETTER S WITH DIAGONAL STROKE +A7CF ; Lowercase # L& LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE A7D1 ; Lowercase # L& LATIN SMALL LETTER CLOSED INSULAR G A7D3 ; Lowercase # L& LATIN SMALL LETTER DOUBLE THORN A7D5 ; Lowercase # L& LATIN SMALL LETTER DOUBLE WYNN A7D7 ; Lowercase # L& LATIN SMALL LETTER MIDDLE SCOTS S A7D9 ; Lowercase # L& LATIN SMALL LETTER SIGMOID S A7DB ; Lowercase # L& LATIN SMALL LETTER LAMBDA -A7F2..A7F4 ; Lowercase # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A7F1..A7F4 ; Lowercase # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F6 ; Lowercase # L& LATIN SMALL LETTER REVERSED HALF H A7F8..A7F9 ; Lowercase # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE A7FA ; Lowercase # L& LATIN LETTER SMALL CAPITAL TURNED M @@ -2105,6 +2131,7 @@ FF41..FF5A ; Lowercase # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L 10D70..10D85 ; Lowercase # L& [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA 118C0..118DF ; Lowercase # L& [32] WARANG CITI SMALL LETTER NGAA..WARANG CITI SMALL LETTER VIYO 16E60..16E7F ; Lowercase # L& [32] MEDEFAIDRIN SMALL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EBB..16ED3 ; Lowercase # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 1D41A..1D433 ; Lowercase # L& [26] MATHEMATICAL BOLD SMALL A..MATHEMATICAL BOLD SMALL Z 1D44E..1D454 ; Lowercase # L& [7] MATHEMATICAL ITALIC SMALL A..MATHEMATICAL ITALIC SMALL G 1D456..1D467 ; Lowercase # L& [18] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL ITALIC SMALL Z @@ -2139,7 +2166,7 @@ FF41..FF5A ; Lowercase # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L 1E030..1E06D ; Lowercase # Lm [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE 1E922..1E943 ; Lowercase # L& [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA -# Total code points: 2569 +# Total code points: 2595 # ================================================ @@ -2750,7 +2777,10 @@ A7C2 ; Uppercase # L& LATIN CAPITAL LETTER ANGLICANA W A7C4..A7C7 ; Uppercase # L& [4] LATIN CAPITAL LETTER C WITH PALATAL HOOK..LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY A7C9 ; Uppercase # L& LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY A7CB..A7CC ; Uppercase # L& [2] LATIN CAPITAL LETTER RAMS HORN..LATIN CAPITAL LETTER S WITH DIAGONAL STROKE +A7CE ; Uppercase # L& LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE A7D0 ; Uppercase # L& LATIN CAPITAL LETTER CLOSED INSULAR G +A7D2 ; Uppercase # L& LATIN CAPITAL LETTER DOUBLE THORN +A7D4 ; Uppercase # L& LATIN CAPITAL LETTER DOUBLE WYNN A7D6 ; Uppercase # L& LATIN CAPITAL LETTER MIDDLE SCOTS S A7D8 ; Uppercase # L& LATIN CAPITAL LETTER SIGMOID S A7DA ; Uppercase # L& LATIN CAPITAL LETTER LAMBDA @@ -2767,6 +2797,7 @@ FF21..FF3A ; Uppercase # L& [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH 10D50..10D65 ; Uppercase # L& [22] GARAY CAPITAL LETTER A..GARAY CAPITAL LETTER OLD NA 118A0..118BF ; Uppercase # L& [32] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI CAPITAL LETTER VIYO 16E40..16E5F ; Uppercase # L& [32] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN CAPITAL LETTER Y +16EA0..16EB8 ; Uppercase # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY 1D400..1D419 ; Uppercase # L& [26] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL BOLD CAPITAL Z 1D434..1D44D ; Uppercase # L& [26] MATHEMATICAL ITALIC CAPITAL A..MATHEMATICAL ITALIC CAPITAL Z 1D468..1D481 ; Uppercase # L& [26] MATHEMATICAL BOLD ITALIC CAPITAL A..MATHEMATICAL BOLD ITALIC CAPITAL Z @@ -2803,7 +2834,7 @@ FF21..FF3A ; Uppercase # L& [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH 1F150..1F169 ; Uppercase # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z 1F170..1F189 ; Uppercase # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z -# Total code points: 1978 +# Total code points: 2006 # ================================================ @@ -2821,7 +2852,7 @@ FF21..FF3A ; Uppercase # L& [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH 00F8..01BA ; Cased # L& [195] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL 01BC..01BF ; Cased # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C4..0293 ; Cased # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL -0295..02AF ; Cased # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0296..02AF ; Cased # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02B8 ; Cased # Lm [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y 02C0..02C1 ; Cased # Lm [2] MODIFIER LETTER GLOTTAL STOP..MODIFIER LETTER REVERSED GLOTTAL STOP 02E0..02E4 ; Cased # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP @@ -2911,11 +2942,8 @@ A722..A76F ; Cased # L& [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN A770 ; Cased # Lm MODIFIER LETTER US A771..A787 ; Cased # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T A78B..A78E ; Cased # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT -A790..A7CD ; Cased # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; Cased # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3 ; Cased # L& LATIN SMALL LETTER DOUBLE THORN -A7D5..A7DC ; Cased # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE -A7F2..A7F4 ; Cased # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A790..A7DC ; Cased # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7F1..A7F4 ; Cased # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F5..A7F6 ; Cased # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H A7F8..A7F9 ; Cased # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE A7FA ; Cased # L& LATIN LETTER SMALL CAPITAL TURNED M @@ -2949,6 +2977,8 @@ FF41..FF5A ; Cased # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN 10D70..10D85 ; Cased # L& [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA 118A0..118DF ; Cased # L& [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO 16E40..16E7F ; Cased # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EA0..16EB8 ; Cased # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; Cased # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 1D400..1D454 ; Cased # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G 1D456..1D49C ; Cased # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A 1D49E..1D49F ; Cased # L& [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D @@ -2988,7 +3018,7 @@ FF41..FF5A ; Cased # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN 1F150..1F169 ; Cased # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z 1F170..1F189 ; Cased # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z -# Total code points: 4578 +# Total code points: 4632 # ================================================ @@ -3194,7 +3224,8 @@ FF41..FF5A ; Cased # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN 1AA7 ; Case_Ignorable # Lm TAI THAM SIGN MAI YAMOK 1AB0..1ABD ; Case_Ignorable # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW 1ABE ; Case_Ignorable # Me COMBINING PARENTHESES OVERLAY -1ABF..1ACE ; Case_Ignorable # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T +1ABF..1ADD ; Case_Ignorable # Mn [31] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOT-AND-RING BELOW +1AE0..1AEB ; Case_Ignorable # Mn [12] COMBINING LEFT TACK ABOVE..COMBINING DOUBLE RIGHTWARDS ARROW ABOVE 1B00..1B03 ; Case_Ignorable # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 1B34 ; Case_Ignorable # Mn BALINESE SIGN REREKAN 1B36..1B3A ; Case_Ignorable # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA @@ -3274,7 +3305,7 @@ A720..A721 ; Case_Ignorable # Sk [2] MODIFIER LETTER STRESS AND HIGH TONE.. A770 ; Case_Ignorable # Lm MODIFIER LETTER US A788 ; Case_Ignorable # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A789..A78A ; Case_Ignorable # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN -A7F2..A7F4 ; Case_Ignorable # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A7F1..A7F4 ; Case_Ignorable # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F8..A7F9 ; Case_Ignorable # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE A802 ; Case_Ignorable # Mn SYLOTI NAGRI SIGN DVISVARA A806 ; Case_Ignorable # Mn SYLOTI NAGRI SIGN HASANTA @@ -3350,7 +3381,8 @@ FFF9..FFFB ; Case_Ignorable # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI 10D69..10D6D ; Case_Ignorable # Mn [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK 10D6F ; Case_Ignorable # Lm GARAY REDUPLICATION MARK 10EAB..10EAC ; Case_Ignorable # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK -10EFC..10EFF ; Case_Ignorable # Mn [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA +10EC5 ; Case_Ignorable # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW +10EFA..10EFF ; Case_Ignorable # Mn [6] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC SMALL LOW WORD MADDA 10F46..10F50 ; Case_Ignorable # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW 10F82..10F85 ; Case_Ignorable # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW 11001 ; Case_Ignorable # Mn BRAHMI SIGN ANUSVARA @@ -3427,6 +3459,9 @@ FFF9..FFFB ; Case_Ignorable # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI 11A59..11A5B ; Case_Ignorable # Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK 11A8A..11A96 ; Case_Ignorable # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA 11A98..11A99 ; Case_Ignorable # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER +11B60 ; Case_Ignorable # Mn SHARADA VOWEL SIGN OE +11B62..11B64 ; Case_Ignorable # Mn [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E +11B66 ; Case_Ignorable # Mn SHARADA VOWEL SIGN CANDRA E 11C30..11C36 ; Case_Ignorable # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L 11C38..11C3D ; Case_Ignorable # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA 11C3F ; Case_Ignorable # Mn BHAIKSUKI SIGN VIRAMA @@ -3442,6 +3477,7 @@ FFF9..FFFB ; Case_Ignorable # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI 11D90..11D91 ; Case_Ignorable # Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI 11D95 ; Case_Ignorable # Mn GUNJALA GONDI SIGN ANUSVARA 11D97 ; Case_Ignorable # Mn GUNJALA GONDI VIRAMA +11DD9 ; Case_Ignorable # Lm TOLONG SIKI SIGN SELA 11EF3..11EF4 ; Case_Ignorable # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U 11F00..11F01 ; Case_Ignorable # Mn [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA 11F36..11F3A ; Case_Ignorable # Mn [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R @@ -3464,6 +3500,7 @@ FFF9..FFFB ; Case_Ignorable # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI 16FE0..16FE1 ; Case_Ignorable # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK 16FE3 ; Case_Ignorable # Lm OLD CHINESE ITERATION MARK 16FE4 ; Case_Ignorable # Mn KHITAN SMALL SCRIPT FILLER +16FF2..16FF3 ; Case_Ignorable # Lm [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER 1AFF0..1AFF3 ; Case_Ignorable # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5 1AFF5..1AFFB ; Case_Ignorable # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5 1AFFD..1AFFE ; Case_Ignorable # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8 @@ -3497,6 +3534,11 @@ FFF9..FFFB ; Case_Ignorable # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI 1E4EB ; Case_Ignorable # Lm NAG MUNDARI SIGN OJOD 1E4EC..1E4EF ; Case_Ignorable # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH 1E5EE..1E5EF ; Case_Ignorable # Mn [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR +1E6E3 ; Case_Ignorable # Mn TAI YO SIGN UE +1E6E6 ; Case_Ignorable # Mn TAI YO SIGN AU +1E6EE..1E6EF ; Case_Ignorable # Mn [2] TAI YO SIGN AY..TAI YO SIGN ANG +1E6F5 ; Case_Ignorable # Mn TAI YO SIGN OM +1E6FF ; Case_Ignorable # Lm TAI YO XAM LAI 1E8D0..1E8D6 ; Case_Ignorable # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS 1E944..1E94A ; Case_Ignorable # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA 1E94B ; Case_Ignorable # Lm ADLAM NASALIZATION MARK @@ -3505,13 +3547,14 @@ E0001 ; Case_Ignorable # Cf LANGUAGE TAG E0020..E007F ; Case_Ignorable # Cf [96] TAG SPACE..CANCEL TAG E0100..E01EF ; Case_Ignorable # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 -# Total code points: 2749 +# Total code points: 2794 # ================================================ # Derived Property: Changes_When_Lowercased (CWL) # Characters whose normalized forms are not stable under a toLowercase mapping. -# For more information, see D139 in Section 3.13, "Default Case Algorithms". +# For more information, see the definition of "isLowercase(X)" +# in the "Conformance" / "Default Case Algorithms" section of the core specification. # Changes_When_Lowercased(X) is true when toLowercase(toNFD(X)) != toNFD(X) 0041..005A ; Changes_When_Lowercased # L& [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z @@ -4110,7 +4153,10 @@ A7C2 ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER ANGLICAN A7C4..A7C7 ; Changes_When_Lowercased # L& [4] LATIN CAPITAL LETTER C WITH PALATAL HOOK..LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY A7C9 ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY A7CB..A7CC ; Changes_When_Lowercased # L& [2] LATIN CAPITAL LETTER RAMS HORN..LATIN CAPITAL LETTER S WITH DIAGONAL STROKE +A7CE ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE A7D0 ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER CLOSED INSULAR G +A7D2 ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER DOUBLE THORN +A7D4 ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER DOUBLE WYNN A7D6 ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER MIDDLE SCOTS S A7D8 ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER SIGMOID S A7DA ; Changes_When_Lowercased # L& LATIN CAPITAL LETTER LAMBDA @@ -4127,15 +4173,17 @@ FF21..FF3A ; Changes_When_Lowercased # L& [26] FULLWIDTH LATIN CAPITAL LETTE 10D50..10D65 ; Changes_When_Lowercased # L& [22] GARAY CAPITAL LETTER A..GARAY CAPITAL LETTER OLD NA 118A0..118BF ; Changes_When_Lowercased # L& [32] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI CAPITAL LETTER VIYO 16E40..16E5F ; Changes_When_Lowercased # L& [32] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN CAPITAL LETTER Y +16EA0..16EB8 ; Changes_When_Lowercased # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY 1E900..1E921 ; Changes_When_Lowercased # L& [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA -# Total code points: 1460 +# Total code points: 1488 # ================================================ # Derived Property: Changes_When_Uppercased (CWU) # Characters whose normalized forms are not stable under a toUppercase mapping. -# For more information, see D140 in Section 3.13, "Default Case Algorithms". +# For more information, see the definition of "isUppercase(X)" +# in the "Conformance" / "Default Case Algorithms" section of the core specification. # Changes_When_Uppercased(X) is true when toUppercase(toNFD(X)) != toNFD(X) 0061..007A ; Changes_When_Uppercased # L& [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z @@ -4747,7 +4795,10 @@ A7C3 ; Changes_When_Uppercased # L& LATIN SMALL LETTER ANGLICANA A7C8 ; Changes_When_Uppercased # L& LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY A7CA ; Changes_When_Uppercased # L& LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY A7CD ; Changes_When_Uppercased # L& LATIN SMALL LETTER S WITH DIAGONAL STROKE +A7CF ; Changes_When_Uppercased # L& LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE A7D1 ; Changes_When_Uppercased # L& LATIN SMALL LETTER CLOSED INSULAR G +A7D3 ; Changes_When_Uppercased # L& LATIN SMALL LETTER DOUBLE THORN +A7D5 ; Changes_When_Uppercased # L& LATIN SMALL LETTER DOUBLE WYNN A7D7 ; Changes_When_Uppercased # L& LATIN SMALL LETTER MIDDLE SCOTS S A7D9 ; Changes_When_Uppercased # L& LATIN SMALL LETTER SIGMOID S A7DB ; Changes_When_Uppercased # L& LATIN SMALL LETTER LAMBDA @@ -4767,15 +4818,17 @@ FF41..FF5A ; Changes_When_Uppercased # L& [26] FULLWIDTH LATIN SMALL LETTER 10D70..10D85 ; Changes_When_Uppercased # L& [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA 118C0..118DF ; Changes_When_Uppercased # L& [32] WARANG CITI SMALL LETTER NGAA..WARANG CITI SMALL LETTER VIYO 16E60..16E7F ; Changes_When_Uppercased # L& [32] MEDEFAIDRIN SMALL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EBB..16ED3 ; Changes_When_Uppercased # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 1E922..1E943 ; Changes_When_Uppercased # L& [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA -# Total code points: 1552 +# Total code points: 1580 # ================================================ # Derived Property: Changes_When_Titlecased (CWT) # Characters whose normalized forms are not stable under a toTitlecase mapping. -# For more information, see D141 in Section 3.13, "Default Case Algorithms". +# For more information, see the definition of "isTitlecase(X)" +# in the "Conformance" / "Default Case Algorithms" section of the core specification. # Changes_When_Titlecased(X) is true when toTitlecase(toNFD(X)) != toNFD(X) 0061..007A ; Changes_When_Titlecased # L& [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z @@ -5386,7 +5439,10 @@ A7C3 ; Changes_When_Titlecased # L& LATIN SMALL LETTER ANGLICANA A7C8 ; Changes_When_Titlecased # L& LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY A7CA ; Changes_When_Titlecased # L& LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY A7CD ; Changes_When_Titlecased # L& LATIN SMALL LETTER S WITH DIAGONAL STROKE +A7CF ; Changes_When_Titlecased # L& LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE A7D1 ; Changes_When_Titlecased # L& LATIN SMALL LETTER CLOSED INSULAR G +A7D3 ; Changes_When_Titlecased # L& LATIN SMALL LETTER DOUBLE THORN +A7D5 ; Changes_When_Titlecased # L& LATIN SMALL LETTER DOUBLE WYNN A7D7 ; Changes_When_Titlecased # L& LATIN SMALL LETTER MIDDLE SCOTS S A7D9 ; Changes_When_Titlecased # L& LATIN SMALL LETTER SIGMOID S A7DB ; Changes_When_Titlecased # L& LATIN SMALL LETTER LAMBDA @@ -5406,15 +5462,17 @@ FF41..FF5A ; Changes_When_Titlecased # L& [26] FULLWIDTH LATIN SMALL LETTER 10D70..10D85 ; Changes_When_Titlecased # L& [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA 118C0..118DF ; Changes_When_Titlecased # L& [32] WARANG CITI SMALL LETTER NGAA..WARANG CITI SMALL LETTER VIYO 16E60..16E7F ; Changes_When_Titlecased # L& [32] MEDEFAIDRIN SMALL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EBB..16ED3 ; Changes_When_Titlecased # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 1E922..1E943 ; Changes_When_Titlecased # L& [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA -# Total code points: 1479 +# Total code points: 1507 # ================================================ # Derived Property: Changes_When_Casefolded (CWCF) # Characters whose normalized forms are not stable under case folding. -# For more information, see D142 in Section 3.13, "Default Case Algorithms". +# For more information, see the definition of "isCasefolded(X)" +# in the "Conformance" / "Default Case Algorithms" section of the core specification. # Changes_When_Casefolded(X) is true when toCasefold(toNFD(X)) != toNFD(X) 0041..005A ; Changes_When_Casefolded # L& [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z @@ -6022,7 +6080,10 @@ A7C2 ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER ANGLICAN A7C4..A7C7 ; Changes_When_Casefolded # L& [4] LATIN CAPITAL LETTER C WITH PALATAL HOOK..LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY A7C9 ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY A7CB..A7CC ; Changes_When_Casefolded # L& [2] LATIN CAPITAL LETTER RAMS HORN..LATIN CAPITAL LETTER S WITH DIAGONAL STROKE +A7CE ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE A7D0 ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER CLOSED INSULAR G +A7D2 ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER DOUBLE THORN +A7D4 ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER DOUBLE WYNN A7D6 ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER MIDDLE SCOTS S A7D8 ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER SIGMOID S A7DA ; Changes_When_Casefolded # L& LATIN CAPITAL LETTER LAMBDA @@ -6042,15 +6103,17 @@ FF21..FF3A ; Changes_When_Casefolded # L& [26] FULLWIDTH LATIN CAPITAL LETTE 10D50..10D65 ; Changes_When_Casefolded # L& [22] GARAY CAPITAL LETTER A..GARAY CAPITAL LETTER OLD NA 118A0..118BF ; Changes_When_Casefolded # L& [32] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI CAPITAL LETTER VIYO 16E40..16E5F ; Changes_When_Casefolded # L& [32] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN CAPITAL LETTER Y +16EA0..16EB8 ; Changes_When_Casefolded # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY 1E900..1E921 ; Changes_When_Casefolded # L& [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA -# Total code points: 1533 +# Total code points: 1561 # ================================================ # Derived Property: Changes_When_Casemapped (CWCM) # Characters whose normalized forms are not stable under case mapping. -# For more information, see D143 in Section 3.13, "Default Case Algorithms". +# For more information, see the definition of "isCased(X)" +# in the "Conformance" / "Default Case Algorithms" section of the core specification. # Changes_When_Casemapped(X) is true when CWL(X), or CWT(X), or CWU(X) 0041..005A ; Changes_When_Casemapped # L& [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z @@ -6156,9 +6219,7 @@ A779..A787 ; Changes_When_Casemapped # L& [15] LATIN CAPITAL LETTER INSULAR A78B..A78D ; Changes_When_Casemapped # L& [3] LATIN CAPITAL LETTER SALTILLO..LATIN CAPITAL LETTER TURNED H A790..A794 ; Changes_When_Casemapped # L& [5] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER C WITH PALATAL HOOK A796..A7AE ; Changes_When_Casemapped # L& [25] LATIN CAPITAL LETTER B WITH FLOURISH..LATIN CAPITAL LETTER SMALL CAPITAL I -A7B0..A7CD ; Changes_When_Casemapped # L& [30] LATIN CAPITAL LETTER TURNED K..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; Changes_When_Casemapped # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D6..A7DC ; Changes_When_Casemapped # L& [7] LATIN CAPITAL LETTER MIDDLE SCOTS S..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7B0..A7DC ; Changes_When_Casemapped # L& [45] LATIN CAPITAL LETTER TURNED K..LATIN CAPITAL LETTER LAMBDA WITH STROKE A7F5..A7F6 ; Changes_When_Casemapped # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H AB53 ; Changes_When_Casemapped # L& LATIN SMALL LETTER CHI AB70..ABBF ; Changes_When_Casemapped # L& [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA @@ -6183,9 +6244,11 @@ FF41..FF5A ; Changes_When_Casemapped # L& [26] FULLWIDTH LATIN SMALL LETTER 10D70..10D85 ; Changes_When_Casemapped # L& [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA 118A0..118DF ; Changes_When_Casemapped # L& [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO 16E40..16E7F ; Changes_When_Casemapped # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EA0..16EB8 ; Changes_When_Casemapped # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; Changes_When_Casemapped # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 1E900..1E943 ; Changes_When_Casemapped # L& [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA -# Total code points: 2981 +# Total code points: 3037 # ================================================ @@ -6210,8 +6273,8 @@ FF41..FF5A ; Changes_When_Casemapped # L& [26] FULLWIDTH LATIN SMALL LETTER 01BC..01BF ; ID_Start # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3 ; ID_Start # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..0293 ; ID_Start # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL -0294 ; ID_Start # Lo LATIN LETTER GLOTTAL STOP -0295..02AF ; ID_Start # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0294..0295 ; ID_Start # Lo [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE +0296..02AF ; ID_Start # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02C1 ; ID_Start # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C6..02D1 ; ID_Start # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON 02E0..02E4 ; ID_Start # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP @@ -6259,7 +6322,7 @@ FF41..FF5A ; Changes_When_Casemapped # L& [26] FULLWIDTH LATIN SMALL LETTER 0840..0858 ; ID_Start # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN 0860..086A ; ID_Start # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA 0870..0887 ; ID_Start # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT -0889..088E ; ID_Start # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL +0889..088F ; ID_Start # Lo [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE 08A0..08C8 ; ID_Start # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF 08C9 ; ID_Start # Lm ARABIC SMALL FARSI YEH 0904..0939 ; ID_Start # Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA @@ -6327,7 +6390,7 @@ FF41..FF5A ; Changes_When_Casemapped # L& [26] FULLWIDTH LATIN SMALL LETTER 0C2A..0C39 ; ID_Start # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA 0C3D ; ID_Start # Lo TELUGU SIGN AVAGRAHA 0C58..0C5A ; ID_Start # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA -0C5D ; ID_Start # Lo TELUGU LETTER NAKAARA POLLU +0C5C..0C5D ; ID_Start # Lo [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU 0C60..0C61 ; ID_Start # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C80 ; ID_Start # Lo KANNADA SIGN SPACING CANDRABINDU 0C85..0C8C ; ID_Start # Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L @@ -6336,7 +6399,7 @@ FF41..FF5A ; Changes_When_Casemapped # L& [26] FULLWIDTH LATIN SMALL LETTER 0CAA..0CB3 ; ID_Start # Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA 0CB5..0CB9 ; ID_Start # Lo [5] KANNADA LETTER VA..KANNADA LETTER HA 0CBD ; ID_Start # Lo KANNADA SIGN AVAGRAHA -0CDD..0CDE ; ID_Start # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA +0CDC..0CDE ; ID_Start # Lo [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA 0CE0..0CE1 ; ID_Start # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CF1..0CF2 ; ID_Start # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA 0D04..0D0C ; ID_Start # Lo [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L @@ -6561,11 +6624,8 @@ A771..A787 ; ID_Start # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER I A788 ; ID_Start # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A78B..A78E ; ID_Start # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F ; ID_Start # Lo LATIN LETTER SINOLOGICAL DOT -A790..A7CD ; ID_Start # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; ID_Start # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3 ; ID_Start # L& LATIN SMALL LETTER DOUBLE THORN -A7D5..A7DC ; ID_Start # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE -A7F2..A7F4 ; ID_Start # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A790..A7DC ; ID_Start # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7F1..A7F4 ; ID_Start # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F5..A7F6 ; ID_Start # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H A7F7 ; ID_Start # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9 ; ID_Start # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE @@ -6702,6 +6762,7 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 108F4..108F5 ; ID_Start # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW 10900..10915 ; ID_Start # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU 10920..10939 ; ID_Start # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C +10940..10959 ; ID_Start # Lo [26] SIDETIC LETTER N01..SIDETIC LETTER N26 10980..109B7 ; ID_Start # Lo [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA 109BE..109BF ; ID_Start # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN 10A00 ; ID_Start # Lo KHAROSHTHI LETTER A @@ -6729,6 +6790,8 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 10E80..10EA9 ; ID_Start # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET 10EB0..10EB1 ; ID_Start # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE 10EC2..10EC4 ; ID_Start # Lo [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW +10EC5 ; ID_Start # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW +10EC6..10EC7 ; ID_Start # Lo [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW 10F00..10F1C ; ID_Start # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL 10F27 ; ID_Start # Lo OLD SOGDIAN LIGATURE AYIN-DALETH 10F30..10F45 ; ID_Start # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN @@ -6821,6 +6884,9 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 11D67..11D68 ; ID_Start # Lo [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI 11D6A..11D89 ; ID_Start # Lo [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA 11D98 ; ID_Start # Lo GUNJALA GONDI OM +11DB0..11DD8 ; ID_Start # Lo [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH +11DD9 ; ID_Start # Lm TOLONG SIKI SIGN SELA +11DDA..11DDB ; ID_Start # Lo [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA 11EE0..11EF2 ; ID_Start # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA 11F02 ; ID_Start # Lo KAWI SIGN REPHA 11F04..11F10 ; ID_Start # Lo [13] KAWI LETTER A..KAWI LETTER O @@ -6847,14 +6913,18 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 16D43..16D6A ; ID_Start # Lo [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU 16D6B..16D6C ; ID_Start # Lm [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT 16E40..16E7F ; ID_Start # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EA0..16EB8 ; ID_Start # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; ID_Start # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 16F00..16F4A ; ID_Start # Lo [75] MIAO LETTER PA..MIAO LETTER RTE 16F50 ; ID_Start # Lo MIAO LETTER NASALIZATION 16F93..16F9F ; ID_Start # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8 16FE0..16FE1 ; ID_Start # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK 16FE3 ; ID_Start # Lm OLD CHINESE ITERATION MARK -17000..187F7 ; ID_Start # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7 -18800..18CD5 ; ID_Start # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5 -18CFF..18D08 ; ID_Start # Lo [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08 +16FF2..16FF3 ; ID_Start # Lm [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER +16FF4..16FF6 ; ID_Start # Nl [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS +17000..18CD5 ; ID_Start # Lo [7382] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CD5 +18CFF..18D1E ; ID_Start # Lo [32] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D1E +18D80..18DF2 ; ID_Start # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883 1AFF0..1AFF3 ; ID_Start # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5 1AFF5..1AFFB ; ID_Start # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5 1AFFD..1AFFE ; ID_Start # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8 @@ -6912,6 +6982,13 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 1E4EB ; ID_Start # Lm NAG MUNDARI SIGN OJOD 1E5D0..1E5ED ; ID_Start # Lo [30] OL ONAL LETTER O..OL ONAL LETTER EG 1E5F0 ; ID_Start # Lo OL ONAL SIGN HODDOND +1E6C0..1E6DE ; ID_Start # Lo [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO +1E6E0..1E6E2 ; ID_Start # Lo [3] TAI YO LETTER AA..TAI YO LETTER UE +1E6E4..1E6E5 ; ID_Start # Lo [2] TAI YO LETTER U..TAI YO LETTER AE +1E6E7..1E6ED ; ID_Start # Lo [7] TAI YO LETTER O..TAI YO LETTER AUE +1E6F0..1E6F4 ; ID_Start # Lo [5] TAI YO LETTER AN..TAI YO LETTER AP +1E6FE ; ID_Start # Lo TAI YO SYMBOL MUEANG +1E6FF ; ID_Start # Lm TAI YO XAM LAI 1E7E0..1E7E6 ; ID_Start # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO 1E7E8..1E7EB ; ID_Start # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE 1E7ED..1E7EE ; ID_Start # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE @@ -6953,16 +7030,15 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 1EEA5..1EEA9 ; ID_Start # Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH 1EEAB..1EEBB ; ID_Start # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN 20000..2A6DF ; ID_Start # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF -2A700..2B739 ; ID_Start # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739 -2B740..2B81D ; ID_Start # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D -2B820..2CEA1 ; ID_Start # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 +2A700..2B81D ; ID_Start # Lo [4382] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81D +2B820..2CEAD ; ID_Start # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD 2CEB0..2EBE0 ; ID_Start # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 2EBF0..2EE5D ; ID_Start # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D 2F800..2FA1D ; ID_Start # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D 30000..3134A ; ID_Start # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A -31350..323AF ; ID_Start # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF +31350..33479 ; ID_Start # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479 -# Total code points: 141269 +# Total code points: 145916 # ================================================ @@ -6991,8 +7067,8 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 01BC..01BF ; ID_Continue # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3 ; ID_Continue # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..0293 ; ID_Continue # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL -0294 ; ID_Continue # Lo LATIN LETTER GLOTTAL STOP -0295..02AF ; ID_Continue # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0294..0295 ; ID_Continue # Lo [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE +0296..02AF ; ID_Continue # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02C1 ; ID_Continue # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C6..02D1 ; ID_Continue # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON 02E0..02E4 ; ID_Continue # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP @@ -7068,7 +7144,7 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 0859..085B ; ID_Continue # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK 0860..086A ; ID_Continue # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA 0870..0887 ; ID_Continue # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT -0889..088E ; ID_Continue # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL +0889..088F ; ID_Continue # Lo [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE 0897..089F ; ID_Continue # Mn [9] ARABIC PEPET..ARABIC HALF MADDA OVER MADDA 08A0..08C8 ; ID_Continue # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF 08C9 ; ID_Continue # Lm ARABIC SMALL FARSI YEH @@ -7218,7 +7294,7 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 0C4A..0C4D ; ID_Continue # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA 0C55..0C56 ; ID_Continue # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK 0C58..0C5A ; ID_Continue # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA -0C5D ; ID_Continue # Lo TELUGU LETTER NAKAARA POLLU +0C5C..0C5D ; ID_Continue # Lo [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU 0C60..0C61 ; ID_Continue # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C62..0C63 ; ID_Continue # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL 0C66..0C6F ; ID_Continue # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE @@ -7240,7 +7316,7 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 0CCA..0CCB ; ID_Continue # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO 0CCC..0CCD ; ID_Continue # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA 0CD5..0CD6 ; ID_Continue # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK -0CDD..0CDE ; ID_Continue # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA +0CDC..0CDE ; ID_Continue # Lo [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA 0CE0..0CE1 ; ID_Continue # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CE2..0CE3 ; ID_Continue # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL 0CE6..0CEF ; ID_Continue # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE @@ -7457,7 +7533,8 @@ FFDA..FFDC ; ID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL 1A90..1A99 ; ID_Continue # Nd [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE 1AA7 ; ID_Continue # Lm TAI THAM SIGN MAI YAMOK 1AB0..1ABD ; ID_Continue # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW -1ABF..1ACE ; ID_Continue # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T +1ABF..1ADD ; ID_Continue # Mn [31] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOT-AND-RING BELOW +1AE0..1AEB ; ID_Continue # Mn [12] COMBINING LEFT TACK ABOVE..COMBINING DOUBLE RIGHTWARDS ARROW ABOVE 1B00..1B03 ; ID_Continue # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 1B04 ; ID_Continue # Mc BALINESE SIGN BISAH 1B05..1B33 ; ID_Continue # Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA @@ -7646,11 +7723,8 @@ A771..A787 ; ID_Continue # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTE A788 ; ID_Continue # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A78B..A78E ; ID_Continue # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F ; ID_Continue # Lo LATIN LETTER SINOLOGICAL DOT -A790..A7CD ; ID_Continue # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; ID_Continue # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3 ; ID_Continue # L& LATIN SMALL LETTER DOUBLE THORN -A7D5..A7DC ; ID_Continue # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE -A7F2..A7F4 ; ID_Continue # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A790..A7DC ; ID_Continue # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7F1..A7F4 ; ID_Continue # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F5..A7F6 ; ID_Continue # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H A7F7 ; ID_Continue # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9 ; ID_Continue # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE @@ -7857,6 +7931,7 @@ FFDA..FFDC ; ID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN 108F4..108F5 ; ID_Continue # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW 10900..10915 ; ID_Continue # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU 10920..10939 ; ID_Continue # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C +10940..10959 ; ID_Continue # Lo [26] SIDETIC LETTER N01..SIDETIC LETTER N26 10980..109B7 ; ID_Continue # Lo [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA 109BE..109BF ; ID_Continue # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN 10A00 ; ID_Continue # Lo KHAROSHTHI LETTER A @@ -7895,7 +7970,9 @@ FFDA..FFDC ; ID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN 10EAB..10EAC ; ID_Continue # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK 10EB0..10EB1 ; ID_Continue # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE 10EC2..10EC4 ; ID_Continue # Lo [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW -10EFC..10EFF ; ID_Continue # Mn [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA +10EC5 ; ID_Continue # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW +10EC6..10EC7 ; ID_Continue # Lo [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW +10EFA..10EFF ; ID_Continue # Mn [6] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC SMALL LOW WORD MADDA 10F00..10F1C ; ID_Continue # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL 10F27 ; ID_Continue # Lo OLD SOGDIAN LIGATURE AYIN-DALETH 10F30..10F45 ; ID_Continue # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN @@ -8122,6 +8199,12 @@ FFDA..FFDC ; ID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN 11A98..11A99 ; ID_Continue # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER 11A9D ; ID_Continue # Lo SOYOMBO MARK PLUTA 11AB0..11AF8 ; ID_Continue # Lo [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL +11B60 ; ID_Continue # Mn SHARADA VOWEL SIGN OE +11B61 ; ID_Continue # Mc SHARADA VOWEL SIGN OOE +11B62..11B64 ; ID_Continue # Mn [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E +11B65 ; ID_Continue # Mc SHARADA VOWEL SIGN SHORT O +11B66 ; ID_Continue # Mn SHARADA VOWEL SIGN CANDRA E +11B67 ; ID_Continue # Mc SHARADA VOWEL SIGN CANDRA O 11BC0..11BE0 ; ID_Continue # Lo [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO 11BF0..11BF9 ; ID_Continue # Nd [10] SUNUWAR DIGIT ZERO..SUNUWAR DIGIT NINE 11C00..11C08 ; ID_Continue # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L @@ -8162,6 +8245,10 @@ FFDA..FFDC ; ID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN 11D97 ; ID_Continue # Mn GUNJALA GONDI VIRAMA 11D98 ; ID_Continue # Lo GUNJALA GONDI OM 11DA0..11DA9 ; ID_Continue # Nd [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE +11DB0..11DD8 ; ID_Continue # Lo [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH +11DD9 ; ID_Continue # Lm TOLONG SIKI SIGN SELA +11DDA..11DDB ; ID_Continue # Lo [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA +11DE0..11DE9 ; ID_Continue # Nd [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE 11EE0..11EF2 ; ID_Continue # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA 11EF3..11EF4 ; ID_Continue # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U 11EF5..11EF6 ; ID_Continue # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O @@ -8212,6 +8299,8 @@ FFDA..FFDC ; ID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN 16D6B..16D6C ; ID_Continue # Lm [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT 16D70..16D79 ; ID_Continue # Nd [10] KIRAT RAI DIGIT ZERO..KIRAT RAI DIGIT NINE 16E40..16E7F ; ID_Continue # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EA0..16EB8 ; ID_Continue # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; ID_Continue # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 16F00..16F4A ; ID_Continue # Lo [75] MIAO LETTER PA..MIAO LETTER RTE 16F4F ; ID_Continue # Mn MIAO SIGN CONSONANT MODIFIER BAR 16F50 ; ID_Continue # Lo MIAO LETTER NASALIZATION @@ -8222,9 +8311,11 @@ FFDA..FFDC ; ID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN 16FE3 ; ID_Continue # Lm OLD CHINESE ITERATION MARK 16FE4 ; ID_Continue # Mn KHITAN SMALL SCRIPT FILLER 16FF0..16FF1 ; ID_Continue # Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY -17000..187F7 ; ID_Continue # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7 -18800..18CD5 ; ID_Continue # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5 -18CFF..18D08 ; ID_Continue # Lo [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08 +16FF2..16FF3 ; ID_Continue # Lm [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER +16FF4..16FF6 ; ID_Continue # Nl [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS +17000..18CD5 ; ID_Continue # Lo [7382] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CD5 +18CFF..18D1E ; ID_Continue # Lo [32] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D1E +18D80..18DF2 ; ID_Continue # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883 1AFF0..1AFF3 ; ID_Continue # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5 1AFF5..1AFFB ; ID_Continue # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5 1AFFD..1AFFE ; ID_Continue # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8 @@ -8315,6 +8406,17 @@ FFDA..FFDC ; ID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN 1E5EE..1E5EF ; ID_Continue # Mn [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR 1E5F0 ; ID_Continue # Lo OL ONAL SIGN HODDOND 1E5F1..1E5FA ; ID_Continue # Nd [10] OL ONAL DIGIT ZERO..OL ONAL DIGIT NINE +1E6C0..1E6DE ; ID_Continue # Lo [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO +1E6E0..1E6E2 ; ID_Continue # Lo [3] TAI YO LETTER AA..TAI YO LETTER UE +1E6E3 ; ID_Continue # Mn TAI YO SIGN UE +1E6E4..1E6E5 ; ID_Continue # Lo [2] TAI YO LETTER U..TAI YO LETTER AE +1E6E6 ; ID_Continue # Mn TAI YO SIGN AU +1E6E7..1E6ED ; ID_Continue # Lo [7] TAI YO LETTER O..TAI YO LETTER AUE +1E6EE..1E6EF ; ID_Continue # Mn [2] TAI YO SIGN AY..TAI YO SIGN ANG +1E6F0..1E6F4 ; ID_Continue # Lo [5] TAI YO LETTER AN..TAI YO LETTER AP +1E6F5 ; ID_Continue # Mn TAI YO SIGN OM +1E6FE ; ID_Continue # Lo TAI YO SYMBOL MUEANG +1E6FF ; ID_Continue # Lm TAI YO XAM LAI 1E7E0..1E7E6 ; ID_Continue # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO 1E7E8..1E7EB ; ID_Continue # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE 1E7ED..1E7EE ; ID_Continue # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE @@ -8360,17 +8462,16 @@ FFDA..FFDC ; ID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN 1EEAB..1EEBB ; ID_Continue # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN 1FBF0..1FBF9 ; ID_Continue # Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE 20000..2A6DF ; ID_Continue # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF -2A700..2B739 ; ID_Continue # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739 -2B740..2B81D ; ID_Continue # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D -2B820..2CEA1 ; ID_Continue # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 +2A700..2B81D ; ID_Continue # Lo [4382] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81D +2B820..2CEAD ; ID_Continue # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD 2CEB0..2EBE0 ; ID_Continue # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 2EBF0..2EE5D ; ID_Continue # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D 2F800..2FA1D ; ID_Continue # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D 30000..3134A ; ID_Continue # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A -31350..323AF ; ID_Continue # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF +31350..33479 ; ID_Continue # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479 E0100..E01EF ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 -# Total code points: 144541 +# Total code points: 149240 # ================================================ @@ -8393,8 +8494,8 @@ E0100..E01EF ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR 01BC..01BF ; XID_Start # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3 ; XID_Start # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..0293 ; XID_Start # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL -0294 ; XID_Start # Lo LATIN LETTER GLOTTAL STOP -0295..02AF ; XID_Start # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0294..0295 ; XID_Start # Lo [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE +0296..02AF ; XID_Start # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02C1 ; XID_Start # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C6..02D1 ; XID_Start # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON 02E0..02E4 ; XID_Start # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP @@ -8441,7 +8542,7 @@ E0100..E01EF ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR 0840..0858 ; XID_Start # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN 0860..086A ; XID_Start # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA 0870..0887 ; XID_Start # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT -0889..088E ; XID_Start # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL +0889..088F ; XID_Start # Lo [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE 08A0..08C8 ; XID_Start # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF 08C9 ; XID_Start # Lm ARABIC SMALL FARSI YEH 0904..0939 ; XID_Start # Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA @@ -8509,7 +8610,7 @@ E0100..E01EF ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR 0C2A..0C39 ; XID_Start # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA 0C3D ; XID_Start # Lo TELUGU SIGN AVAGRAHA 0C58..0C5A ; XID_Start # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA -0C5D ; XID_Start # Lo TELUGU LETTER NAKAARA POLLU +0C5C..0C5D ; XID_Start # Lo [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU 0C60..0C61 ; XID_Start # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C80 ; XID_Start # Lo KANNADA SIGN SPACING CANDRABINDU 0C85..0C8C ; XID_Start # Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L @@ -8518,7 +8619,7 @@ E0100..E01EF ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR 0CAA..0CB3 ; XID_Start # Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA 0CB5..0CB9 ; XID_Start # Lo [5] KANNADA LETTER VA..KANNADA LETTER HA 0CBD ; XID_Start # Lo KANNADA SIGN AVAGRAHA -0CDD..0CDE ; XID_Start # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA +0CDC..0CDE ; XID_Start # Lo [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA 0CE0..0CE1 ; XID_Start # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CF1..0CF2 ; XID_Start # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA 0D04..0D0C ; XID_Start # Lo [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L @@ -8742,11 +8843,8 @@ A771..A787 ; XID_Start # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER A788 ; XID_Start # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A78B..A78E ; XID_Start # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F ; XID_Start # Lo LATIN LETTER SINOLOGICAL DOT -A790..A7CD ; XID_Start # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; XID_Start # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3 ; XID_Start # L& LATIN SMALL LETTER DOUBLE THORN -A7D5..A7DC ; XID_Start # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE -A7F2..A7F4 ; XID_Start # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A790..A7DC ; XID_Start # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7F1..A7F4 ; XID_Start # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F5..A7F6 ; XID_Start # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H A7F7 ; XID_Start # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9 ; XID_Start # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE @@ -8888,6 +8986,7 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 108F4..108F5 ; XID_Start # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW 10900..10915 ; XID_Start # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU 10920..10939 ; XID_Start # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C +10940..10959 ; XID_Start # Lo [26] SIDETIC LETTER N01..SIDETIC LETTER N26 10980..109B7 ; XID_Start # Lo [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA 109BE..109BF ; XID_Start # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN 10A00 ; XID_Start # Lo KHAROSHTHI LETTER A @@ -8915,6 +9014,8 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 10E80..10EA9 ; XID_Start # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET 10EB0..10EB1 ; XID_Start # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE 10EC2..10EC4 ; XID_Start # Lo [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW +10EC5 ; XID_Start # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW +10EC6..10EC7 ; XID_Start # Lo [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW 10F00..10F1C ; XID_Start # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL 10F27 ; XID_Start # Lo OLD SOGDIAN LIGATURE AYIN-DALETH 10F30..10F45 ; XID_Start # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN @@ -9007,6 +9108,9 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 11D67..11D68 ; XID_Start # Lo [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI 11D6A..11D89 ; XID_Start # Lo [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA 11D98 ; XID_Start # Lo GUNJALA GONDI OM +11DB0..11DD8 ; XID_Start # Lo [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH +11DD9 ; XID_Start # Lm TOLONG SIKI SIGN SELA +11DDA..11DDB ; XID_Start # Lo [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA 11EE0..11EF2 ; XID_Start # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA 11F02 ; XID_Start # Lo KAWI SIGN REPHA 11F04..11F10 ; XID_Start # Lo [13] KAWI LETTER A..KAWI LETTER O @@ -9033,14 +9137,18 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 16D43..16D6A ; XID_Start # Lo [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU 16D6B..16D6C ; XID_Start # Lm [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT 16E40..16E7F ; XID_Start # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EA0..16EB8 ; XID_Start # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; XID_Start # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 16F00..16F4A ; XID_Start # Lo [75] MIAO LETTER PA..MIAO LETTER RTE 16F50 ; XID_Start # Lo MIAO LETTER NASALIZATION 16F93..16F9F ; XID_Start # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8 16FE0..16FE1 ; XID_Start # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK 16FE3 ; XID_Start # Lm OLD CHINESE ITERATION MARK -17000..187F7 ; XID_Start # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7 -18800..18CD5 ; XID_Start # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5 -18CFF..18D08 ; XID_Start # Lo [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08 +16FF2..16FF3 ; XID_Start # Lm [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER +16FF4..16FF6 ; XID_Start # Nl [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS +17000..18CD5 ; XID_Start # Lo [7382] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CD5 +18CFF..18D1E ; XID_Start # Lo [32] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D1E +18D80..18DF2 ; XID_Start # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883 1AFF0..1AFF3 ; XID_Start # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5 1AFF5..1AFFB ; XID_Start # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5 1AFFD..1AFFE ; XID_Start # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8 @@ -9098,6 +9206,13 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 1E4EB ; XID_Start # Lm NAG MUNDARI SIGN OJOD 1E5D0..1E5ED ; XID_Start # Lo [30] OL ONAL LETTER O..OL ONAL LETTER EG 1E5F0 ; XID_Start # Lo OL ONAL SIGN HODDOND +1E6C0..1E6DE ; XID_Start # Lo [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO +1E6E0..1E6E2 ; XID_Start # Lo [3] TAI YO LETTER AA..TAI YO LETTER UE +1E6E4..1E6E5 ; XID_Start # Lo [2] TAI YO LETTER U..TAI YO LETTER AE +1E6E7..1E6ED ; XID_Start # Lo [7] TAI YO LETTER O..TAI YO LETTER AUE +1E6F0..1E6F4 ; XID_Start # Lo [5] TAI YO LETTER AN..TAI YO LETTER AP +1E6FE ; XID_Start # Lo TAI YO SYMBOL MUEANG +1E6FF ; XID_Start # Lm TAI YO XAM LAI 1E7E0..1E7E6 ; XID_Start # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO 1E7E8..1E7EB ; XID_Start # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE 1E7ED..1E7EE ; XID_Start # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE @@ -9139,16 +9254,15 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 1EEA5..1EEA9 ; XID_Start # Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH 1EEAB..1EEBB ; XID_Start # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN 20000..2A6DF ; XID_Start # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF -2A700..2B739 ; XID_Start # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739 -2B740..2B81D ; XID_Start # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D -2B820..2CEA1 ; XID_Start # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 +2A700..2B81D ; XID_Start # Lo [4382] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81D +2B820..2CEAD ; XID_Start # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD 2CEB0..2EBE0 ; XID_Start # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 2EBF0..2EE5D ; XID_Start # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D 2F800..2FA1D ; XID_Start # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D 30000..3134A ; XID_Start # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A -31350..323AF ; XID_Start # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF +31350..33479 ; XID_Start # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479 -# Total code points: 141246 +# Total code points: 145893 # ================================================ @@ -9174,8 +9288,8 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 01BC..01BF ; XID_Continue # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3 ; XID_Continue # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..0293 ; XID_Continue # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL -0294 ; XID_Continue # Lo LATIN LETTER GLOTTAL STOP -0295..02AF ; XID_Continue # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0294..0295 ; XID_Continue # Lo [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE +0296..02AF ; XID_Continue # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02C1 ; XID_Continue # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C6..02D1 ; XID_Continue # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON 02E0..02E4 ; XID_Continue # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP @@ -9250,7 +9364,7 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 0859..085B ; XID_Continue # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK 0860..086A ; XID_Continue # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA 0870..0887 ; XID_Continue # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT -0889..088E ; XID_Continue # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL +0889..088F ; XID_Continue # Lo [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE 0897..089F ; XID_Continue # Mn [9] ARABIC PEPET..ARABIC HALF MADDA OVER MADDA 08A0..08C8 ; XID_Continue # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF 08C9 ; XID_Continue # Lm ARABIC SMALL FARSI YEH @@ -9400,7 +9514,7 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 0C4A..0C4D ; XID_Continue # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA 0C55..0C56 ; XID_Continue # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK 0C58..0C5A ; XID_Continue # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA -0C5D ; XID_Continue # Lo TELUGU LETTER NAKAARA POLLU +0C5C..0C5D ; XID_Continue # Lo [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU 0C60..0C61 ; XID_Continue # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C62..0C63 ; XID_Continue # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL 0C66..0C6F ; XID_Continue # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE @@ -9422,7 +9536,7 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 0CCA..0CCB ; XID_Continue # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO 0CCC..0CCD ; XID_Continue # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA 0CD5..0CD6 ; XID_Continue # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK -0CDD..0CDE ; XID_Continue # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA +0CDC..0CDE ; XID_Continue # Lo [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA 0CE0..0CE1 ; XID_Continue # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CE2..0CE3 ; XID_Continue # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL 0CE6..0CEF ; XID_Continue # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE @@ -9639,7 +9753,8 @@ FFDA..FFDC ; XID_Start # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU 1A90..1A99 ; XID_Continue # Nd [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE 1AA7 ; XID_Continue # Lm TAI THAM SIGN MAI YAMOK 1AB0..1ABD ; XID_Continue # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW -1ABF..1ACE ; XID_Continue # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T +1ABF..1ADD ; XID_Continue # Mn [31] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOT-AND-RING BELOW +1AE0..1AEB ; XID_Continue # Mn [12] COMBINING LEFT TACK ABOVE..COMBINING DOUBLE RIGHTWARDS ARROW ABOVE 1B00..1B03 ; XID_Continue # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 1B04 ; XID_Continue # Mc BALINESE SIGN BISAH 1B05..1B33 ; XID_Continue # Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA @@ -9827,11 +9942,8 @@ A771..A787 ; XID_Continue # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETT A788 ; XID_Continue # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A78B..A78E ; XID_Continue # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F ; XID_Continue # Lo LATIN LETTER SINOLOGICAL DOT -A790..A7CD ; XID_Continue # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; XID_Continue # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3 ; XID_Continue # L& LATIN SMALL LETTER DOUBLE THORN -A7D5..A7DC ; XID_Continue # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE -A7F2..A7F4 ; XID_Continue # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A790..A7DC ; XID_Continue # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7F1..A7F4 ; XID_Continue # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F5..A7F6 ; XID_Continue # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H A7F7 ; XID_Continue # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9 ; XID_Continue # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE @@ -10044,6 +10156,7 @@ FFDA..FFDC ; XID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA 108F4..108F5 ; XID_Continue # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW 10900..10915 ; XID_Continue # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU 10920..10939 ; XID_Continue # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C +10940..10959 ; XID_Continue # Lo [26] SIDETIC LETTER N01..SIDETIC LETTER N26 10980..109B7 ; XID_Continue # Lo [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA 109BE..109BF ; XID_Continue # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN 10A00 ; XID_Continue # Lo KHAROSHTHI LETTER A @@ -10082,7 +10195,9 @@ FFDA..FFDC ; XID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA 10EAB..10EAC ; XID_Continue # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK 10EB0..10EB1 ; XID_Continue # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE 10EC2..10EC4 ; XID_Continue # Lo [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW -10EFC..10EFF ; XID_Continue # Mn [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA +10EC5 ; XID_Continue # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW +10EC6..10EC7 ; XID_Continue # Lo [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW +10EFA..10EFF ; XID_Continue # Mn [6] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC SMALL LOW WORD MADDA 10F00..10F1C ; XID_Continue # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL 10F27 ; XID_Continue # Lo OLD SOGDIAN LIGATURE AYIN-DALETH 10F30..10F45 ; XID_Continue # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN @@ -10309,6 +10424,12 @@ FFDA..FFDC ; XID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA 11A98..11A99 ; XID_Continue # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER 11A9D ; XID_Continue # Lo SOYOMBO MARK PLUTA 11AB0..11AF8 ; XID_Continue # Lo [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL +11B60 ; XID_Continue # Mn SHARADA VOWEL SIGN OE +11B61 ; XID_Continue # Mc SHARADA VOWEL SIGN OOE +11B62..11B64 ; XID_Continue # Mn [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E +11B65 ; XID_Continue # Mc SHARADA VOWEL SIGN SHORT O +11B66 ; XID_Continue # Mn SHARADA VOWEL SIGN CANDRA E +11B67 ; XID_Continue # Mc SHARADA VOWEL SIGN CANDRA O 11BC0..11BE0 ; XID_Continue # Lo [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO 11BF0..11BF9 ; XID_Continue # Nd [10] SUNUWAR DIGIT ZERO..SUNUWAR DIGIT NINE 11C00..11C08 ; XID_Continue # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L @@ -10349,6 +10470,10 @@ FFDA..FFDC ; XID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA 11D97 ; XID_Continue # Mn GUNJALA GONDI VIRAMA 11D98 ; XID_Continue # Lo GUNJALA GONDI OM 11DA0..11DA9 ; XID_Continue # Nd [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE +11DB0..11DD8 ; XID_Continue # Lo [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH +11DD9 ; XID_Continue # Lm TOLONG SIKI SIGN SELA +11DDA..11DDB ; XID_Continue # Lo [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA +11DE0..11DE9 ; XID_Continue # Nd [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE 11EE0..11EF2 ; XID_Continue # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA 11EF3..11EF4 ; XID_Continue # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U 11EF5..11EF6 ; XID_Continue # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O @@ -10399,6 +10524,8 @@ FFDA..FFDC ; XID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA 16D6B..16D6C ; XID_Continue # Lm [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT 16D70..16D79 ; XID_Continue # Nd [10] KIRAT RAI DIGIT ZERO..KIRAT RAI DIGIT NINE 16E40..16E7F ; XID_Continue # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y +16EA0..16EB8 ; XID_Continue # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; XID_Continue # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY 16F00..16F4A ; XID_Continue # Lo [75] MIAO LETTER PA..MIAO LETTER RTE 16F4F ; XID_Continue # Mn MIAO SIGN CONSONANT MODIFIER BAR 16F50 ; XID_Continue # Lo MIAO LETTER NASALIZATION @@ -10409,9 +10536,11 @@ FFDA..FFDC ; XID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA 16FE3 ; XID_Continue # Lm OLD CHINESE ITERATION MARK 16FE4 ; XID_Continue # Mn KHITAN SMALL SCRIPT FILLER 16FF0..16FF1 ; XID_Continue # Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY -17000..187F7 ; XID_Continue # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7 -18800..18CD5 ; XID_Continue # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5 -18CFF..18D08 ; XID_Continue # Lo [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08 +16FF2..16FF3 ; XID_Continue # Lm [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER +16FF4..16FF6 ; XID_Continue # Nl [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS +17000..18CD5 ; XID_Continue # Lo [7382] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CD5 +18CFF..18D1E ; XID_Continue # Lo [32] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D1E +18D80..18DF2 ; XID_Continue # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883 1AFF0..1AFF3 ; XID_Continue # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5 1AFF5..1AFFB ; XID_Continue # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5 1AFFD..1AFFE ; XID_Continue # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8 @@ -10502,6 +10631,17 @@ FFDA..FFDC ; XID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA 1E5EE..1E5EF ; XID_Continue # Mn [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR 1E5F0 ; XID_Continue # Lo OL ONAL SIGN HODDOND 1E5F1..1E5FA ; XID_Continue # Nd [10] OL ONAL DIGIT ZERO..OL ONAL DIGIT NINE +1E6C0..1E6DE ; XID_Continue # Lo [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO +1E6E0..1E6E2 ; XID_Continue # Lo [3] TAI YO LETTER AA..TAI YO LETTER UE +1E6E3 ; XID_Continue # Mn TAI YO SIGN UE +1E6E4..1E6E5 ; XID_Continue # Lo [2] TAI YO LETTER U..TAI YO LETTER AE +1E6E6 ; XID_Continue # Mn TAI YO SIGN AU +1E6E7..1E6ED ; XID_Continue # Lo [7] TAI YO LETTER O..TAI YO LETTER AUE +1E6EE..1E6EF ; XID_Continue # Mn [2] TAI YO SIGN AY..TAI YO SIGN ANG +1E6F0..1E6F4 ; XID_Continue # Lo [5] TAI YO LETTER AN..TAI YO LETTER AP +1E6F5 ; XID_Continue # Mn TAI YO SIGN OM +1E6FE ; XID_Continue # Lo TAI YO SYMBOL MUEANG +1E6FF ; XID_Continue # Lm TAI YO XAM LAI 1E7E0..1E7E6 ; XID_Continue # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO 1E7E8..1E7EB ; XID_Continue # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE 1E7ED..1E7EE ; XID_Continue # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE @@ -10547,17 +10687,16 @@ FFDA..FFDC ; XID_Continue # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA 1EEAB..1EEBB ; XID_Continue # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN 1FBF0..1FBF9 ; XID_Continue # Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE 20000..2A6DF ; XID_Continue # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF -2A700..2B739 ; XID_Continue # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739 -2B740..2B81D ; XID_Continue # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D -2B820..2CEA1 ; XID_Continue # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 +2A700..2B81D ; XID_Continue # Lo [4382] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81D +2B820..2CEAD ; XID_Continue # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD 2CEB0..2EBE0 ; XID_Continue # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 2EBF0..2EE5D ; XID_Continue # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D 2F800..2FA1D ; XID_Continue # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D 30000..3134A ; XID_Continue # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A -31350..323AF ; XID_Continue # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF +31350..33479 ; XID_Continue # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479 E0100..E01EF ; XID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 -# Total code points: 144522 +# Total code points: 149221 # ================================================ @@ -10778,7 +10917,8 @@ E01F0..E0FFF ; Default_Ignorable_Code_Point # Cn [3600] .... -2B76..2B95 ; Pattern_Syntax # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW -2B96 ; Pattern_Syntax # Cn -2B97..2BFF ; Pattern_Syntax # So [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL +2B76..2BFF ; Pattern_Syntax # So [138] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..HELLSCHREIBER PAUSE SYMBOL 2E00..2E01 ; Pattern_Syntax # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER 2E02 ; Pattern_Syntax # Pi LEFT SUBSTITUTION BRACKET 2E03 ; Pattern_Syntax # Pf RIGHT SUBSTITUTION BRACKET diff --git a/src/java.base/share/data/unicodedata/PropertyValueAliases.txt b/src/java.base/share/data/unicodedata/PropertyValueAliases.txt index 97980e2d902c8..b92662eda2867 100644 --- a/src/java.base/share/data/unicodedata/PropertyValueAliases.txt +++ b/src/java.base/share/data/unicodedata/PropertyValueAliases.txt @@ -1,6 +1,6 @@ -# PropertyValueAliases-16.0.0.txt -# Date: 2024-07-30, 19:59:00 GMT -# Copyright (c) 2024 Unicode, Inc. +# PropertyValueAliases-17.0.0.txt +# Date: 2025-06-30, 06:16:21 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 # @@ -93,6 +93,7 @@ age; 14.0 ; V14_0 age; 15.0 ; V15_0 age; 15.1 ; V15_1 age; 16.0 ; V16_0 +age; 17.0 ; V17_0 age; NA ; Unassigned # Alphabetic (Alpha) @@ -179,6 +180,7 @@ blk; Bamum_Sup ; Bamum_Supplement blk; Bassa_Vah ; Bassa_Vah blk; Batak ; Batak blk; Bengali ; Bengali +blk; Beria_Erfe ; Beria_Erfe blk; Bhaiksuki ; Bhaiksuki blk; Block_Elements ; Block_Elements blk; Bopomofo ; Bopomofo @@ -211,6 +213,7 @@ blk; CJK_Ext_F ; CJK_Unified_Ideographs_Extension_F blk; CJK_Ext_G ; CJK_Unified_Ideographs_Extension_G blk; CJK_Ext_H ; CJK_Unified_Ideographs_Extension_H blk; CJK_Ext_I ; CJK_Unified_Ideographs_Extension_I +blk; CJK_Ext_J ; CJK_Unified_Ideographs_Extension_J blk; CJK_Radicals_Sup ; CJK_Radicals_Supplement blk; CJK_Strokes ; CJK_Strokes blk; CJK_Symbols ; CJK_Symbols_And_Punctuation @@ -360,6 +363,7 @@ blk; Misc_Math_Symbols_A ; Miscellaneous_Mathematical_Symbols_A blk; Misc_Math_Symbols_B ; Miscellaneous_Mathematical_Symbols_B blk; Misc_Pictographs ; Miscellaneous_Symbols_And_Pictographs blk; Misc_Symbols ; Miscellaneous_Symbols +blk; Misc_Symbols_Sup ; Miscellaneous_Symbols_Supplement blk; Misc_Technical ; Miscellaneous_Technical blk; Modi ; Modi blk; Modifier_Letters ; Spacing_Modifier_Letters @@ -419,9 +423,11 @@ blk; Runic ; Runic blk; Samaritan ; Samaritan blk; Saurashtra ; Saurashtra blk; Sharada ; Sharada +blk; Sharada_Sup ; Sharada_Supplement blk; Shavian ; Shavian blk; Shorthand_Format_Controls ; Shorthand_Format_Controls blk; Siddham ; Siddham +blk; Sidetic ; Sidetic blk; Sinhala ; Sinhala blk; Sinhala_Archaic_Numbers ; Sinhala_Archaic_Numbers blk; Small_Forms ; Small_Form_Variants @@ -456,12 +462,14 @@ blk; Tai_Le ; Tai_Le blk; Tai_Tham ; Tai_Tham blk; Tai_Viet ; Tai_Viet blk; Tai_Xuan_Jing ; Tai_Xuan_Jing_Symbols +blk; Tai_Yo ; Tai_Yo blk; Takri ; Takri blk; Tamil ; Tamil blk; Tamil_Sup ; Tamil_Supplement blk; Tangsa ; Tangsa blk; Tangut ; Tangut blk; Tangut_Components ; Tangut_Components +blk; Tangut_Components_Sup ; Tangut_Components_Supplement blk; Tangut_Sup ; Tangut_Supplement blk; Telugu ; Telugu blk; Thaana ; Thaana @@ -470,6 +478,7 @@ blk; Tibetan ; Tibetan blk; Tifinagh ; Tifinagh blk; Tirhuta ; Tirhuta blk; Todhri ; Todhri +blk; Tolong_Siki ; Tolong_Siki blk; Toto ; Toto blk; Transport_And_Map ; Transport_And_Map_Symbols blk; Tulu_Tigalari ; Tulu_Tigalari @@ -878,7 +887,7 @@ InPC; Bottom_And_Left ; Bottom_And_Left InPC; Bottom_And_Right ; Bottom_And_Right InPC; Left ; Left InPC; Left_And_Right ; Left_And_Right -InPC; NA ; NA +InPC; NA ; Not_Applicable InPC; Overstruck ; Overstruck InPC; Right ; Right InPC; Top ; Top @@ -1088,6 +1097,7 @@ jg ; Taw ; Taw jg ; Teh_Marbuta ; Teh_Marbuta jg ; Teh_Marbuta_Goal ; Teh_Marbuta_Goal ; Hamza_On_Heh_Goal jg ; Teth ; Teth +jg ; Thin_Noon ; Thin_Noon jg ; Thin_Yeh ; Thin_Yeh jg ; Vertical_Tail ; Vertical_Tail jg ; Waw ; Waw @@ -1131,6 +1141,7 @@ lb ; EX ; Exclamation lb ; GL ; Glue lb ; H2 ; H2 lb ; H3 ; H3 +lb ; HH ; Unambiguous_Hyphen lb ; HL ; Hebrew_Letter lb ; HY ; Hyphen lb ; ID ; Ideographic @@ -1319,6 +1330,7 @@ sc ; Bamu ; Bamum sc ; Bass ; Bassa_Vah sc ; Batk ; Batak sc ; Beng ; Bengali +sc ; Berf ; Beria_Erfe sc ; Bhks ; Bhaiksuki sc ; Bopo ; Bopomofo sc ; Brah ; Brahmi @@ -1438,6 +1450,7 @@ sc ; Sgnw ; SignWriting sc ; Shaw ; Shavian sc ; Shrd ; Sharada sc ; Sidd ; Siddham +sc ; Sidt ; Sidetic sc ; Sind ; Khudawadi sc ; Sinh ; Sinhala sc ; Sogd ; Sogdian @@ -1455,6 +1468,7 @@ sc ; Talu ; New_Tai_Lue sc ; Taml ; Tamil sc ; Tang ; Tangut sc ; Tavt ; Tai_Viet +sc ; Tayo ; Tai_Yo sc ; Telu ; Telugu sc ; Tfng ; Tifinagh sc ; Tglg ; Tagalog @@ -1464,6 +1478,7 @@ sc ; Tibt ; Tibetan sc ; Tirh ; Tirhuta sc ; Tnsa ; Tangsa sc ; Todr ; Todhri +sc ; Tols ; Tolong_Siki sc ; Toto ; Toto sc ; Tutg ; Tulu_Tigalari sc ; Ugar ; Ugaritic @@ -1705,4 +1720,16 @@ kEH_NoMirror; Y ; Yes ; T kEH_NoRotate; N ; No ; F ; False kEH_NoRotate; Y ; Yes ; T ; True +# kMandarin (cjkMandarin) + +# @missing: 0000..10FFFF; kMandarin; + +# kTotalStrokes (cjkTotalStrokes) + +# @missing: 0000..10FFFF; kTotalStrokes; + +# kUnihanCore2020 (cjkUnihanCore2020) + +# @missing: 0000..10FFFF; kUnihanCore2020; + # EOF diff --git a/src/java.base/share/data/unicodedata/ReadMe.txt b/src/java.base/share/data/unicodedata/ReadMe.txt index 81d489a057538..268f62b8a93c6 100644 --- a/src/java.base/share/data/unicodedata/ReadMe.txt +++ b/src/java.base/share/data/unicodedata/ReadMe.txt @@ -1,17 +1,24 @@ # Unicode Character Database -# Date: 2024-08-25 -# Copyright (c) 2024 Unicode, Inc. +# Date: 2025-08-15 +# © 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 # # For documentation, see the following: -# NamesList.html +# ucd/NamesList.html # UAX #38, "Unicode Han Database (Unihan)" +# UAX #42, "Unicode Character Database in XML" # UAX #44, "Unicode Character Database" # UTS #51, "Unicode Emoji" # UAX #57, "Unicode Egyptian Hieroglyph Database" # -# The UAXes and UTS #51 can be accessed at https://www.unicode.org/versions/Unicode16.0.0/ +# The UAXes and UTS #51 can be accessed at https://www.unicode.org/versions/Unicode17.0.0/ -This directory contains final data files -for the Unicode Character Database, for Version 16.0.0 of the Unicode Standard. +This directory contains the final data files +for Version 17.0.0 of the Unicode Standard. + +The "charts" subdirectory contains an archival set of +pdf code charts corresponding exactly to Version 17.0.0. + +The other subdirectories contain the data files for the +Unicode Character Database and for the synchronized Unicode Technical Standards. diff --git a/src/java.base/share/data/unicodedata/Scripts.txt b/src/java.base/share/data/unicodedata/Scripts.txt index 14d48d94162eb..5574fdd6ae35d 100644 --- a/src/java.base/share/data/unicodedata/Scripts.txt +++ b/src/java.base/share/data/unicodedata/Scripts.txt @@ -1,6 +1,6 @@ -# Scripts-16.0.0.txt -# Date: 2024-04-30, 21:48:40 GMT -# Copyright (c) 2024 Unicode, Inc. +# Scripts-17.0.0.txt +# Date: 2025-07-24, 13:28:55 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 # @@ -154,7 +154,7 @@ 208A..208C ; Common # Sm [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN 208D ; Common # Ps SUBSCRIPT LEFT PARENTHESIS 208E ; Common # Pe SUBSCRIPT RIGHT PARENTHESIS -20A0..20C0 ; Common # Sc [33] EURO-CURRENCY SIGN..SOM SIGN +20A0..20C1 ; Common # Sc [34] EURO-CURRENCY SIGN..SAUDI RIYAL SIGN 2100..2101 ; Common # So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT 2102 ; Common # L& DOUBLE-STRUCK CAPITAL C 2103..2106 ; Common # So [4] DEGREE CELSIUS..CADA UNA @@ -306,8 +306,7 @@ 2B45..2B46 ; Common # So [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW 2B47..2B4C ; Common # Sm [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR 2B4D..2B73 ; Common # So [39] DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR -2B76..2B95 ; Common # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW -2B97..2BFF ; Common # So [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL +2B76..2BFF ; Common # So [138] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..HELLSCHREIBER PAUSE SYMBOL 2E00..2E01 ; Common # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER 2E02 ; Common # Pi LEFT SUBSTITUTION BRACKET 2E03 ; Common # Pf RIGHT SUBSTITUTION BRACKET @@ -524,7 +523,11 @@ FFFC..FFFD ; Common # So [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHAR 1BCA0..1BCA3 ; Common # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP 1CC00..1CCEF ; Common # So [240] UP-POINTING GO-KART..OUTLINED LATIN CAPITAL LETTER Z 1CCF0..1CCF9 ; Common # Nd [10] OUTLINED DIGIT ZERO..OUTLINED DIGIT NINE +1CCFA..1CCFC ; Common # So [3] SNAKE SYMBOL..NOSE SYMBOL 1CD00..1CEB3 ; Common # So [436] BLOCK OCTANT-3..BLACK RIGHT TRIANGLE CARET +1CEBA..1CED0 ; Common # So [23] FRAGILE SYMBOL..LEUKOTHEA +1CEE0..1CEEF ; Common # So [16] GEOMANTIC FIGURE POPULUS..GEOMANTIC FIGURE VIA +1CEF0 ; Common # Sm MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR 1CF50..1CFC3 ; Common # So [116] ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK 1D000..1D0F5 ; Common # So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO 1D100..1D126 ; Common # So [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2 @@ -605,11 +608,10 @@ FFFC..FFFD ; Common # So [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHAR 1F260..1F265 ; Common # So [6] ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI 1F300..1F3FA ; Common # So [251] CYCLONE..AMPHORA 1F3FB..1F3FF ; Common # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 -1F400..1F6D7 ; Common # So [728] RAT..ELEVATOR +1F400..1F6D8 ; Common # So [729] RAT..LANDSLIDE 1F6DC..1F6EC ; Common # So [17] WIRELESS..AIRPLANE ARRIVING 1F6F0..1F6FC ; Common # So [13] SATELLITE..ROLLER SKATE -1F700..1F776 ; Common # So [119] ALCHEMICAL SYMBOL FOR QUINTESSENCE..LUNAR ECLIPSE -1F77B..1F7D9 ; Common # So [95] HAUMEA..NINE POINTED WHITE STAR +1F700..1F7D9 ; Common # So [218] ALCHEMICAL SYMBOL FOR QUINTESSENCE..NINE POINTED WHITE STAR 1F7E0..1F7EB ; Common # So [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE 1F7F0 ; Common # So HEAVY EQUALS SIGN 1F800..1F80B ; Common # So [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD @@ -619,21 +621,24 @@ FFFC..FFFD ; Common # So [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHAR 1F890..1F8AD ; Common # So [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS 1F8B0..1F8BB ; Common # So [12] ARROW POINTING UPWARDS THEN NORTH WEST..SOUTH WEST ARROW FROM BAR 1F8C0..1F8C1 ; Common # So [2] LEFTWARDS ARROW FROM DOWNWARDS ARROW..RIGHTWARDS ARROW FROM DOWNWARDS ARROW -1F900..1FA53 ; Common # So [340] CIRCLED CROSS FORMEE WITH FOUR DOTS..BLACK CHESS KNIGHT-BISHOP +1F8D0..1F8D8 ; Common # Sm [9] LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW..LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE +1F900..1FA57 ; Common # So [344] CIRCLED CROSS FORMEE WITH FOUR DOTS..BLACK CHESS ALFIL 1FA60..1FA6D ; Common # So [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER 1FA70..1FA7C ; Common # So [13] BALLET SHOES..CRUTCH -1FA80..1FA89 ; Common # So [10] YO-YO..HARP -1FA8F..1FAC6 ; Common # So [56] SHOVEL..FINGERPRINT -1FACE..1FADC ; Common # So [15] MOOSE..ROOT VEGETABLE -1FADF..1FAE9 ; Common # So [11] SPLATTER..FACE WITH BAGS UNDER EYES -1FAF0..1FAF8 ; Common # So [9] HAND WITH INDEX FINGER AND THUMB CROSSED..RIGHTWARDS PUSHING HAND +1FA80..1FA8A ; Common # So [11] YO-YO..TROMBONE +1FA8E..1FAC6 ; Common # So [57] TREASURE CHEST..FINGERPRINT +1FAC8 ; Common # So HAIRY CREATURE +1FACD..1FADC ; Common # So [16] ORCA..ROOT VEGETABLE +1FADF..1FAEA ; Common # So [12] SPLATTER..DISTORTED FACE +1FAEF..1FAF8 ; Common # So [10] FIGHT CLOUD..RIGHTWARDS PUSHING HAND 1FB00..1FB92 ; Common # So [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK 1FB94..1FBEF ; Common # So [92] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE 1FBF0..1FBF9 ; Common # Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE +1FBFA ; Common # So ALARM BELL SYMBOL E0001 ; Common # Cf LANGUAGE TAG E0020..E007F ; Common # Cf [96] TAG SPACE..CANCEL TAG -# Total code points: 9053 +# Total code points: 9123 # ================================================ @@ -648,8 +653,8 @@ E0020..E007F ; Common # Cf [96] TAG SPACE..CANCEL TAG 01BC..01BF ; Latin # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3 ; Latin # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..0293 ; Latin # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL -0294 ; Latin # Lo LATIN LETTER GLOTTAL STOP -0295..02AF ; Latin # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL +0294..0295 ; Latin # Lo [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE +0296..02AF ; Latin # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02B8 ; Latin # Lm [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y 02E0..02E4 ; Latin # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP 1D00..1D25 ; Latin # L& [38] LATIN LETTER SMALL CAPITAL A..LATIN LETTER AIN @@ -676,11 +681,8 @@ A770 ; Latin # Lm MODIFIER LETTER US A771..A787 ; Latin # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T A78B..A78E ; Latin # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F ; Latin # Lo LATIN LETTER SINOLOGICAL DOT -A790..A7CD ; Latin # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE -A7D0..A7D1 ; Latin # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G -A7D3 ; Latin # L& LATIN SMALL LETTER DOUBLE THORN -A7D5..A7DC ; Latin # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE -A7F2..A7F4 ; Latin # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q +A790..A7DC ; Latin # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE +A7F1..A7F4 ; Latin # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q A7F5..A7F6 ; Latin # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H A7F7 ; Latin # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9 ; Latin # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE @@ -702,7 +704,7 @@ FF41..FF5A ; Latin # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN 1DF0B..1DF1E ; Latin # L& [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL 1DF25..1DF2A ; Latin # L& [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK -# Total code points: 1487 +# Total code points: 1492 # ================================================ @@ -869,7 +871,7 @@ FB46..FB4F ; Hebrew # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATU 0750..077F ; Arabic # Lo [48] ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS ABOVE 0870..0887 ; Arabic # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT 0888 ; Arabic # Sk ARABIC RAISED ROUND DOT -0889..088E ; Arabic # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL +0889..088F ; Arabic # Lo [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE 0890..0891 ; Arabic # Cf [2] ARABIC POUND MARK ABOVE..ARABIC PIASTRE MARK ABOVE 0897..089F ; Arabic # Mn [9] ARABIC PEPET..ARABIC HALF MADDA OVER MADDA 08A0..08C8 ; Arabic # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF @@ -878,11 +880,13 @@ FB46..FB4F ; Hebrew # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATU 08E3..08FF ; Arabic # Mn [29] ARABIC TURNED DAMMA BELOW..ARABIC MARK SIDEWAYS NOON GHUNNA FB50..FBB1 ; Arabic # Lo [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM FBB2..FBC2 ; Arabic # Sk [17] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL WASLA ABOVE +FBC3..FBD2 ; Arabic # So [16] ARABIC LIGATURE JALLA WA-ALAA..ARABIC LIGATURE ALAYHI AR-RAHMAH FBD3..FD3D ; Arabic # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM FD40..FD4F ; Arabic # So [16] ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH FD50..FD8F ; Arabic # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM +FD90..FD91 ; Arabic # So [2] ARABIC LIGATURE RAHMATU ALLAAHI ALAYH..ARABIC LIGATURE RAHMATU ALLAAHI ALAYHAA FD92..FDC7 ; Arabic # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM -FDCF ; Arabic # So ARABIC LIGATURE SALAAMUHU ALAYNAA +FDC8..FDCF ; Arabic # So [8] ARABIC LIGATURE RAHIMAHU ALLAAH TAAALAA..ARABIC LIGATURE SALAAMUHU ALAYNAA FDF0..FDFB ; Arabic # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU FDFC ; Arabic # Sc RIAL SIGN FDFD..FDFF ; Arabic # So [3] ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM..ARABIC LIGATURE AZZA WA JALL @@ -890,7 +894,11 @@ FE70..FE74 ; Arabic # Lo [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN FE76..FEFC ; Arabic # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM 10E60..10E7E ; Arabic # No [31] RUMI DIGIT ONE..RUMI FRACTION TWO THIRDS 10EC2..10EC4 ; Arabic # Lo [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW -10EFC..10EFF ; Arabic # Mn [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA +10EC5 ; Arabic # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW +10EC6..10EC7 ; Arabic # Lo [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW +10ED0 ; Arabic # Po ARABIC BIBLICAL END OF VERSE +10ED1..10ED8 ; Arabic # So [8] ARABIC LIGATURE ALAYHAA AS-SALAATU WAS-SALAAM..ARABIC LIGATURE NAWWARA ALLAAHU MARQADAH +10EFA..10EFF ; Arabic # Mn [6] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC SMALL LOW WORD MADDA 1EE00..1EE03 ; Arabic # Lo [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL 1EE05..1EE1F ; Arabic # Lo [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF 1EE21..1EE22 ; Arabic # Lo [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM @@ -926,7 +934,7 @@ FE76..FEFC ; Arabic # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LA 1EEAB..1EEBB ; Arabic # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN 1EEF0..1EEF1 ; Arabic # Sm [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL -# Total code points: 1373 +# Total code points: 1413 # ================================================ @@ -1155,7 +1163,7 @@ A8FF ; Devanagari # Mn DEVANAGARI VOWEL SIGN AY 0C4A..0C4D ; Telugu # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA 0C55..0C56 ; Telugu # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK 0C58..0C5A ; Telugu # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA -0C5D ; Telugu # Lo TELUGU LETTER NAKAARA POLLU +0C5C..0C5D ; Telugu # Lo [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU 0C60..0C61 ; Telugu # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C62..0C63 ; Telugu # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL 0C66..0C6F ; Telugu # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE @@ -1163,7 +1171,7 @@ A8FF ; Devanagari # Mn DEVANAGARI VOWEL SIGN AY 0C78..0C7E ; Telugu # No [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR 0C7F ; Telugu # So TELUGU SIGN TUUMU -# Total code points: 100 +# Total code points: 101 # ================================================ @@ -1186,14 +1194,14 @@ A8FF ; Devanagari # Mn DEVANAGARI VOWEL SIGN AY 0CCA..0CCB ; Kannada # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO 0CCC..0CCD ; Kannada # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA 0CD5..0CD6 ; Kannada # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK -0CDD..0CDE ; Kannada # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA +0CDC..0CDE ; Kannada # Lo [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA 0CE0..0CE1 ; Kannada # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CE2..0CE3 ; Kannada # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL 0CE6..0CEF ; Kannada # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE 0CF1..0CF2 ; Kannada # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA 0CF3 ; Kannada # Mc KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT -# Total code points: 91 +# Total code points: 92 # ================================================ @@ -1594,17 +1602,18 @@ FA70..FAD9 ; Han # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILI 16FE2 ; Han # Po OLD CHINESE HOOK MARK 16FE3 ; Han # Lm OLD CHINESE ITERATION MARK 16FF0..16FF1 ; Han # Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY +16FF2..16FF3 ; Han # Lm [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER +16FF4..16FF6 ; Han # Nl [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS 20000..2A6DF ; Han # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF -2A700..2B739 ; Han # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739 -2B740..2B81D ; Han # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D -2B820..2CEA1 ; Han # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 +2A700..2B81D ; Han # Lo [4382] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81D +2B820..2CEAD ; Han # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD 2CEB0..2EBE0 ; Han # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 2EBF0..2EE5D ; Han # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D 2F800..2FA1D ; Han # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D 30000..3134A ; Han # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A -31350..323AF ; Han # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF +31350..33479 ; Han # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479 -# Total code points: 99030 +# Total code points: 103351 # ================================================ @@ -1647,7 +1656,8 @@ A490..A4C6 ; Yi # So [55] YI RADICAL QOT..YI RADICAL KE 0951..0954 ; Inherited # Mn [4] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI ACUTE ACCENT 1AB0..1ABD ; Inherited # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW 1ABE ; Inherited # Me COMBINING PARENTHESES OVERLAY -1ABF..1ACE ; Inherited # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T +1ABF..1ADD ; Inherited # Mn [31] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOT-AND-RING BELOW +1AE0..1AEB ; Inherited # Mn [12] COMBINING LEFT TACK ABOVE..COMBINING DOUBLE RIGHTWARDS ARROW ABOVE 1CD0..1CD2 ; Inherited # Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA 1CD4..1CE0 ; Inherited # Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA 1CE2..1CE8 ; Inherited # Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL @@ -1676,7 +1686,7 @@ FE20..FE2D ; Inherited # Mn [14] COMBINING LIGATURE LEFT HALF..COMBINING CON 1D1AA..1D1AD ; Inherited # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO E0100..E01EF ; Inherited # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 -# Total code points: 657 +# Total code points: 684 # ================================================ @@ -2347,8 +2357,14 @@ ABF0..ABF9 ; Meetei_Mayek # Nd [10] MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DI 111DB ; Sharada # Po SHARADA SIGN SIDDHAM 111DC ; Sharada # Lo SHARADA HEADSTROKE 111DD..111DF ; Sharada # Po [3] SHARADA CONTINUATION SIGN..SHARADA SECTION MARK-2 +11B60 ; Sharada # Mn SHARADA VOWEL SIGN OE +11B61 ; Sharada # Mc SHARADA VOWEL SIGN OOE +11B62..11B64 ; Sharada # Mn [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E +11B65 ; Sharada # Mc SHARADA VOWEL SIGN SHORT O +11B66 ; Sharada # Mn SHARADA VOWEL SIGN CANDRA E +11B67 ; Sharada # Mc SHARADA VOWEL SIGN CANDRA O -# Total code points: 96 +# Total code points: 104 # ================================================ @@ -2756,11 +2772,11 @@ ABF0..ABF9 ; Meetei_Mayek # Nd [10] MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DI # ================================================ 16FE0 ; Tangut # Lm TANGUT ITERATION MARK -17000..187F7 ; Tangut # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7 -18800..18AFF ; Tangut # Lo [768] TANGUT COMPONENT-001..TANGUT COMPONENT-768 -18D00..18D08 ; Tangut # Lo [9] TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D08 +17000..18AFF ; Tangut # Lo [6912] TANGUT IDEOGRAPH-17000..TANGUT COMPONENT-768 +18D00..18D1E ; Tangut # Lo [31] TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D1E +18D80..18DF2 ; Tangut # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883 -# Total code points: 6914 +# Total code points: 7059 # ================================================ @@ -3125,4 +3141,42 @@ ABF0..ABF9 ; Meetei_Mayek # Nd [10] MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DI # Total code points: 80 +# ================================================ + +10940..10959 ; Sidetic # Lo [26] SIDETIC LETTER N01..SIDETIC LETTER N26 + +# Total code points: 26 + +# ================================================ + +1E6C0..1E6DE ; Tai_Yo # Lo [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO +1E6E0..1E6E2 ; Tai_Yo # Lo [3] TAI YO LETTER AA..TAI YO LETTER UE +1E6E3 ; Tai_Yo # Mn TAI YO SIGN UE +1E6E4..1E6E5 ; Tai_Yo # Lo [2] TAI YO LETTER U..TAI YO LETTER AE +1E6E6 ; Tai_Yo # Mn TAI YO SIGN AU +1E6E7..1E6ED ; Tai_Yo # Lo [7] TAI YO LETTER O..TAI YO LETTER AUE +1E6EE..1E6EF ; Tai_Yo # Mn [2] TAI YO SIGN AY..TAI YO SIGN ANG +1E6F0..1E6F4 ; Tai_Yo # Lo [5] TAI YO LETTER AN..TAI YO LETTER AP +1E6F5 ; Tai_Yo # Mn TAI YO SIGN OM +1E6FE ; Tai_Yo # Lo TAI YO SYMBOL MUEANG +1E6FF ; Tai_Yo # Lm TAI YO XAM LAI + +# Total code points: 55 + +# ================================================ + +11DB0..11DD8 ; Tolong_Siki # Lo [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH +11DD9 ; Tolong_Siki # Lm TOLONG SIKI SIGN SELA +11DDA..11DDB ; Tolong_Siki # Lo [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA +11DE0..11DE9 ; Tolong_Siki # Nd [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE + +# Total code points: 54 + +# ================================================ + +16EA0..16EB8 ; Beria_Erfe # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY +16EBB..16ED3 ; Beria_Erfe # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY + +# Total code points: 50 + # EOF diff --git a/src/java.base/share/data/unicodedata/SpecialCasing.txt b/src/java.base/share/data/unicodedata/SpecialCasing.txt index 74700b5d32149..1013344a6f203 100644 --- a/src/java.base/share/data/unicodedata/SpecialCasing.txt +++ b/src/java.base/share/data/unicodedata/SpecialCasing.txt @@ -1,6 +1,6 @@ -# SpecialCasing-16.0.0.txt -# Date: 2024-05-10, 22:49:00 GMT -# Copyright (c) 2024 Unicode, Inc. +# SpecialCasing-17.0.0.txt +# Date: 2025-07-31, 22:11:55 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 # @@ -47,8 +47,8 @@ # # A language ID is defined by BCP 47, with '-' and '_' treated equivalently. # -# A casing context for a character is defined by Section 3.13 Default Case Algorithms -# of The Unicode Standard. +# A casing context for a character is defined in the +# "Conformance" / "Default Case Algorithms" section of the core specification. # # Parsers of this file must be prepared to deal with future additions to this format: # * Additional contexts @@ -57,6 +57,10 @@ # ================================================================================ # Unconditional mappings +# The mappings in this section are not language-sensitive nor context-sensitive. +# +# Note that comments provide additional information but +# do not modify the case mapping algorithms in the core specification, chapter 3. # ================================================================================ # The German es-zed is special--the normal mapping is to SS. diff --git a/src/java.base/share/data/unicodedata/UnicodeData.txt b/src/java.base/share/data/unicodedata/UnicodeData.txt index 64258a3739535..fca68e3e154e6 100644 --- a/src/java.base/share/data/unicodedata/UnicodeData.txt +++ b/src/java.base/share/data/unicodedata/UnicodeData.txt @@ -659,7 +659,7 @@ 0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7 0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;; 0294;LATIN LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;; -0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;; +0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Lo;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;; 0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;; 0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;; 0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;; @@ -2121,6 +2121,7 @@ 088C;ARABIC LETTER TAH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; 088D;ARABIC LETTER KEHEH WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;; 088E;ARABIC VERTICAL TAIL;Lo;0;AL;;;;;N;;;;; +088F;ARABIC LETTER NOON WITH RING ABOVE;Lo;0;AL;;;;;N;;;;; 0890;ARABIC POUND MARK ABOVE;Cf;0;AN;;;;;N;;;;; 0891;ARABIC PIASTRE MARK ABOVE;Cf;0;AN;;;;;N;;;;; 0897;ARABIC PEPET;Mn;230;NSM;;;;;N;;;;; @@ -2862,6 +2863,7 @@ 0C58;TELUGU LETTER TSA;Lo;0;L;;;;;N;;;;; 0C59;TELUGU LETTER DZA;Lo;0;L;;;;;N;;;;; 0C5A;TELUGU LETTER RRRA;Lo;0;L;;;;;N;;;;; +0C5C;TELUGU ARCHAIC SHRII;Lo;0;L;;;;;N;;;;; 0C5D;TELUGU LETTER NAKAARA POLLU;Lo;0;L;;;;;N;;;;; 0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; 0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; @@ -2958,6 +2960,7 @@ 0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; 0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;; 0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;; +0CDC;KANNADA ARCHAIC SHRII;Lo;0;L;;;;;N;;;;; 0CDD;KANNADA LETTER NAKAARA POLLU;Lo;0;L;;;;;N;;;;; 0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;; 0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; @@ -6137,6 +6140,33 @@ 1ACC;COMBINING LATIN SMALL LETTER INSULAR G;Mn;230;NSM;;;;;N;;;;; 1ACD;COMBINING LATIN SMALL LETTER INSULAR R;Mn;230;NSM;;;;;N;;;;; 1ACE;COMBINING LATIN SMALL LETTER INSULAR T;Mn;230;NSM;;;;;N;;;;; +1ACF;COMBINING DOUBLE CARON;Mn;230;NSM;;;;;N;;;;; +1AD0;COMBINING VERTICAL-LINE-ACUTE;Mn;230;NSM;;;;;N;;;;; +1AD1;COMBINING GRAVE-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;; +1AD2;COMBINING VERTICAL-LINE-GRAVE;Mn;230;NSM;;;;;N;;;;; +1AD3;COMBINING ACUTE-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;; +1AD4;COMBINING VERTICAL-LINE-MACRON;Mn;230;NSM;;;;;N;;;;; +1AD5;COMBINING MACRON-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;; +1AD6;COMBINING VERTICAL-LINE-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;; +1AD7;COMBINING VERTICAL-LINE-GRAVE-ACUTE;Mn;230;NSM;;;;;N;;;;; +1AD8;COMBINING MACRON-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;; +1AD9;COMBINING SHARP SIGN;Mn;230;NSM;;;;;N;;;;; +1ADA;COMBINING FLAT SIGN;Mn;230;NSM;;;;;N;;;;; +1ADB;COMBINING DOWN TACK ABOVE;Mn;230;NSM;;;;;N;;;;; +1ADC;COMBINING DIAERESIS WITH RAISED LEFT DOT;Mn;230;NSM;;;;;N;;;;; +1ADD;COMBINING DOT-AND-RING BELOW;Mn;220;NSM;;;;;N;;;;; +1AE0;COMBINING LEFT TACK ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE1;COMBINING RIGHT TACK ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE2;COMBINING MINUS SIGN ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE3;COMBINING INVERTED BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE4;COMBINING SQUARE ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE5;COMBINING SEAGULL ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE6;COMBINING DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;;;;; +1AE7;COMBINING DOUBLE ARCH ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE8;COMBINING EQUALS SIGN ABOVE;Mn;230;NSM;;;;;N;;;;; +1AE9;COMBINING LEFT ANGLE CENTRED ABOVE;Mn;230;NSM;;;;;N;;;;; +1AEA;COMBINING UPWARDS ARROW ABOVE;Mn;230;NSM;;;;;N;;;;; +1AEB;COMBINING DOUBLE RIGHTWARDS ARROW ABOVE;Mn;234;NSM;;;;;N;;;;; 1B00;BALINESE SIGN ULU RICEM;Mn;0;NSM;;;;;N;;;;; 1B01;BALINESE SIGN ULU CANDRA;Mn;0;NSM;;;;;N;;;;; 1B02;BALINESE SIGN CECEK;Mn;0;NSM;;;;;N;;;;; @@ -7545,6 +7575,7 @@ 20BE;LARI SIGN;Sc;0;ET;;;;;N;;;;; 20BF;BITCOIN SIGN;Sc;0;ET;;;;;N;;;;; 20C0;SOM SIGN;Sc;0;ET;;;;;N;;;;; +20C1;SAUDI RIYAL SIGN;Sc;0;ET;;;;;N;;;;; 20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;; 20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;; 20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;; @@ -10239,6 +10270,7 @@ 2B93;NEWLINE RIGHT;So;0;ON;;;;;N;;;;; 2B94;FOUR CORNER ARROWS CIRCLING ANTICLOCKWISE;So;0;ON;;;;;N;;;;; 2B95;RIGHTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;; +2B96;EQUALS SIGN WITH INFINITY ABOVE;So;0;ON;;;;;N;;;;; 2B97;SYMBOL FOR TYPE A ELECTRONICS;So;0;ON;;;;;N;;;;; 2B98;THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; 2B99;THREE-D RIGHT-LIGHTED UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;; @@ -14274,10 +14306,14 @@ A7CA;LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY;Ll;0;L;;;;;N;;;A7C9;;A7C9 A7CB;LATIN CAPITAL LETTER RAMS HORN;Lu;0;L;;;;;N;;;;0264; A7CC;LATIN CAPITAL LETTER S WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A7CD; A7CD;LATIN SMALL LETTER S WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A7CC;;A7CC +A7CE;LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE;Lu;0;L;;;;;N;;;;A7CF; +A7CF;LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;;;A7CE;;A7CE A7D0;LATIN CAPITAL LETTER CLOSED INSULAR G;Lu;0;L;;;;;N;;;;A7D1; A7D1;LATIN SMALL LETTER CLOSED INSULAR G;Ll;0;L;;;;;N;;;A7D0;;A7D0 -A7D3;LATIN SMALL LETTER DOUBLE THORN;Ll;0;L;;;;;N;;;;; -A7D5;LATIN SMALL LETTER DOUBLE WYNN;Ll;0;L;;;;;N;;;;; +A7D2;LATIN CAPITAL LETTER DOUBLE THORN;Lu;0;L;;;;;N;;;;A7D3; +A7D3;LATIN SMALL LETTER DOUBLE THORN;Ll;0;L;;;;;N;;;A7D2;;A7D2 +A7D4;LATIN CAPITAL LETTER DOUBLE WYNN;Lu;0;L;;;;;N;;;;A7D5; +A7D5;LATIN SMALL LETTER DOUBLE WYNN;Ll;0;L;;;;;N;;;A7D4;;A7D4 A7D6;LATIN CAPITAL LETTER MIDDLE SCOTS S;Lu;0;L;;;;;N;;;;A7D7; A7D7;LATIN SMALL LETTER MIDDLE SCOTS S;Ll;0;L;;;;;N;;;A7D6;;A7D6 A7D8;LATIN CAPITAL LETTER SIGMOID S;Lu;0;L;;;;;N;;;;A7D9; @@ -14285,6 +14321,7 @@ A7D9;LATIN SMALL LETTER SIGMOID S;Ll;0;L;;;;;N;;;A7D8;;A7D8 A7DA;LATIN CAPITAL LETTER LAMBDA;Lu;0;L;;;;;N;;;;A7DB; A7DB;LATIN SMALL LETTER LAMBDA;Ll;0;L;;;;;N;;;A7DA;;A7DA A7DC;LATIN CAPITAL LETTER LAMBDA WITH STROKE;Lu;0;L;;;;;N;;;;019B; +A7F1;MODIFIER LETTER CAPITAL S;Lm;0;L; 0053;;;;N;;;;; A7F2;MODIFIER LETTER CAPITAL C;Lm;0;L; 0043;;;;N;;;;; A7F3;MODIFIER LETTER CAPITAL F;Lm;0;L; 0046;;;;N;;;;; A7F4;MODIFIER LETTER CAPITAL Q;Lm;0;L; 0051;;;;N;;;;; @@ -15925,6 +15962,22 @@ FBBF;ARABIC SYMBOL RING;Sk;0;AL;;;;;N;;;;; FBC0;ARABIC SYMBOL SMALL TAH ABOVE;Sk;0;AL;;;;;N;;;;; FBC1;ARABIC SYMBOL SMALL TAH BELOW;Sk;0;AL;;;;;N;;;;; FBC2;ARABIC SYMBOL WASLA ABOVE;Sk;0;AL;;;;;N;;;;; +FBC3;ARABIC LIGATURE JALLA WA-ALAA;So;0;ON;;;;;N;;;;; +FBC4;ARABIC LIGATURE DAAMAT BARAKAATUHUM;So;0;ON;;;;;N;;;;; +FBC5;ARABIC LIGATURE RAHMATU ALLAAHI TAAALAA ALAYH;So;0;ON;;;;;N;;;;; +FBC6;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHIM;So;0;ON;;;;;N;;;;; +FBC7;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHIMAA;So;0;ON;;;;;N;;;;; +FBC8;ARABIC LIGATURE RAHIMAHUM ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBC9;ARABIC LIGATURE RAHIMAHUMAA ALLAAH;So;0;ON;;;;;N;;;;; +FBCA;ARABIC LIGATURE RAHIMAHUMAA ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBCB;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHUM;So;0;ON;;;;;N;;;;; +FBCC;ARABIC LIGATURE HAFIZAHU ALLAAH;So;0;ON;;;;;N;;;;; +FBCD;ARABIC LIGATURE HAFIZAHU ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBCE;ARABIC LIGATURE HAFIZAHUM ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBCF;ARABIC LIGATURE HAFIZAHUMAA ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;; +FBD0;ARABIC LIGATURE SALLALLAAHU TAAALAA ALAYHI WA-SALLAM;So;0;ON;;;;;N;;;;; +FBD1;ARABIC LIGATURE AJJAL ALLAAHU FARAJAHU ASH-SHAREEF;So;0;ON;;;;;N;;;;; +FBD2;ARABIC LIGATURE ALAYHI AR-RAHMAH;So;0;ON;;;;;N;;;;; FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL; 06AD;;;;N;;;;; FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL; 06AD;;;;N;;;;; FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL; 06AD;;;;N;;;;; @@ -16370,6 +16423,8 @@ FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL; 0645 FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0645 062C 0645;;;;N;;;;; FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL; 0645 062E 062C;;;;N;;;;; FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL; 0645 062E 0645;;;;N;;;;; +FD90;ARABIC LIGATURE RAHMATU ALLAAHI ALAYH;So;0;ON;;;;;N;;;;; +FD91;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHAA;So;0;ON;;;;;N;;;;; FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL; 0645 062C 062E;;;;N;;;;; FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL; 0647 0645 062C;;;;N;;;;; FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0647 0645 0645;;;;N;;;;; @@ -16424,6 +16479,13 @@ FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0639 FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL; 0635 0645 0645;;;;N;;;;; FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL; 0633 062E 064A;;;;N;;;;; FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL; 0646 062C 064A;;;;N;;;;; +FDC8;ARABIC LIGATURE RAHIMAHU ALLAAH TAAALAA;So;0;ON;;;;;N;;;;; +FDC9;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANH;So;0;ON;;;;;N;;;;; +FDCA;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHAA;So;0;ON;;;;;N;;;;; +FDCB;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHUMAA;So;0;ON;;;;;N;;;;; +FDCC;ARABIC LIGATURE SALLALLAHU ALAYHI WA-ALAA AALIHEE WA-SALLAM;So;0;ON;;;;;N;;;;; +FDCD;ARABIC LIGATURE AJJAL ALLAAHU TAAALAA FARAJAHU ASH-SHAREEF;So;0;ON;;;;;N;;;;; +FDCE;ARABIC LIGATURE KARRAMA ALLAAHU WAJHAH;So;0;ON;;;;;N;;;;; FDCF;ARABIC LIGATURE SALAAMUHU ALAYNAA;So;0;ON;;;;;N;;;;; FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL; 0635 0644 06D2;;;;N;;;;; FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL; 0642 0644 06D2;;;;N;;;;; @@ -18708,6 +18770,32 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 10938;LYDIAN LETTER NN;Lo;0;R;;;;;N;;;;; 10939;LYDIAN LETTER C;Lo;0;R;;;;;N;;;;; 1093F;LYDIAN TRIANGULAR MARK;Po;0;R;;;;;N;;;;; +10940;SIDETIC LETTER N01;Lo;0;R;;;;;N;;;;; +10941;SIDETIC LETTER N02;Lo;0;R;;;;;N;;;;; +10942;SIDETIC LETTER N03;Lo;0;R;;;;;N;;;;; +10943;SIDETIC LETTER N04;Lo;0;R;;;;;N;;;;; +10944;SIDETIC LETTER N05;Lo;0;R;;;;;N;;;;; +10945;SIDETIC LETTER N06;Lo;0;R;;;;;N;;;;; +10946;SIDETIC LETTER N07;Lo;0;R;;;;;N;;;;; +10947;SIDETIC LETTER N08;Lo;0;R;;;;;N;;;;; +10948;SIDETIC LETTER N09;Lo;0;R;;;;;N;;;;; +10949;SIDETIC LETTER N10;Lo;0;R;;;;;N;;;;; +1094A;SIDETIC LETTER N11;Lo;0;R;;;;;N;;;;; +1094B;SIDETIC LETTER N12;Lo;0;R;;;;;N;;;;; +1094C;SIDETIC LETTER N13;Lo;0;R;;;;;N;;;;; +1094D;SIDETIC LETTER N14;Lo;0;R;;;;;N;;;;; +1094E;SIDETIC LETTER N15;Lo;0;R;;;;;N;;;;; +1094F;SIDETIC LETTER N16;Lo;0;R;;;;;N;;;;; +10950;SIDETIC LETTER N17;Lo;0;R;;;;;N;;;;; +10951;SIDETIC LETTER N18;Lo;0;R;;;;;N;;;;; +10952;SIDETIC LETTER N19;Lo;0;R;;;;;N;;;;; +10953;SIDETIC LETTER N20;Lo;0;R;;;;;N;;;;; +10954;SIDETIC LETTER N21;Lo;0;R;;;;;N;;;;; +10955;SIDETIC LETTER N22;Lo;0;R;;;;;N;;;;; +10956;SIDETIC LETTER N23;Lo;0;R;;;;;N;;;;; +10957;SIDETIC LETTER N24;Lo;0;R;;;;;N;;;;; +10958;SIDETIC LETTER N25;Lo;0;R;;;;;N;;;;; +10959;SIDETIC LETTER N26;Lo;0;R;;;;;N;;;;; 10980;MEROITIC HIEROGLYPHIC LETTER A;Lo;0;R;;;;;N;;;;; 10981;MEROITIC HIEROGLYPHIC LETTER E;Lo;0;R;;;;;N;;;;; 10982;MEROITIC HIEROGLYPHIC LETTER I;Lo;0;R;;;;;N;;;;; @@ -19541,6 +19629,20 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 10EC2;ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;; 10EC3;ARABIC LETTER TAH WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;; 10EC4;ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;; +10EC5;ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW;Lm;0;AL;;;;;N;;;;; +10EC6;ARABIC LETTER THIN NOON;Lo;0;AL;;;;;N;;;;; +10EC7;ARABIC LETTER YEH WITH FOUR DOTS BELOW;Lo;0;AL;;;;;N;;;;; +10ED0;ARABIC BIBLICAL END OF VERSE;Po;0;ON;;;;;N;;;;; +10ED1;ARABIC LIGATURE ALAYHAA AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;; +10ED2;ARABIC LIGATURE ALAYHIM AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;; +10ED3;ARABIC LIGATURE ALAYHIMAA AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;; +10ED4;ARABIC LIGATURE QADDASA ALLAAHU SIRRAH;So;0;ON;;;;;N;;;;; +10ED5;ARABIC LIGATURE QUDDISA SIRRUHUM;So;0;ON;;;;;N;;;;; +10ED6;ARABIC LIGATURE QUDDISA SIRRUHUMAA;So;0;ON;;;;;N;;;;; +10ED7;ARABIC LIGATURE QUDDISAT ASRAARUHUM;So;0;ON;;;;;N;;;;; +10ED8;ARABIC LIGATURE NAWWARA ALLAAHU MARQADAH;So;0;ON;;;;;N;;;;; +10EFA;ARABIC DOUBLE VERTICAL BAR BELOW;Mn;220;NSM;;;;;N;;;;; +10EFB;ARABIC SMALL LOW NOON;Mn;220;NSM;;;;;N;;;;; 10EFC;ARABIC COMBINING ALEF OVERLAY;Mn;0;NSM;;;;;N;;;;; 10EFD;ARABIC SMALL LOW WORD SAKTA;Mn;220;NSM;;;;;N;;;;; 10EFE;ARABIC SMALL LOW WORD QASR;Mn;220;NSM;;;;;N;;;;; @@ -21521,6 +21623,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 11B07;DEVANAGARI SIGN WESTERN NINE-LIKE BHALE;Po;0;L;;;;;N;;;;; 11B08;DEVANAGARI SIGN REVERSED NINE-LIKE BHALE;Po;0;L;;;;;N;;;;; 11B09;DEVANAGARI SIGN MINDU;Po;0;L;;;;;N;;;;; +11B60;SHARADA VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;; +11B61;SHARADA VOWEL SIGN OOE;Mc;0;L;;;;;N;;;;; +11B62;SHARADA VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;; +11B63;SHARADA VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;; +11B64;SHARADA VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;; +11B65;SHARADA VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;; +11B66;SHARADA VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;; +11B67;SHARADA VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;; 11BC0;SUNUWAR LETTER DEVI;Lo;0;L;;;;;N;;;;; 11BC1;SUNUWAR LETTER TASLA;Lo;0;L;;;;;N;;;;; 11BC2;SUNUWAR LETTER EKO;Lo;0;L;;;;;N;;;;; @@ -21868,6 +21978,60 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 11DA7;GUNJALA GONDI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 11DA8;GUNJALA GONDI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 11DA9;GUNJALA GONDI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11DB0;TOLONG SIKI LETTER I;Lo;0;L;;;;;N;;;;; +11DB1;TOLONG SIKI LETTER E;Lo;0;L;;;;;N;;;;; +11DB2;TOLONG SIKI LETTER U;Lo;0;L;;;;;N;;;;; +11DB3;TOLONG SIKI LETTER O;Lo;0;L;;;;;N;;;;; +11DB4;TOLONG SIKI LETTER A;Lo;0;L;;;;;N;;;;; +11DB5;TOLONG SIKI LETTER AA;Lo;0;L;;;;;N;;;;; +11DB6;TOLONG SIKI LETTER P;Lo;0;L;;;;;N;;;;; +11DB7;TOLONG SIKI LETTER PH;Lo;0;L;;;;;N;;;;; +11DB8;TOLONG SIKI LETTER B;Lo;0;L;;;;;N;;;;; +11DB9;TOLONG SIKI LETTER BH;Lo;0;L;;;;;N;;;;; +11DBA;TOLONG SIKI LETTER M;Lo;0;L;;;;;N;;;;; +11DBB;TOLONG SIKI LETTER T;Lo;0;L;;;;;N;;;;; +11DBC;TOLONG SIKI LETTER TH;Lo;0;L;;;;;N;;;;; +11DBD;TOLONG SIKI LETTER D;Lo;0;L;;;;;N;;;;; +11DBE;TOLONG SIKI LETTER DH;Lo;0;L;;;;;N;;;;; +11DBF;TOLONG SIKI LETTER N;Lo;0;L;;;;;N;;;;; +11DC0;TOLONG SIKI LETTER TT;Lo;0;L;;;;;N;;;;; +11DC1;TOLONG SIKI LETTER TTH;Lo;0;L;;;;;N;;;;; +11DC2;TOLONG SIKI LETTER DD;Lo;0;L;;;;;N;;;;; +11DC3;TOLONG SIKI LETTER DDH;Lo;0;L;;;;;N;;;;; +11DC4;TOLONG SIKI LETTER NN;Lo;0;L;;;;;N;;;;; +11DC5;TOLONG SIKI LETTER C;Lo;0;L;;;;;N;;;;; +11DC6;TOLONG SIKI LETTER CH;Lo;0;L;;;;;N;;;;; +11DC7;TOLONG SIKI LETTER J;Lo;0;L;;;;;N;;;;; +11DC8;TOLONG SIKI LETTER JH;Lo;0;L;;;;;N;;;;; +11DC9;TOLONG SIKI LETTER NY;Lo;0;L;;;;;N;;;;; +11DCA;TOLONG SIKI LETTER K;Lo;0;L;;;;;N;;;;; +11DCB;TOLONG SIKI LETTER KH;Lo;0;L;;;;;N;;;;; +11DCC;TOLONG SIKI LETTER G;Lo;0;L;;;;;N;;;;; +11DCD;TOLONG SIKI LETTER GH;Lo;0;L;;;;;N;;;;; +11DCE;TOLONG SIKI LETTER NG;Lo;0;L;;;;;N;;;;; +11DCF;TOLONG SIKI LETTER Y;Lo;0;L;;;;;N;;;;; +11DD0;TOLONG SIKI LETTER R;Lo;0;L;;;;;N;;;;; +11DD1;TOLONG SIKI LETTER L;Lo;0;L;;;;;N;;;;; +11DD2;TOLONG SIKI LETTER V;Lo;0;L;;;;;N;;;;; +11DD3;TOLONG SIKI LETTER NNY;Lo;0;L;;;;;N;;;;; +11DD4;TOLONG SIKI LETTER S;Lo;0;L;;;;;N;;;;; +11DD5;TOLONG SIKI LETTER H;Lo;0;L;;;;;N;;;;; +11DD6;TOLONG SIKI LETTER X;Lo;0;L;;;;;N;;;;; +11DD7;TOLONG SIKI LETTER RR;Lo;0;L;;;;;N;;;;; +11DD8;TOLONG SIKI LETTER RRH;Lo;0;L;;;;;N;;;;; +11DD9;TOLONG SIKI SIGN SELA;Lm;0;L;;;;;N;;;;; +11DDA;TOLONG SIKI SIGN HECAKA;Lo;0;L;;;;;N;;;;; +11DDB;TOLONG SIKI UNGGA;Lo;0;L;;;;;N;;;;; +11DE0;TOLONG SIKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11DE1;TOLONG SIKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11DE2;TOLONG SIKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11DE3;TOLONG SIKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +11DE4;TOLONG SIKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +11DE5;TOLONG SIKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +11DE6;TOLONG SIKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +11DE7;TOLONG SIKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +11DE8;TOLONG SIKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +11DE9;TOLONG SIKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 11EE0;MAKASAR LETTER KA;Lo;0;L;;;;;N;;;;; 11EE1;MAKASAR LETTER GA;Lo;0;L;;;;;N;;;;; 11EE2;MAKASAR LETTER NGA;Lo;0;L;;;;;N;;;;; @@ -22088,8 +22252,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12035;CUNEIFORM SIGN ARAD TIMES KUR;Lo;0;L;;;;;N;;;;; 12036;CUNEIFORM SIGN ARKAB;Lo;0;L;;;;;N;;;;; 12037;CUNEIFORM SIGN ASAL2;Lo;0;L;;;;;N;;;;; -12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;;N;;;;; -12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;;N;;;;; +12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;1;N;;;;; +12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;1;N;;;;; 1203A;CUNEIFORM SIGN ASH KABA TENU;Lo;0;L;;;;;N;;;;; 1203B;CUNEIFORM SIGN ASH OVER ASH TUG2 OVER TUG2 TUG2 OVER TUG2 PAP;Lo;0;L;;;;;N;;;;; 1203C;CUNEIFORM SIGN ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;; @@ -22153,7 +22317,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12076;CUNEIFORM SIGN DIM2;Lo;0;L;;;;;N;;;;; 12077;CUNEIFORM SIGN DIN;Lo;0;L;;;;;N;;;;; 12078;CUNEIFORM SIGN DIN KASKAL U GUNU DISH;Lo;0;L;;;;;N;;;;; -12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;;N;;;;; +12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;1;N;;;;; 1207A;CUNEIFORM SIGN DU;Lo;0;L;;;;;N;;;;; 1207B;CUNEIFORM SIGN DU OVER DU;Lo;0;L;;;;;N;;;;; 1207C;CUNEIFORM SIGN DU GUNU;Lo;0;L;;;;;N;;;;; @@ -22582,12 +22746,12 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12223;CUNEIFORM SIGN MA2;Lo;0;L;;;;;N;;;;; 12224;CUNEIFORM SIGN MAH;Lo;0;L;;;;;N;;;;; 12225;CUNEIFORM SIGN MAR;Lo;0;L;;;;;N;;;;; -12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;;N;;;;; +12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;1/2;N;;;;; 12227;CUNEIFORM SIGN MASH2;Lo;0;L;;;;;N;;;;; 12228;CUNEIFORM SIGN ME;Lo;0;L;;;;;N;;;;; 12229;CUNEIFORM SIGN MES;Lo;0;L;;;;;N;;;;; 1222A;CUNEIFORM SIGN MI;Lo;0;L;;;;;N;;;;; -1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;;N;;;;; +1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;2;N;;;;; 1222C;CUNEIFORM SIGN MU;Lo;0;L;;;;;N;;;;; 1222D;CUNEIFORM SIGN MU OVER MU;Lo;0;L;;;;;N;;;;; 1222E;CUNEIFORM SIGN MUG;Lo;0;L;;;;;N;;;;; @@ -22811,9 +22975,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12308;CUNEIFORM SIGN TUM;Lo;0;L;;;;;N;;;;; 12309;CUNEIFORM SIGN TUR;Lo;0;L;;;;;N;;;;; 1230A;CUNEIFORM SIGN TUR OVER TUR ZA OVER ZA;Lo;0;L;;;;;N;;;;; -1230B;CUNEIFORM SIGN U;Lo;0;L;;;;;N;;;;; +1230B;CUNEIFORM SIGN U;Lo;0;L;;;;1;N;;;;; 1230C;CUNEIFORM SIGN U GUD;Lo;0;L;;;;;N;;;;; -1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;;N;;;;; +1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;3;N;;;;; 1230E;CUNEIFORM SIGN U OVER U PA OVER PA GAR OVER GAR;Lo;0;L;;;;;N;;;;; 1230F;CUNEIFORM SIGN U OVER U SUR OVER SUR;Lo;0;L;;;;;N;;;;; 12310;CUNEIFORM SIGN U OVER U U REVERSED OVER U REVERSED;Lo;0;L;;;;;N;;;;; @@ -22953,7 +23117,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 12396;CUNEIFORM SIGN SAG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;; 12397;CUNEIFORM SIGN TI2;Lo;0;L;;;;;N;;;;; 12398;CUNEIFORM SIGN UM TIMES ME;Lo;0;L;;;;;N;;;;; -12399;CUNEIFORM SIGN U U;Lo;0;L;;;;;N;;;;; +12399;CUNEIFORM SIGN U U;Lo;0;L;;;;2;N;;;;; 12400;CUNEIFORM NUMERIC SIGN TWO ASH;Nl;0;L;;;;2;N;;;;; 12401;CUNEIFORM NUMERIC SIGN THREE ASH;Nl;0;L;;;;3;N;;;;; 12402;CUNEIFORM NUMERIC SIGN FOUR ASH;Nl;0;L;;;;4;N;;;;; @@ -30124,6 +30288,56 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 16E98;MEDEFAIDRIN FULL STOP;Po;0;L;;;;;N;;;;; 16E99;MEDEFAIDRIN SYMBOL AIVA;Po;0;L;;;;;N;;;;; 16E9A;MEDEFAIDRIN EXCLAMATION OH;Po;0;L;;;;;N;;;;; +16EA0;BERIA ERFE CAPITAL LETTER ARKAB;Lu;0;L;;;;;N;;;;16EBB; +16EA1;BERIA ERFE CAPITAL LETTER BASIGNA;Lu;0;L;;;;;N;;;;16EBC; +16EA2;BERIA ERFE CAPITAL LETTER DARBAI;Lu;0;L;;;;;N;;;;16EBD; +16EA3;BERIA ERFE CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;16EBE; +16EA4;BERIA ERFE CAPITAL LETTER FITKO;Lu;0;L;;;;;N;;;;16EBF; +16EA5;BERIA ERFE CAPITAL LETTER GOWAY;Lu;0;L;;;;;N;;;;16EC0; +16EA6;BERIA ERFE CAPITAL LETTER HIRDEABO;Lu;0;L;;;;;N;;;;16EC1; +16EA7;BERIA ERFE CAPITAL LETTER I;Lu;0;L;;;;;N;;;;16EC2; +16EA8;BERIA ERFE CAPITAL LETTER DJAI;Lu;0;L;;;;;N;;;;16EC3; +16EA9;BERIA ERFE CAPITAL LETTER KOBO;Lu;0;L;;;;;N;;;;16EC4; +16EAA;BERIA ERFE CAPITAL LETTER LAKKO;Lu;0;L;;;;;N;;;;16EC5; +16EAB;BERIA ERFE CAPITAL LETTER MERI;Lu;0;L;;;;;N;;;;16EC6; +16EAC;BERIA ERFE CAPITAL LETTER NINI;Lu;0;L;;;;;N;;;;16EC7; +16EAD;BERIA ERFE CAPITAL LETTER GNA;Lu;0;L;;;;;N;;;;16EC8; +16EAE;BERIA ERFE CAPITAL LETTER NGAY;Lu;0;L;;;;;N;;;;16EC9; +16EAF;BERIA ERFE CAPITAL LETTER OI;Lu;0;L;;;;;N;;;;16ECA; +16EB0;BERIA ERFE CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;16ECB; +16EB1;BERIA ERFE CAPITAL LETTER ERIGO;Lu;0;L;;;;;N;;;;16ECC; +16EB2;BERIA ERFE CAPITAL LETTER ERIGO TAMURA;Lu;0;L;;;;;N;;;;16ECD; +16EB3;BERIA ERFE CAPITAL LETTER SERI;Lu;0;L;;;;;N;;;;16ECE; +16EB4;BERIA ERFE CAPITAL LETTER SHEP;Lu;0;L;;;;;N;;;;16ECF; +16EB5;BERIA ERFE CAPITAL LETTER TATASOUE;Lu;0;L;;;;;N;;;;16ED0; +16EB6;BERIA ERFE CAPITAL LETTER UI;Lu;0;L;;;;;N;;;;16ED1; +16EB7;BERIA ERFE CAPITAL LETTER WASSE;Lu;0;L;;;;;N;;;;16ED2; +16EB8;BERIA ERFE CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;16ED3; +16EBB;BERIA ERFE SMALL LETTER ARKAB;Ll;0;L;;;;;N;;;16EA0;;16EA0 +16EBC;BERIA ERFE SMALL LETTER BASIGNA;Ll;0;L;;;;;N;;;16EA1;;16EA1 +16EBD;BERIA ERFE SMALL LETTER DARBAI;Ll;0;L;;;;;N;;;16EA2;;16EA2 +16EBE;BERIA ERFE SMALL LETTER EH;Ll;0;L;;;;;N;;;16EA3;;16EA3 +16EBF;BERIA ERFE SMALL LETTER FITKO;Ll;0;L;;;;;N;;;16EA4;;16EA4 +16EC0;BERIA ERFE SMALL LETTER GOWAY;Ll;0;L;;;;;N;;;16EA5;;16EA5 +16EC1;BERIA ERFE SMALL LETTER HIRDEABO;Ll;0;L;;;;;N;;;16EA6;;16EA6 +16EC2;BERIA ERFE SMALL LETTER I;Ll;0;L;;;;;N;;;16EA7;;16EA7 +16EC3;BERIA ERFE SMALL LETTER DJAI;Ll;0;L;;;;;N;;;16EA8;;16EA8 +16EC4;BERIA ERFE SMALL LETTER KOBO;Ll;0;L;;;;;N;;;16EA9;;16EA9 +16EC5;BERIA ERFE SMALL LETTER LAKKO;Ll;0;L;;;;;N;;;16EAA;;16EAA +16EC6;BERIA ERFE SMALL LETTER MERI;Ll;0;L;;;;;N;;;16EAB;;16EAB +16EC7;BERIA ERFE SMALL LETTER NINI;Ll;0;L;;;;;N;;;16EAC;;16EAC +16EC8;BERIA ERFE SMALL LETTER GNA;Ll;0;L;;;;;N;;;16EAD;;16EAD +16EC9;BERIA ERFE SMALL LETTER NGAY;Ll;0;L;;;;;N;;;16EAE;;16EAE +16ECA;BERIA ERFE SMALL LETTER OI;Ll;0;L;;;;;N;;;16EAF;;16EAF +16ECB;BERIA ERFE SMALL LETTER PI;Ll;0;L;;;;;N;;;16EB0;;16EB0 +16ECC;BERIA ERFE SMALL LETTER ERIGO;Ll;0;L;;;;;N;;;16EB1;;16EB1 +16ECD;BERIA ERFE SMALL LETTER ERIGO TAMURA;Ll;0;L;;;;;N;;;16EB2;;16EB2 +16ECE;BERIA ERFE SMALL LETTER SERI;Ll;0;L;;;;;N;;;16EB3;;16EB3 +16ECF;BERIA ERFE SMALL LETTER SHEP;Ll;0;L;;;;;N;;;16EB4;;16EB4 +16ED0;BERIA ERFE SMALL LETTER TATASOUE;Ll;0;L;;;;;N;;;16EB5;;16EB5 +16ED1;BERIA ERFE SMALL LETTER UI;Ll;0;L;;;;;N;;;16EB6;;16EB6 +16ED2;BERIA ERFE SMALL LETTER WASSE;Ll;0;L;;;;;N;;;16EB7;;16EB7 +16ED3;BERIA ERFE SMALL LETTER AY;Ll;0;L;;;;;N;;;16EB8;;16EB8 16F00;MIAO LETTER PA;Lo;0;L;;;;;N;;;;; 16F01;MIAO LETTER BA;Lo;0;L;;;;;N;;;;; 16F02;MIAO LETTER YI PA;Lo;0;L;;;;;N;;;;; @@ -30280,8 +30494,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 16FE4;KHITAN SMALL SCRIPT FILLER;Mn;0;NSM;;;;;N;;;;; 16FF0;VIETNAMESE ALTERNATE READING MARK CA;Mc;6;L;;;;;N;;;;; 16FF1;VIETNAMESE ALTERNATE READING MARK NHAY;Mc;6;L;;;;;N;;;;; +16FF2;CHINESE SMALL SIMPLIFIED ER;Lm;0;L;;;;;N;;;;; +16FF3;CHINESE SMALL TRADITIONAL ER;Lm;0;L;;;;;N;;;;; +16FF4;YANGQIN SIGN SLOW ONE BEAT;Nl;0;L;;;;1;N;;;;; +16FF5;YANGQIN SIGN SLOW THREE HALF BEATS;Nl;0;L;;;;3/2;N;;;;; +16FF6;YANGQIN SIGN SLOW TWO BEATS;Nl;0;L;;;;2;N;;;;; 17000;;Lo;0;L;;;;;N;;;;; -187F7;;Lo;0;L;;;;;N;;;;; +187FF;;Lo;0;L;;;;;N;;;;; 18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;; 18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;; 18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;; @@ -31522,7 +31741,122 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 18CD5;KHITAN SMALL SCRIPT CHARACTER-18CD5;Lo;0;L;;;;;N;;;;; 18CFF;KHITAN SMALL SCRIPT CHARACTER-18CFF;Lo;0;L;;;;;N;;;;; 18D00;;Lo;0;L;;;;;N;;;;; -18D08;;Lo;0;L;;;;;N;;;;; +18D1E;;Lo;0;L;;;;;N;;;;; +18D80;TANGUT COMPONENT-769;Lo;0;L;;;;;N;;;;; +18D81;TANGUT COMPONENT-770;Lo;0;L;;;;;N;;;;; +18D82;TANGUT COMPONENT-771;Lo;0;L;;;;;N;;;;; +18D83;TANGUT COMPONENT-772;Lo;0;L;;;;;N;;;;; +18D84;TANGUT COMPONENT-773;Lo;0;L;;;;;N;;;;; +18D85;TANGUT COMPONENT-774;Lo;0;L;;;;;N;;;;; +18D86;TANGUT COMPONENT-775;Lo;0;L;;;;;N;;;;; +18D87;TANGUT COMPONENT-776;Lo;0;L;;;;;N;;;;; +18D88;TANGUT COMPONENT-777;Lo;0;L;;;;;N;;;;; +18D89;TANGUT COMPONENT-778;Lo;0;L;;;;;N;;;;; +18D8A;TANGUT COMPONENT-779;Lo;0;L;;;;;N;;;;; +18D8B;TANGUT COMPONENT-780;Lo;0;L;;;;;N;;;;; +18D8C;TANGUT COMPONENT-781;Lo;0;L;;;;;N;;;;; +18D8D;TANGUT COMPONENT-782;Lo;0;L;;;;;N;;;;; +18D8E;TANGUT COMPONENT-783;Lo;0;L;;;;;N;;;;; +18D8F;TANGUT COMPONENT-784;Lo;0;L;;;;;N;;;;; +18D90;TANGUT COMPONENT-785;Lo;0;L;;;;;N;;;;; +18D91;TANGUT COMPONENT-786;Lo;0;L;;;;;N;;;;; +18D92;TANGUT COMPONENT-787;Lo;0;L;;;;;N;;;;; +18D93;TANGUT COMPONENT-788;Lo;0;L;;;;;N;;;;; +18D94;TANGUT COMPONENT-789;Lo;0;L;;;;;N;;;;; +18D95;TANGUT COMPONENT-790;Lo;0;L;;;;;N;;;;; +18D96;TANGUT COMPONENT-791;Lo;0;L;;;;;N;;;;; +18D97;TANGUT COMPONENT-792;Lo;0;L;;;;;N;;;;; +18D98;TANGUT COMPONENT-793;Lo;0;L;;;;;N;;;;; +18D99;TANGUT COMPONENT-794;Lo;0;L;;;;;N;;;;; +18D9A;TANGUT COMPONENT-795;Lo;0;L;;;;;N;;;;; +18D9B;TANGUT COMPONENT-796;Lo;0;L;;;;;N;;;;; +18D9C;TANGUT COMPONENT-797;Lo;0;L;;;;;N;;;;; +18D9D;TANGUT COMPONENT-798;Lo;0;L;;;;;N;;;;; +18D9E;TANGUT COMPONENT-799;Lo;0;L;;;;;N;;;;; +18D9F;TANGUT COMPONENT-800;Lo;0;L;;;;;N;;;;; +18DA0;TANGUT COMPONENT-801;Lo;0;L;;;;;N;;;;; +18DA1;TANGUT COMPONENT-802;Lo;0;L;;;;;N;;;;; +18DA2;TANGUT COMPONENT-803;Lo;0;L;;;;;N;;;;; +18DA3;TANGUT COMPONENT-804;Lo;0;L;;;;;N;;;;; +18DA4;TANGUT COMPONENT-805;Lo;0;L;;;;;N;;;;; +18DA5;TANGUT COMPONENT-806;Lo;0;L;;;;;N;;;;; +18DA6;TANGUT COMPONENT-807;Lo;0;L;;;;;N;;;;; +18DA7;TANGUT COMPONENT-808;Lo;0;L;;;;;N;;;;; +18DA8;TANGUT COMPONENT-809;Lo;0;L;;;;;N;;;;; +18DA9;TANGUT COMPONENT-810;Lo;0;L;;;;;N;;;;; +18DAA;TANGUT COMPONENT-811;Lo;0;L;;;;;N;;;;; +18DAB;TANGUT COMPONENT-812;Lo;0;L;;;;;N;;;;; +18DAC;TANGUT COMPONENT-813;Lo;0;L;;;;;N;;;;; +18DAD;TANGUT COMPONENT-814;Lo;0;L;;;;;N;;;;; +18DAE;TANGUT COMPONENT-815;Lo;0;L;;;;;N;;;;; +18DAF;TANGUT COMPONENT-816;Lo;0;L;;;;;N;;;;; +18DB0;TANGUT COMPONENT-817;Lo;0;L;;;;;N;;;;; +18DB1;TANGUT COMPONENT-818;Lo;0;L;;;;;N;;;;; +18DB2;TANGUT COMPONENT-819;Lo;0;L;;;;;N;;;;; +18DB3;TANGUT COMPONENT-820;Lo;0;L;;;;;N;;;;; +18DB4;TANGUT COMPONENT-821;Lo;0;L;;;;;N;;;;; +18DB5;TANGUT COMPONENT-822;Lo;0;L;;;;;N;;;;; +18DB6;TANGUT COMPONENT-823;Lo;0;L;;;;;N;;;;; +18DB7;TANGUT COMPONENT-824;Lo;0;L;;;;;N;;;;; +18DB8;TANGUT COMPONENT-825;Lo;0;L;;;;;N;;;;; +18DB9;TANGUT COMPONENT-826;Lo;0;L;;;;;N;;;;; +18DBA;TANGUT COMPONENT-827;Lo;0;L;;;;;N;;;;; +18DBB;TANGUT COMPONENT-828;Lo;0;L;;;;;N;;;;; +18DBC;TANGUT COMPONENT-829;Lo;0;L;;;;;N;;;;; +18DBD;TANGUT COMPONENT-830;Lo;0;L;;;;;N;;;;; +18DBE;TANGUT COMPONENT-831;Lo;0;L;;;;;N;;;;; +18DBF;TANGUT COMPONENT-832;Lo;0;L;;;;;N;;;;; +18DC0;TANGUT COMPONENT-833;Lo;0;L;;;;;N;;;;; +18DC1;TANGUT COMPONENT-834;Lo;0;L;;;;;N;;;;; +18DC2;TANGUT COMPONENT-835;Lo;0;L;;;;;N;;;;; +18DC3;TANGUT COMPONENT-836;Lo;0;L;;;;;N;;;;; +18DC4;TANGUT COMPONENT-837;Lo;0;L;;;;;N;;;;; +18DC5;TANGUT COMPONENT-838;Lo;0;L;;;;;N;;;;; +18DC6;TANGUT COMPONENT-839;Lo;0;L;;;;;N;;;;; +18DC7;TANGUT COMPONENT-840;Lo;0;L;;;;;N;;;;; +18DC8;TANGUT COMPONENT-841;Lo;0;L;;;;;N;;;;; +18DC9;TANGUT COMPONENT-842;Lo;0;L;;;;;N;;;;; +18DCA;TANGUT COMPONENT-843;Lo;0;L;;;;;N;;;;; +18DCB;TANGUT COMPONENT-844;Lo;0;L;;;;;N;;;;; +18DCC;TANGUT COMPONENT-845;Lo;0;L;;;;;N;;;;; +18DCD;TANGUT COMPONENT-846;Lo;0;L;;;;;N;;;;; +18DCE;TANGUT COMPONENT-847;Lo;0;L;;;;;N;;;;; +18DCF;TANGUT COMPONENT-848;Lo;0;L;;;;;N;;;;; +18DD0;TANGUT COMPONENT-849;Lo;0;L;;;;;N;;;;; +18DD1;TANGUT COMPONENT-850;Lo;0;L;;;;;N;;;;; +18DD2;TANGUT COMPONENT-851;Lo;0;L;;;;;N;;;;; +18DD3;TANGUT COMPONENT-852;Lo;0;L;;;;;N;;;;; +18DD4;TANGUT COMPONENT-853;Lo;0;L;;;;;N;;;;; +18DD5;TANGUT COMPONENT-854;Lo;0;L;;;;;N;;;;; +18DD6;TANGUT COMPONENT-855;Lo;0;L;;;;;N;;;;; +18DD7;TANGUT COMPONENT-856;Lo;0;L;;;;;N;;;;; +18DD8;TANGUT COMPONENT-857;Lo;0;L;;;;;N;;;;; +18DD9;TANGUT COMPONENT-858;Lo;0;L;;;;;N;;;;; +18DDA;TANGUT COMPONENT-859;Lo;0;L;;;;;N;;;;; +18DDB;TANGUT COMPONENT-860;Lo;0;L;;;;;N;;;;; +18DDC;TANGUT COMPONENT-861;Lo;0;L;;;;;N;;;;; +18DDD;TANGUT COMPONENT-862;Lo;0;L;;;;;N;;;;; +18DDE;TANGUT COMPONENT-863;Lo;0;L;;;;;N;;;;; +18DDF;TANGUT COMPONENT-864;Lo;0;L;;;;;N;;;;; +18DE0;TANGUT COMPONENT-865;Lo;0;L;;;;;N;;;;; +18DE1;TANGUT COMPONENT-866;Lo;0;L;;;;;N;;;;; +18DE2;TANGUT COMPONENT-867;Lo;0;L;;;;;N;;;;; +18DE3;TANGUT COMPONENT-868;Lo;0;L;;;;;N;;;;; +18DE4;TANGUT COMPONENT-869;Lo;0;L;;;;;N;;;;; +18DE5;TANGUT COMPONENT-870;Lo;0;L;;;;;N;;;;; +18DE6;TANGUT COMPONENT-871;Lo;0;L;;;;;N;;;;; +18DE7;TANGUT COMPONENT-872;Lo;0;L;;;;;N;;;;; +18DE8;TANGUT COMPONENT-873;Lo;0;L;;;;;N;;;;; +18DE9;TANGUT COMPONENT-874;Lo;0;L;;;;;N;;;;; +18DEA;TANGUT COMPONENT-875;Lo;0;L;;;;;N;;;;; +18DEB;TANGUT COMPONENT-876;Lo;0;L;;;;;N;;;;; +18DEC;TANGUT COMPONENT-877;Lo;0;L;;;;;N;;;;; +18DED;TANGUT COMPONENT-878;Lo;0;L;;;;;N;;;;; +18DEE;TANGUT COMPONENT-879;Lo;0;L;;;;;N;;;;; +18DEF;TANGUT COMPONENT-880;Lo;0;L;;;;;N;;;;; +18DF0;TANGUT COMPONENT-881;Lo;0;L;;;;;N;;;;; +18DF1;TANGUT COMPONENT-882;Lo;0;L;;;;;N;;;;; +18DF2;TANGUT COMPONENT-883;Lo;0;L;;;;;N;;;;; 1AFF0;KATAKANA LETTER MINNAN TONE-2;Lm;0;L;;;;;N;;;;; 1AFF1;KATAKANA LETTER MINNAN TONE-3;Lm;0;L;;;;;N;;;;; 1AFF2;KATAKANA LETTER MINNAN TONE-4;Lm;0;L;;;;;N;;;;; @@ -32629,6 +32963,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1CCF7;OUTLINED DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1CCF8;OUTLINED DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1CCF9;OUTLINED DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; +1CCFA;SNAKE SYMBOL;So;0;ON;;;;;N;;;;; +1CCFB;FLYING SAUCER SYMBOL;So;0;ON;;;;;N;;;;; +1CCFC;NOSE SYMBOL;So;0;ON;;;;;N;;;;; 1CD00;BLOCK OCTANT-3;So;0;ON;;;;;N;;;;; 1CD01;BLOCK OCTANT-23;So;0;ON;;;;;N;;;;; 1CD02;BLOCK OCTANT-123;So;0;ON;;;;;N;;;;; @@ -33065,6 +33402,46 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1CEB1;KEYHOLE;So;0;ON;;;;;N;;;;; 1CEB2;OLD PERSONAL COMPUTER WITH MONITOR IN PORTRAIT ORIENTATION;So;0;ON;;;;;N;;;;; 1CEB3;BLACK RIGHT TRIANGLE CARET;So;0;ON;;;;;N;;;;; +1CEBA;FRAGILE SYMBOL;So;0;ON;;;;;N;;;;; +1CEBB;OFFICE BUILDING SYMBOL;So;0;ON;;;;;N;;;;; +1CEBC;TREE SYMBOL;So;0;ON;;;;;N;;;;; +1CEBD;APPLE SYMBOL;So;0;ON;;;;;N;;;;; +1CEBE;CHERRY SYMBOL;So;0;ON;;;;;N;;;;; +1CEBF;STRAWBERRY SYMBOL;So;0;ON;;;;;N;;;;; +1CEC0;HEBE;So;0;ON;;;;;N;;;;; +1CEC1;IRIS;So;0;ON;;;;;N;;;;; +1CEC2;FLORA;So;0;ON;;;;;N;;;;; +1CEC3;METIS;So;0;ON;;;;;N;;;;; +1CEC4;PARTHENOPE;So;0;ON;;;;;N;;;;; +1CEC5;VICTORIA;So;0;ON;;;;;N;;;;; +1CEC6;EGERIA;So;0;ON;;;;;N;;;;; +1CEC7;IRENE;So;0;ON;;;;;N;;;;; +1CEC8;EUNOMIA;So;0;ON;;;;;N;;;;; +1CEC9;PSYCHE;So;0;ON;;;;;N;;;;; +1CECA;THETIS;So;0;ON;;;;;N;;;;; +1CECB;MELPOMENE;So;0;ON;;;;;N;;;;; +1CECC;FORTUNA;So;0;ON;;;;;N;;;;; +1CECD;ASTRONOMICAL SYMBOL FOR ASTEROID PROSERPINA;So;0;ON;;;;;N;;;;; +1CECE;BELLONA;So;0;ON;;;;;N;;;;; +1CECF;AMPHITRITE;So;0;ON;;;;;N;;;;; +1CED0;LEUKOTHEA;So;0;ON;;;;;N;;;;; +1CEE0;GEOMANTIC FIGURE POPULUS;So;0;ON;;;;;N;;;;; +1CEE1;GEOMANTIC FIGURE TRISTITIA;So;0;ON;;;;;N;;;;; +1CEE2;GEOMANTIC FIGURE ALBUS;So;0;ON;;;;;N;;;;; +1CEE3;GEOMANTIC FIGURE FORTUNA MAJOR;So;0;ON;;;;;N;;;;; +1CEE4;GEOMANTIC FIGURE RUBEUS;So;0;ON;;;;;N;;;;; +1CEE5;GEOMANTIC FIGURE ACQUISITIO;So;0;ON;;;;;N;;;;; +1CEE6;GEOMANTIC FIGURE CONJUNCTIO;So;0;ON;;;;;N;;;;; +1CEE7;GEOMANTIC FIGURE CAPUT DRACONIS;So;0;ON;;;;;N;;;;; +1CEE8;GEOMANTIC FIGURE LAETITIA;So;0;ON;;;;;N;;;;; +1CEE9;GEOMANTIC FIGURE CARCER;So;0;ON;;;;;N;;;;; +1CEEA;GEOMANTIC FIGURE AMISSIO;So;0;ON;;;;;N;;;;; +1CEEB;GEOMANTIC FIGURE PUELLA;So;0;ON;;;;;N;;;;; +1CEEC;GEOMANTIC FIGURE FORTUNA MINOR;So;0;ON;;;;;N;;;;; +1CEED;GEOMANTIC FIGURE PUER;So;0;ON;;;;;N;;;;; +1CEEE;GEOMANTIC FIGURE CAUDA DRACONIS;So;0;ON;;;;;N;;;;; +1CEEF;GEOMANTIC FIGURE VIA;So;0;ON;;;;;N;;;;; +1CEF0;MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;; 1CF00;ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT;Mn;0;NSM;;;;;N;;;;; 1CF01;ZNAMENNY COMBINING MARK NIZKO S KRYZHEM ON LEFT;Mn;0;NSM;;;;;N;;;;; 1CF02;ZNAMENNY COMBINING MARK TSATA ON LEFT;Mn;0;NSM;;;;;N;;;;; @@ -36004,6 +36381,61 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1E5F9;OL ONAL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1E5FA;OL ONAL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 1E5FF;OL ONAL ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +1E6C0;TAI YO LETTER LOW KO;Lo;0;L;;;;;N;;;;; +1E6C1;TAI YO LETTER HIGH KO;Lo;0;L;;;;;N;;;;; +1E6C2;TAI YO LETTER LOW KHO;Lo;0;L;;;;;N;;;;; +1E6C3;TAI YO LETTER HIGH KHO;Lo;0;L;;;;;N;;;;; +1E6C4;TAI YO LETTER GO;Lo;0;L;;;;;N;;;;; +1E6C5;TAI YO LETTER NGO;Lo;0;L;;;;;N;;;;; +1E6C6;TAI YO LETTER CO;Lo;0;L;;;;;N;;;;; +1E6C7;TAI YO LETTER LOW XO;Lo;0;L;;;;;N;;;;; +1E6C8;TAI YO LETTER HIGH XO;Lo;0;L;;;;;N;;;;; +1E6C9;TAI YO LETTER LOW NYO;Lo;0;L;;;;;N;;;;; +1E6CA;TAI YO LETTER HIGH NYO;Lo;0;L;;;;;N;;;;; +1E6CB;TAI YO LETTER DO;Lo;0;L;;;;;N;;;;; +1E6CC;TAI YO LETTER LOW TO;Lo;0;L;;;;;N;;;;; +1E6CD;TAI YO LETTER HIGH TO;Lo;0;L;;;;;N;;;;; +1E6CE;TAI YO LETTER THO;Lo;0;L;;;;;N;;;;; +1E6CF;TAI YO LETTER NO;Lo;0;L;;;;;N;;;;; +1E6D0;TAI YO LETTER BO;Lo;0;L;;;;;N;;;;; +1E6D1;TAI YO LETTER LOW PO;Lo;0;L;;;;;N;;;;; +1E6D2;TAI YO LETTER HIGH PO;Lo;0;L;;;;;N;;;;; +1E6D3;TAI YO LETTER PHO;Lo;0;L;;;;;N;;;;; +1E6D4;TAI YO LETTER LOW FO;Lo;0;L;;;;;N;;;;; +1E6D5;TAI YO LETTER HIGH FO;Lo;0;L;;;;;N;;;;; +1E6D6;TAI YO LETTER MO;Lo;0;L;;;;;N;;;;; +1E6D7;TAI YO LETTER YO;Lo;0;L;;;;;N;;;;; +1E6D8;TAI YO LETTER LO;Lo;0;L;;;;;N;;;;; +1E6D9;TAI YO LETTER VO;Lo;0;L;;;;;N;;;;; +1E6DA;TAI YO LETTER LOW HO;Lo;0;L;;;;;N;;;;; +1E6DB;TAI YO LETTER HIGH HO;Lo;0;L;;;;;N;;;;; +1E6DC;TAI YO LETTER QO;Lo;0;L;;;;;N;;;;; +1E6DD;TAI YO LETTER LOW KVO;Lo;0;L;;;;;N;;;;; +1E6DE;TAI YO LETTER HIGH KVO;Lo;0;L;;;;;N;;;;; +1E6E0;TAI YO LETTER AA;Lo;0;L;;;;;N;;;;; +1E6E1;TAI YO LETTER I;Lo;0;L;;;;;N;;;;; +1E6E2;TAI YO LETTER UE;Lo;0;L;;;;;N;;;;; +1E6E3;TAI YO SIGN UE;Mn;230;NSM;;;;;N;;;;; +1E6E4;TAI YO LETTER U;Lo;0;L;;;;;N;;;;; +1E6E5;TAI YO LETTER AE;Lo;0;L;;;;;N;;;;; +1E6E6;TAI YO SIGN AU;Mn;230;NSM;;;;;N;;;;; +1E6E7;TAI YO LETTER O;Lo;0;L;;;;;N;;;;; +1E6E8;TAI YO LETTER E;Lo;0;L;;;;;N;;;;; +1E6E9;TAI YO LETTER IA;Lo;0;L;;;;;N;;;;; +1E6EA;TAI YO LETTER UEA;Lo;0;L;;;;;N;;;;; +1E6EB;TAI YO LETTER UA;Lo;0;L;;;;;N;;;;; +1E6EC;TAI YO LETTER OO;Lo;0;L;;;;;N;;;;; +1E6ED;TAI YO LETTER AUE;Lo;0;L;;;;;N;;;;; +1E6EE;TAI YO SIGN AY;Mn;230;NSM;;;;;N;;;;; +1E6EF;TAI YO SIGN ANG;Mn;230;NSM;;;;;N;;;;; +1E6F0;TAI YO LETTER AN;Lo;0;L;;;;;N;;;;; +1E6F1;TAI YO LETTER AM;Lo;0;L;;;;;N;;;;; +1E6F2;TAI YO LETTER AK;Lo;0;L;;;;;N;;;;; +1E6F3;TAI YO LETTER AT;Lo;0;L;;;;;N;;;;; +1E6F4;TAI YO LETTER AP;Lo;0;L;;;;;N;;;;; +1E6F5;TAI YO SIGN OM;Mn;230;NSM;;;;;N;;;;; +1E6FE;TAI YO SYMBOL MUEANG;Lo;0;L;;;;;N;;;;; +1E6FF;TAI YO XAM LAI;Lm;0;L;;;;;N;;;;; 1E7E0;ETHIOPIC SYLLABLE HHYA;Lo;0;L;;;;;N;;;;; 1E7E1;ETHIOPIC SYLLABLE HHYU;Lo;0;L;;;;;N;;;;; 1E7E2;ETHIOPIC SYLLABLE HHYI;Lo;0;L;;;;;N;;;;; @@ -38079,6 +38511,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;; 1F6D6;HUT;So;0;ON;;;;;N;;;;; 1F6D7;ELEVATOR;So;0;ON;;;;;N;;;;; +1F6D8;LANDSLIDE;So;0;ON;;;;;N;;;;; 1F6DC;WIRELESS;So;0;ON;;;;;N;;;;; 1F6DD;PLAYGROUND SLIDE;So;0;ON;;;;;N;;;;; 1F6DE;WHEEL;So;0;ON;;;;;N;;;;; @@ -38228,6 +38661,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F774;LOT OF FORTUNE;So;0;ON;;;;;N;;;;; 1F775;OCCULTATION;So;0;ON;;;;;N;;;;; 1F776;LUNAR ECLIPSE;So;0;ON;;;;;N;;;;; +1F777;VESTA FORM TWO;So;0;ON;;;;;N;;;;; +1F778;ASTRAEA FORM TWO;So;0;ON;;;;;N;;;;; +1F779;HYGIEA FORM TWO;So;0;ON;;;;;N;;;;; +1F77A;PARTHENOPE FORM TWO;So;0;ON;;;;;N;;;;; 1F77B;HAUMEA;So;0;ON;;;;;N;;;;; 1F77C;MAKEMAKE;So;0;ON;;;;;N;;;;; 1F77D;GONGGONG;So;0;ON;;;;;N;;;;; @@ -38498,6 +38935,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F8BB;SOUTH WEST ARROW FROM BAR;So;0;ON;;;;;N;;;;; 1F8C0;LEFTWARDS ARROW FROM DOWNWARDS ARROW;So;0;ON;;;;;N;;;;; 1F8C1;RIGHTWARDS ARROW FROM DOWNWARDS ARROW;So;0;ON;;;;;N;;;;; +1F8D0;LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +1F8D1;LONG RIGHTWARDS HARPOON OVER LONG LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D2;LONG RIGHTWARDS HARPOON ABOVE SHORT LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D3;SHORT RIGHTWARDS HARPOON ABOVE LONG LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D4;LONG LEFTWARDS HARPOON ABOVE SHORT RIGHTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D5;SHORT LEFTWARDS HARPOON ABOVE LONG RIGHTWARDS HARPOON;Sm;0;ON;;;;;N;;;;; +1F8D6;LONG RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;; +1F8D7;LONG RIGHTWARDS ARROW WITH DOUBLE SLASH;Sm;0;ON;;;;;N;;;;; +1F8D8;LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE;Sm;0;ON;;;;;N;;;;; 1F900;CIRCLED CROSS FORMEE WITH FOUR DOTS;So;0;ON;;;;;N;;;;; 1F901;CIRCLED CROSS FORMEE WITH TWO DOTS;So;0;ON;;;;;N;;;;; 1F902;CIRCLED CROSS FORMEE;So;0;ON;;;;;N;;;;; @@ -38838,6 +39284,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FA51;BLACK CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;; 1FA52;BLACK CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;; 1FA53;BLACK CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;; +1FA54;WHITE CHESS FERZ;So;0;ON;;;;;N;;;;; +1FA55;WHITE CHESS ALFIL;So;0;ON;;;;;N;;;;; +1FA56;BLACK CHESS FERZ;So;0;ON;;;;;N;;;;; +1FA57;BLACK CHESS ALFIL;So;0;ON;;;;;N;;;;; 1FA60;XIANGQI RED GENERAL;So;0;ON;;;;;N;;;;; 1FA61;XIANGQI RED MANDARIN;So;0;ON;;;;;N;;;;; 1FA62;XIANGQI RED ELEPHANT;So;0;ON;;;;;N;;;;; @@ -38875,6 +39325,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FA87;MARACAS;So;0;ON;;;;;N;;;;; 1FA88;FLUTE;So;0;ON;;;;;N;;;;; 1FA89;HARP;So;0;ON;;;;;N;;;;; +1FA8A;TROMBONE;So;0;ON;;;;;N;;;;; +1FA8E;TREASURE CHEST;So;0;ON;;;;;N;;;;; 1FA8F;SHOVEL;So;0;ON;;;;;N;;;;; 1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;; 1FA91;CHAIR;So;0;ON;;;;;N;;;;; @@ -38931,6 +39383,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FAC4;PREGNANT PERSON;So;0;ON;;;;;N;;;;; 1FAC5;PERSON WITH CROWN;So;0;ON;;;;;N;;;;; 1FAC6;FINGERPRINT;So;0;ON;;;;;N;;;;; +1FAC8;HAIRY CREATURE;So;0;ON;;;;;N;;;;; +1FACD;ORCA;So;0;ON;;;;;N;;;;; 1FACE;MOOSE;So;0;ON;;;;;N;;;;; 1FACF;DONKEY;So;0;ON;;;;;N;;;;; 1FAD0;BLUEBERRIES;So;0;ON;;;;;N;;;;; @@ -38957,6 +39411,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FAE7;BUBBLES;So;0;ON;;;;;N;;;;; 1FAE8;SHAKING FACE;So;0;ON;;;;;N;;;;; 1FAE9;FACE WITH BAGS UNDER EYES;So;0;ON;;;;;N;;;;; +1FAEA;DISTORTED FACE;So;0;ON;;;;;N;;;;; +1FAEF;FIGHT CLOUD;So;0;ON;;;;;N;;;;; 1FAF0;HAND WITH INDEX FINGER AND THUMB CROSSED;So;0;ON;;;;;N;;;;; 1FAF1;RIGHTWARDS HAND;So;0;ON;;;;;N;;;;; 1FAF2;LEFTWARDS HAND;So;0;ON;;;;;N;;;;; @@ -39215,14 +39671,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1FBF7;SEGMENTED DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1FBF8;SEGMENTED DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1FBF9;SEGMENTED DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; +1FBFA;ALARM BELL SYMBOL;So;0;ON;;;;;N;;;;; 20000;;Lo;0;L;;;;;N;;;;; 2A6DF;;Lo;0;L;;;;;N;;;;; 2A700;;Lo;0;L;;;;;N;;;;; -2B739;;Lo;0;L;;;;;N;;;;; +2B73F;;Lo;0;L;;;;;N;;;;; 2B740;;Lo;0;L;;;;;N;;;;; 2B81D;;Lo;0;L;;;;;N;;;;; 2B820;;Lo;0;L;;;;;N;;;;; -2CEA1;;Lo;0;L;;;;;N;;;;; +2CEAD;;Lo;0;L;;;;;N;;;;; 2CEB0;;Lo;0;L;;;;;N;;;;; 2EBE0;;Lo;0;L;;;;;N;;;;; 2EBF0;;Lo;0;L;;;;;N;;;;; @@ -39773,6 +40230,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 3134A;;Lo;0;L;;;;;N;;;;; 31350;;Lo;0;L;;;;;N;;;;; 323AF;;Lo;0;L;;;;;N;;;;; +323B0;;Lo;0;L;;;;;N;;;;; +33479;;Lo;0;L;;;;;N;;;;; E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;; E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;; E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;; diff --git a/src/java.base/share/data/unicodedata/auxiliary/GraphemeBreakProperty.txt b/src/java.base/share/data/unicodedata/auxiliary/GraphemeBreakProperty.txt index a4b7b6fbc3ce3..19b13571f347a 100644 --- a/src/java.base/share/data/unicodedata/auxiliary/GraphemeBreakProperty.txt +++ b/src/java.base/share/data/unicodedata/auxiliary/GraphemeBreakProperty.txt @@ -1,6 +1,6 @@ -# GraphemeBreakProperty-16.0.0.txt -# Date: 2024-05-31, 18:09:38 GMT -# Copyright (c) 2024 Unicode, Inc. +# GraphemeBreakProperty-17.0.0.txt +# Date: 2025-06-30, 06:20:23 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 # @@ -30,12 +30,11 @@ 113D1 ; Prepend # Lo TULU-TIGALARI REPHA 1193F ; Prepend # Lo DIVES AKURU PREFIXED NASAL SIGN 11941 ; Prepend # Lo DIVES AKURU INITIAL RA -11A3A ; Prepend # Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA 11A84..11A89 ; Prepend # Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA 11D46 ; Prepend # Lo MASARAM GONDI REPHA 11F02 ; Prepend # Lo KAWI SIGN REPHA -# Total code points: 28 +# Total code points: 27 # ================================================ @@ -243,7 +242,8 @@ E01F0..E0FFF ; Control # Cn [3600] .. 1A7F ; Extend # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT 1AB0..1ABD ; Extend # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW 1ABE ; Extend # Me COMBINING PARENTHESES OVERLAY -1ABF..1ACE ; Extend # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T +1ABF..1ADD ; Extend # Mn [31] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOT-AND-RING BELOW +1AE0..1AEB ; Extend # Mn [12] COMBINING LEFT TACK ABOVE..COMBINING DOUBLE RIGHTWARDS ARROW ABOVE 1B00..1B03 ; Extend # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 1B34 ; Extend # Mn BALINESE SIGN REREKAN 1B35 ; Extend # Mc BALINESE VOWEL SIGN TEDUNG @@ -339,7 +339,7 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT 10D24..10D27 ; Extend # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI 10D69..10D6D ; Extend # Mn [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK 10EAB..10EAC ; Extend # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK -10EFC..10EFF ; Extend # Mn [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA +10EFA..10EFF ; Extend # Mn [6] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC SMALL LOW WORD MADDA 10F46..10F50 ; Extend # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW 10F82..10F85 ; Extend # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW 11001 ; Extend # Mn BRAHMI SIGN ANUSVARA @@ -430,6 +430,9 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT 11A59..11A5B ; Extend # Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK 11A8A..11A96 ; Extend # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA 11A98..11A99 ; Extend # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER +11B60 ; Extend # Mn SHARADA VOWEL SIGN OE +11B62..11B64 ; Extend # Mn [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E +11B66 ; Extend # Mn SHARADA VOWEL SIGN CANDRA E 11C30..11C36 ; Extend # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L 11C38..11C3D ; Extend # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA 11C3F ; Extend # Mn BHAIKSUKI SIGN VIRAMA @@ -489,13 +492,17 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT 1E2EC..1E2EF ; Extend # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI 1E4EC..1E4EF ; Extend # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH 1E5EE..1E5EF ; Extend # Mn [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR +1E6E3 ; Extend # Mn TAI YO SIGN UE +1E6E6 ; Extend # Mn TAI YO SIGN AU +1E6EE..1E6EF ; Extend # Mn [2] TAI YO SIGN AY..TAI YO SIGN ANG +1E6F5 ; Extend # Mn TAI YO SIGN OM 1E8D0..1E8D6 ; Extend # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS 1E944..1E94A ; Extend # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA 1F3FB..1F3FF ; Extend # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 E0020..E007F ; Extend # Cf [96] TAG SPACE..CANCEL TAG E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 -# Total code points: 2198 +# Total code points: 2237 # ================================================ @@ -646,6 +653,9 @@ ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK 11A39 ; SpacingMark # Mc ZANABAZAR SQUARE SIGN VISARGA 11A57..11A58 ; SpacingMark # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU 11A97 ; SpacingMark # Mc SOYOMBO SIGN VISARGA +11B61 ; SpacingMark # Mc SHARADA VOWEL SIGN OOE +11B65 ; SpacingMark # Mc SHARADA VOWEL SIGN SHORT O +11B67 ; SpacingMark # Mc SHARADA VOWEL SIGN CANDRA O 11C2F ; SpacingMark # Mc BHAIKSUKI VOWEL SIGN AA 11C3E ; SpacingMark # Mc BHAIKSUKI SIGN VISARGA 11CA9 ; SpacingMark # Mc MARCHEN SUBJOINED LETTER YA @@ -661,7 +671,7 @@ ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK 1612A..1612C ; SpacingMark # Mc [3] GURUNG KHEMA CONSONANT SIGN MEDIAL YA..GURUNG KHEMA CONSONANT SIGN MEDIAL HA 16F51..16F87 ; SpacingMark # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI -# Total code points: 378 +# Total code points: 381 # ================================================ diff --git a/src/java.base/share/data/unicodedata/auxiliary/GraphemeBreakTest.txt b/src/java.base/share/data/unicodedata/auxiliary/GraphemeBreakTest.txt index 3eb4b307e8e8b..e1215547c58f9 100644 --- a/src/java.base/share/data/unicodedata/auxiliary/GraphemeBreakTest.txt +++ b/src/java.base/share/data/unicodedata/auxiliary/GraphemeBreakTest.txt @@ -1,6 +1,6 @@ -# GraphemeBreakTest-16.0.0.txt -# Date: 2024-05-02, 15:02:48 GMT -# Copyright (c) 2024 Unicode, Inc. +# GraphemeBreakTest-17.0.0.txt +# Date: 2025-03-24, 14:45:55 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 # @@ -16,1106 +16,781 @@ # × wherever there is not. # the format can change, but currently it shows: # - the sample character name -# - (x) the Grapheme_Cluster_Break property value for the sample character +# - (x) the Grapheme_Cluster_Break property value for the sample character and +# any other properties relevant to the algorithm, as described in +# GraphemeBreakTest.html # - [x] the rule that determines whether there is a break or not, # as listed in the Rules section of GraphemeBreakTest.html # # These samples may be extended or changed in the future. # -÷ 0020 ÷ 0020 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0020 × 0308 ÷ 0020 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0020 ÷ 000D ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] (CR) ÷ [0.3] -÷ 0020 × 0308 ÷ 000D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0020 ÷ 000A ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] (LF) ÷ [0.3] -÷ 0020 × 0308 ÷ 000A ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0020 ÷ 0001 ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] (Control) ÷ [0.3] -÷ 0020 × 0308 ÷ 0001 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0020 × 200C ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0020 × 0308 × 200C ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0020 ÷ 1F1E6 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0020 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0020 ÷ 0600 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0020 × 0308 ÷ 0600 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0020 × 0A03 ÷ # ÷ [0.2] SPACE (Other) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0020 × 0308 × 0A03 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0020 ÷ 1100 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0020 × 0308 ÷ 1100 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0020 ÷ 1160 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0020 × 0308 ÷ 1160 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0020 ÷ 11A8 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0020 × 0308 ÷ 11A8 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0020 ÷ AC00 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0020 × 0308 ÷ AC00 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0020 ÷ AC01 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0020 × 0308 ÷ AC01 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0020 × 0903 ÷ # ÷ [0.2] SPACE (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0020 × 0308 × 0903 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0020 ÷ 0904 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0020 × 0308 ÷ 0904 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0020 ÷ 0D4E ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0020 × 0308 ÷ 0D4E ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0020 ÷ 0915 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0020 × 0308 ÷ 0915 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0020 ÷ 231A ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0020 × 0308 ÷ 231A ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0020 × 0300 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0020 × 0308 × 0300 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0020 × 0900 ÷ # ÷ [0.2] SPACE (Other) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0020 × 0308 × 0900 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0020 × 094D ÷ # ÷ [0.2] SPACE (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0020 × 0308 × 094D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0020 × 200D ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0020 × 0308 × 200D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0020 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] (Other) ÷ [0.3] -÷ 0020 × 0308 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 000D ÷ 0020 ÷ # ÷ [0.2] (CR) ÷ [4.0] SPACE (Other) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] ÷ 000D ÷ 000D ÷ # ÷ [0.2] (CR) ÷ [4.0] (CR) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] ÷ 000D × 000A ÷ # ÷ [0.2] (CR) × [3.0] (LF) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 000D ÷ 0001 ÷ # ÷ [0.2] (CR) ÷ [4.0] (Control) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 000D ÷ 200C ÷ # ÷ [0.2] (CR) ÷ [4.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 000D ÷ 0308 × 200C ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 000D ÷ 0000 ÷ # ÷ [0.2] (CR) ÷ [4.0] (Control) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0000 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 000D ÷ 094D ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 000D ÷ 0308 × 094D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 000D ÷ 0300 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 000D ÷ 0308 × 0300 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 000D ÷ 200C ÷ # ÷ [0.2] (CR) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 000D ÷ 0308 × 200C ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 000D ÷ 200D ÷ # ÷ [0.2] (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 000D ÷ 0308 × 200D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] ÷ 000D ÷ 1F1E6 ÷ # ÷ [0.2] (CR) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 000D ÷ 0600 ÷ # ÷ [0.2] (CR) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 000D ÷ 0A03 ÷ # ÷ [0.2] (CR) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 000D ÷ 0308 × 0A03 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 000D ÷ 06DD ÷ # ÷ [0.2] (CR) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 06DD ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 000D ÷ 0903 ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 000D ÷ 0308 × 0903 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] ÷ 000D ÷ 1100 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ 000D ÷ 1160 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] ÷ 000D ÷ 11A8 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] ÷ 000D ÷ AC00 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 000D ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 000D ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] ÷ 000D ÷ AC01 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 000D ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 000D ÷ 0903 ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 000D ÷ 0308 × 0903 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 000D ÷ 0904 ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 0904 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 000D ÷ 0D4E ÷ # ÷ [0.2] (CR) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 0D4E ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 000D ÷ 0915 ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 000D ÷ 231A ÷ # ÷ [0.2] (CR) ÷ [4.0] WATCH (ExtPict) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 231A ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 000D ÷ 0300 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 000D ÷ 0308 × 0300 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 000D ÷ 0900 ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 000D ÷ 0308 × 0900 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 000D ÷ 094D ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 000D ÷ 0308 × 094D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 000D ÷ 200D ÷ # ÷ [0.2] (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 000D ÷ 0308 × 200D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 000D ÷ 0378 ÷ # ÷ [0.2] (CR) ÷ [4.0] (Other) ÷ [0.3] -÷ 000D ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 000A ÷ 0020 ÷ # ÷ [0.2] (LF) ÷ [4.0] SPACE (Other) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 000D ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 000D ÷ 0915 ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 000D ÷ 00A9 ÷ # ÷ [0.2] (CR) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 00A9 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 000D ÷ 0020 ÷ # ÷ [0.2] (CR) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 000D ÷ 0378 ÷ # ÷ [0.2] (CR) ÷ [4.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] ÷ 000A ÷ 000D ÷ # ÷ [0.2] (LF) ÷ [4.0] (CR) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] ÷ 000A ÷ 000A ÷ # ÷ [0.2] (LF) ÷ [4.0] (LF) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 000A ÷ 0001 ÷ # ÷ [0.2] (LF) ÷ [4.0] (Control) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 000A ÷ 200C ÷ # ÷ [0.2] (LF) ÷ [4.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 000A ÷ 0308 × 200C ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 000A ÷ 0000 ÷ # ÷ [0.2] (LF) ÷ [4.0] (Control) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0000 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 000A ÷ 094D ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 000A ÷ 0308 × 094D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 000A ÷ 0300 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 000A ÷ 0308 × 0300 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 000A ÷ 200C ÷ # ÷ [0.2] (LF) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 000A ÷ 0308 × 200C ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 000A ÷ 200D ÷ # ÷ [0.2] (LF) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 000A ÷ 0308 × 200D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] ÷ 000A ÷ 1F1E6 ÷ # ÷ [0.2] (LF) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 000A ÷ 0600 ÷ # ÷ [0.2] (LF) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 000A ÷ 0A03 ÷ # ÷ [0.2] (LF) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 000A ÷ 0308 × 0A03 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 000A ÷ 06DD ÷ # ÷ [0.2] (LF) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 06DD ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 000A ÷ 0903 ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 000A ÷ 0308 × 0903 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] ÷ 000A ÷ 1100 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ 000A ÷ 1160 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] ÷ 000A ÷ 11A8 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] ÷ 000A ÷ AC00 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 000A ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 000A ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] ÷ 000A ÷ AC01 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 000A ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 000A ÷ 0903 ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 000A ÷ 0308 × 0903 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 000A ÷ 0904 ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 0904 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 000A ÷ 0D4E ÷ # ÷ [0.2] (LF) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 0D4E ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 000A ÷ 0915 ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 000A ÷ 231A ÷ # ÷ [0.2] (LF) ÷ [4.0] WATCH (ExtPict) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 231A ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 000A ÷ 0300 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 000A ÷ 0308 × 0300 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 000A ÷ 0900 ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 000A ÷ 0308 × 0900 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 000A ÷ 094D ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 000A ÷ 0308 × 094D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 000A ÷ 200D ÷ # ÷ [0.2] (LF) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 000A ÷ 0308 × 200D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 000A ÷ 0378 ÷ # ÷ [0.2] (LF) ÷ [4.0] (Other) ÷ [0.3] -÷ 000A ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0001 ÷ 0020 ÷ # ÷ [0.2] (Control) ÷ [4.0] SPACE (Other) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0001 ÷ 000D ÷ # ÷ [0.2] (Control) ÷ [4.0] (CR) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0001 ÷ 000A ÷ # ÷ [0.2] (Control) ÷ [4.0] (LF) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0001 ÷ 0001 ÷ # ÷ [0.2] (Control) ÷ [4.0] (Control) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0001 ÷ 200C ÷ # ÷ [0.2] (Control) ÷ [4.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0001 ÷ 0308 × 200C ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0001 ÷ 1F1E6 ÷ # ÷ [0.2] (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0001 ÷ 0600 ÷ # ÷ [0.2] (Control) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0001 ÷ 0A03 ÷ # ÷ [0.2] (Control) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0001 ÷ 0308 × 0A03 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0001 ÷ 1100 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0001 ÷ 1160 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0001 ÷ 11A8 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0001 ÷ AC00 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0001 ÷ AC01 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0001 ÷ 0903 ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0001 ÷ 0308 × 0903 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0001 ÷ 0904 ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 0904 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0001 ÷ 0D4E ÷ # ÷ [0.2] (Control) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 0D4E ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0001 ÷ 0915 ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0001 ÷ 231A ÷ # ÷ [0.2] (Control) ÷ [4.0] WATCH (ExtPict) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 231A ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0001 ÷ 0300 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0001 ÷ 0308 × 0300 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0001 ÷ 0900 ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0001 ÷ 0308 × 0900 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0001 ÷ 094D ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0001 ÷ 0308 × 094D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0001 ÷ 200D ÷ # ÷ [0.2] (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0001 ÷ 0308 × 200D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0001 ÷ 0378 ÷ # ÷ [0.2] (Control) ÷ [4.0] (Other) ÷ [0.3] -÷ 0001 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 200C ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 200C × 0308 ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 200C ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [5.0] (CR) ÷ [0.3] -÷ 200C × 0308 ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 200C ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [5.0] (LF) ÷ [0.3] -÷ 200C × 0308 ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 200C ÷ 0001 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [5.0] (Control) ÷ [0.3] -÷ 200C × 0308 ÷ 0001 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 200C × 200C ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 200C × 0308 × 200C ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 200C ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 200C × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 200C ÷ 0600 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 200C × 0308 ÷ 0600 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 200C × 0A03 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 200C × 0308 × 0A03 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 200C ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 200C × 0308 ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 200C ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 200C × 0308 ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 200C ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 200C × 0308 ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 200C ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 200C × 0308 ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 200C ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 200C × 0308 ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 200C × 0903 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 200C × 0308 × 0903 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 200C ÷ 0904 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 200C × 0308 ÷ 0904 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 200C ÷ 0D4E ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 200C × 0308 ÷ 0D4E ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 200C ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 200C × 0308 ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 200C ÷ 231A ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 200C × 0308 ÷ 231A ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 200C × 0300 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 200C × 0308 × 0300 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 200C × 0900 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 200C × 0308 × 0900 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 200C × 094D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 200C × 0308 × 094D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 200C × 200D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 200C × 0308 × 200D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 200C ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] (Other) ÷ [0.3] -÷ 200C × 0308 ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 1F1E6 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 000A ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 000A ÷ 0915 ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 000A ÷ 00A9 ÷ # ÷ [0.2] (LF) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 00A9 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 000A ÷ 0020 ÷ # ÷ [0.2] (LF) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 000A ÷ 0378 ÷ # ÷ [0.2] (LF) ÷ [4.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0000 ÷ 000D ÷ # ÷ [0.2] (Control) ÷ [4.0] (CR) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 0000 ÷ 000A ÷ # ÷ [0.2] (Control) ÷ [4.0] (LF) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 0000 ÷ 0000 ÷ # ÷ [0.2] (Control) ÷ [4.0] (Control) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 0000 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 0000 ÷ 094D ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0000 ÷ 0308 × 094D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0000 ÷ 0300 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0000 ÷ 0308 × 0300 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0000 ÷ 200C ÷ # ÷ [0.2] (Control) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0000 ÷ 0308 × 200C ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0000 ÷ 200D ÷ # ÷ [0.2] (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0000 ÷ 0308 × 200D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0000 ÷ 1F1E6 ÷ # ÷ [0.2] (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0000 ÷ 06DD ÷ # ÷ [0.2] (Control) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 06DD ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0000 ÷ 0903 ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0000 ÷ 0308 × 0903 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0000 ÷ 1100 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0000 ÷ 1160 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0000 ÷ 11A8 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0000 ÷ AC00 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0000 ÷ AC01 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0000 ÷ 0915 ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0000 ÷ 00A9 ÷ # ÷ [0.2] (Control) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 00A9 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0000 ÷ 0020 ÷ # ÷ [0.2] (Control) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0000 ÷ 0378 ÷ # ÷ [0.2] (Control) ÷ [4.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0000 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 094D ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 094D × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 094D ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 094D × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 094D ÷ 0000 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 094D × 0308 ÷ 0000 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 094D × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 094D × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 094D × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 094D × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 094D × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 094D × 0308 × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 094D × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 094D × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 094D ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 094D × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 094D ÷ 06DD ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 094D × 0308 ÷ 06DD ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 094D × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 094D × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 094D ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 094D × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 094D ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 094D × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 094D ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 094D × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 094D ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 094D × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 094D ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 094D × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 094D ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 094D × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 094D ÷ 00A9 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 094D × 0308 ÷ 00A9 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 094D ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 094D × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 094D ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 094D × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0300 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 0300 × 0308 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 0300 ÷ 000A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 0300 × 0308 ÷ 000A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 0300 ÷ 0000 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 0300 × 0308 ÷ 0000 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 0300 × 094D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0300 × 0308 × 094D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0300 × 0300 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0300 × 0308 × 0300 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0300 × 200C ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0300 × 0308 × 200C ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0300 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0300 × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0300 ÷ 1F1E6 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0300 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0300 ÷ 06DD ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0300 × 0308 ÷ 06DD ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0300 × 0903 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0300 × 0308 × 0903 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0300 ÷ 1100 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0300 × 0308 ÷ 1100 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0300 ÷ 1160 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0300 × 0308 ÷ 1160 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0300 ÷ 11A8 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0300 × 0308 ÷ 11A8 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0300 ÷ AC00 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0300 × 0308 ÷ AC00 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0300 ÷ AC01 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0300 × 0308 ÷ AC01 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0300 ÷ 0915 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0300 × 0308 ÷ 0915 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0300 ÷ 00A9 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0300 × 0308 ÷ 00A9 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0300 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0300 × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0300 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0300 × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 200C ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] (CR) ÷ [0.3] +÷ 200C × 0308 ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 200C ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] (LF) ÷ [0.3] +÷ 200C × 0308 ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 200C ÷ 0000 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] (Control) ÷ [0.3] +÷ 200C × 0308 ÷ 0000 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 200C × 094D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 200C × 0308 × 094D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 200C × 0300 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 200C × 0308 × 0300 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 200C × 200C ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 200C × 0308 × 200C ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 200C × 200D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 200C × 0308 × 200D ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 200C ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 200C × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 200C ÷ 06DD ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 200C × 0308 ÷ 06DD ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 200C × 0903 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 200C × 0308 × 0903 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 200C ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 200C × 0308 ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 200C ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 200C × 0308 ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 200C ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 200C × 0308 ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 200C ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 200C × 0308 ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 200C ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 200C × 0308 ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 200C ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 200C × 0308 ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 200C ÷ 00A9 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 200C × 0308 ÷ 00A9 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 200C ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 200C × 0308 ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 200C ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 200C × 0308 ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 200D ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] (CR) ÷ [0.3] +÷ 200D × 0308 ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 200D ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] (LF) ÷ [0.3] +÷ 200D × 0308 ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 200D ÷ 0000 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] (Control) ÷ [0.3] +÷ 200D × 0308 ÷ 0000 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 200D × 094D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 200D × 0308 × 094D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 200D × 0300 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 200D × 0308 × 0300 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 200D × 200C ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 200D × 0308 × 200C ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 200D × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 200D × 0308 × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 200D ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 200D × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 200D ÷ 06DD ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 200D × 0308 ÷ 06DD ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 200D × 0903 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 200D × 0308 × 0903 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 200D ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 200D × 0308 ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 200D ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 200D × 0308 ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 200D ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 200D × 0308 ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 200D ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 200D × 0308 ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 200D ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 200D × 0308 ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 200D ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 200D × 0308 ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 200D ÷ 00A9 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 200D × 0308 ÷ 00A9 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 200D ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 200D × 0308 ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 200D ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 200D × 0308 ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] ÷ 1F1E6 ÷ 000D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (CR) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 000D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 000D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] ÷ 1F1E6 ÷ 000A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (LF) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 000A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 1F1E6 ÷ 0001 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (Control) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 0001 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 1F1E6 × 200C ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 1F1E6 × 0308 × 200C ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 000A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 1F1E6 ÷ 0000 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (Control) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0000 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 1F1E6 × 094D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 1F1E6 × 0308 × 094D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 1F1E6 × 0300 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1F1E6 × 0308 × 0300 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1F1E6 × 200C ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 1F1E6 × 0308 × 200C ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 1F1E6 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 1F1E6 × 0308 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] ÷ 1F1E6 × 1F1E6 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 1F1E6 ÷ 0600 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 0600 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 1F1E6 × 0A03 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 1F1E6 × 0308 × 0A03 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1F1E6 ÷ 06DD ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 06DD ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 1F1E6 × 0903 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1F1E6 × 0308 × 0903 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] ÷ 1F1E6 ÷ 1100 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 1100 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 1100 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ 1F1E6 ÷ 1160 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 1160 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 1160 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] ÷ 1F1E6 ÷ 11A8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 11A8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 11A8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] ÷ 1F1E6 ÷ AC00 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ AC00 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ AC00 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] ÷ 1F1E6 ÷ AC01 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ AC01 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 1F1E6 × 0903 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 1F1E6 × 0308 × 0903 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 1F1E6 ÷ 0904 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 0904 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 1F1E6 ÷ 0D4E ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 0D4E ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 1F1E6 ÷ 0915 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 0915 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 1F1E6 ÷ 231A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 231A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 1F1E6 × 0300 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 1F1E6 × 0308 × 0300 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 1F1E6 × 0900 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 1F1E6 × 0308 × 0900 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 1F1E6 × 094D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 1F1E6 × 0308 × 094D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 1F1E6 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 1F1E6 × 0308 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 1F1E6 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] (Other) ÷ [0.3] -÷ 1F1E6 × 0308 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0600 × 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] SPACE (Other) ÷ [0.3] -÷ 0600 × 0308 ÷ 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0600 ÷ 000D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (CR) ÷ [0.3] -÷ 0600 × 0308 ÷ 000D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0600 ÷ 000A ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (LF) ÷ [0.3] -÷ 0600 × 0308 ÷ 000A ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0600 ÷ 0001 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (Control) ÷ [0.3] -÷ 0600 × 0308 ÷ 0001 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0600 × 200C ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0600 × 0308 × 200C ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0600 × 1F1E6 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0600 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0600 × 0600 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0600 × 0308 ÷ 0600 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0600 × 0A03 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0600 × 0308 × 0A03 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0600 × 1100 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0600 × 0308 ÷ 1100 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0600 × 1160 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0600 × 0308 ÷ 1160 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0600 × 11A8 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0600 × 0308 ÷ 11A8 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0600 × AC00 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0600 × 0308 ÷ AC00 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0600 × AC01 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0600 × 0308 ÷ AC01 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0600 × 0903 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0600 × 0308 × 0903 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0600 × 0904 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0600 × 0308 ÷ 0904 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0600 × 0D4E ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0600 × 0308 ÷ 0D4E ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0600 × 0915 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0600 × 0308 ÷ 0915 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0600 × 231A ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] WATCH (ExtPict) ÷ [0.3] -÷ 0600 × 0308 ÷ 231A ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0600 × 0300 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0600 × 0308 × 0300 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0600 × 0900 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0600 × 0308 × 0900 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0600 × 094D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0600 × 0308 × 094D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0600 × 200D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0600 × 0308 × 200D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0600 × 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] (Other) ÷ [0.3] -÷ 0600 × 0308 ÷ 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0A03 ÷ 0020 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0A03 × 0308 ÷ 0020 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0A03 ÷ 000D ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] (CR) ÷ [0.3] -÷ 0A03 × 0308 ÷ 000D ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0A03 ÷ 000A ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] (LF) ÷ [0.3] -÷ 0A03 × 0308 ÷ 000A ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0A03 ÷ 0001 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] (Control) ÷ [0.3] -÷ 0A03 × 0308 ÷ 0001 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0A03 × 200C ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0A03 × 0308 × 200C ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0A03 ÷ 1F1E6 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0A03 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0A03 ÷ 0600 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0A03 × 0308 ÷ 0600 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0A03 × 0A03 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0A03 × 0308 × 0A03 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0A03 ÷ 1100 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0A03 × 0308 ÷ 1100 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0A03 ÷ 1160 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0A03 × 0308 ÷ 1160 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0A03 ÷ 11A8 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0A03 × 0308 ÷ 11A8 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0A03 ÷ AC00 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0A03 × 0308 ÷ AC00 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0A03 ÷ AC01 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0A03 × 0308 ÷ AC01 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0A03 × 0903 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0A03 × 0308 × 0903 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0A03 ÷ 0904 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0A03 × 0308 ÷ 0904 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0A03 ÷ 0D4E ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0A03 × 0308 ÷ 0D4E ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0A03 ÷ 0915 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0A03 × 0308 ÷ 0915 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0A03 ÷ 231A ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0A03 × 0308 ÷ 231A ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0A03 × 0300 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0A03 × 0308 × 0300 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0A03 × 0900 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0A03 × 0308 × 0900 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0A03 × 094D ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0A03 × 0308 × 094D ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0A03 × 200D ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0A03 × 0308 × 200D ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0A03 ÷ 0378 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] (Other) ÷ [0.3] -÷ 0A03 × 0308 ÷ 0378 ÷ # ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 1100 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 1100 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ AC01 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1F1E6 ÷ 0915 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0915 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1F1E6 ÷ 00A9 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 00A9 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 1F1E6 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1F1E6 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 06DD ÷ 000D ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] (CR) ÷ [0.3] +÷ 06DD × 0308 ÷ 000D ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 06DD ÷ 000A ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] (LF) ÷ [0.3] +÷ 06DD × 0308 ÷ 000A ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 06DD ÷ 0000 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] (Control) ÷ [0.3] +÷ 06DD × 0308 ÷ 0000 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 06DD × 094D ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 06DD × 0308 × 094D ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 06DD × 0300 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 06DD × 0308 × 0300 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 06DD × 200C ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 06DD × 0308 × 200C ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 06DD × 200D ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 06DD × 0308 × 200D ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 06DD × 1F1E6 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 06DD × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 06DD × 06DD ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 06DD × 0308 ÷ 06DD ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 06DD × 0903 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 06DD × 0308 × 0903 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 06DD × 1100 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 06DD × 0308 ÷ 1100 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 06DD × 1160 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 06DD × 0308 ÷ 1160 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 06DD × 11A8 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 06DD × 0308 ÷ 11A8 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 06DD × AC00 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 06DD × 0308 ÷ AC00 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 06DD × AC01 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 06DD × 0308 ÷ AC01 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 06DD × 0915 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 06DD × 0308 ÷ 0915 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 06DD × 00A9 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 06DD × 0308 ÷ 00A9 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 06DD × 0020 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 06DD × 0308 ÷ 0020 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 06DD × 0378 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 06DD × 0308 ÷ 0378 ÷ # ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0903 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (CR) ÷ [0.3] +÷ 0903 × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 0903 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (LF) ÷ [0.3] +÷ 0903 × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 0903 ÷ 0000 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (Control) ÷ [0.3] +÷ 0903 × 0308 ÷ 0000 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 0903 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0903 × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0903 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0903 × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0903 × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0903 × 0308 × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0903 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0903 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0903 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0903 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0903 ÷ 06DD ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0903 × 0308 ÷ 06DD ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0903 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0903 × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0903 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0903 × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0903 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0903 × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0903 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0903 × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0903 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0903 × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0903 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0903 × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0903 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0903 × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0903 ÷ 00A9 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0903 × 0308 ÷ 00A9 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0903 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0903 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0903 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0903 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] ÷ 1100 ÷ 000D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (CR) ÷ [0.3] -÷ 1100 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 1100 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] ÷ 1100 ÷ 000A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (LF) ÷ [0.3] -÷ 1100 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 1100 ÷ 0001 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (Control) ÷ [0.3] -÷ 1100 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 1100 × 200C ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 1100 × 0308 × 200C ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] +÷ 1100 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 1100 ÷ 0000 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (Control) ÷ [0.3] +÷ 1100 × 0308 ÷ 0000 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 1100 × 094D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 1100 × 0308 × 094D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 1100 × 0300 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1100 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1100 × 200C ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 1100 × 0308 × 200C ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 1100 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 1100 × 0308 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] ÷ 1100 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 1100 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 1100 ÷ 0600 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 1100 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 1100 × 0A03 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 1100 × 0308 × 0A03 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1100 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1100 ÷ 06DD ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 1100 × 0308 ÷ 06DD ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 1100 × 0903 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1100 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] ÷ 1100 × 1100 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 1100 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1100 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ 1100 × 1160 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 1100 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1100 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] ÷ 1100 ÷ 11A8 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 1100 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1100 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] ÷ 1100 × AC00 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 1100 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1100 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] ÷ 1100 × AC01 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 1100 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 1100 × 0903 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 1100 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 1100 ÷ 0904 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 1100 × 0308 ÷ 0904 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 1100 ÷ 0D4E ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 1100 × 0308 ÷ 0D4E ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 1100 ÷ 0915 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 1100 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 1100 ÷ 231A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 1100 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 1100 × 0300 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 1100 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 1100 × 0900 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 1100 × 0308 × 0900 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 1100 × 094D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 1100 × 0308 × 094D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 1100 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 1100 × 0308 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 1100 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] (Other) ÷ [0.3] -÷ 1100 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 1160 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 1160 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1100 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1100 ÷ 0915 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1100 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1100 ÷ 00A9 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 1100 × 0308 ÷ 00A9 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 1100 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1100 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1100 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1100 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] ÷ 1160 ÷ 000D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (CR) ÷ [0.3] -÷ 1160 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 1160 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] ÷ 1160 ÷ 000A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (LF) ÷ [0.3] -÷ 1160 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 1160 ÷ 0001 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (Control) ÷ [0.3] -÷ 1160 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 1160 × 200C ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 1160 × 0308 × 200C ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] +÷ 1160 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 1160 ÷ 0000 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (Control) ÷ [0.3] +÷ 1160 × 0308 ÷ 0000 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 1160 × 094D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 1160 × 0308 × 094D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 1160 × 0300 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1160 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1160 × 200C ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 1160 × 0308 × 200C ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 1160 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 1160 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] ÷ 1160 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 1160 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 1160 ÷ 0600 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 1160 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 1160 × 0A03 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 1160 × 0308 × 0A03 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1160 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1160 ÷ 06DD ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 1160 × 0308 ÷ 06DD ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 1160 × 0903 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1160 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] ÷ 1160 ÷ 1100 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 1160 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1160 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ 1160 × 1160 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 1160 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1160 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] ÷ 1160 × 11A8 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 1160 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1160 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] ÷ 1160 ÷ AC00 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 1160 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1160 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] ÷ 1160 ÷ AC01 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 1160 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 1160 × 0903 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 1160 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 1160 ÷ 0904 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 1160 × 0308 ÷ 0904 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 1160 ÷ 0D4E ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 1160 × 0308 ÷ 0D4E ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 1160 ÷ 0915 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 1160 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 1160 ÷ 231A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 1160 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 1160 × 0300 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 1160 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 1160 × 0900 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 1160 × 0308 × 0900 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 1160 × 094D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 1160 × 0308 × 094D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 1160 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 1160 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 1160 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] (Other) ÷ [0.3] -÷ 1160 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 11A8 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 11A8 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1160 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1160 ÷ 0915 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1160 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1160 ÷ 00A9 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 1160 × 0308 ÷ 00A9 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 1160 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1160 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1160 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1160 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] ÷ 11A8 ÷ 000D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (CR) ÷ [0.3] -÷ 11A8 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 11A8 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] ÷ 11A8 ÷ 000A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (LF) ÷ [0.3] -÷ 11A8 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 11A8 ÷ 0001 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (Control) ÷ [0.3] -÷ 11A8 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 11A8 × 200C ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 11A8 × 0308 × 200C ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] +÷ 11A8 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 11A8 ÷ 0000 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (Control) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0000 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 11A8 × 094D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 11A8 × 0308 × 094D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 11A8 × 0300 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 11A8 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 11A8 × 200C ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 11A8 × 0308 × 200C ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 11A8 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 11A8 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] ÷ 11A8 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 11A8 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 11A8 ÷ 0600 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 11A8 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 11A8 × 0A03 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 11A8 × 0308 × 0A03 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 11A8 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 11A8 ÷ 06DD ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 11A8 × 0308 ÷ 06DD ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 11A8 × 0903 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 11A8 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] ÷ 11A8 ÷ 1100 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 11A8 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 11A8 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ 11A8 ÷ 1160 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 11A8 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 11A8 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] ÷ 11A8 × 11A8 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 11A8 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 11A8 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] ÷ 11A8 ÷ AC00 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 11A8 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 11A8 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] ÷ 11A8 ÷ AC01 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 11A8 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 11A8 × 0903 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 11A8 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 11A8 ÷ 0904 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 11A8 × 0308 ÷ 0904 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 11A8 ÷ 0D4E ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 11A8 × 0308 ÷ 0D4E ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 11A8 ÷ 0915 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 11A8 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 11A8 ÷ 231A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 11A8 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 11A8 × 0300 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 11A8 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 11A8 × 0900 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 11A8 × 0308 × 0900 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 11A8 × 094D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 11A8 × 0308 × 094D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 11A8 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 11A8 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 11A8 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] (Other) ÷ [0.3] -÷ 11A8 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ AC00 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ AC00 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 11A8 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 11A8 ÷ 0915 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 11A8 ÷ 00A9 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 11A8 × 0308 ÷ 00A9 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 11A8 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 11A8 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] ÷ AC00 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (CR) ÷ [0.3] -÷ AC00 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ AC00 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] ÷ AC00 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (LF) ÷ [0.3] -÷ AC00 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ AC00 ÷ 0001 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (Control) ÷ [0.3] -÷ AC00 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ AC00 × 200C ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ AC00 × 0308 × 200C ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] +÷ AC00 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ AC00 ÷ 0000 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (Control) ÷ [0.3] +÷ AC00 × 0308 ÷ 0000 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ AC00 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ AC00 × 0308 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ AC00 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ AC00 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ AC00 × 200C ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ AC00 × 0308 × 200C ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ AC00 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ AC00 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] ÷ AC00 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ AC00 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ AC00 ÷ 0600 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ AC00 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ AC00 × 0A03 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ AC00 × 0308 × 0A03 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ AC00 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ AC00 ÷ 06DD ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ AC00 × 0308 ÷ 06DD ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ AC00 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ AC00 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] ÷ AC00 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ AC00 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ AC00 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ AC00 × 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ AC00 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ AC00 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] ÷ AC00 × 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ AC00 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ AC00 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] ÷ AC00 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ AC00 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ AC00 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] ÷ AC00 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ AC00 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ AC00 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ AC00 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ AC00 ÷ 0904 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ AC00 × 0308 ÷ 0904 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ AC00 ÷ 0D4E ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ AC00 × 0308 ÷ 0D4E ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ AC00 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ AC00 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ AC00 ÷ 231A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ AC00 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ AC00 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ AC00 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ AC00 × 0900 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ AC00 × 0308 × 0900 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ AC00 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ AC00 × 0308 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ AC00 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ AC00 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ AC00 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] (Other) ÷ [0.3] -÷ AC00 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ AC01 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ AC01 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ AC00 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ AC00 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ AC00 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ AC00 ÷ 00A9 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ AC00 × 0308 ÷ 00A9 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ AC00 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ AC00 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ AC00 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ AC00 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] ÷ AC01 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (CR) ÷ [0.3] -÷ AC01 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ AC01 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] ÷ AC01 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (LF) ÷ [0.3] -÷ AC01 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ AC01 ÷ 0001 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (Control) ÷ [0.3] -÷ AC01 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ AC01 × 200C ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ AC01 × 0308 × 200C ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] +÷ AC01 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ AC01 ÷ 0000 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (Control) ÷ [0.3] +÷ AC01 × 0308 ÷ 0000 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ AC01 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ AC01 × 0308 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ AC01 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ AC01 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ AC01 × 200C ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ AC01 × 0308 × 200C ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ AC01 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ AC01 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] ÷ AC01 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ AC01 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ AC01 ÷ 0600 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ AC01 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ AC01 × 0A03 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ AC01 × 0308 × 0A03 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ AC01 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ AC01 ÷ 06DD ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ AC01 × 0308 ÷ 06DD ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ AC01 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ AC01 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] ÷ AC01 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ AC01 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ AC01 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ AC01 ÷ 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ AC01 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ AC01 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] ÷ AC01 × 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ AC01 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ AC01 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] ÷ AC01 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ AC01 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ AC01 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] ÷ AC01 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ AC01 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ AC01 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ AC01 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ AC01 ÷ 0904 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ AC01 × 0308 ÷ 0904 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ AC01 ÷ 0D4E ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ AC01 × 0308 ÷ 0D4E ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ AC01 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ AC01 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ AC01 ÷ 231A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ AC01 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ AC01 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ AC01 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ AC01 × 0900 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ AC01 × 0308 × 0900 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ AC01 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ AC01 × 0308 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ AC01 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ AC01 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ AC01 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] (Other) ÷ [0.3] -÷ AC01 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0903 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0903 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0903 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] (CR) ÷ [0.3] -÷ 0903 × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0903 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] (LF) ÷ [0.3] -÷ 0903 × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0903 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] (Control) ÷ [0.3] -÷ 0903 × 0308 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0903 × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0903 × 0308 × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0903 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0903 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0903 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0903 × 0308 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0903 × 0A03 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0903 × 0308 × 0A03 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0903 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0903 × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0903 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0903 × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0903 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0903 × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0903 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0903 × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0903 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0903 × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0903 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0903 × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0903 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0903 × 0308 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0903 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0903 × 0308 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0903 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0903 × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0903 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0903 × 0308 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0903 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0903 × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0903 × 0900 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0903 × 0308 × 0900 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0903 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0903 × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0903 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0903 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0903 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] (Other) ÷ [0.3] -÷ 0903 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0904 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0904 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0904 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] (CR) ÷ [0.3] -÷ 0904 × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0904 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] (LF) ÷ [0.3] -÷ 0904 × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0904 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] (Control) ÷ [0.3] -÷ 0904 × 0308 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0904 × 200C ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0904 × 0308 × 200C ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0904 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0904 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0904 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0904 × 0308 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0904 × 0A03 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0904 × 0308 × 0A03 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0904 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0904 × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0904 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0904 × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0904 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0904 × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0904 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0904 × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0904 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0904 × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0904 × 0903 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0904 × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0904 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0904 × 0308 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0904 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0904 × 0308 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0904 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0904 × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0904 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0904 × 0308 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0904 × 0300 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0904 × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0904 × 0900 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0904 × 0308 × 0900 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0904 × 094D ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0904 × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0904 × 200D ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0904 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0904 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] (Other) ÷ [0.3] -÷ 0904 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0D4E × 0020 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] SPACE (Other) ÷ [0.3] -÷ 0D4E × 0308 ÷ 0020 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0D4E ÷ 000D ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] (CR) ÷ [0.3] -÷ 0D4E × 0308 ÷ 000D ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0D4E ÷ 000A ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] (LF) ÷ [0.3] -÷ 0D4E × 0308 ÷ 000A ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0D4E ÷ 0001 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] (Control) ÷ [0.3] -÷ 0D4E × 0308 ÷ 0001 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0D4E × 200C ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0D4E × 0308 × 200C ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0D4E × 1F1E6 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0D4E × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0D4E × 0600 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0D4E × 0308 ÷ 0600 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0D4E × 0A03 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0D4E × 0308 × 0A03 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0D4E × 1100 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0D4E × 0308 ÷ 1100 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0D4E × 1160 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0D4E × 0308 ÷ 1160 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0D4E × 11A8 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0D4E × 0308 ÷ 11A8 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0D4E × AC00 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0D4E × 0308 ÷ AC00 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0D4E × AC01 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0D4E × 0308 ÷ AC01 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0D4E × 0903 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0D4E × 0308 × 0903 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0D4E × 0904 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0D4E × 0308 ÷ 0904 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0D4E × 0D4E ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0D4E × 0308 ÷ 0D4E ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0D4E × 0915 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0D4E × 0308 ÷ 0915 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0D4E × 231A ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] WATCH (ExtPict) ÷ [0.3] -÷ 0D4E × 0308 ÷ 231A ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0D4E × 0300 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0D4E × 0308 × 0300 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0D4E × 0900 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0D4E × 0308 × 0900 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0D4E × 094D ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0D4E × 0308 × 094D ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0D4E × 200D ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0D4E × 0308 × 200D ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0D4E × 0378 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] (Other) ÷ [0.3] -÷ 0D4E × 0308 ÷ 0378 ÷ # ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0915 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0915 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0915 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] (CR) ÷ [0.3] -÷ 0915 × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0915 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] (LF) ÷ [0.3] -÷ 0915 × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0915 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] (Control) ÷ [0.3] -÷ 0915 × 0308 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0915 × 200C ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0915 × 0308 × 200C ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0915 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0915 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0915 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0915 × 0308 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0915 × 0A03 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0915 × 0308 × 0A03 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0915 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0915 × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0915 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0915 × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0915 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0915 × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0915 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0915 × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0915 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0915 × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0915 × 0903 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0915 × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0915 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0915 × 0308 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0915 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0915 × 0308 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0915 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0915 × 0308 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0915 × 0300 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0915 × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0915 × 0900 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0915 × 0308 × 0900 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0915 × 094D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0915 × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0915 × 200D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0915 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0915 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] (Other) ÷ [0.3] -÷ 0915 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 231A ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 231A × 0308 ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 231A ÷ 000D ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (CR) ÷ [0.3] -÷ 231A × 0308 ÷ 000D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 231A ÷ 000A ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (LF) ÷ [0.3] -÷ 231A × 0308 ÷ 000A ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 231A ÷ 0001 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (Control) ÷ [0.3] -÷ 231A × 0308 ÷ 0001 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 231A × 200C ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 231A × 0308 × 200C ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 231A ÷ 1F1E6 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 231A × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 231A ÷ 0600 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 231A × 0308 ÷ 0600 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 231A × 0A03 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 231A × 0308 × 0A03 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 231A ÷ 1100 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 231A × 0308 ÷ 1100 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 231A ÷ 1160 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 231A × 0308 ÷ 1160 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 231A ÷ 11A8 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 231A × 0308 ÷ 11A8 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 231A ÷ AC00 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 231A × 0308 ÷ AC00 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 231A ÷ AC01 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 231A × 0308 ÷ AC01 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 231A × 0903 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 231A × 0308 × 0903 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 231A ÷ 0904 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 231A × 0308 ÷ 0904 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 231A ÷ 0D4E ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 231A × 0308 ÷ 0D4E ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 231A ÷ 0915 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 231A × 0308 ÷ 0915 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 231A ÷ 231A ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 231A × 0308 ÷ 231A ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 231A × 0300 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 231A × 0308 × 0300 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 231A × 0900 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 231A × 0308 × 0900 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 231A × 094D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 231A × 0308 × 094D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 231A × 200D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 231A × 0308 × 200D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 231A ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] (Other) ÷ [0.3] -÷ 231A × 0308 ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0300 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0300 × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0300 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0300 × 0308 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0300 ÷ 000A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0300 × 0308 ÷ 000A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0300 ÷ 0001 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0300 × 0308 ÷ 0001 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0300 × 200C ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0300 × 0308 × 200C ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0300 ÷ 1F1E6 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0300 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0300 ÷ 0600 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0300 × 0308 ÷ 0600 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0300 × 0A03 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0300 × 0308 × 0A03 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0300 ÷ 1100 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0300 × 0308 ÷ 1100 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0300 ÷ 1160 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0300 × 0308 ÷ 1160 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0300 ÷ 11A8 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0300 × 0308 ÷ 11A8 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0300 ÷ AC00 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0300 × 0308 ÷ AC00 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0300 ÷ AC01 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0300 × 0308 ÷ AC01 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0300 × 0903 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0300 × 0308 × 0903 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0300 ÷ 0904 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0300 × 0308 ÷ 0904 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0300 ÷ 0D4E ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0300 × 0308 ÷ 0D4E ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0300 ÷ 0915 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0300 × 0308 ÷ 0915 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0300 ÷ 231A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0300 × 0308 ÷ 231A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0300 × 0300 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0300 × 0308 × 0300 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0300 × 0900 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0300 × 0308 × 0900 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0300 × 094D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0300 × 0308 × 094D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0300 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0300 × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0300 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0300 × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0900 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0900 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0900 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0900 × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0900 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0900 × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0900 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0900 × 0308 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0900 × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0900 × 0308 × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0900 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0900 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0900 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0900 × 0308 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0900 × 0A03 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0900 × 0308 × 0A03 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0900 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0900 × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0900 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0900 × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0900 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0900 × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0900 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0900 × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0900 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0900 × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0900 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0900 × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0900 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0900 × 0308 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0900 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0900 × 0308 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0900 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0900 × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0900 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0900 × 0308 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0900 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0900 × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0900 × 0900 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0900 × 0308 × 0900 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0900 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0900 × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0900 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0900 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0900 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0900 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 094D ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 094D × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 094D ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 094D × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 094D ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 094D × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 094D ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 094D × 0308 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 094D × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 094D × 0308 × 200C ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 094D ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 094D × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 094D ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 094D × 0308 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 094D × 0A03 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 094D × 0308 × 0A03 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 094D ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 094D × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 094D ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 094D × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 094D ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 094D × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 094D ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 094D × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 094D ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 094D × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 094D × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 094D × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 094D ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 094D × 0308 ÷ 0904 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 094D ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 094D × 0308 ÷ 0D4E ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 094D ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 094D × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 094D ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 094D × 0308 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 094D × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 094D × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 094D × 0900 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 094D × 0308 × 0900 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 094D × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 094D × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 094D × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 094D × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 094D ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 094D × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 200D ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 200D × 0308 ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 200D ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 200D × 0308 ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 200D ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 200D × 0308 ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 200D ÷ 0001 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 200D × 0308 ÷ 0001 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 200D × 200C ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 200D × 0308 × 200C ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 200D ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 200D × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 200D ÷ 0600 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 200D × 0308 ÷ 0600 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 200D × 0A03 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 200D × 0308 × 0A03 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 200D ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 200D × 0308 ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 200D ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 200D × 0308 ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 200D ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 200D × 0308 ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 200D ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 200D × 0308 ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 200D ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 200D × 0308 ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 200D × 0903 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 200D × 0308 × 0903 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 200D ÷ 0904 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 200D × 0308 ÷ 0904 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 200D ÷ 0D4E ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 200D × 0308 ÷ 0D4E ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 200D ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 200D × 0308 ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 200D ÷ 231A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 200D × 0308 ÷ 231A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 200D × 0300 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 200D × 0308 × 0300 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 200D × 0900 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 200D × 0308 × 0900 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 200D × 094D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 200D × 0308 × 094D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 200D × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 200D × 0308 × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 200D ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 200D × 0308 ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 0378 ÷ 0020 ÷ # ÷ [0.2] (Other) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0378 × 0308 ÷ 0020 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] -÷ 0378 ÷ 000D ÷ # ÷ [0.2] (Other) ÷ [5.0] (CR) ÷ [0.3] -÷ 0378 × 0308 ÷ 000D ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] -÷ 0378 ÷ 000A ÷ # ÷ [0.2] (Other) ÷ [5.0] (LF) ÷ [0.3] -÷ 0378 × 0308 ÷ 000A ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] -÷ 0378 ÷ 0001 ÷ # ÷ [0.2] (Other) ÷ [5.0] (Control) ÷ [0.3] -÷ 0378 × 0308 ÷ 0001 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] -÷ 0378 × 200C ÷ # ÷ [0.2] (Other) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0378 × 0308 × 200C ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3] -÷ 0378 ÷ 1F1E6 ÷ # ÷ [0.2] (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0378 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] -÷ 0378 ÷ 0600 ÷ # ÷ [0.2] (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0378 × 0308 ÷ 0600 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] -÷ 0378 × 0A03 ÷ # ÷ [0.2] (Other) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0378 × 0308 × 0A03 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3] -÷ 0378 ÷ 1100 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0378 × 0308 ÷ 1100 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 0378 ÷ 1160 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0378 × 0308 ÷ 1160 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] -÷ 0378 ÷ 11A8 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0378 × 0308 ÷ 11A8 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] -÷ 0378 ÷ AC00 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0378 × 0308 ÷ AC00 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] -÷ 0378 ÷ AC01 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0378 × 0308 ÷ AC01 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] -÷ 0378 × 0903 ÷ # ÷ [0.2] (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0378 × 0308 × 0903 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3] -÷ 0378 ÷ 0904 ÷ # ÷ [0.2] (Other) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0378 × 0308 ÷ 0904 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3] -÷ 0378 ÷ 0D4E ÷ # ÷ [0.2] (Other) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0378 × 0308 ÷ 0D4E ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3] -÷ 0378 ÷ 0915 ÷ # ÷ [0.2] (Other) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0378 × 0308 ÷ 0915 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0378 ÷ 231A ÷ # ÷ [0.2] (Other) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0378 × 0308 ÷ 231A ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] -÷ 0378 × 0300 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0378 × 0308 × 0300 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] -÷ 0378 × 0900 ÷ # ÷ [0.2] (Other) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0378 × 0308 × 0900 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3] -÷ 0378 × 094D ÷ # ÷ [0.2] (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0378 × 0308 × 094D ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3] -÷ 0378 × 200D ÷ # ÷ [0.2] (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0378 × 0308 × 200D ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0378 ÷ 0378 ÷ # ÷ [0.2] (Other) ÷ [999.0] (Other) ÷ [0.3] -÷ 0378 × 0308 ÷ 0378 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] -÷ 000D × 000A ÷ 0061 ÷ 000A ÷ 0308 ÷ # ÷ [0.2] (CR) × [3.0] (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3] -÷ 0061 × 0308 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3] -÷ 0020 × 200D ÷ 0646 ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3] -÷ 0646 × 200D ÷ 0020 ÷ # ÷ [0.2] ARABIC LETTER NOON (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ AC01 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ AC01 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ AC01 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ AC01 ÷ 00A9 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ AC01 × 0308 ÷ 00A9 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ AC01 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ AC01 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ AC01 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ AC01 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0915 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] (CR) ÷ [0.3] +÷ 0915 × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 0915 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] (LF) ÷ [0.3] +÷ 0915 × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 0915 ÷ 0000 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] (Control) ÷ [0.3] +÷ 0915 × 0308 ÷ 0000 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 0915 × 094D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0915 × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0915 × 0300 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0915 × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0915 × 200C ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0915 × 0308 × 200C ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0915 × 200D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0915 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0915 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0915 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0915 ÷ 06DD ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0915 × 0308 ÷ 06DD ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0915 × 0903 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0915 × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0915 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0915 × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0915 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0915 × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0915 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0915 × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0915 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0915 × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0915 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0915 × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0915 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0915 ÷ 00A9 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0915 × 0308 ÷ 00A9 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0915 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0915 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0915 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0915 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 00A9 ÷ 000D ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] (CR) ÷ [0.3] +÷ 00A9 × 0308 ÷ 000D ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 00A9 ÷ 000A ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] (LF) ÷ [0.3] +÷ 00A9 × 0308 ÷ 000A ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 00A9 ÷ 0000 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] (Control) ÷ [0.3] +÷ 00A9 × 0308 ÷ 0000 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 00A9 × 094D ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 00A9 × 0308 × 094D ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 00A9 × 0300 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 00A9 × 0308 × 0300 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 00A9 × 200C ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 00A9 × 0308 × 200C ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 00A9 × 200D ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 00A9 × 0308 × 200D ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 00A9 ÷ 1F1E6 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 00A9 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 00A9 ÷ 06DD ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 00A9 × 0308 ÷ 06DD ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 00A9 × 0903 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 00A9 × 0308 × 0903 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 00A9 ÷ 1100 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 00A9 × 0308 ÷ 1100 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 00A9 ÷ 1160 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 00A9 × 0308 ÷ 1160 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 00A9 ÷ 11A8 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 00A9 × 0308 ÷ 11A8 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 00A9 ÷ AC00 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 00A9 × 0308 ÷ AC00 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 00A9 ÷ AC01 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 00A9 × 0308 ÷ AC01 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 00A9 ÷ 0915 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 00A9 × 0308 ÷ 0915 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 00A9 ÷ 00A9 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 00A9 × 0308 ÷ 00A9 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 00A9 ÷ 0020 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 00A9 × 0308 ÷ 0020 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 00A9 ÷ 0378 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 00A9 × 0308 ÷ 0378 ÷ # ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0020 ÷ 000D ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] (CR) ÷ [0.3] +÷ 0020 × 0308 ÷ 000D ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 0020 ÷ 000A ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] (LF) ÷ [0.3] +÷ 0020 × 0308 ÷ 000A ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 0020 ÷ 0000 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] (Control) ÷ [0.3] +÷ 0020 × 0308 ÷ 0000 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 0020 × 094D ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0020 × 0308 × 094D ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0020 × 0300 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0020 × 0308 × 0300 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0020 × 200C ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0020 × 0308 × 200C ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0020 × 200D ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0020 × 0308 × 200D ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0020 ÷ 1F1E6 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0020 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0020 ÷ 06DD ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0020 × 0308 ÷ 06DD ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0020 × 0903 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0020 × 0308 × 0903 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0020 ÷ 1100 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0020 × 0308 ÷ 1100 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0020 ÷ 1160 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0020 × 0308 ÷ 1160 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0020 ÷ 11A8 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0020 × 0308 ÷ 11A8 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0020 ÷ AC00 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0020 × 0308 ÷ AC00 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0020 ÷ AC01 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0020 × 0308 ÷ AC01 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0020 ÷ 0915 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0020 × 0308 ÷ 0915 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0020 ÷ 00A9 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0020 × 0308 ÷ 00A9 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0020 ÷ 0020 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0020 × 0308 ÷ 0020 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0020 ÷ 0378 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0020 × 0308 ÷ 0378 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0378 ÷ 000D ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [5.0] (CR) ÷ [0.3] +÷ 0378 × 0308 ÷ 000D ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (CR) ÷ [0.3] +÷ 0378 ÷ 000A ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [5.0] (LF) ÷ [0.3] +÷ 0378 × 0308 ÷ 000A ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (LF) ÷ [0.3] +÷ 0378 ÷ 0000 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [5.0] (Control) ÷ [0.3] +÷ 0378 × 0308 ÷ 0000 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] (Control) ÷ [0.3] +÷ 0378 × 094D ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0378 × 0308 × 094D ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3] +÷ 0378 × 0300 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0378 × 0308 × 0300 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0378 × 200C ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0378 × 0308 × 200C ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3] +÷ 0378 × 200D ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0378 × 0308 × 200D ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0378 ÷ 1F1E6 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0378 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0378 ÷ 06DD ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0378 × 0308 ÷ 06DD ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3] +÷ 0378 × 0903 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0378 × 0308 × 0903 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0378 ÷ 1100 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0378 × 0308 ÷ 1100 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0378 ÷ 1160 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0378 × 0308 ÷ 1160 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0378 ÷ 11A8 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0378 × 0308 ÷ 11A8 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0378 ÷ AC00 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0378 × 0308 ÷ AC00 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0378 ÷ AC01 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0378 × 0308 ÷ AC01 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0378 ÷ 0915 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0378 × 0308 ÷ 0915 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0378 ÷ 00A9 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0378 × 0308 ÷ 00A9 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3] +÷ 0378 ÷ 0020 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0378 × 0308 ÷ 0020 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0378 ÷ 0378 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0378 × 0308 ÷ 0378 ÷ # ÷ [0.2] (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 000D × 000A ÷ 0061 ÷ 000A ÷ 0308 ÷ # ÷ [0.2] (CR) × [3.0] (LF) ÷ [4.0] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [5.0] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0061 × 0308 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 0020 × 200D ÷ 0646 ÷ # ÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] ARABIC LETTER NOON (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0646 × 200D ÷ 0020 ÷ # ÷ [0.2] ARABIC LETTER NOON (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3] ÷ 1100 × 1100 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ AC00 × 11A8 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] ÷ AC01 × 11A8 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] -÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] -÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] -÷ 0061 ÷ 1F1E6 × 1F1E7 × 200D ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] -÷ 0061 ÷ 1F1E6 × 200D ÷ 1F1E7 × 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] -÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 × 1F1E9 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER D (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] -÷ 0061 × 200D ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] -÷ 0061 × 0308 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] -÷ 0061 × 0903 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] -÷ 0061 ÷ 0600 × 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) × [9.2] LATIN SMALL LETTER B (Other) ÷ [0.3] -÷ 1F476 × 1F3FF ÷ 1F476 ÷ # ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) ÷ [999.0] BABY (ExtPict) ÷ [0.3] -÷ 0061 × 1F3FF ÷ 1F476 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) ÷ [999.0] BABY (ExtPict) ÷ [0.3] -÷ 0061 × 1F3FF ÷ 1F476 × 200D × 1F6D1 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) ÷ [999.0] BABY (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] -÷ 1F476 × 1F3FF × 0308 × 200D × 1F476 × 1F3FF ÷ # ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) ÷ [0.3] -÷ 1F6D1 × 200D × 1F6D1 ÷ # ÷ [0.2] OCTAGONAL SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] -÷ 0061 × 200D ÷ 1F6D1 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] -÷ 2701 × 200D × 2701 ÷ # ÷ [0.2] UPPER BLADE SCISSORS (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] UPPER BLADE SCISSORS (Other) ÷ [0.3] -÷ 0061 × 200D ÷ 2701 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] UPPER BLADE SCISSORS (Other) ÷ [0.3] -÷ 0915 ÷ 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 094D × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 094D × 200D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 093C × 200D × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 093C × 094D × 200D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 094D × 0924 × 094D × 092F ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER YA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 094D ÷ 0061 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] LATIN SMALL LETTER A (Other) ÷ [0.3] -÷ 0061 × 094D ÷ 0924 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 003F × 094D ÷ 0924 ÷ # ÷ [0.2] QUESTION MARK (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] -÷ 0915 × 094D × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3] +÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 ÷ 1F1E6 × 1F1E7 × 200D ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 ÷ 1F1E6 × 200D ÷ 1F1E7 × 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 × 1F1E9 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER D (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 × 200D ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3] +÷ 0061 × 0308 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 × 0903 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 ÷ 0600 × 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) × [9.2] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 1F476 × 1F3FF ÷ 1F476 ÷ # ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) ÷ [0.3] +÷ 0061 × 1F3FF ÷ 1F476 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) ÷ [0.3] +÷ 0061 × 1F3FF ÷ 1F476 × 200D × 1F6D1 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] +÷ 1F476 × 1F3FF × 0308 × 200D × 1F476 × 1F3FF ÷ # ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1F6D1 × 200D × 1F6D1 ÷ # ÷ [0.2] OCTAGONAL SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] +÷ 0061 × 200D ÷ 1F6D1 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] +÷ 2701 × 200D ÷ 2701 ÷ # ÷ [0.2] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 × 200D ÷ 2701 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0915 ÷ 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 200D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 093C × 200D × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 093C × 094D × 200D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 0924 × 094D × 092F ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER YA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D ÷ 0061 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [0.3] +÷ 0061 × 094D ÷ 0924 ÷ # ÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 003F × 094D ÷ 0924 ÷ # ÷ [0.2] QUESTION MARK (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0AB8 × 0AFB × 0ACD × 0AB8 × 0AFB ÷ # ÷ [0.2] GUJARATI LETTER SA (LinkingConsonant) × [9.0] GUJARATI SIGN SHADDA (Extend_ConjunctExtendermConjunctLinker) × [9.0] GUJARATI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] GUJARATI LETTER SA (LinkingConsonant) × [9.0] GUJARATI SIGN SHADDA (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1019 × 1039 × 1018 ÷ 102C × 1037 ÷ # ÷ [0.2] MYANMAR LETTER MA (LinkingConsonant) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER BHA (LinkingConsonant) ÷ [999.0] MYANMAR VOWEL SIGN AA (XXmLinkingConsonantmExtPict) × [9.0] MYANMAR SIGN DOT BELOW (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1004 × 103A × 1039 × 1011 × 1039 × 1011 ÷ # ÷ [0.2] MYANMAR LETTER NGA (LinkingConsonant) × [9.0] MYANMAR SIGN ASAT (Extend_ConjunctExtendermConjunctLinker) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER THA (LinkingConsonant) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER THA (LinkingConsonant) ÷ [0.3] +÷ 1B12 × 1B01 ÷ 1B32 × 1B44 × 1B2F ÷ 1B32 × 1B44 × 1B22 × 1B44 × 1B2C ÷ 1B32 × 1B44 × 1B22 × 1B38 ÷ # ÷ [0.2] BALINESE LETTER OKARA TEDUNG (XXmLinkingConsonantmExtPict) × [9.0] BALINESE SIGN ULU CANDRA (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER WA (LinkingConsonant) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER TA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER YA (LinkingConsonant) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER TA (LinkingConsonant) × [9.0] BALINESE VOWEL SIGN SUKU (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 179F × 17D2 × 178F × 17D2 × 179A × 17B8 ÷ # ÷ [0.2] KHMER LETTER SA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER LETTER TA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER LETTER RO (LinkingConsonant) × [9.0] KHMER VOWEL SIGN II (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3] +÷ 1B26 ÷ 1B17 × 1B44 × 1B13 ÷ # ÷ [0.2] BALINESE LETTER NA (LinkingConsonant) ÷ [999.0] BALINESE LETTER NGA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1B27 ÷ 1B13 × 1B44 × 1B0B ÷ 1B0B × 1B04 ÷ # ÷ [0.2] BALINESE LETTER PA (LinkingConsonant) ÷ [999.0] BALINESE LETTER KA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER RA REPA (LinkingConsonant) ÷ [999.0] BALINESE LETTER RA REPA (LinkingConsonant) × [9.1] BALINESE SIGN BISAH (SpacingMark) ÷ [0.3] +÷ 1795 × 17D2 × 17AF ÷ 1798 ÷ # ÷ [0.2] KHMER LETTER PHA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER INDEPENDENT VOWEL QE (LinkingConsonant) ÷ [999.0] KHMER LETTER MO (LinkingConsonant) ÷ [0.3] +÷ 17A0 × 17D2 × 17AB ÷ 1791 × 17D0 ÷ 1799 ÷ # ÷ [0.2] KHMER LETTER HA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER INDEPENDENT VOWEL RY (LinkingConsonant) ÷ [999.0] KHMER LETTER TO (LinkingConsonant) × [9.0] KHMER SIGN SAMYOK SANNYA (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] KHMER LETTER YO (LinkingConsonant) ÷ [0.3] # -# Lines: 1093 +# Lines: 766 # # EOF diff --git a/src/java.base/share/data/unicodedata/emoji/emoji-data.txt b/src/java.base/share/data/unicodedata/emoji/emoji-data.txt index 12f83273cf5b2..450252c4df3fc 100644 --- a/src/java.base/share/data/unicodedata/emoji/emoji-data.txt +++ b/src/java.base/share/data/unicodedata/emoji/emoji-data.txt @@ -1,11 +1,11 @@ # emoji-data.txt -# Date: 2024-05-01, 21:25:24 GMT -# Copyright (c) 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 # @@ -340,6 +340,7 @@ 1F6D1..1F6D2 ; Emoji # E3.0 [2] (🛑..🛒) stop sign..shopping cart 1F6D5 ; Emoji # E12.0 [1] (🛕) hindu temple 1F6D6..1F6D7 ; Emoji # E13.0 [2] (🛖..🛗) hut..elevator +1F6D8 ; Emoji # E17.0 [1] (🛘) landslide 1F6DC ; Emoji # E15.0 [1] (🛜) wireless 1F6DD..1F6DF ; Emoji # E14.0 [3] (🛝..🛟) playground slide..ring buoy 1F6E0..1F6E5 ; Emoji # E0.7 [6] (🛠️..🛥️) hammer and wrench..motor boat @@ -408,6 +409,8 @@ 1FA83..1FA86 ; Emoji # E13.0 [4] (🪃..🪆) boomerang..nesting dolls 1FA87..1FA88 ; Emoji # E15.0 [2] (🪇..🪈) maracas..flute 1FA89 ; Emoji # E16.0 [1] (🪉) harp +1FA8A ; Emoji # E17.0 [1] (🪊) trombone +1FA8E ; Emoji # E17.0 [1] (🪎) treasure chest 1FA8F ; Emoji # E16.0 [1] (🪏) shovel 1FA90..1FA95 ; Emoji # E12.0 [6] (🪐..🪕) ringed planet..banjo 1FA96..1FAA8 ; Emoji # E13.0 [19] (🪖..🪨) military helmet..rock @@ -421,6 +424,8 @@ 1FAC0..1FAC2 ; Emoji # E13.0 [3] (🫀..🫂) anatomical heart..people hugging 1FAC3..1FAC5 ; Emoji # E14.0 [3] (🫃..🫅) pregnant man..person with crown 1FAC6 ; Emoji # E16.0 [1] (🫆) fingerprint +1FAC8 ; Emoji # E17.0 [1] (🫈) hairy creature +1FACD ; Emoji # E17.0 [1] (🫍) orca 1FACE..1FACF ; Emoji # E15.0 [2] (🫎..🫏) moose..donkey 1FAD0..1FAD6 ; Emoji # E13.0 [7] (🫐..🫖) blueberries..teapot 1FAD7..1FAD9 ; Emoji # E14.0 [3] (🫗..🫙) pouring liquid..jar @@ -430,10 +435,12 @@ 1FAE0..1FAE7 ; Emoji # E14.0 [8] (🫠..🫧) melting face..bubbles 1FAE8 ; Emoji # E15.0 [1] (🫨) shaking face 1FAE9 ; Emoji # E16.0 [1] (🫩) face with bags under eyes +1FAEA ; Emoji # E17.0 [1] (🫪) distorted face +1FAEF ; Emoji # E17.0 [1] (🫯) fight cloud 1FAF0..1FAF6 ; Emoji # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands 1FAF7..1FAF8 ; Emoji # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand -# Total elements: 1431 +# Total elements: 1438 # ================================================ @@ -640,6 +647,7 @@ 1F6D1..1F6D2 ; Emoji_Presentation # E3.0 [2] (🛑..🛒) stop sign..shopping cart 1F6D5 ; Emoji_Presentation # E12.0 [1] (🛕) hindu temple 1F6D6..1F6D7 ; Emoji_Presentation # E13.0 [2] (🛖..🛗) hut..elevator +1F6D8 ; Emoji_Presentation # E17.0 [1] (🛘) landslide 1F6DC ; Emoji_Presentation # E15.0 [1] (🛜) wireless 1F6DD..1F6DF ; Emoji_Presentation # E14.0 [3] (🛝..🛟) playground slide..ring buoy 1F6EB..1F6EC ; Emoji_Presentation # E1.0 [2] (🛫..🛬) airplane departure..airplane arrival @@ -704,6 +712,8 @@ 1FA83..1FA86 ; Emoji_Presentation # E13.0 [4] (🪃..🪆) boomerang..nesting dolls 1FA87..1FA88 ; Emoji_Presentation # E15.0 [2] (🪇..🪈) maracas..flute 1FA89 ; Emoji_Presentation # E16.0 [1] (🪉) harp +1FA8A ; Emoji_Presentation # E17.0 [1] (🪊) trombone +1FA8E ; Emoji_Presentation # E17.0 [1] (🪎) treasure chest 1FA8F ; Emoji_Presentation # E16.0 [1] (🪏) shovel 1FA90..1FA95 ; Emoji_Presentation # E12.0 [6] (🪐..🪕) ringed planet..banjo 1FA96..1FAA8 ; Emoji_Presentation # E13.0 [19] (🪖..🪨) military helmet..rock @@ -717,6 +727,8 @@ 1FAC0..1FAC2 ; Emoji_Presentation # E13.0 [3] (🫀..🫂) anatomical heart..people hugging 1FAC3..1FAC5 ; Emoji_Presentation # E14.0 [3] (🫃..🫅) pregnant man..person with crown 1FAC6 ; Emoji_Presentation # E16.0 [1] (🫆) fingerprint +1FAC8 ; Emoji_Presentation # E17.0 [1] (🫈) hairy creature +1FACD ; Emoji_Presentation # E17.0 [1] (🫍) orca 1FACE..1FACF ; Emoji_Presentation # E15.0 [2] (🫎..🫏) moose..donkey 1FAD0..1FAD6 ; Emoji_Presentation # E13.0 [7] (🫐..🫖) blueberries..teapot 1FAD7..1FAD9 ; Emoji_Presentation # E14.0 [3] (🫗..🫙) pouring liquid..jar @@ -726,10 +738,12 @@ 1FAE0..1FAE7 ; Emoji_Presentation # E14.0 [8] (🫠..🫧) melting face..bubbles 1FAE8 ; Emoji_Presentation # E15.0 [1] (🫨) shaking face 1FAE9 ; Emoji_Presentation # E16.0 [1] (🫩) face with bags under eyes +1FAEA ; Emoji_Presentation # E17.0 [1] (🫪) distorted face +1FAEF ; Emoji_Presentation # E17.0 [1] (🫯) fight cloud 1FAF0..1FAF6 ; Emoji_Presentation # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands 1FAF7..1FAF8 ; Emoji_Presentation # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand -# Total elements: 1212 +# Total elements: 1219 # ================================================ @@ -827,7 +841,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 21A9..21AA ; Extended_Pictographic# E0.6 [2] (↩️..↪️) right arrow curving left..left arrow curving right 231A..231B ; Extended_Pictographic# E0.6 [2] (⌚..⌛) watch..hourglass done 2328 ; Extended_Pictographic# E1.0 [1] (⌨️) keyboard -2388 ; Extended_Pictographic# E0.0 [1] (⎈) HELM SYMBOL 23CF ; Extended_Pictographic# E1.0 [1] (⏏️) eject button 23E9..23EC ; Extended_Pictographic# E0.6 [4] (⏩..⏬) fast-forward button..fast down button 23ED..23EE ; Extended_Pictographic# E0.7 [2] (⏭️..⏮️) next track button..last track button @@ -844,106 +857,63 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 2600..2601 ; Extended_Pictographic# E0.6 [2] (☀️..☁️) sun..cloud 2602..2603 ; Extended_Pictographic# E0.7 [2] (☂️..☃️) umbrella..snowman 2604 ; Extended_Pictographic# E1.0 [1] (☄️) comet -2605 ; Extended_Pictographic# E0.0 [1] (★) BLACK STAR -2607..260D ; Extended_Pictographic# E0.0 [7] (☇..☍) LIGHTNING..OPPOSITION 260E ; Extended_Pictographic# E0.6 [1] (☎️) telephone -260F..2610 ; Extended_Pictographic# E0.0 [2] (☏..☐) WHITE TELEPHONE..BALLOT BOX 2611 ; Extended_Pictographic# E0.6 [1] (☑️) check box with check -2612 ; Extended_Pictographic# E0.0 [1] (☒) BALLOT BOX WITH X 2614..2615 ; Extended_Pictographic# E0.6 [2] (☔..☕) umbrella with rain drops..hot beverage -2616..2617 ; Extended_Pictographic# E0.0 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE 2618 ; Extended_Pictographic# E1.0 [1] (☘️) shamrock -2619..261C ; Extended_Pictographic# E0.0 [4] (☙..☜) REVERSED ROTATED FLORAL HEART BULLET..WHITE LEFT POINTING INDEX 261D ; Extended_Pictographic# E0.6 [1] (☝️) index pointing up -261E..261F ; Extended_Pictographic# E0.0 [2] (☞..☟) WHITE RIGHT POINTING INDEX..WHITE DOWN POINTING INDEX 2620 ; Extended_Pictographic# E1.0 [1] (☠️) skull and crossbones -2621 ; Extended_Pictographic# E0.0 [1] (☡) CAUTION SIGN 2622..2623 ; Extended_Pictographic# E1.0 [2] (☢️..☣️) radioactive..biohazard -2624..2625 ; Extended_Pictographic# E0.0 [2] (☤..☥) CADUCEUS..ANKH 2626 ; Extended_Pictographic# E1.0 [1] (☦️) orthodox cross -2627..2629 ; Extended_Pictographic# E0.0 [3] (☧..☩) CHI RHO..CROSS OF JERUSALEM 262A ; Extended_Pictographic# E0.7 [1] (☪️) star and crescent -262B..262D ; Extended_Pictographic# E0.0 [3] (☫..☭) FARSI SYMBOL..HAMMER AND SICKLE 262E ; Extended_Pictographic# E1.0 [1] (☮️) peace symbol 262F ; Extended_Pictographic# E0.7 [1] (☯️) yin yang -2630..2637 ; Extended_Pictographic# E0.0 [8] (☰..☷) TRIGRAM FOR HEAVEN..TRIGRAM FOR EARTH 2638..2639 ; Extended_Pictographic# E0.7 [2] (☸️..☹️) wheel of dharma..frowning face 263A ; Extended_Pictographic# E0.6 [1] (☺️) smiling face -263B..263F ; Extended_Pictographic# E0.0 [5] (☻..☿) BLACK SMILING FACE..MERCURY 2640 ; Extended_Pictographic# E4.0 [1] (♀️) female sign -2641 ; Extended_Pictographic# E0.0 [1] (♁) EARTH 2642 ; Extended_Pictographic# E4.0 [1] (♂️) male sign -2643..2647 ; Extended_Pictographic# E0.0 [5] (♃..♇) JUPITER..PLUTO 2648..2653 ; Extended_Pictographic# E0.6 [12] (♈..♓) Aries..Pisces -2654..265E ; Extended_Pictographic# E0.0 [11] (♔..♞) WHITE CHESS KING..BLACK CHESS KNIGHT 265F ; Extended_Pictographic# E11.0 [1] (♟️) chess pawn 2660 ; Extended_Pictographic# E0.6 [1] (♠️) spade suit -2661..2662 ; Extended_Pictographic# E0.0 [2] (♡..♢) WHITE HEART SUIT..WHITE DIAMOND SUIT 2663 ; Extended_Pictographic# E0.6 [1] (♣️) club suit -2664 ; Extended_Pictographic# E0.0 [1] (♤) WHITE SPADE SUIT 2665..2666 ; Extended_Pictographic# E0.6 [2] (♥️..♦️) heart suit..diamond suit -2667 ; Extended_Pictographic# E0.0 [1] (♧) WHITE CLUB SUIT 2668 ; Extended_Pictographic# E0.6 [1] (♨️) hot springs -2669..267A ; Extended_Pictographic# E0.0 [18] (♩..♺) QUARTER NOTE..RECYCLING SYMBOL FOR GENERIC MATERIALS 267B ; Extended_Pictographic# E0.6 [1] (♻️) recycling symbol -267C..267D ; Extended_Pictographic# E0.0 [2] (♼..♽) RECYCLED PAPER SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL 267E ; Extended_Pictographic# E11.0 [1] (♾️) infinity 267F ; Extended_Pictographic# E0.6 [1] (♿) wheelchair symbol -2680..2685 ; Extended_Pictographic# E0.0 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6 -2690..2691 ; Extended_Pictographic# E0.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG 2692 ; Extended_Pictographic# E1.0 [1] (⚒️) hammer and pick 2693 ; Extended_Pictographic# E0.6 [1] (⚓) anchor 2694 ; Extended_Pictographic# E1.0 [1] (⚔️) crossed swords 2695 ; Extended_Pictographic# E4.0 [1] (⚕️) medical symbol 2696..2697 ; Extended_Pictographic# E1.0 [2] (⚖️..⚗️) balance scale..alembic -2698 ; Extended_Pictographic# E0.0 [1] (⚘) FLOWER 2699 ; Extended_Pictographic# E1.0 [1] (⚙️) gear -269A ; Extended_Pictographic# E0.0 [1] (⚚) STAFF OF HERMES 269B..269C ; Extended_Pictographic# E1.0 [2] (⚛️..⚜️) atom symbol..fleur-de-lis -269D..269F ; Extended_Pictographic# E0.0 [3] (⚝..⚟) OUTLINED WHITE STAR..THREE LINES CONVERGING LEFT 26A0..26A1 ; Extended_Pictographic# E0.6 [2] (⚠️..⚡) warning..high voltage -26A2..26A6 ; Extended_Pictographic# E0.0 [5] (⚢..⚦) DOUBLED FEMALE SIGN..MALE WITH STROKE SIGN 26A7 ; Extended_Pictographic# E13.0 [1] (⚧️) transgender symbol -26A8..26A9 ; Extended_Pictographic# E0.0 [2] (⚨..⚩) VERTICAL MALE WITH STROKE SIGN..HORIZONTAL MALE WITH STROKE SIGN 26AA..26AB ; Extended_Pictographic# E0.6 [2] (⚪..⚫) white circle..black circle -26AC..26AF ; Extended_Pictographic# E0.0 [4] (⚬..⚯) MEDIUM SMALL WHITE CIRCLE..UNMARRIED PARTNERSHIP SYMBOL 26B0..26B1 ; Extended_Pictographic# E1.0 [2] (⚰️..⚱️) coffin..funeral urn -26B2..26BC ; Extended_Pictographic# E0.0 [11] (⚲..⚼) NEUTER..SESQUIQUADRATE 26BD..26BE ; Extended_Pictographic# E0.6 [2] (⚽..⚾) soccer ball..baseball -26BF..26C3 ; Extended_Pictographic# E0.0 [5] (⚿..⛃) SQUARED KEY..BLACK DRAUGHTS KING 26C4..26C5 ; Extended_Pictographic# E0.6 [2] (⛄..⛅) snowman without snow..sun behind cloud -26C6..26C7 ; Extended_Pictographic# E0.0 [2] (⛆..⛇) RAIN..BLACK SNOWMAN 26C8 ; Extended_Pictographic# E0.7 [1] (⛈️) cloud with lightning and rain -26C9..26CD ; Extended_Pictographic# E0.0 [5] (⛉..⛍) TURNED WHITE SHOGI PIECE..DISABLED CAR 26CE ; Extended_Pictographic# E0.6 [1] (⛎) Ophiuchus 26CF ; Extended_Pictographic# E0.7 [1] (⛏️) pick -26D0 ; Extended_Pictographic# E0.0 [1] (⛐) CAR SLIDING 26D1 ; Extended_Pictographic# E0.7 [1] (⛑️) rescue worker’s helmet -26D2 ; Extended_Pictographic# E0.0 [1] (⛒) CIRCLED CROSSING LANES 26D3 ; Extended_Pictographic# E0.7 [1] (⛓️) chains 26D4 ; Extended_Pictographic# E0.6 [1] (⛔) no entry -26D5..26E8 ; Extended_Pictographic# E0.0 [20] (⛕..⛨) ALTERNATE ONE-WAY LEFT WAY TRAFFIC..BLACK CROSS ON SHIELD 26E9 ; Extended_Pictographic# E0.7 [1] (⛩️) shinto shrine 26EA ; Extended_Pictographic# E0.6 [1] (⛪) church -26EB..26EF ; Extended_Pictographic# E0.0 [5] (⛫..⛯) CASTLE..MAP SYMBOL FOR LIGHTHOUSE 26F0..26F1 ; Extended_Pictographic# E0.7 [2] (⛰️..⛱️) mountain..umbrella on ground 26F2..26F3 ; Extended_Pictographic# E0.6 [2] (⛲..⛳) fountain..flag in hole 26F4 ; Extended_Pictographic# E0.7 [1] (⛴️) ferry 26F5 ; Extended_Pictographic# E0.6 [1] (⛵) sailboat -26F6 ; Extended_Pictographic# E0.0 [1] (⛶) SQUARE FOUR CORNERS 26F7..26F9 ; Extended_Pictographic# E0.7 [3] (⛷️..⛹️) skier..person bouncing ball 26FA ; Extended_Pictographic# E0.6 [1] (⛺) tent -26FB..26FC ; Extended_Pictographic# E0.0 [2] (⛻..⛼) JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL 26FD ; Extended_Pictographic# E0.6 [1] (⛽) fuel pump -26FE..2701 ; Extended_Pictographic# E0.0 [4] (⛾..✁) CUP ON BLACK SQUARE..UPPER BLADE SCISSORS 2702 ; Extended_Pictographic# E0.6 [1] (✂️) scissors -2703..2704 ; Extended_Pictographic# E0.0 [2] (✃..✄) LOWER BLADE SCISSORS..WHITE SCISSORS 2705 ; Extended_Pictographic# E0.6 [1] (✅) check mark button 2708..270C ; Extended_Pictographic# E0.6 [5] (✈️..✌️) airplane..victory hand 270D ; Extended_Pictographic# E0.7 [1] (✍️) writing hand -270E ; Extended_Pictographic# E0.0 [1] (✎) LOWER RIGHT PENCIL 270F ; Extended_Pictographic# E0.6 [1] (✏️) pencil -2710..2711 ; Extended_Pictographic# E0.0 [2] (✐..✑) UPPER RIGHT PENCIL..WHITE NIB 2712 ; Extended_Pictographic# E0.6 [1] (✒️) black nib 2714 ; Extended_Pictographic# E0.6 [1] (✔️) check mark 2716 ; Extended_Pictographic# E0.6 [1] (✖️) multiply @@ -959,7 +929,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 2757 ; Extended_Pictographic# E0.6 [1] (❗) red exclamation mark 2763 ; Extended_Pictographic# E1.0 [1] (❣️) heart exclamation 2764 ; Extended_Pictographic# E0.6 [1] (❤️) red heart -2765..2767 ; Extended_Pictographic# E0.0 [3] (❥..❧) ROTATED HEAVY BLACK HEART BULLET..ROTATED FLORAL HEART BULLET 2795..2797 ; Extended_Pictographic# E0.6 [3] (➕..➗) plus..divide 27A1 ; Extended_Pictographic# E0.6 [1] (➡️) right arrow 27B0 ; Extended_Pictographic# E0.6 [1] (➰) curly loop @@ -973,19 +942,19 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 303D ; Extended_Pictographic# E0.6 [1] (〽️) part alternation mark 3297 ; Extended_Pictographic# E0.6 [1] (㊗️) Japanese “congratulations” button 3299 ; Extended_Pictographic# E0.6 [1] (㊙️) Japanese “secret” button -1F000..1F003 ; Extended_Pictographic# E0.0 [4] (🀀..🀃) MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND 1F004 ; Extended_Pictographic# E0.6 [1] (🀄) mahjong red dragon -1F005..1F0CE ; Extended_Pictographic# E0.0 [202] (🀅..🃎) MAHJONG TILE GREEN DRAGON..PLAYING CARD KING OF DIAMONDS +1F02C..1F02F ; Extended_Pictographic# E0.0 [4] (🀬..🀯) .. +1F094..1F09F ; Extended_Pictographic# E0.0 [12] (🂔..🂟) .. +1F0AF..1F0B0 ; Extended_Pictographic# E0.0 [2] (🂯..🂰) .. +1F0C0 ; Extended_Pictographic# E0.0 [1] (🃀) 1F0CF ; Extended_Pictographic# E0.6 [1] (🃏) joker -1F0D0..1F0FF ; Extended_Pictographic# E0.0 [48] (🃐..🃿) .. -1F10D..1F10F ; Extended_Pictographic# E0.0 [3] (🄍..🄏) CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH -1F12F ; Extended_Pictographic# E0.0 [1] (🄯) COPYLEFT SYMBOL -1F16C..1F16F ; Extended_Pictographic# E0.0 [4] (🅬..🅯) RAISED MR SIGN..CIRCLED HUMAN FIGURE +1F0D0 ; Extended_Pictographic# E0.0 [1] (🃐) +1F0F6..1F0FF ; Extended_Pictographic# E0.0 [10] (🃶..🃿) .. 1F170..1F171 ; Extended_Pictographic# E0.6 [2] (🅰️..🅱️) A button (blood type)..B button (blood type) 1F17E..1F17F ; Extended_Pictographic# E0.6 [2] (🅾️..🅿️) O button (blood type)..P button 1F18E ; Extended_Pictographic# E0.6 [1] (🆎) AB button (blood type) 1F191..1F19A ; Extended_Pictographic# E0.6 [10] (🆑..🆚) CL button..VS button -1F1AD..1F1E5 ; Extended_Pictographic# E0.0 [57] (🆭..🇥) MASK WORK SYMBOL.. +1F1AE..1F1E5 ; Extended_Pictographic# E0.0 [56] (🆮..🇥) .. 1F201..1F202 ; Extended_Pictographic# E0.6 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button 1F203..1F20F ; Extended_Pictographic# E0.0 [13] (🈃..🈏) .. 1F21A ; Extended_Pictographic# E0.6 [1] (🈚) Japanese “free of charge” button @@ -994,7 +963,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F23C..1F23F ; Extended_Pictographic# E0.0 [4] (🈼..🈿) .. 1F249..1F24F ; Extended_Pictographic# E0.0 [7] (🉉..🉏) .. 1F250..1F251 ; Extended_Pictographic# E0.6 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button -1F252..1F2FF ; Extended_Pictographic# E0.0 [174] (🉒..🋿) .. +1F252..1F25F ; Extended_Pictographic# E0.0 [14] (🉒..🉟) .. +1F266..1F2FF ; Extended_Pictographic# E0.0 [154] (🉦..🋿) .. 1F300..1F30C ; Extended_Pictographic# E0.6 [13] (🌀..🌌) cyclone..milky way 1F30D..1F30E ; Extended_Pictographic# E0.7 [2] (🌍..🌎) globe showing Europe-Africa..globe showing Americas 1F30F ; Extended_Pictographic# E0.6 [1] (🌏) globe showing Asia-Australia @@ -1010,7 +980,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F31D..1F31E ; Extended_Pictographic# E1.0 [2] (🌝..🌞) full moon face..sun with face 1F31F..1F320 ; Extended_Pictographic# E0.6 [2] (🌟..🌠) glowing star..shooting star 1F321 ; Extended_Pictographic# E0.7 [1] (🌡️) thermometer -1F322..1F323 ; Extended_Pictographic# E0.0 [2] (🌢..🌣) BLACK DROPLET..WHITE SUN 1F324..1F32C ; Extended_Pictographic# E0.7 [9] (🌤️..🌬️) sun behind small cloud..wind face 1F32D..1F32F ; Extended_Pictographic# E1.0 [3] (🌭..🌯) hot dog..burrito 1F330..1F331 ; Extended_Pictographic# E0.6 [2] (🌰..🌱) chestnut..seedling @@ -1026,11 +995,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F37D ; Extended_Pictographic# E0.7 [1] (🍽️) fork and knife with plate 1F37E..1F37F ; Extended_Pictographic# E1.0 [2] (🍾..🍿) bottle with popping cork..popcorn 1F380..1F393 ; Extended_Pictographic# E0.6 [20] (🎀..🎓) ribbon..graduation cap -1F394..1F395 ; Extended_Pictographic# E0.0 [2] (🎔..🎕) HEART WITH TIP ON THE LEFT..BOUQUET OF FLOWERS 1F396..1F397 ; Extended_Pictographic# E0.7 [2] (🎖️..🎗️) military medal..reminder ribbon -1F398 ; Extended_Pictographic# E0.0 [1] (🎘) MUSICAL KEYBOARD WITH JACKS 1F399..1F39B ; Extended_Pictographic# E0.7 [3] (🎙️..🎛️) studio microphone..control knobs -1F39C..1F39D ; Extended_Pictographic# E0.0 [2] (🎜..🎝) BEAMED ASCENDING MUSICAL NOTES..BEAMED DESCENDING MUSICAL NOTES 1F39E..1F39F ; Extended_Pictographic# E0.7 [2] (🎞️..🎟️) film frames..admission tickets 1F3A0..1F3C4 ; Extended_Pictographic# E0.6 [37] (🎠..🏄) carousel horse..person surfing 1F3C5 ; Extended_Pictographic# E1.0 [1] (🏅) sports medal @@ -1045,11 +1011,9 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F3E0..1F3E3 ; Extended_Pictographic# E0.6 [4] (🏠..🏣) house..Japanese post office 1F3E4 ; Extended_Pictographic# E1.0 [1] (🏤) post office 1F3E5..1F3F0 ; Extended_Pictographic# E0.6 [12] (🏥..🏰) hospital..castle -1F3F1..1F3F2 ; Extended_Pictographic# E0.0 [2] (🏱..🏲) WHITE PENNANT..BLACK PENNANT 1F3F3 ; Extended_Pictographic# E0.7 [1] (🏳️) white flag 1F3F4 ; Extended_Pictographic# E1.0 [1] (🏴) black flag 1F3F5 ; Extended_Pictographic# E0.7 [1] (🏵️) rosette -1F3F6 ; Extended_Pictographic# E0.0 [1] (🏶) BLACK ROSETTE 1F3F7 ; Extended_Pictographic# E0.7 [1] (🏷️) label 1F3F8..1F3FA ; Extended_Pictographic# E1.0 [3] (🏸..🏺) badminton..amphora 1F400..1F407 ; Extended_Pictographic# E1.0 [8] (🐀..🐇) rat..rabbit @@ -1086,7 +1050,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F4F8 ; Extended_Pictographic# E1.0 [1] (📸) camera with flash 1F4F9..1F4FC ; Extended_Pictographic# E0.6 [4] (📹..📼) video camera..videocassette 1F4FD ; Extended_Pictographic# E0.7 [1] (📽️) film projector -1F4FE ; Extended_Pictographic# E0.0 [1] (📾) PORTABLE STEREO 1F4FF..1F502 ; Extended_Pictographic# E1.0 [4] (📿..🔂) prayer beads..repeat single button 1F503 ; Extended_Pictographic# E0.6 [1] (🔃) clockwise vertical arrows 1F504..1F507 ; Extended_Pictographic# E1.0 [4] (🔄..🔇) counterclockwise arrows button..muted speaker @@ -1097,51 +1060,30 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F516..1F52B ; Extended_Pictographic# E0.6 [22] (🔖..🔫) bookmark..water pistol 1F52C..1F52D ; Extended_Pictographic# E1.0 [2] (🔬..🔭) microscope..telescope 1F52E..1F53D ; Extended_Pictographic# E0.6 [16] (🔮..🔽) crystal ball..downwards button -1F546..1F548 ; Extended_Pictographic# E0.0 [3] (🕆..🕈) WHITE LATIN CROSS..CELTIC CROSS 1F549..1F54A ; Extended_Pictographic# E0.7 [2] (🕉️..🕊️) om..dove 1F54B..1F54E ; Extended_Pictographic# E1.0 [4] (🕋..🕎) kaaba..menorah -1F54F ; Extended_Pictographic# E0.0 [1] (🕏) BOWL OF HYGIEIA 1F550..1F55B ; Extended_Pictographic# E0.6 [12] (🕐..🕛) one o’clock..twelve o’clock 1F55C..1F567 ; Extended_Pictographic# E0.7 [12] (🕜..🕧) one-thirty..twelve-thirty -1F568..1F56E ; Extended_Pictographic# E0.0 [7] (🕨..🕮) RIGHT SPEAKER..BOOK 1F56F..1F570 ; Extended_Pictographic# E0.7 [2] (🕯️..🕰️) candle..mantelpiece clock -1F571..1F572 ; Extended_Pictographic# E0.0 [2] (🕱..🕲) BLACK SKULL AND CROSSBONES..NO PIRACY 1F573..1F579 ; Extended_Pictographic# E0.7 [7] (🕳️..🕹️) hole..joystick 1F57A ; Extended_Pictographic# E3.0 [1] (🕺) man dancing -1F57B..1F586 ; Extended_Pictographic# E0.0 [12] (🕻..🖆) LEFT HAND TELEPHONE RECEIVER..PEN OVER STAMPED ENVELOPE 1F587 ; Extended_Pictographic# E0.7 [1] (🖇️) linked paperclips -1F588..1F589 ; Extended_Pictographic# E0.0 [2] (🖈..🖉) BLACK PUSHPIN..LOWER LEFT PENCIL 1F58A..1F58D ; Extended_Pictographic# E0.7 [4] (🖊️..🖍️) pen..crayon -1F58E..1F58F ; Extended_Pictographic# E0.0 [2] (🖎..🖏) LEFT WRITING HAND..TURNED OK HAND SIGN 1F590 ; Extended_Pictographic# E0.7 [1] (🖐️) hand with fingers splayed -1F591..1F594 ; Extended_Pictographic# E0.0 [4] (🖑..🖔) REVERSED RAISED HAND WITH FINGERS SPLAYED..REVERSED VICTORY HAND 1F595..1F596 ; Extended_Pictographic# E1.0 [2] (🖕..🖖) middle finger..vulcan salute -1F597..1F5A3 ; Extended_Pictographic# E0.0 [13] (🖗..🖣) WHITE DOWN POINTING LEFT HAND INDEX..BLACK DOWN POINTING BACKHAND INDEX 1F5A4 ; Extended_Pictographic# E3.0 [1] (🖤) black heart 1F5A5 ; Extended_Pictographic# E0.7 [1] (🖥️) desktop computer -1F5A6..1F5A7 ; Extended_Pictographic# E0.0 [2] (🖦..🖧) KEYBOARD AND MOUSE..THREE NETWORKED COMPUTERS 1F5A8 ; Extended_Pictographic# E0.7 [1] (🖨️) printer -1F5A9..1F5B0 ; Extended_Pictographic# E0.0 [8] (🖩..🖰) POCKET CALCULATOR..TWO BUTTON MOUSE 1F5B1..1F5B2 ; Extended_Pictographic# E0.7 [2] (🖱️..🖲️) computer mouse..trackball -1F5B3..1F5BB ; Extended_Pictographic# E0.0 [9] (🖳..🖻) OLD PERSONAL COMPUTER..DOCUMENT WITH PICTURE 1F5BC ; Extended_Pictographic# E0.7 [1] (🖼️) framed picture -1F5BD..1F5C1 ; Extended_Pictographic# E0.0 [5] (🖽..🗁) FRAME WITH TILES..OPEN FOLDER 1F5C2..1F5C4 ; Extended_Pictographic# E0.7 [3] (🗂️..🗄️) card index dividers..file cabinet -1F5C5..1F5D0 ; Extended_Pictographic# E0.0 [12] (🗅..🗐) EMPTY NOTE..PAGES 1F5D1..1F5D3 ; Extended_Pictographic# E0.7 [3] (🗑️..🗓️) wastebasket..spiral calendar -1F5D4..1F5DB ; Extended_Pictographic# E0.0 [8] (🗔..🗛) DESKTOP WINDOW..DECREASE FONT SIZE SYMBOL 1F5DC..1F5DE ; Extended_Pictographic# E0.7 [3] (🗜️..🗞️) clamp..rolled-up newspaper -1F5DF..1F5E0 ; Extended_Pictographic# E0.0 [2] (🗟..🗠) PAGE WITH CIRCLED TEXT..STOCK CHART 1F5E1 ; Extended_Pictographic# E0.7 [1] (🗡️) dagger -1F5E2 ; Extended_Pictographic# E0.0 [1] (🗢) LIPS 1F5E3 ; Extended_Pictographic# E0.7 [1] (🗣️) speaking head -1F5E4..1F5E7 ; Extended_Pictographic# E0.0 [4] (🗤..🗧) THREE RAYS ABOVE..THREE RAYS RIGHT 1F5E8 ; Extended_Pictographic# E2.0 [1] (🗨️) left speech bubble -1F5E9..1F5EE ; Extended_Pictographic# E0.0 [6] (🗩..🗮) RIGHT SPEECH BUBBLE..LEFT ANGER BUBBLE 1F5EF ; Extended_Pictographic# E0.7 [1] (🗯️) right anger bubble -1F5F0..1F5F2 ; Extended_Pictographic# E0.0 [3] (🗰..🗲) MOOD BUBBLE..LIGHTNING MOOD 1F5F3 ; Extended_Pictographic# E0.7 [1] (🗳️) ballot box with ballot -1F5F4..1F5F9 ; Extended_Pictographic# E0.0 [6] (🗴..🗹) BALLOT SCRIPT X..BALLOT BOX WITH BOLD CHECK 1F5FA ; Extended_Pictographic# E0.7 [1] (🗺️) world map 1F5FB..1F5FF ; Extended_Pictographic# E0.6 [5] (🗻..🗿) mount fuji..moai 1F600 ; Extended_Pictographic# E1.0 [1] (😀) grinning face @@ -1210,26 +1152,22 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F6BF ; Extended_Pictographic# E1.0 [1] (🚿) shower 1F6C0 ; Extended_Pictographic# E0.6 [1] (🛀) person taking bath 1F6C1..1F6C5 ; Extended_Pictographic# E1.0 [5] (🛁..🛅) bathtub..left luggage -1F6C6..1F6CA ; Extended_Pictographic# E0.0 [5] (🛆..🛊) TRIANGLE WITH ROUNDED CORNERS..GIRLS SYMBOL 1F6CB ; Extended_Pictographic# E0.7 [1] (🛋️) couch and lamp 1F6CC ; Extended_Pictographic# E1.0 [1] (🛌) person in bed 1F6CD..1F6CF ; Extended_Pictographic# E0.7 [3] (🛍️..🛏️) shopping bags..bed 1F6D0 ; Extended_Pictographic# E1.0 [1] (🛐) place of worship 1F6D1..1F6D2 ; Extended_Pictographic# E3.0 [2] (🛑..🛒) stop sign..shopping cart -1F6D3..1F6D4 ; Extended_Pictographic# E0.0 [2] (🛓..🛔) STUPA..PAGODA 1F6D5 ; Extended_Pictographic# E12.0 [1] (🛕) hindu temple 1F6D6..1F6D7 ; Extended_Pictographic# E13.0 [2] (🛖..🛗) hut..elevator -1F6D8..1F6DB ; Extended_Pictographic# E0.0 [4] (🛘..🛛) .. +1F6D8 ; Extended_Pictographic# E17.0 [1] (🛘) landslide +1F6D9..1F6DB ; Extended_Pictographic# E0.0 [3] (🛙..🛛) .. 1F6DC ; Extended_Pictographic# E15.0 [1] (🛜) wireless 1F6DD..1F6DF ; Extended_Pictographic# E14.0 [3] (🛝..🛟) playground slide..ring buoy 1F6E0..1F6E5 ; Extended_Pictographic# E0.7 [6] (🛠️..🛥️) hammer and wrench..motor boat -1F6E6..1F6E8 ; Extended_Pictographic# E0.0 [3] (🛦..🛨) UP-POINTING MILITARY AIRPLANE..UP-POINTING SMALL AIRPLANE 1F6E9 ; Extended_Pictographic# E0.7 [1] (🛩️) small airplane -1F6EA ; Extended_Pictographic# E0.0 [1] (🛪) NORTHEAST-POINTING AIRPLANE 1F6EB..1F6EC ; Extended_Pictographic# E1.0 [2] (🛫..🛬) airplane departure..airplane arrival 1F6ED..1F6EF ; Extended_Pictographic# E0.0 [3] (🛭..🛯) .. 1F6F0 ; Extended_Pictographic# E0.7 [1] (🛰️) satellite -1F6F1..1F6F2 ; Extended_Pictographic# E0.0 [2] (🛱..🛲) ONCOMING FIRE ENGINE..DIESEL LOCOMOTIVE 1F6F3 ; Extended_Pictographic# E0.7 [1] (🛳️) passenger ship 1F6F4..1F6F6 ; Extended_Pictographic# E3.0 [3] (🛴..🛶) kick scooter..canoe 1F6F7..1F6F8 ; Extended_Pictographic# E5.0 [2] (🛷..🛸) sled..flying saucer @@ -1237,8 +1175,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F6FA ; Extended_Pictographic# E12.0 [1] (🛺) auto rickshaw 1F6FB..1F6FC ; Extended_Pictographic# E13.0 [2] (🛻..🛼) pickup truck..roller skate 1F6FD..1F6FF ; Extended_Pictographic# E0.0 [3] (🛽..🛿) .. -1F774..1F77F ; Extended_Pictographic# E0.0 [12] (🝴..🝿) LOT OF FORTUNE..ORCUS -1F7D5..1F7DF ; Extended_Pictographic# E0.0 [11] (🟕..🟟) CIRCLED TRIANGLE.. +1F7DA..1F7DF ; Extended_Pictographic# E0.0 [6] (🟚..🟟) .. 1F7E0..1F7EB ; Extended_Pictographic# E12.0 [12] (🟠..🟫) orange circle..brown square 1F7EC..1F7EF ; Extended_Pictographic# E0.0 [4] (🟬..🟯) .. 1F7F0 ; Extended_Pictographic# E14.0 [1] (🟰) heavy equals sign @@ -1247,7 +1184,10 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F848..1F84F ; Extended_Pictographic# E0.0 [8] (🡈..🡏) .. 1F85A..1F85F ; Extended_Pictographic# E0.0 [6] (🡚..🡟) .. 1F888..1F88F ; Extended_Pictographic# E0.0 [8] (🢈..🢏) .. -1F8AE..1F8FF ; Extended_Pictographic# E0.0 [82] (🢮..🣿) .. +1F8AE..1F8AF ; Extended_Pictographic# E0.0 [2] (🢮..🢯) .. +1F8BC..1F8BF ; Extended_Pictographic# E0.0 [4] (🢼..🢿) .. +1F8C2..1F8CF ; Extended_Pictographic# E0.0 [14] (🣂..🣏) .. +1F8D9..1F8FF ; Extended_Pictographic# E0.0 [39] (🣙..🣿) .. 1F90C ; Extended_Pictographic# E13.0 [1] (🤌) pinched fingers 1F90D..1F90F ; Extended_Pictographic# E12.0 [3] (🤍..🤏) white heart..pinching hand 1F910..1F918 ; Extended_Pictographic# E1.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns @@ -1293,7 +1233,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1F9CD..1F9CF ; Extended_Pictographic# E12.0 [3] (🧍..🧏) person standing..deaf person 1F9D0..1F9E6 ; Extended_Pictographic# E5.0 [23] (🧐..🧦) face with monocle..socks 1F9E7..1F9FF ; Extended_Pictographic# E11.0 [25] (🧧..🧿) red envelope..nazar amulet -1FA00..1FA6F ; Extended_Pictographic# E0.0 [112] (🨀..🩯) NEUTRAL CHESS KING.. +1FA58..1FA5F ; Extended_Pictographic# E0.0 [8] (🩘..🩟) .. +1FA6E..1FA6F ; Extended_Pictographic# E0.0 [2] (🩮..🩯) .. 1FA70..1FA73 ; Extended_Pictographic# E12.0 [4] (🩰..🩳) ballet shoes..shorts 1FA74 ; Extended_Pictographic# E13.0 [1] (🩴) thong sandal 1FA75..1FA77 ; Extended_Pictographic# E15.0 [3] (🩵..🩷) light blue heart..pink heart @@ -1304,7 +1245,9 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1FA83..1FA86 ; Extended_Pictographic# E13.0 [4] (🪃..🪆) boomerang..nesting dolls 1FA87..1FA88 ; Extended_Pictographic# E15.0 [2] (🪇..🪈) maracas..flute 1FA89 ; Extended_Pictographic# E16.0 [1] (🪉) harp -1FA8A..1FA8E ; Extended_Pictographic# E0.0 [5] (🪊..🪎) .. +1FA8A ; Extended_Pictographic# E17.0 [1] (🪊) trombone +1FA8B..1FA8D ; Extended_Pictographic# E0.0 [3] (🪋..🪍) .. +1FA8E ; Extended_Pictographic# E17.0 [1] (🪎) treasure chest 1FA8F ; Extended_Pictographic# E16.0 [1] (🪏) shovel 1FA90..1FA95 ; Extended_Pictographic# E12.0 [6] (🪐..🪕) ringed planet..banjo 1FA96..1FAA8 ; Extended_Pictographic# E13.0 [19] (🪖..🪨) military helmet..rock @@ -1318,7 +1261,10 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1FAC0..1FAC2 ; Extended_Pictographic# E13.0 [3] (🫀..🫂) anatomical heart..people hugging 1FAC3..1FAC5 ; Extended_Pictographic# E14.0 [3] (🫃..🫅) pregnant man..person with crown 1FAC6 ; Extended_Pictographic# E16.0 [1] (🫆) fingerprint -1FAC7..1FACD ; Extended_Pictographic# E0.0 [7] (🫇..🫍) .. +1FAC7 ; Extended_Pictographic# E0.0 [1] (🫇) +1FAC8 ; Extended_Pictographic# E17.0 [1] (🫈) hairy creature +1FAC9..1FACC ; Extended_Pictographic# E0.0 [4] (🫉..🫌) .. +1FACD ; Extended_Pictographic# E17.0 [1] (🫍) orca 1FACE..1FACF ; Extended_Pictographic# E15.0 [2] (🫎..🫏) moose..donkey 1FAD0..1FAD6 ; Extended_Pictographic# E13.0 [7] (🫐..🫖) blueberries..teapot 1FAD7..1FAD9 ; Extended_Pictographic# E14.0 [3] (🫗..🫙) pouring liquid..jar @@ -1329,12 +1275,14 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c 1FAE0..1FAE7 ; Extended_Pictographic# E14.0 [8] (🫠..🫧) melting face..bubbles 1FAE8 ; Extended_Pictographic# E15.0 [1] (🫨) shaking face 1FAE9 ; Extended_Pictographic# E16.0 [1] (🫩) face with bags under eyes -1FAEA..1FAEF ; Extended_Pictographic# E0.0 [6] (🫪..🫯) .. +1FAEA ; Extended_Pictographic# E17.0 [1] (🫪) distorted face +1FAEB..1FAEE ; Extended_Pictographic# E0.0 [4] (🫫..🫮) .. +1FAEF ; Extended_Pictographic# E17.0 [1] (🫯) fight cloud 1FAF0..1FAF6 ; Extended_Pictographic# E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands 1FAF7..1FAF8 ; Extended_Pictographic# E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand 1FAF9..1FAFF ; Extended_Pictographic# E0.0 [7] (🫹..🫿) .. 1FC00..1FFFD ; Extended_Pictographic# E0.0[1022] (🰀..🿽) .. -# Total elements: 3537 +# Total elements: 2848 #EOF diff --git a/src/java.base/share/legal/icu.md b/src/java.base/share/legal/icu.md index e27193e10bed8..634fea70d922b 100644 --- a/src/java.base/share/legal/icu.md +++ b/src/java.base/share/legal/icu.md @@ -1,4 +1,4 @@ -## International Components for Unicode (ICU4J) v76.1 +## International Components for Unicode (ICU4J) v78.1 ### ICU4J License ``` @@ -6,7 +6,7 @@ UNICODE LICENSE V3 COPYRIGHT AND PERMISSION NOTICE -Copyright © 2016-2024 Unicode, Inc. +Copyright © 2016-2025 Unicode, Inc. NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR diff --git a/src/java.base/share/legal/unicode.md b/src/java.base/share/legal/unicode.md index 8bd2ed8bd134c..a1009c70c1c95 100644 --- a/src/java.base/share/legal/unicode.md +++ b/src/java.base/share/legal/unicode.md @@ -1,4 +1,4 @@ -## The Unicode Standard, Unicode Character Database, Version 16.0.0 +## The Unicode Standard, Unicode Character Database, Version 17.0.0 ### Unicode Character Database ``` @@ -7,7 +7,7 @@ UNICODE LICENSE V3 COPYRIGHT AND PERMISSION NOTICE -Copyright © 1991-2024 Unicode, Inc. +Copyright © 1991-2025 Unicode, Inc. NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR From 27a38d9093958ae4851bc61b8d3f0d71dc780823 Mon Sep 17 00:00:00 2001 From: Chad Rakoczy Date: Tue, 18 Nov 2025 20:28:33 +0000 Subject: [PATCH 13/44] 8371121: compiler/whitebox/DeoptimizeRelocatedNMethod.java fails with C1 Reviewed-by: thartmann, chagedorn --- .../whitebox/DeoptimizeRelocatedNMethod.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java index 42f29044e8c88..c7cf355259be6 100644 --- a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java +++ b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java @@ -28,6 +28,8 @@ * @library /test/lib / * @modules java.base/jdk.internal.misc java.management * @requires vm.opt.DeoptimizeALot != true + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @requires !vm.emulatedClient * @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 -Xbatch -XX:+SegmentedCodeCache @@ -55,8 +57,8 @@ public static void main(String [] args) throws Exception { // Verify not initially compiled CompilerWhiteBoxTest.checkNotCompiled(method, false); - // Call function enough to compile - callFunction(); + // Enqueue method for compilation. This will block since background compilation is disabled + WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); // Verify now compiled CompilerWhiteBoxTest.checkCompiled(method, false); @@ -91,13 +93,6 @@ public static void main(String [] args) throws Exception { function(); } - // Call function multiple times to trigger compilation - private static void callFunction() { - for (int i = 0; i < CompilerWhiteBoxTest.THRESHOLD; i++) { - function(); - } - } - public static void function() { FUNCTION_RESULT = Math.random(); } From 66fb015267058f9b5e6788eaeaa758be56ba553e Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Tue, 18 Nov 2025 21:51:28 +0000 Subject: [PATCH 14/44] 8357579: Compilation error: first argument in call to 'memset' is a pointer to non-trivially copyable type Co-authored-by: Ioi Lam Reviewed-by: iklam, asmehra --- src/hotspot/share/oops/resolvedFieldEntry.cpp | 13 +++- src/hotspot/share/oops/resolvedFieldEntry.hpp | 60 ++++--------------- .../share/oops/resolvedMethodEntry.cpp | 17 ++++-- .../share/oops/resolvedMethodEntry.hpp | 51 +++++++--------- 4 files changed, 52 insertions(+), 89 deletions(-) diff --git a/src/hotspot/share/oops/resolvedFieldEntry.cpp b/src/hotspot/share/oops/resolvedFieldEntry.cpp index dd0a81ce0f3cb..83f1a6919a6ae 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.cpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.cpp @@ -23,8 +23,17 @@ */ #include "cds/archiveBuilder.hpp" +#include "cppstdlib/type_traits.hpp" #include "oops/resolvedFieldEntry.hpp" +static_assert(std::is_trivially_copyable_v); + +// Detect inadvertently introduced trailing padding. +class ResolvedFieldEntryWithExtra : public ResolvedFieldEntry { + u1 _extra_field; +}; +static_assert(sizeof(ResolvedFieldEntryWithExtra) > sizeof(ResolvedFieldEntry)); + void ResolvedFieldEntry::print_on(outputStream* st) const { st->print_cr("Field Entry:"); @@ -45,9 +54,7 @@ void ResolvedFieldEntry::print_on(outputStream* st) const { #if INCLUDE_CDS void ResolvedFieldEntry::remove_unshareable_info() { - u2 saved_cpool_index = _cpool_index; - memset(this, 0, sizeof(*this)); - _cpool_index = saved_cpool_index; + *this = ResolvedFieldEntry(_cpool_index); } void ResolvedFieldEntry::mark_and_relocate() { diff --git a/src/hotspot/share/oops/resolvedFieldEntry.hpp b/src/hotspot/share/oops/resolvedFieldEntry.hpp index 1df4ae8d956bc..77ad4815730e2 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.hpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.hpp @@ -43,6 +43,9 @@ // Field bytecodes start with a constant pool index as their operand, which is then rewritten to // a "field index", which is an index into the array of ResolvedFieldEntry. +// The explicit paddings are necessary for generating deterministic CDS archives. They prevent +// the C++ compiler from potentially inserting random values in unused gaps. + //class InstanceKlass; class ResolvedFieldEntry { friend class VMStructs; @@ -54,17 +57,9 @@ class ResolvedFieldEntry { u1 _tos_state; // TOS state u1 _flags; // Flags: [0000|00|is_final|is_volatile] u1 _get_code, _put_code; // Get and Put bytecodes of the field - - void copy_from(const ResolvedFieldEntry& other) { - _field_holder = other._field_holder; - _field_offset = other._field_offset; - _field_index = other._field_index; - _cpool_index = other._cpool_index; - _tos_state = other._tos_state; - _flags = other._flags; - _get_code = other._get_code; - _put_code = other._put_code; - } +#ifdef _LP64 + u4 _padding; +#endif public: ResolvedFieldEntry(u2 cpi) : @@ -75,48 +70,15 @@ class ResolvedFieldEntry { _tos_state(0), _flags(0), _get_code(0), - _put_code(0) {} + _put_code(0) +#ifdef _LP64 + , _padding(0) +#endif + {} ResolvedFieldEntry() : ResolvedFieldEntry(0) {} - // Notes on copy constructor, copy assignment operator, and copy_from(). - // These are necessary for generating deterministic CDS archives. - // - // We have some unused padding on 64-bit platforms (4 bytes at the tail end). - // - // When ResolvedFieldEntries in a ConstantPoolCache are allocated from the metaspace, - // their entire content (including the padding) is filled with zeros. They are - // then initialized with initialize_resolved_entries_array() in cpCache.cpp from a - // GrowableArray. - // - // The GrowableArray is initialized in rewriter.cpp, using ResolvedFieldEntries that - // are originally allocated from the C++ stack. Functions like GrowableArray::expand_to() - // will also allocate ResolvedFieldEntries from the stack. These may have random bits - // in the padding as the C++ compiler is allowed to leave the padding in uninitialized - // states. - // - // If we use the default copy constructor and/or default copy assignment operator, - // the random padding will be copied into the GrowableArray, from there - // to the ConstantPoolCache, and eventually to the CDS archive. As a result, the - // CDS archive will contain random bits, causing failures in - // test/hotspot/jtreg/runtime/cds/DeterministicDump.java (usually on Windows). - // - // By using copy_from(), we can prevent the random padding from being copied, - // ensuring that the ResolvedFieldEntries in a ConstantPoolCache (and thus the - // CDS archive) will have all zeros in the padding. - - // Copy constructor - ResolvedFieldEntry(const ResolvedFieldEntry& other) { - copy_from(other); - } - - // Copy assignment operator - ResolvedFieldEntry& operator=(const ResolvedFieldEntry& other) { - copy_from(other); - return *this; - } - // Bit shift to get flags // Note: Only two flags exists at the moment but more could be added enum { diff --git a/src/hotspot/share/oops/resolvedMethodEntry.cpp b/src/hotspot/share/oops/resolvedMethodEntry.cpp index 2dc533dbee00a..bb96ca86012b5 100644 --- a/src/hotspot/share/oops/resolvedMethodEntry.cpp +++ b/src/hotspot/share/oops/resolvedMethodEntry.cpp @@ -23,9 +23,18 @@ */ #include "cds/archiveBuilder.hpp" +#include "cppstdlib/type_traits.hpp" #include "oops/method.hpp" #include "oops/resolvedMethodEntry.hpp" +static_assert(std::is_trivially_copyable_v); + +// Detect inadvertently introduced trailing padding. +class ResolvedMethodEntryWithExtra : public ResolvedMethodEntry { + u1 _extra_field; +}; +static_assert(sizeof(ResolvedMethodEntryWithExtra) > sizeof(ResolvedMethodEntry)); + bool ResolvedMethodEntry::check_no_old_or_obsolete_entry() { // return false if m refers to a non-deleted old or obsolete method if (_method != nullptr) { @@ -39,14 +48,10 @@ bool ResolvedMethodEntry::check_no_old_or_obsolete_entry() { void ResolvedMethodEntry::reset_entry() { if (has_resolved_references_index()) { u2 saved_resolved_references_index = _entry_specific._resolved_references_index; - u2 saved_cpool_index = _cpool_index; - memset(this, 0, sizeof(*this)); + *this = ResolvedMethodEntry(_cpool_index); set_resolved_references_index(saved_resolved_references_index); - _cpool_index = saved_cpool_index; } else { - u2 saved_cpool_index = _cpool_index; - memset(this, 0, sizeof(*this)); - _cpool_index = saved_cpool_index; + *this = ResolvedMethodEntry(_cpool_index); } } diff --git a/src/hotspot/share/oops/resolvedMethodEntry.hpp b/src/hotspot/share/oops/resolvedMethodEntry.hpp index c95efb751e961..802cf252a6d3c 100644 --- a/src/hotspot/share/oops/resolvedMethodEntry.hpp +++ b/src/hotspot/share/oops/resolvedMethodEntry.hpp @@ -61,6 +61,9 @@ // pool entry and thus the same resolved method entry. // The is_vfinal flag indicates method pointer for a final method or an index. +// The explicit paddings are necessary for generating deterministic CDS archives. They prevent +// the C++ compiler from potentially inserting random values in unused gaps. + class InstanceKlass; class ResolvedMethodEntry { friend class VMStructs; @@ -70,6 +73,7 @@ class ResolvedMethodEntry { InstanceKlass* _interface_klass; // for interface and static u2 _resolved_references_index; // Index of resolved references array that holds the appendix oop for invokehandle u2 _table_index; // vtable/itable index for virtual and interface calls + // The padding field is unused here, as the parent constructor zeroes the union. } _entry_specific; u2 _cpool_index; // Constant pool index @@ -80,51 +84,36 @@ class ResolvedMethodEntry { #ifdef ASSERT bool _has_interface_klass; bool _has_table_index; +# ifdef _LP64 + u2 _padding1; + u4 _padding2; +# else + u1 _padding1; + u1 _padding2; +# endif #endif - // See comments in resolvedFieldEntry.hpp about copy_from and padding. - // We have unused padding on debug builds. - void copy_from(const ResolvedMethodEntry& other) { - _method = other._method; - _entry_specific = other._entry_specific; - _cpool_index = other._cpool_index; - _number_of_parameters = other._number_of_parameters; - _tos_state = other._tos_state; - _flags = other._flags; - _bytecode1 = other._bytecode1; - _bytecode2 = other._bytecode2; -#ifdef ASSERT - _has_interface_klass = other._has_interface_klass; - _has_table_index = other._has_table_index; -#endif - } - // Constructors public: ResolvedMethodEntry(u2 cpi) : _method(nullptr), + _entry_specific{nullptr}, _cpool_index(cpi), _number_of_parameters(0), _tos_state(0), _flags(0), _bytecode1(0), - _bytecode2(0) { - _entry_specific._interface_klass = nullptr; - DEBUG_ONLY(_has_interface_klass = false;) - DEBUG_ONLY(_has_table_index = false;) - } + _bytecode2(0) +#ifdef ASSERT + , _has_interface_klass(false), + _has_table_index(false), + _padding1(0), + _padding2(0) +#endif + {} ResolvedMethodEntry() : ResolvedMethodEntry(0) {} - ResolvedMethodEntry(const ResolvedMethodEntry& other) { - copy_from(other); - } - - ResolvedMethodEntry& operator=(const ResolvedMethodEntry& other) { - copy_from(other); - return *this; - } - // Bit shift to get flags enum { From b086e34f7170631d7568dc50a7c075dc9c2f173b Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 18 Nov 2025 21:51:54 +0000 Subject: [PATCH 15/44] 8371771: CDS test SharedStringsStress.java failed with insufficient heap Reviewed-by: kvn --- .../runtime/cds/appcds/sharedStrings/SharedStringsStress.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java index c51a67c445b1a..4d176c949c680 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java @@ -83,8 +83,10 @@ public static void test(String[] args) throws Exception { 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. OutputAnalyzer execOutput = TestCommon.exec(appJar, - TestCommon.concat(vmOptionsPrefix, "-Xlog:aot,cds", "HelloString")); + TestCommon.concat(vmOptionsPrefix, "-Xlog:aot,cds", "-Xms128m", "HelloString")); TestCommon.checkExec(execOutput); } } From 256a9beffc106d6657a912a33f97e7f97acbb1e1 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Tue, 18 Nov 2025 22:29:37 +0000 Subject: [PATCH 16/44] 8280469: C2: CHA support for interface calls when inlining through method handle linker Reviewed-by: kvn, roland --- src/hotspot/share/ci/ciInstanceKlass.cpp | 3 +- src/hotspot/share/ci/ciInstanceKlass.hpp | 1 + src/hotspot/share/opto/doCall.cpp | 27 ++- .../cha/StrengthReduceInterfaceCall.java | 205 ++++++++++++++++-- 4 files changed, 204 insertions(+), 32 deletions(-) diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp index 9bbf005356cec..64b9acf9146a1 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.cpp +++ b/src/hotspot/share/ci/ciInstanceKlass.cpp @@ -605,7 +605,7 @@ bool ciInstanceKlass::is_leaf_type() { if (is_shared()) { return is_final(); // approximately correct } else { - return !has_subklass() && (nof_implementors() == 0); + return !has_subklass() && (!is_interface() || nof_implementors() == 0); } } @@ -619,6 +619,7 @@ bool ciInstanceKlass::is_leaf_type() { // This is OK, since any dependencies we decide to assert // will be checked later under the Compile_lock. ciInstanceKlass* ciInstanceKlass::implementor() { + assert(is_interface(), "required"); ciInstanceKlass* impl = _implementor; if (impl == nullptr) { if (is_shared()) { diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp index ec8fc789c7d19..1f887771f5413 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.hpp +++ b/src/hotspot/share/ci/ciInstanceKlass.hpp @@ -259,6 +259,7 @@ class ciInstanceKlass : public ciKlass { ciInstanceKlass* unique_implementor() { assert(is_loaded(), "must be loaded"); + assert(is_interface(), "must be"); ciInstanceKlass* impl = implementor(); return (impl != this ? impl : nullptr); } diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index 754b0fa8d1c14..91bb743618b31 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -97,10 +97,9 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool Bytecodes::Code bytecode = caller->java_code_at_bci(bci); ciMethod* orig_callee = caller->get_method_at_bci(bci); - const bool is_virtual_or_interface = (bytecode == Bytecodes::_invokevirtual) || - (bytecode == Bytecodes::_invokeinterface) || - (orig_callee->intrinsic_id() == vmIntrinsics::_linkToVirtual) || - (orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface); + const bool is_virtual = (bytecode == Bytecodes::_invokevirtual) || (orig_callee->intrinsic_id() == vmIntrinsics::_linkToVirtual); + const bool is_interface = (bytecode == Bytecodes::_invokeinterface) || (orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface); + const bool is_virtual_or_interface = is_virtual || is_interface; const bool check_access = !orig_callee->is_method_handle_intrinsic(); // method handle intrinsics don't perform access checks @@ -339,17 +338,25 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool // number of implementors for decl_interface is 0 or 1. If // it's 0 then no class implements decl_interface and there's // no point in inlining. - if (call_does_dispatch && bytecode == Bytecodes::_invokeinterface) { - ciInstanceKlass* declared_interface = - caller->get_declared_method_holder_at_bci(bci)->as_instance_klass(); + if (call_does_dispatch && is_interface) { + ciInstanceKlass* declared_interface = nullptr; + if (orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface) { + // MemberName doesn't keep information about resolved interface class (REFC) once + // resolution is over, but resolved method holder (DECC) can be used as a + // conservative approximation. + declared_interface = callee->holder(); + } else { + assert(!orig_callee->is_method_handle_intrinsic(), "not allowed"); + declared_interface = caller->get_declared_method_holder_at_bci(bci)->as_instance_klass(); + } + assert(declared_interface->is_interface(), "required"); ciInstanceKlass* singleton = declared_interface->unique_implementor(); if (singleton != nullptr) { assert(singleton != declared_interface, "not a unique implementor"); - assert(check_access, "required"); ciMethod* cha_monomorphic_target = - callee->find_monomorphic_target(caller->holder(), declared_interface, singleton); + callee->find_monomorphic_target(caller->holder(), declared_interface, singleton, check_access); if (cha_monomorphic_target != nullptr && cha_monomorphic_target->holder() != env()->Object_klass()) { // subtype check against Object is useless @@ -372,7 +379,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool } } } - } // call_does_dispatch && bytecode == Bytecodes::_invokeinterface + } // call_does_dispatch && is_interface // Nothing claimed the intrinsic, we go with straight-forward inlining // for already discovered intrinsic. diff --git a/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java b/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java index aa014cfa63d21..5e2dc2e3d56b1 100644 --- a/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java +++ b/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java @@ -24,6 +24,7 @@ /* * @test * @requires !vm.graal.enabled + * @requires vm.opt.StressMethodHandleLinkerInlining == null | !vm.opt.StressMethodHandleLinkerInlining * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @modules java.base/jdk.internal.misc * java.base/jdk.internal.vm.annotation @@ -55,6 +56,9 @@ import jdk.internal.vm.annotation.DontInline; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + import static compiler.cha.Utils.*; public class StrengthReduceInterfaceCall { @@ -66,6 +70,18 @@ public static void main(String[] args) { run(ThreeLevelHierarchyAbstractVsDefault.class); run(ThreeLevelDefaultHierarchy.class); run(ThreeLevelDefaultHierarchy1.class); + + // Implementation limitation: CHA is not performed by C1 during inlining through MH linkers. + if (!jdk.test.whitebox.code.Compiler.isC1Enabled()) { + run(ObjectToString.TestMH.class, ObjectToString.class); + run(ObjectHashCode.TestMH.class, ObjectHashCode.class); + run(TwoLevelHierarchyLinear.TestMH.class, TwoLevelHierarchyLinear.class); + run(ThreeLevelHierarchyLinear.TestMH.class, ThreeLevelHierarchyLinear.class); + run(ThreeLevelHierarchyAbstractVsDefault.TestMH.class, ThreeLevelHierarchyAbstractVsDefault.class); + run(ThreeLevelDefaultHierarchy.TestMH.class, ThreeLevelDefaultHierarchy.class); + run(ThreeLevelDefaultHierarchy1.TestMH.class, ThreeLevelDefaultHierarchy1.class); + } + System.out.println("TEST PASSED"); } @@ -87,7 +103,7 @@ static class DJ1 implements J {} static class DJ2 implements J { public String toString() { return "DJ2"; }} @Override - public Object test(I i) { return ObjectToStringHelper.testHelper(i); /* invokeinterface I.toString() */ } + public Object test(I i) throws Throwable { return ObjectToStringHelper.testHelper(i); /* invokeinterface I.toString() */ } @TestCase public void testMono() { @@ -155,6 +171,20 @@ public void checkInvalidReceiver() { }); assertCompiled(); } + + public static class TestMH extends ObjectToString { + static final MethodHandle TEST_MH = findVirtualHelper(I.class, "toString", String.class, MethodHandles.lookup()); + + @Override + public void checkInvalidReceiver() { + // receiver type check failures trigger nmethod invalidation + } + + @Override + public Object test(I obj) throws Throwable { + return (String)TEST_MH.invokeExact(obj); // invokeinterface I.toString() + } + } } public static class ObjectHashCode extends ATest { @@ -175,7 +205,7 @@ static class DJ1 implements J {} static class DJ2 implements J { public int hashCode() { return super.hashCode(); }} @Override - public Object test(I i) { + public Object test(I i) throws Throwable { return ObjectHashCodeHelper.testHelper(i); /* invokeinterface I.hashCode() */ } @@ -242,6 +272,20 @@ public void checkInvalidReceiver() { }); assertCompiled(); } + + public static class TestMH extends ObjectHashCode { + static final MethodHandle TEST_MH = findVirtualHelper(I.class, "hashCode", int.class, MethodHandles.lookup()); + + @Override + public void checkInvalidReceiver() { + // receiver type check failures trigger nmethod invalidation + } + + @Override + public Object test(I obj) throws Throwable { + return (int)TEST_MH.invokeExact(obj); // invokeinterface I.hashCode() + } + } } public static class TwoLevelHierarchyLinear extends ATest { @@ -263,7 +307,7 @@ static class DJ1 implements J {} static class DJ2 implements J { public Object m() { return WRONG; }} @DontInline - public Object test(I i) { + public Object test(I i) throws Throwable { return i.m(); } @@ -366,6 +410,20 @@ public void checkInvalidReceiver() { }); assertCompiled(); } + + public static class TestMH extends TwoLevelHierarchyLinear { + static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup()); + + @Override + public void checkInvalidReceiver() { + // receiver type check failures trigger nmethod invalidation + } + + @Override + public Object test(I obj) throws Throwable { + return TEST_MH.invokeExact(obj); // invokeinterface I.m() + } + } } public static class ThreeLevelHierarchyLinear extends ATest { @@ -385,7 +443,7 @@ static class DI implements I { public Object m() { return WRONG; }} static class DJ implements J { public Object m() { return WRONG; }} @DontInline - public Object test(I i) { + public Object test(I i) throws Throwable { return i.m(); // I <: J.m ABSTRACT } @@ -404,10 +462,16 @@ public void testMega1() { assertCompiled(); // No deopt on not-yet-seen receiver // 2. No dependency invalidation: different context - initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT - K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT - K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT - assertCompiled(); + if (contextClass() == I.class) { + initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT + K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT + K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT + assertCompiled(); + } else if (contextClass() == J.class) { + // no classes to initialize w/o breaking a dependency + } else { + throw new InternalError("unsupported context: " + contextClass()); + } // 3. Dependency invalidation: DI.m <: I initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT @@ -491,6 +555,30 @@ public void checkInvalidReceiver() { }); assertCompiled(); } + + Class contextClass() { + return I.class; + } + + public static class TestMH extends ThreeLevelHierarchyLinear { + static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup()); + + @Override + public void checkInvalidReceiver() { + // receiver type check failures trigger nmethod invalidation + } + + @Override + Class contextClass() { + return J.class; + } + + @Override + public Object test(I obj) throws Throwable { + return TEST_MH.invokeExact(obj); // invokeinterface I.m() + } + } + } public static class ThreeLevelHierarchyAbstractVsDefault extends ATest { @@ -503,7 +591,7 @@ interface I extends J1, J2 {} // intf I. static class C implements I { public Object m() { return CORRECT; }} @DontInline - public Object test(I i) { + public Object test(I i) throws Throwable { return i.m(); // intf I.m OVERPASS } @@ -598,6 +686,20 @@ public void checkInvalidReceiver() { }); assertCompiled(); } + + public static class TestMH extends ThreeLevelHierarchyAbstractVsDefault { + static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup()); + + @Override + public void checkInvalidReceiver() { + // receiver type check failures trigger nmethod invalidation + } + + @Override + public Object test(I obj) throws Throwable { + return TEST_MH.invokeExact(obj); // invokeinterface I.m() + } + } } public static class ThreeLevelDefaultHierarchy extends ATest { @@ -617,7 +719,7 @@ static class DJ implements J { public Object m() { return WRONG; }} static class DK3 implements K3 {} @DontInline - public Object test(I i) { + public Object test(I i) throws Throwable { return i.m(); } @@ -636,11 +738,17 @@ public void testMega() { assertCompiled(); // 2. No dependency invalidation - initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT - K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT - K2.class, // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT - DK3.class); // DK3.m <: intf K3.m DEFAULT <: intf J.m ABSTRACT - assertCompiled(); + if (contextClass() == I.class) { + initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT + K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT + K2.class, // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT + DK3.class); // DK3.m <: intf K3.m DEFAULT <: intf J.m ABSTRACT + assertCompiled(); + } else if (contextClass() == J.class) { + // no classes to initialize w/o breaking a dependency + } else { + throw new InternalError("unsupported context: " + contextClass()); + } // 3. Dependency invalidation initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT @@ -666,6 +774,29 @@ public void checkInvalidReceiver() { }); assertCompiled(); } + + Class contextClass() { + return I.class; + } + + public static class TestMH extends ThreeLevelDefaultHierarchy { + static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup()); + + @Override + public void checkInvalidReceiver() { + // receiver type check failures trigger nmethod invalidation + } + + @Override + Class contextClass() { + return J.class; + } + + @Override + public Object test(I obj) throws Throwable { + return TEST_MH.invokeExact(obj); // invokeinterface I.m() + } + } } public static class ThreeLevelDefaultHierarchy1 extends ATest { @@ -686,7 +817,7 @@ static class DJ1 implements J1 { public Object m() { return WRONG; }} static class DJ2 implements J2 { public Object m() { return WRONG; }} @DontInline - public Object test(I i) { + public Object test(I i) throws Throwable { return i.m(); } @@ -705,11 +836,20 @@ public void testMega() { assertCompiled(); // 2. No dependency invalidation - initialize(DJ1.class, - DJ2.class, - K1.class, - K2.class, - K3.class); + if (contextClass() == I.class) { + initialize(DJ1.class, // DJ1.m <: intf J1 + DJ2.class, // DJ2.m <: intf J2.m ABSTRACT + K1.class, // intf K1 <: intf I <: intf J1, intf J2.m ABSTRACT + K2.class, // intf K2.m ABSTRACT <: intf I <: intf J1, intf J2.m ABSTRACT + K3.class); // intf K3.m DEFAULT <: intf I <: intf J1, intf J2.m ABSTRACT + } else if (contextClass() == J2.class) { + initialize(DJ1.class, // DJ1.m <: intf J1 + K1.class, // intf K1 <: intf I <: intf J1, intf J2.m ABSTRACT + K2.class, // intf K2.m ABSTRACT <: intf I <: intf J1, intf J2.m ABSTRACT + K3.class); // intf K3.m DEFAULT <: intf I <: intf J1, intf J2.m ABSTRACT + } else { + throw new InternalError("unsupported context: " + contextClass()); + } assertCompiled(); // 3. Dependency invalidation @@ -742,5 +882,28 @@ public void checkInvalidReceiver() { }); assertCompiled(); } + + Class contextClass() { + return I.class; + } + + public static class TestMH extends ThreeLevelDefaultHierarchy1 { + static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup()); + + @Override + public void checkInvalidReceiver() { + // receiver type check failures trigger nmethod invalidation + } + + @Override + Class contextClass() { + return J2.class; + } + + @Override + public Object test(I obj) throws Throwable { + return TEST_MH.invokeExact(obj); // invokeinterface I.m() + } + } } } From aeea8497562aabda12f292ad93c9f0f6935cc842 Mon Sep 17 00:00:00 2001 From: John Engebretson Date: Tue, 18 Nov 2025 23:37:06 +0000 Subject: [PATCH 17/44] 8371164: ArrayList.addAll() optimizations Reviewed-by: smarks, ogillespie --- .../share/classes/java/util/ArrayList.java | 14 +- .../share/classes/java/util/Collections.java | 14 ++ test/jdk/java/util/Collection/MOAT.java | 26 +++- .../java/util/ArrayListBulkOpsBenchmark.java | 128 ++++++++++++++++++ 4 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/util/ArrayListBulkOpsBenchmark.java diff --git a/src/java.base/share/classes/java/util/ArrayList.java b/src/java.base/share/classes/java/util/ArrayList.java index c00b130a553a2..53e818b99c51f 100644 --- a/src/java.base/share/classes/java/util/ArrayList.java +++ b/src/java.base/share/classes/java/util/ArrayList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -750,9 +750,17 @@ public void clear() { * @throws NullPointerException if the specified collection is null */ public boolean addAll(Collection c) { - Object[] a = c.toArray(); + Object[] a; + int numNew; + if (c.getClass() == ArrayList.class) { + ArrayList src = (ArrayList) c; + a = src.elementData; + numNew = src.size; + } else { + a = c.toArray(); + numNew = a.length; + } modCount++; - int numNew = a.length; if (numNew == 0) return false; Object[] elementData; diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index c48dbd8cf6c13..316458d6f9091 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -5253,6 +5253,20 @@ public boolean removeIf(Predicate filter) { public int hashCode() { return Objects.hashCode(element); } + @Override + public Object[] toArray() { + return new Object[] {element}; + } + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + if (a.length < 1) + a = (T[])Array.newInstance(a.getClass().getComponentType(), 1); + a[0] = (T)element; + if (a.length > 1) + a[1] = null; + return a; + } } /** diff --git a/test/jdk/java/util/Collection/MOAT.java b/test/jdk/java/util/Collection/MOAT.java index d0d27c8f91e14..687ac9fbd5fb2 100644 --- a/test/jdk/java/util/Collection/MOAT.java +++ b/test/jdk/java/util/Collection/MOAT.java @@ -26,7 +26,7 @@ * @bug 6207984 6272521 6192552 6269713 6197726 6260652 5073546 4137464 * 4155650 4216399 4294891 6282555 6318622 6355327 6383475 6420753 * 6431845 4802633 6570566 6570575 6570631 6570924 6691185 6691215 - * 4802647 7123424 8024709 8193128 8327858 8368178 + * 4802647 7123424 8024709 8193128 8327858 8368178 8371164 * @summary Run many tests on many Collection and Map implementations * @author Martin Buchholz * @modules java.base/java.util:open @@ -887,6 +887,28 @@ private static void testStringElement(Collection c) { catch (Throwable t) { unexpected(t); } } + private static void testAddAll(Collection c) { + clear(c); + + // Test ArrayList source + ArrayList arrayListSource = new ArrayList<>(); + arrayListSource.add(42); + arrayListSource.add(99); + check(c.addAll(arrayListSource)); + equal(c.size(), arrayListSource.size()); + check(c.containsAll(arrayListSource)); + + clear(c); + + // Test non-ArrayList source + LinkedList linkedListSource = new LinkedList<>(); + linkedListSource.add(77); + linkedListSource.add(88); + check(c.addAll(linkedListSource)); + equal(c.size(), linkedListSource.size()); + check(c.containsAll(linkedListSource)); + } + private static void testConcurrentCollection(Collection c) { try { c.add(1); @@ -1294,6 +1316,8 @@ private static void testCollection1(Collection c) { clear(c); testStringElement(c); oneElement(c); testStringElement(c); + testAddAll(c); + if (c.getClass().getName().matches(".*concurrent.*")) testConcurrentCollection(c); diff --git a/test/micro/org/openjdk/bench/java/util/ArrayListBulkOpsBenchmark.java b/test/micro/org/openjdk/bench/java/util/ArrayListBulkOpsBenchmark.java new file mode 100644 index 0000000000000..78b902f0c3add --- /dev/null +++ b/test/micro/org/openjdk/bench/java/util/ArrayListBulkOpsBenchmark.java @@ -0,0 +1,128 @@ +/* + * 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. + */ +package org.openjdk.bench.java.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.WeakHashMap; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + + +/** + * Benchmark measuring ArrayList addAll() performance. + * + * Tests the performance of ArrayList.addAll() when copying from another ArrayList. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(value = 1, jvmArgs = { "-XX:+UseParallelGC", "-Xmx3g" }) +public class ArrayListBulkOpsBenchmark { + @Param({"0", "1", "5", "75"}) + int size; + + @Param({"ArrayList", "LinkedList"}) + String type; + + List source; + + @Setup(Level.Trial) + public void setup() { + switch (type) { + case "ArrayList" -> source = new ArrayList<>(size); + case "LinkedList" -> source = new LinkedList<>(); + } + for (int i = 0; i < size; i++) source.add("key" + i); + } + + @Benchmark + public ArrayList addAll() { + ArrayList result = new ArrayList<>(size); + result.addAll(source); + return result; + } + + static void poisonCallSites() { + HashMap hashMapSource = new HashMap<>(); + TreeSet treeSetSource = new TreeSet<>(); + WeakHashMap weakHashMapSource = new WeakHashMap<>(); + for (int i = 0; i < 75; i++) { + hashMapSource.put("key" + i, "value" + i); + treeSetSource.add("key" + i); + weakHashMapSource.put("key" + i, "value" + i); + } + // Poison ArrayList.addAll() with different Collection types + for (int i = 0; i < 40_000; i++) { + ArrayList temp = new ArrayList<>(); + temp.addAll(hashMapSource.entrySet()); + temp.addAll(treeSetSource); + temp.addAll(weakHashMapSource.keySet()); + } + } + + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @State(Scope.Benchmark) + @Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(value = 1, jvmArgs = { "-XX:+UseParallelGC", "-Xmx3g" }) + public static class SingletonSet { + Set singletonSetSource = Collections.singleton("key"); + + @Param({ "false", "true" }) + private boolean poison; + + @Setup(Level.Trial) + public void setup() { + if (poison) poisonCallSites(); + } + + @Benchmark + public ArrayList addAllSingletonSet() { + ArrayList result = new ArrayList<>(1); + result.addAll(singletonSetSource); + return result; + } + } +} From 152cd4d8bab7d3428d0330c56a3cb9ed7feef313 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Tue, 18 Nov 2025 23:43:22 +0000 Subject: [PATCH 18/44] 8371956: Convert OopStorage to use Atomic Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/shared/oopStorage.cpp | 157 +++++++++--------- src/hotspot/share/gc/shared/oopStorage.hpp | 11 +- .../share/gc/shared/oopStorage.inline.hpp | 19 ++- .../share/gc/shared/oopStorageParState.hpp | 5 +- .../gtest/gc/shared/test_oopStorage.cpp | 4 +- 5 files changed, 101 insertions(+), 95 deletions(-) diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index d52efc13dacaf..a1cc3ffa553f0 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -28,7 +28,7 @@ #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" #include "nmt/memTracker.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" @@ -122,7 +122,7 @@ OopStorage::ActiveArray::ActiveArray(size_t size) : {} OopStorage::ActiveArray::~ActiveArray() { - assert(_refcount == 0, "precondition"); + assert(_refcount.load_relaxed() == 0, "precondition"); } OopStorage::ActiveArray* OopStorage::ActiveArray::create(size_t size, @@ -144,32 +144,32 @@ size_t OopStorage::ActiveArray::size() const { } size_t OopStorage::ActiveArray::block_count() const { - return _block_count; + return _block_count.load_relaxed(); } size_t OopStorage::ActiveArray::block_count_acquire() const { - return AtomicAccess::load_acquire(&_block_count); + return _block_count.load_acquire(); } void OopStorage::ActiveArray::increment_refcount() const { - int new_value = AtomicAccess::add(&_refcount, 1); - assert(new_value >= 1, "negative refcount %d", new_value - 1); + int old_value = _refcount.fetch_then_add(1); + assert(old_value >= 0, "negative refcount %d", old_value); } bool OopStorage::ActiveArray::decrement_refcount() const { - int new_value = AtomicAccess::sub(&_refcount, 1); + int new_value = _refcount.sub_then_fetch(1); assert(new_value >= 0, "negative refcount %d", new_value); return new_value == 0; } bool OopStorage::ActiveArray::push(Block* block) { - size_t index = _block_count; + size_t index = _block_count.load_relaxed(); if (index < _size) { block->set_active_index(index); *block_ptr(index) = block; // Use a release_store to ensure all the setup is complete before // making the block visible. - AtomicAccess::release_store(&_block_count, index + 1); + _block_count.release_store(index + 1); return true; } else { return false; @@ -177,19 +177,19 @@ bool OopStorage::ActiveArray::push(Block* block) { } void OopStorage::ActiveArray::remove(Block* block) { - assert(_block_count > 0, "array is empty"); + assert(_block_count.load_relaxed() > 0, "array is empty"); size_t index = block->active_index(); assert(*block_ptr(index) == block, "block not present"); - size_t last_index = _block_count - 1; + size_t last_index = _block_count.load_relaxed() - 1; Block* last_block = *block_ptr(last_index); last_block->set_active_index(index); *block_ptr(index) = last_block; - _block_count = last_index; + _block_count.store_relaxed(last_index); } void OopStorage::ActiveArray::copy_from(const ActiveArray* from) { - assert(_block_count == 0, "array must be empty"); - size_t count = from->_block_count; + assert(_block_count.load_relaxed() == 0, "array must be empty"); + size_t count = from->_block_count.load_relaxed(); assert(count <= _size, "precondition"); Block* const* from_ptr = from->block_ptr(0); Block** to_ptr = block_ptr(0); @@ -198,7 +198,7 @@ void OopStorage::ActiveArray::copy_from(const ActiveArray* from) { assert(block->active_index() == i, "invariant"); *to_ptr++ = block; } - _block_count = count; + _block_count.store_relaxed(count); } // Blocks start with an array of BitsPerWord oop entries. That array @@ -230,14 +230,17 @@ OopStorage::Block::Block(const OopStorage* owner, void* memory) : assert(is_aligned(this, block_alignment), "misaligned block"); } +#ifdef ASSERT OopStorage::Block::~Block() { - assert(_release_refcount == 0, "deleting block while releasing"); - assert(_deferred_updates_next == nullptr, "deleting block with deferred update"); + assert(_release_refcount.load_relaxed() == 0, "deleting block while releasing"); + assert(_deferred_updates_next.load_relaxed() == nullptr, "deleting block with deferred update"); // Clear fields used by block_for_ptr and entry validation, which - // might help catch bugs. Volatile to prevent dead-store elimination. - const_cast(_allocated_bitmask) = 0; + // might help catch bugs. + _allocated_bitmask.store_relaxed(0); + // Volatile to prevent dead-store elimination. const_cast(_owner_address) = 0; } +#endif // ASSERT size_t OopStorage::Block::allocation_size() { // _data must be first member, so aligning Block aligns _data. @@ -272,16 +275,16 @@ uintx OopStorage::Block::bitmask_for_entry(const oop* ptr) const { bool OopStorage::Block::is_safe_to_delete() const { assert(is_empty(), "precondition"); OrderAccess::loadload(); - return (AtomicAccess::load_acquire(&_release_refcount) == 0) && - (AtomicAccess::load_acquire(&_deferred_updates_next) == nullptr); + return ((_release_refcount.load_acquire() == 0) && + (_deferred_updates_next.load_acquire() == nullptr)); } OopStorage::Block* OopStorage::Block::deferred_updates_next() const { - return _deferred_updates_next; + return _deferred_updates_next.load_relaxed(); } void OopStorage::Block::set_deferred_updates_next(Block* block) { - _deferred_updates_next = block; + _deferred_updates_next.store_relaxed(block); } bool OopStorage::Block::contains(const oop* ptr) const { @@ -321,9 +324,8 @@ void OopStorage::Block::atomic_add_allocated(uintx add) { // we can use an atomic add to implement the operation. The assert post // facto verifies the precondition held; if there were any set bits in // common, then after the add at least one of them will be zero. - uintx sum = AtomicAccess::add(&_allocated_bitmask, add); - assert((sum & add) == add, "some already present: %zu:%zu", - sum, add); + uintx sum = _allocated_bitmask.add_then_fetch(add); + assert((sum & add) == add, "some already present: %zu:%zu", sum, add); } oop* OopStorage::Block::allocate() { @@ -452,7 +454,7 @@ oop* OopStorage::allocate() { oop* result = block->allocate(); assert(result != nullptr, "allocation failed"); assert(!block->is_empty(), "postcondition"); - AtomicAccess::inc(&_allocation_count); // release updates outside lock. + _allocation_count.add_then_fetch(1u); // release updates outside lock. if (block->is_full()) { // Transitioning from not full to full. // Remove full blocks from consideration by future allocates. @@ -490,7 +492,7 @@ size_t OopStorage::allocate(oop** ptrs, size_t size) { assert(!is_empty_bitmask(taken), "invariant"); } // Drop lock, now that we've taken all available entries from block. size_t num_taken = population_count(taken); - AtomicAccess::add(&_allocation_count, num_taken); + _allocation_count.add_then_fetch(num_taken); // Fill ptrs from those taken entries. size_t limit = MIN2(num_taken, size); for (size_t i = 0; i < limit; ++i) { @@ -506,7 +508,7 @@ size_t OopStorage::allocate(oop** ptrs, size_t size) { assert(size == limit, "invariant"); assert(num_taken == (limit + population_count(taken)), "invariant"); block->release_entries(taken, this); - AtomicAccess::sub(&_allocation_count, num_taken - limit); + _allocation_count.sub_then_fetch(num_taken - limit); } log_trace(oopstorage, ref)("%s: bulk allocate %zu, returned %zu", name(), limit, num_taken - limit); @@ -527,9 +529,9 @@ bool OopStorage::try_add_block() { if (block == nullptr) return false; // Add new block to the _active_array, growing if needed. - if (!_active_array->push(block)) { + if (!_active_array.load_relaxed()->push(block)) { if (expand_active_array()) { - guarantee(_active_array->push(block), "push failed after expansion"); + guarantee(_active_array.load_relaxed()->push(block), "push failed after expansion"); } else { log_debug(oopstorage, blocks)("%s: failed active array expand", name()); Block::delete_block(*block); @@ -576,7 +578,7 @@ OopStorage::Block* OopStorage::block_for_allocation() { // indicate allocation failure. bool OopStorage::expand_active_array() { assert_lock_strong(_allocation_mutex); - ActiveArray* old_array = _active_array; + ActiveArray* old_array = _active_array.load_relaxed(); size_t new_size = 2 * old_array->size(); log_debug(oopstorage, blocks)("%s: expand active array %zu", name(), new_size); @@ -599,7 +601,7 @@ void OopStorage::replace_active_array(ActiveArray* new_array) { // Update new_array refcount to account for the new reference. new_array->increment_refcount(); // Install new_array, ensuring its initialization is complete first. - AtomicAccess::release_store(&_active_array, new_array); + _active_array.release_store(new_array); // Wait for any readers that could read the old array from _active_array. // Can't use GlobalCounter here, because this is called from allocate(), // which may be called in the scope of a GlobalCounter critical section @@ -617,7 +619,7 @@ void OopStorage::replace_active_array(ActiveArray* new_array) { // using it. OopStorage::ActiveArray* OopStorage::obtain_active_array() const { SingleWriterSynchronizer::CriticalSection cs(&_protect_active); - ActiveArray* result = AtomicAccess::load_acquire(&_active_array); + ActiveArray* result = _active_array.load_acquire(); result->increment_refcount(); return result; } @@ -625,7 +627,7 @@ OopStorage::ActiveArray* OopStorage::obtain_active_array() const { // Decrement refcount of array and destroy if refcount is zero. void OopStorage::relinquish_block_array(ActiveArray* array) const { if (array->decrement_refcount()) { - assert(array != _active_array, "invariant"); + assert(array != _active_array.load_relaxed(), "invariant"); ActiveArray::destroy(array); } } @@ -672,14 +674,14 @@ static void log_release_transitions(uintx releasing, void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) { assert(releasing != 0, "preconditon"); // Prevent empty block deletion when transitioning to empty. - AtomicAccess::inc(&_release_refcount); + _release_refcount.add_then_fetch(1u); // Atomically update allocated bitmask. - uintx old_allocated = _allocated_bitmask; + uintx old_allocated = _allocated_bitmask.load_relaxed(); while (true) { assert((releasing & ~old_allocated) == 0, "releasing unallocated entries"); uintx new_value = old_allocated ^ releasing; - uintx fetched = AtomicAccess::cmpxchg(&_allocated_bitmask, old_allocated, new_value); + uintx fetched = _allocated_bitmask.compare_exchange(old_allocated, new_value); if (fetched == old_allocated) break; // Successful update. old_allocated = fetched; // Retry with updated bitmask. } @@ -698,12 +700,12 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) { // then someone else has made such a claim and the deferred update has not // yet been processed and will include our change, so we don't need to do // anything further. - if (AtomicAccess::replace_if_null(&_deferred_updates_next, this)) { + if (_deferred_updates_next.compare_exchange(nullptr, this) == nullptr) { // Successfully claimed. Push, with self-loop for end-of-list. - Block* head = owner->_deferred_updates; + Block* head = owner->_deferred_updates.load_relaxed(); while (true) { - _deferred_updates_next = (head == nullptr) ? this : head; - Block* fetched = AtomicAccess::cmpxchg(&owner->_deferred_updates, head, this); + _deferred_updates_next.store_relaxed((head == nullptr) ? this : head); + Block* fetched = owner->_deferred_updates.compare_exchange(head, this); if (fetched == head) break; // Successful update. head = fetched; // Retry with updated head. } @@ -720,7 +722,7 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) { } } // Release hold on empty block deletion. - AtomicAccess::dec(&_release_refcount); + _release_refcount.sub_then_fetch(1u); } // Process one available deferred update. Returns true if one was processed. @@ -729,13 +731,13 @@ bool OopStorage::reduce_deferred_updates() { // Atomically pop a block off the list, if any available. // No ABA issue because this is only called by one thread at a time. // The atomicity is wrto pushes by release(). - Block* block = AtomicAccess::load_acquire(&_deferred_updates); + Block* block = _deferred_updates.load_acquire(); while (true) { if (block == nullptr) return false; // Try atomic pop of block from list. Block* tail = block->deferred_updates_next(); if (block == tail) tail = nullptr; // Handle self-loop end marker. - Block* fetched = AtomicAccess::cmpxchg(&_deferred_updates, block, tail); + Block* fetched = _deferred_updates.compare_exchange(block, tail); if (fetched == block) break; // Update successful. block = fetched; // Retry with updated block. } @@ -780,7 +782,7 @@ void OopStorage::release(const oop* ptr) { assert(block != nullptr, "%s: invalid release " PTR_FORMAT, name(), p2i(ptr)); log_trace(oopstorage, ref)("%s: releasing " PTR_FORMAT, name(), p2i(ptr)); block->release_entries(block->bitmask_for_entry(ptr), this); - AtomicAccess::dec(&_allocation_count); + _allocation_count.sub_then_fetch(1u); } void OopStorage::release(const oop* const* ptrs, size_t size) { @@ -806,7 +808,7 @@ void OopStorage::release(const oop* const* ptrs, size_t size) { } // Release the contiguous entries that are in block. block->release_entries(releasing, this); - AtomicAccess::sub(&_allocation_count, count); + _allocation_count.sub_then_fetch(count); } } @@ -837,7 +839,7 @@ OopStorage::OopStorage(const char* name, MemTag mem_tag) : _mem_tag(mem_tag), _needs_cleanup(false) { - _active_array->increment_refcount(); + _active_array.load_relaxed()->increment_refcount(); assert(_active_mutex->rank() < _allocation_mutex->rank(), "%s: active_mutex must have lower rank than allocation_mutex", _name); assert(Service_lock->rank() < _active_mutex->rank(), @@ -852,20 +854,21 @@ void OopStorage::delete_empty_block(const Block& block) { OopStorage::~OopStorage() { Block* block; - while ((block = _deferred_updates) != nullptr) { - _deferred_updates = block->deferred_updates_next(); + while ((block = _deferred_updates.load_relaxed()) != nullptr) { + _deferred_updates.store_relaxed(block->deferred_updates_next()); block->set_deferred_updates_next(nullptr); } while ((block = _allocation_list.head()) != nullptr) { _allocation_list.unlink(*block); } - bool unreferenced = _active_array->decrement_refcount(); + ActiveArray* array = _active_array.load_relaxed(); + bool unreferenced = array->decrement_refcount(); assert(unreferenced, "deleting storage while _active_array is referenced"); - for (size_t i = _active_array->block_count(); 0 < i; ) { - block = _active_array->at(--i); + for (size_t i = array->block_count(); 0 < i; ) { + block = array->at(--i); Block::delete_block(*block); } - ActiveArray::destroy(_active_array); + ActiveArray::destroy(array); os::free(const_cast(_name)); } @@ -894,7 +897,7 @@ bool OopStorage::should_report_num_dead() const { // face of frequent explicit ServiceThread wakeups, hence the defer period. // Global cleanup request state. -static volatile bool needs_cleanup_requested = false; +static Atomic needs_cleanup_requested{false}; // Time after which a cleanup is permitted. static jlong cleanup_permit_time = 0; @@ -906,12 +909,11 @@ const jlong cleanup_defer_period = 500 * NANOSECS_PER_MILLISEC; bool OopStorage::has_cleanup_work_and_reset() { assert_lock_strong(Service_lock); - if (AtomicAccess::load_acquire(&needs_cleanup_requested) && - os::javaTimeNanos() > cleanup_permit_time) { - cleanup_permit_time = - os::javaTimeNanos() + cleanup_defer_period; + if (needs_cleanup_requested.load_acquire() && + (os::javaTimeNanos() > cleanup_permit_time)) { + cleanup_permit_time = os::javaTimeNanos() + cleanup_defer_period; // Set the request flag false and return its old value. - AtomicAccess::release_store(&needs_cleanup_requested, false); + needs_cleanup_requested.release_store(false); return true; } else { return false; @@ -923,22 +925,22 @@ bool OopStorage::has_cleanup_work_and_reset() { void OopStorage::record_needs_cleanup() { // Set local flag first, else ServiceThread could wake up and miss // the request. - AtomicAccess::release_store(&_needs_cleanup, true); - AtomicAccess::release_store_fence(&needs_cleanup_requested, true); + _needs_cleanup.release_store(true); + needs_cleanup_requested.release_store_fence(true); } bool OopStorage::delete_empty_blocks() { // ServiceThread might have oopstorage work, but not for this object. // But check for deferred updates, which might provide cleanup work. - if (!AtomicAccess::load_acquire(&_needs_cleanup) && - (AtomicAccess::load_acquire(&_deferred_updates) == nullptr)) { + if (!_needs_cleanup.load_acquire() && + (_deferred_updates.load_acquire() == nullptr)) { return false; } MutexLocker ml(_allocation_mutex, Mutex::_no_safepoint_check_flag); // Clear the request before processing. - AtomicAccess::release_store_fence(&_needs_cleanup, false); + _needs_cleanup.release_store_fence(false); // Other threads could be adding to the empty block count or the // deferred update list while we're working. Set an upper bound on @@ -977,7 +979,7 @@ bool OopStorage::delete_empty_blocks() { // but don't re-notify, to avoid useless spinning of the // ServiceThread. Instead, iteration completion notifies. if (_concurrent_iteration_count > 0) return true; - _active_array->remove(block); + _active_array.load_relaxed()->remove(block); } // Remove block from _allocation_list and delete it. _allocation_list.unlink(*block); @@ -1001,8 +1003,9 @@ OopStorage::EntryStatus OopStorage::allocation_status(const oop* ptr) const { MutexLocker ml(_allocation_mutex, Mutex::_no_safepoint_check_flag); // Block could be a false positive, so get index carefully. size_t index = Block::active_index_safe(block); - if ((index < _active_array->block_count()) && - (block == _active_array->at(index)) && + ActiveArray* array = _active_array.load_relaxed(); + if ((index < array->block_count()) && + (block == array->at(index)) && block->contains(ptr)) { if ((block->allocated_bitmask() & block->bitmask_for_entry(ptr)) != 0) { return ALLOCATED_ENTRY; @@ -1015,7 +1018,7 @@ OopStorage::EntryStatus OopStorage::allocation_status(const oop* ptr) const { } size_t OopStorage::allocation_count() const { - return _allocation_count; + return _allocation_count.load_relaxed(); } size_t OopStorage::block_count() const { @@ -1084,7 +1087,7 @@ void OopStorage::BasicParState::update_concurrent_iteration_count(int value) { bool OopStorage::BasicParState::claim_next_segment(IterationData* data) { data->_processed += data->_segment_end - data->_segment_start; - size_t start = AtomicAccess::load_acquire(&_next_block); + size_t start = _next_block.load_acquire(); if (start >= _block_count) { return finish_iteration(data); // No more blocks available. } @@ -1097,11 +1100,11 @@ bool OopStorage::BasicParState::claim_next_segment(IterationData* data) { size_t max_step = 10; size_t remaining = _block_count - start; size_t step = MIN2(max_step, 1 + (remaining / _estimated_thread_count)); - // AtomicAccess::add with possible overshoot. This can perform better + // Atomic add with possible overshoot. This can perform better // than a CAS loop on some platforms when there is contention. // We can cope with the uncertainty by recomputing start/end from // the result of the add, and dealing with potential overshoot. - size_t end = AtomicAccess::add(&_next_block, step); + size_t end = _next_block.add_then_fetch(step); // _next_block may have changed, so recompute start from result of add. start = end - step; // _next_block may have changed so much that end has overshot. @@ -1128,15 +1131,15 @@ bool OopStorage::BasicParState::finish_iteration(const IterationData* data) cons } size_t OopStorage::BasicParState::num_dead() const { - return AtomicAccess::load(&_num_dead); + return _num_dead.load_relaxed(); } void OopStorage::BasicParState::increment_num_dead(size_t num_dead) { - AtomicAccess::add(&_num_dead, num_dead); + _num_dead.add_then_fetch(num_dead); } void OopStorage::BasicParState::report_num_dead() const { - _storage->report_num_dead(AtomicAccess::load(&_num_dead)); + _storage->report_num_dead(_num_dead.load_relaxed()); } const char* OopStorage::name() const { return _name; } @@ -1164,8 +1167,8 @@ bool OopStorage::Block::print_containing(const oop* addr, outputStream* st) { #ifndef PRODUCT void OopStorage::print_on(outputStream* st) const { - size_t allocations = _allocation_count; - size_t blocks = _active_array->block_count(); + size_t allocations = _allocation_count.load_relaxed(); + size_t blocks = _active_array.load_relaxed()->block_count(); double data_size = section_size * section_count; double alloc_percentage = percent_of((double)allocations, blocks * data_size); diff --git a/src/hotspot/share/gc/shared/oopStorage.hpp b/src/hotspot/share/gc/shared/oopStorage.hpp index 34c980a058659..6097eeaa4f479 100644 --- a/src/hotspot/share/gc/shared/oopStorage.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.hpp @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "oops/oop.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/singleWriterSynchronizer.hpp" @@ -258,15 +259,15 @@ class OopStorage : public CHeapObjBase { private: const char* _name; - ActiveArray* _active_array; + Atomic _active_array; AllocationList _allocation_list; - Block* volatile _deferred_updates; + Atomic _deferred_updates; Mutex* _allocation_mutex; Mutex* _active_mutex; NumDeadCallback _num_dead_callback; - // Volatile for racy unlocked accesses. - volatile size_t _allocation_count; + // Atomic for racy unlocked accesses. + Atomic _allocation_count; // Protection for _active_array. mutable SingleWriterSynchronizer _protect_active; @@ -278,7 +279,7 @@ class OopStorage : public CHeapObjBase { MemTag _mem_tag; // Flag indicating this storage object is a candidate for empty block deletion. - volatile bool _needs_cleanup; + Atomic _needs_cleanup; // Clients construct via "create" factory function. OopStorage(const char* name, MemTag mem_tag); diff --git a/src/hotspot/share/gc/shared/oopStorage.inline.hpp b/src/hotspot/share/gc/shared/oopStorage.inline.hpp index 4fb1d8fcaf126..c2747781a6b88 100644 --- a/src/hotspot/share/gc/shared/oopStorage.inline.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.inline.hpp @@ -30,6 +30,7 @@ #include "cppstdlib/type_traits.hpp" #include "memory/allocation.hpp" #include "oops/oop.hpp" +#include "runtime/atomic.hpp" #include "runtime/safepoint.hpp" #include "utilities/align.hpp" #include "utilities/count_trailing_zeros.hpp" @@ -42,8 +43,8 @@ class OopStorage::ActiveArray { friend class OopStorage::TestAccess; size_t _size; - volatile size_t _block_count; - mutable volatile int _refcount; + Atomic _block_count; + mutable Atomic _refcount; // Block* _blocks[1]; // Pseudo flexible array member. ActiveArray(size_t size); @@ -104,7 +105,7 @@ inline OopStorage::Block** OopStorage::ActiveArray::block_ptr(size_t index) { } inline OopStorage::Block* OopStorage::ActiveArray::at(size_t index) const { - assert(index < _block_count, "precondition"); + assert(index < _block_count.load_relaxed(), "precondition"); return *block_ptr(index); } @@ -135,16 +136,16 @@ class OopStorage::Block /* No base class, to avoid messing up alignment. */ { oop _data[BitsPerWord]; static const unsigned _data_pos = 0; // Position of _data. - volatile uintx _allocated_bitmask; // One bit per _data element. + Atomic _allocated_bitmask; // One bit per _data element. intptr_t _owner_address; void* _memory; // Unaligned storage containing block. size_t _active_index; AllocationListEntry _allocation_list_entry; - Block* volatile _deferred_updates_next; - volatile uintx _release_refcount; + Atomic _deferred_updates_next; + Atomic _release_refcount; Block(const OopStorage* owner, void* memory); - ~Block(); + ~Block() NOT_DEBUG(= default); void check_index(unsigned index) const; unsigned get_index(const oop* ptr) const; @@ -322,7 +323,7 @@ inline const oop* OopStorage::Block::get_pointer(unsigned index) const { } inline uintx OopStorage::Block::allocated_bitmask() const { - return _allocated_bitmask; + return _allocated_bitmask.load_relaxed(); } inline uintx OopStorage::Block::bitmask_for_index(unsigned index) const { @@ -366,7 +367,7 @@ inline bool OopStorage::iterate_impl(F f, Storage* storage) { // Propagate const/non-const iteration to the block layer, by using // const or non-const blocks as corresponding to Storage. using BlockPtr = std::conditional_t::value, const Block*, Block*>; - ActiveArray* blocks = storage->_active_array; + ActiveArray* blocks = storage->_active_array.load_relaxed(); size_t limit = blocks->block_count(); for (size_t i = 0; i < limit; ++i) { BlockPtr block = blocks->at(i); diff --git a/src/hotspot/share/gc/shared/oopStorageParState.hpp b/src/hotspot/share/gc/shared/oopStorageParState.hpp index 046bf9de8c239..cad1a1f0cf62c 100644 --- a/src/hotspot/share/gc/shared/oopStorageParState.hpp +++ b/src/hotspot/share/gc/shared/oopStorageParState.hpp @@ -27,6 +27,7 @@ #include "cppstdlib/type_traits.hpp" #include "gc/shared/oopStorage.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" ////////////////////////////////////////////////////////////////////////////// @@ -131,10 +132,10 @@ class OopStorage::BasicParState { const OopStorage* _storage; ActiveArray* _active_array; size_t _block_count; - volatile size_t _next_block; + Atomic _next_block; uint _estimated_thread_count; bool _concurrent; - volatile size_t _num_dead; + Atomic _num_dead; NONCOPYABLE(BasicParState); diff --git a/test/hotspot/gtest/gc/shared/test_oopStorage.cpp b/test/hotspot/gtest/gc/shared/test_oopStorage.cpp index 285cc2630a32d..b343e7fc47fe3 100644 --- a/test/hotspot/gtest/gc/shared/test_oopStorage.cpp +++ b/test/hotspot/gtest/gc/shared/test_oopStorage.cpp @@ -47,7 +47,7 @@ class OopStorage::TestAccess : public AllStatic { typedef OopStorage::ActiveArray ActiveArray; static ActiveArray& active_array(const OopStorage& storage) { - return *storage._active_array; + return *storage._active_array.load_relaxed(); } static AllocationList& allocation_list(OopStorage& storage) { @@ -90,7 +90,7 @@ class OopStorage::TestAccess : public AllStatic { } static void block_array_set_block_count(ActiveArray* blocks, size_t count) { - blocks->_block_count = count; + blocks->_block_count.store_relaxed(count); } static const oop* get_block_pointer(const Block& block, unsigned index) { From 902aa4dcd297fef34cb302e468b030c48665ec84 Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Tue, 18 Nov 2025 23:51:32 +0000 Subject: [PATCH 19/44] 8372120: Add missing sound keyword to MIDI tests Reviewed-by: kcr, dholmes --- .../javax/sound/midi/MidiDeviceConnectors/TestAllDevices.java | 1 + test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java | 1 + .../sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java | 1 + test/jdk/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java | 1 + .../javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java | 1 + 5 files changed, 5 insertions(+) diff --git a/test/jdk/javax/sound/midi/MidiDeviceConnectors/TestAllDevices.java b/test/jdk/javax/sound/midi/MidiDeviceConnectors/TestAllDevices.java index 27290738a0cc8..afac39b83fbfd 100644 --- a/test/jdk/javax/sound/midi/MidiDeviceConnectors/TestAllDevices.java +++ b/test/jdk/javax/sound/midi/MidiDeviceConnectors/TestAllDevices.java @@ -24,6 +24,7 @@ /** * @test * @bug 4933700 + * @key sound * @summary Tests that default devices return MidiDeviceTransmitter/Receiver and returned objects return correct MidiDevice * @compile TestAllDevices.java * @run main TestAllDevices diff --git a/test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java b/test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java index 00c57f46c98cf..7b8c4b0a3f27f 100644 --- a/test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java +++ b/test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java @@ -35,6 +35,7 @@ /** * @test * @bug 8074211 8237495 8301310 + * @key sound * @summary fail with memory errors when asked to send a sysex message starting * with 0xF7 */ diff --git a/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java b/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java index efb57eeeae2d9..05ba16cded560 100644 --- a/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java +++ b/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java @@ -32,6 +32,7 @@ /** * @test * @bug 8143909 + * @key sound * @author Sergey Bylokhov */ public final class ExpectedNPEOnNull { diff --git a/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java b/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java index 8eabb992bca96..71d27c4943c01 100644 --- a/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java +++ b/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java @@ -35,6 +35,7 @@ /** * @test * @bug 8059743 + * @key sound * @summary MidiDeviceProvider shouldn't returns incorrect results in case of * some unknown MidiDevice.Info * @author Sergey Bylokhov diff --git a/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java b/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java index 685a5e8af629e..5d37759fbad71 100644 --- a/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java +++ b/test/jdk/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java @@ -30,6 +30,7 @@ /** * @test * @bug 8058115 + * @key sound * @summary MidiDeviceProvider shouldn't returns incorrect results in case of * unsupported MidiDevice.Info * @author Sergey Bylokhov From 02ff38f2d7f6abc0e4661e8226bc6780b7a11c3a Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 19 Nov 2025 05:04:34 +0000 Subject: [PATCH 20/44] 8363986: Heap region in CDS archive is not at deterministic address Reviewed-by: kvn, asmehra --- src/hotspot/share/cds/aotArtifactFinder.hpp | 2 +- src/hotspot/share/cds/aotMapLogger.cpp | 2 +- src/hotspot/share/cds/aotMappedHeapWriter.cpp | 105 ++++++++++++++++-- src/hotspot/share/cds/aotMappedHeapWriter.hpp | 44 ++++---- src/hotspot/share/cds/filemap.cpp | 10 +- src/hotspot/share/cds/heapShared.hpp | 2 +- test/hotspot/jtreg/ProblemList.txt | 1 - 7 files changed, 125 insertions(+), 41 deletions(-) diff --git a/src/hotspot/share/cds/aotArtifactFinder.hpp b/src/hotspot/share/cds/aotArtifactFinder.hpp index 405222a8753e7..05bcde6b0ace5 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.hpp +++ b/src/hotspot/share/cds/aotArtifactFinder.hpp @@ -39,7 +39,7 @@ class TypeArrayKlass; // It also decides what Klasses must be cached in aot-initialized state. // // ArchiveBuilder uses [1] as roots to scan for all MetaspaceObjs that need to be cached. -// ArchiveHeapWriter uses [2] to create an image of the archived heap. +// HeapShared uses [2] to create an image of the archived heap. // // [1] is stored in _all_cached_classes in aotArtifactFinder.cpp. // [2] is stored in HeapShared::archived_object_cache(). diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index d0a63c56093e5..a252eae4b84c6 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -796,7 +796,7 @@ void AOTMapLogger::dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* heap_i address buffer_start = address(r.start()); // start of the current oop inside the buffer address buffer_end = address(r.end()); - address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE; + 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; log_region_range("heap", buffer_start, buffer_end, requested_start); diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index ff9319d266ba9..98f400c989c78 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -55,7 +55,7 @@ GrowableArrayCHeap* AOTMappedHeapWriter::_buffer = nullptr; -// The following are offsets from buffer_bottom() +bool AOTMappedHeapWriter::_is_writing_deterministic_heap = false; size_t AOTMappedHeapWriter::_buffer_used; // Heap root segments @@ -74,7 +74,7 @@ AOTMappedHeapWriter::_buffer_offset_to_source_obj_table = nullptr; DumpedInternedStrings *AOTMappedHeapWriter::_dumped_interned_strings = nullptr; typedef HashTable< - size_t, // offset of a filler from ArchiveHeapWriter::buffer_bottom() + size_t, // offset of a filler from AOTMappedHeapWriter::buffer_bottom() size_t, // size of this filler (in bytes) 127, // prime number AnyObj::C_HEAP, @@ -96,6 +96,45 @@ void AOTMappedHeapWriter::init() { _source_objs = new GrowableArrayCHeap(10000); guarantee(MIN_GC_REGION_ALIGNMENT <= G1HeapRegion::min_region_size_in_words() * HeapWordSize, "must be"); + + if (CDSConfig::old_cds_flags_used()) { + // With the old CDS workflow, we can guatantee determninistic output: given + // the same classlist file, we can generate the same static CDS archive. + // To ensure determinism, we always use the same compressed oop encoding + // (zero-based, no shift). See set_requested_address_range(). + _is_writing_deterministic_heap = true; + } else { + // Determninistic output is not supported by the new AOT workflow, so + // we don't force the (zero-based, no shift) encoding. This way, it is more + // likely that we can avoid oop relocation in the production run. + _is_writing_deterministic_heap = false; + } + } +} + +// For AOTMappedHeapWriter::narrow_oop_{mode, base, shift}(), see comments +// in AOTMappedHeapWriter::set_requested_address_range(), +CompressedOops::Mode AOTMappedHeapWriter::narrow_oop_mode() { + if (is_writing_deterministic_heap()) { + return CompressedOops::UnscaledNarrowOop; + } else { + return CompressedOops::mode(); + } +} + +address AOTMappedHeapWriter::narrow_oop_base() { + if (is_writing_deterministic_heap()) { + return (address)0; + } else { + return CompressedOops::base(); + } +} + +int AOTMappedHeapWriter::narrow_oop_shift() { + if (is_writing_deterministic_heap()) { + return 0; + } else { + return CompressedOops::shift(); } } @@ -116,7 +155,7 @@ void AOTMappedHeapWriter::write(GrowableArrayCHeap* roots, assert(CDSConfig::is_dumping_heap(), "sanity"); allocate_buffer(); copy_source_objs_to_buffer(roots); - set_requested_address(heap_info); + set_requested_address_range(heap_info); relocate_embedded_oops(roots, heap_info); } @@ -536,14 +575,55 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { return buffered_obj_offset; } -void AOTMappedHeapWriter::set_requested_address(ArchiveMappedHeapInfo* info) { +// Set the range [_requested_bottom, _requested_top), the requested address range of all +// the archived heap objects in the production run. +// +// (1) UseCompressedOops == true && !is_writing_deterministic_heap() +// +// The archived objects are stored using the COOPS encoding of the assembly phase. +// We pick a range within the heap used by the assembly phase. +// +// In the production run, if different COOPS encodings are used: +// - The heap contents needs to be relocated. +// +// (2) UseCompressedOops == true && is_writing_deterministic_heap() +// +// We always use zero-based, zero-shift encoding. _requested_top is aligned to 0x10000000. +// +// (3) UseCompressedOops == false: +// +// In the production run, the heap range is usually picked (randomly) by the OS, so we +// will almost always need to perform relocation, regardless of how we pick the requested +// address range. +// +// So we just hard code it to NOCOOPS_REQUESTED_BASE. +// +void AOTMappedHeapWriter::set_requested_address_range(ArchiveMappedHeapInfo* info) { assert(!info->is_used(), "only set once"); size_t heap_region_byte_size = _buffer_used; assert(heap_region_byte_size > 0, "must archived at least one object!"); if (UseCompressedOops) { - if (UseG1GC) { + if (is_writing_deterministic_heap()) { + // Pick a heap range so that requested addresses can be encoded with zero-base/no shift. + // We align the requested bottom to at least 1 MB: if the production run uses G1 with a small + // heap (e.g., -Xmx256m), it's likely that we can map the archived objects at the + // requested location to avoid relocation. + // + // For other collectors or larger heaps, relocation is unavoidable, but is usually + // quite cheap. If you really want to avoid relocation, use the AOT workflow instead. + address heap_end = (address)0x100000000; + size_t alignment = MAX2(MIN_GC_REGION_ALIGNMENT, 1024 * 1024); + if (align_up(heap_region_byte_size, alignment) >= (size_t)heap_end) { + log_error(aot, heap)("cached heap space is too large: %zu bytes", heap_region_byte_size); + AOTMetaspace::unrecoverable_writing_error(); + } + _requested_bottom = align_down(heap_end - heap_region_byte_size, alignment); + } else if (UseG1GC) { + // For G1, pick the range at the top of the current heap. If the exact same heap sizes + // are used in the production run, it's likely that we can map the archived objects + // at the requested location to avoid relocation. address heap_end = (address)G1CollectedHeap::heap()->reserved().end(); log_info(aot, heap)("Heap end = %p", heap_end); _requested_bottom = align_down(heap_end - heap_region_byte_size, G1HeapRegion::GrainBytes); @@ -612,7 +692,14 @@ oop AOTMappedHeapWriter::load_oop_from_buffer(narrowOop* buffered_addr) { template void AOTMappedHeapWriter::relocate_field_in_buffer(T* field_addr_in_buffer, oop source_referent, CHeapBitMap* oopmap) { oop request_referent = source_obj_to_requested_obj(source_referent); - store_requested_oop_in_buffer(field_addr_in_buffer, request_referent); + if (UseCompressedOops && is_writing_deterministic_heap()) { + // We use zero-based, 0-shift encoding, so the narrowOop is just the lower + // 32 bits of request_referent + intptr_t addr = cast_from_oop(request_referent); + *((narrowOop*)field_addr_in_buffer) = checked_cast(addr); + } else { + store_requested_oop_in_buffer(field_addr_in_buffer, request_referent); + } if (request_referent != nullptr) { mark_oop_pointer(field_addr_in_buffer, oopmap); } @@ -918,9 +1005,9 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHe address buffer_start = address(r.start()); address buffer_end = address(r.end()); - address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE; - address requested_start = UseCompressedOops ? buffered_addr_to_requested_addr(buffer_start) : requested_base; - int requested_shift = CompressedOops::shift(); + 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) { diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.hpp b/src/hotspot/share/cds/aotMappedHeapWriter.hpp index 9a85b83d3d187..eafd38ac8bb98 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.hpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.hpp @@ -29,6 +29,7 @@ #include "cds/heapShared.hpp" #include "memory/allocation.hpp" #include "memory/allStatic.hpp" +#include "oops/compressedOops.hpp" #include "oops/oopHandle.hpp" #include "utilities/bitMap.hpp" #include "utilities/exceptions.hpp" @@ -71,7 +72,7 @@ class AOTMappedHeapWriter : AllStatic { // These are entered into HeapShared::archived_object_cache(). // // - "buffered objects" are copies of the "source objects", and are stored in into - // ArchiveHeapWriter::_buffer, which is a GrowableArray that sits outside of + // AOTMappedHeapWriter::_buffer, which is a GrowableArray that sits outside of // the valid heap range. Therefore we avoid using the addresses of these copies // as oops. They are usually called "buffered_addr" in the code (of the type "address"). // @@ -81,26 +82,11 @@ class AOTMappedHeapWriter : AllStatic { // - Each archived object has a "requested address" -- at run time, if the object // can be mapped at this address, we can avoid relocation. // - // The requested address is implemented differently depending on UseCompressedOops: + // The requested address of an archived object is essentially its buffered_addr + delta, + // where delta is (_requested_bottom - buffer_bottom()); // - // UseCompressedOops == true: - // The archived objects are stored assuming that the runtime COOPS compression - // scheme is exactly the same as in dump time (or else a more expensive runtime relocation - // would be needed.) - // - // At dump time, we assume that the runtime heap range is exactly the same as - // in dump time. The requested addresses of the archived objects are chosen such that - // they would occupy the top end of a G1 heap (TBD when dumping is supported by other - // collectors. See JDK-8298614). - // - // UseCompressedOops == false: - // At runtime, the heap range is usually picked (randomly) by the OS, so we will almost always - // need to perform relocation. Hence, the goal of the "requested address" is to ensure that - // the contents of the archived objects are deterministic. I.e., the oop fields of archived - // objects will always point to deterministic addresses. - // - // For G1, the archived heap is written such that the lowest archived object is placed - // at NOCOOPS_REQUESTED_BASE. (TBD after JDK-8298614). + // The requested addresses of all archived objects are within [_requested_bottom, _requested_top). + // See AOTMappedHeapWriter::set_requested_address_range() for more info. // ---------------------------------------------------------------------- public: @@ -111,6 +97,15 @@ class AOTMappedHeapWriter : AllStatic { // Shenandoah heap region size can never be smaller than 256K. static constexpr int MIN_GC_REGION_ALIGNMENT = 256 * K; + // The heap contents are required to be deterministic when dumping "old" CDS archives, in order + // to support reproducible lib/server/classes*.jsa when building the JDK. + static bool is_writing_deterministic_heap() { return _is_writing_deterministic_heap; } + + // The oop encoding used by the archived heap objects. + static CompressedOops::Mode narrow_oop_mode(); + static address narrow_oop_base(); + static int narrow_oop_shift(); + static const int INITIAL_TABLE_SIZE = 15889; // prime number static const int MAX_TABLE_SIZE = 1000000; @@ -121,6 +116,7 @@ class AOTMappedHeapWriter : AllStatic { int _field_offset; }; + static bool _is_writing_deterministic_heap; static GrowableArrayCHeap* _buffer; // The number of bytes that have written into _buffer (may be smaller than _buffer->length()). @@ -130,15 +126,15 @@ class AOTMappedHeapWriter : AllStatic { static HeapRootSegments _heap_root_segments; // The address range of the requested location of the archived heap objects. - static address _requested_bottom; - static address _requested_top; + static address _requested_bottom; // The requested address of the lowest archived heap object + static address _requested_top; // The exclusive end of the highest archived heap object static GrowableArrayCHeap* _native_pointers; static GrowableArrayCHeap* _source_objs; static DumpedInternedStrings *_dumped_interned_strings; // We sort _source_objs_order to minimize the number of bits in ptrmap and oopmap. - // See comments near the body of ArchiveHeapWriter::compare_objs_by_oop_fields(). + // See comments near the body of AOTMappedHeapWriter::compare_objs_by_oop_fields(). // The objects will be written in the order of: //_source_objs->at(_source_objs_order->at(0)._index) // source_objs->at(_source_objs_order->at(1)._index) @@ -200,7 +196,7 @@ class AOTMappedHeapWriter : AllStatic { 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(ArchiveMappedHeapInfo* info); + static void set_requested_address_range(ArchiveMappedHeapInfo* info); static void mark_native_pointers(oop orig_obj); static void relocate_embedded_oops(GrowableArrayCHeap* roots, ArchiveMappedHeapInfo* info); static void compute_ptrmap(ArchiveMappedHeapInfo *info); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index ae92ce3105843..61df0a86b413a 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -216,12 +216,14 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, _obj_alignment = ObjectAlignmentInBytes; _compact_strings = CompactStrings; _compact_headers = UseCompactObjectHeaders; +#if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_dumping_heap()) { _object_streaming_mode = HeapShared::is_writing_streaming_mode(); - _narrow_oop_mode = CompressedOops::mode(); - _narrow_oop_base = CompressedOops::base(); - _narrow_oop_shift = CompressedOops::shift(); + _narrow_oop_mode = AOTMappedHeapWriter::narrow_oop_mode(); + _narrow_oop_base = AOTMappedHeapWriter::narrow_oop_base(); + _narrow_oop_shift = AOTMappedHeapWriter::narrow_oop_shift(); } +#endif _compressed_oops = UseCompressedOops; _compressed_class_ptrs = UseCompressedClassPointers; if (UseCompressedClassPointers) { @@ -911,7 +913,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size, if (HeapShared::is_writing_mapping_mode()) { requested_base = (char*)AOTMappedHeapWriter::requested_address(); if (UseCompressedOops) { - mapping_offset = (size_t)((address)requested_base - CompressedOops::base()); + mapping_offset = (size_t)((address)requested_base - AOTMappedHeapWriter::narrow_oop_base()); assert((mapping_offset >> CompressedOops::shift()) << CompressedOops::shift() == mapping_offset, "must be"); } } else { diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 2c782f7231bd7..118c60faa6011 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -332,7 +332,7 @@ class HeapShared: AllStatic { // Used by CDSHeapVerifier. OopHandle _orig_referrer; - // The location of this object inside ArchiveHeapWriter::_buffer + // The location of this object inside {AOTMappedHeapWriter, AOTStreamedHeapWriter}::_buffer size_t _buffer_offset; // One or more fields in this object are pointing to non-null oops. diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index a45fdc09323a1..934ef03a9874a 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -100,7 +100,6 @@ gc/shenandoah/TestSieveObjects.java#no-tlab-genshen 8361099 generic-all # :hotspot_runtime -runtime/cds/DeterministicDump.java 8363986 macosx-x64,macosx-aarch64 runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64 runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64 From 99135d2e05bb501fe9f9f0d36abd25894d0f93de Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Wed, 19 Nov 2025 08:47:57 +0000 Subject: [PATCH 21/44] 8359145: Implement JEP 530: Primitive Types in Patterns, instanceof, and switch (Fourth Preview) Reviewed-by: jlahoda --- .../java/lang/runtime/SwitchBootstraps.java | 2 +- .../com/sun/tools/javac/code/TypeTag.java | 12 +- .../com/sun/tools/javac/code/Types.java | 135 +++++++-- .../com/sun/tools/javac/comp/Attr.java | 2 +- .../com/sun/tools/javac/comp/Check.java | 29 +- .../javac/comp/ExhaustivenessComputer.java | 4 +- .../com/sun/tools/javac/comp/Lower.java | 5 +- .../sun/tools/javac/comp/TransPatterns.java | 2 +- .../tools/javac/patterns/Domination.java | 12 +- .../tools/javac/patterns/DominationWithPP.out | 14 + .../PrimitivePatternsSwitchConstants.java | 71 +++++ .../PrimitivePatternsSwitchConstants.out | 11 + .../PrimitivePatternsSwitchErrors.java | 67 ++++- .../PrimitivePatternsSwitchErrors.out | 11 +- ...veUnconditionallyExactInAssignability.java | 138 +++++++++ ...onditionallyExactInExhaustiveSwitches.java | 280 ++++++++++++++++++ .../tools/javac/patterns/T8332463a.java | 8 +- .../tools/javac/patterns/T8332463a.out | 6 + .../tools/javac/patterns/T8332463b.java | 5 +- .../tools/javac/patterns/T8332463b.out | 2 + .../tools/javac/types/UnknownTypeTest.java | 4 +- .../tools/lib/types/TypeHarness.java | 15 + 22 files changed, 776 insertions(+), 59 deletions(-) create mode 100644 test/langtools/tools/javac/patterns/DominationWithPP.out create mode 100644 test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.java create mode 100644 test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.out create mode 100644 test/langtools/tools/javac/patterns/PrimitiveUnconditionallyExactInAssignability.java create mode 100644 test/langtools/tools/javac/patterns/PrimitiveUnconditionallyExactInExhaustiveSwitches.java create mode 100644 test/langtools/tools/javac/patterns/T8332463a.out create mode 100644 test/langtools/tools/javac/patterns/T8332463b.out diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index 99716baf43963..30b6df0073ec2 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -777,7 +777,7 @@ private static String typeSwitchClassName(Class targetClass) { return name + "$$TypeSwitch"; } - // this method should be in sync with com.sun.tools.javac.code.Types.checkUnconditionallyExactPrimitives + // this method should be in sync with com.sun.tools.javac.code.Types.isUnconditionallyExactTypeBased private static boolean unconditionalExactnessCheck(Class selectorType, Class targetType) { Wrapper selectorWrapper = Wrapper.forBasicType(selectorType); Wrapper targetWrapper = Wrapper.forBasicType(targetType); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeTag.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeTag.java index 0b97b119119c0..b2771556eb21e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeTag.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeTag.java @@ -26,7 +26,7 @@ package com.sun.tools.javac.code; import com.sun.source.tree.Tree.Kind; - +import java.lang.runtime.ExactConversionsSupport; import javax.lang.model.type.TypeKind; import static com.sun.tools.javac.code.TypeTag.NumericClasses.*; @@ -186,6 +186,10 @@ public boolean isInSuperClassesOf(TypeTag tag) { return (this.numericClass & tag.superClasses) != 0; } + public boolean isNumeric() { + return this.numericClass != 0; + } + /** Returns the number of type tags. */ public static int getTypeTagCount() { @@ -247,11 +251,11 @@ public boolean checkRange(int value) { case BOOLEAN: return 0 <= value && value <= 1; case BYTE: - return Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE; + return ExactConversionsSupport.isIntToByteExact(value); case CHAR: - return Character.MIN_VALUE <= value && value <= Character.MAX_VALUE; + return ExactConversionsSupport.isIntToCharExact(value); case SHORT: - return Short.MIN_VALUE <= value && value <= Short.MAX_VALUE; + return ExactConversionsSupport.isIntToShortExact(value); case INT: return true; default: 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 d59505555f2d2..3f3eb1f962376 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 @@ -26,6 +26,7 @@ package com.sun.tools.javac.code; import java.lang.ref.SoftReference; +import java.lang.runtime.ExactConversionsSupport; import java.util.HashSet; import java.util.HashMap; import java.util.Locale; @@ -5086,46 +5087,128 @@ public Type visitCapturedType(CapturedType t, S s) { } // - // - /** Check unconditionality between any combination of reference or primitive types. + // + /** Check type-based unconditional exactness between any combination of + * reference or primitive types according to JLS 5.7.2. * - * Rules: - * an identity conversion - * a widening reference conversion - * a widening primitive conversion (delegates to `checkUnconditionallyExactPrimitives`) - * a boxing conversion - * a boxing conversion followed by a widening reference conversion + * The following are unconditionally exact regardless of the input + * expression: + * + * - an identity conversion + * - a widening reference conversion + * - an exact widening primitive conversion + * - a boxing conversion + * - a boxing conversion followed by a widening reference conversion * * @param source Source primitive or reference type * @param target Target primitive or reference type */ - public boolean isUnconditionallyExact(Type source, Type target) { + public boolean isUnconditionallyExactTypeBased(Type source, Type target) { if (isSameType(source, target)) { return true; } - return target.isPrimitive() - ? isUnconditionallyExactPrimitives(source, target) - : isSubtype(boxedTypeOrType(erasure(source)), target); + if (target.isPrimitive()) { + if (source.isPrimitive() && + ((source.getTag().isStrictSubRangeOf(target.getTag())) && + !((source.hasTag(BYTE) && target.hasTag(CHAR)) || + (source.hasTag(INT) && target.hasTag(FLOAT)) || + (source.hasTag(LONG) && (target.hasTag(DOUBLE) || target.hasTag(FLOAT)))))) return true; + else { + return false; + } + } else { + return isSubtype(boxedTypeOrType(erasure(source)), target); + } } - /** Check unconditionality between primitive types. + /** Check value-based unconditional exactness between any combination of + * reference or primitive types for the value of a constant expression + * according to JLS 5.7.2. + * + * The following can be unconditionally exact if the source primitive is a + * constant expression and the conversions is exact for that constant + * expression: * - * - widening from one integral type to another, - * - widening from one floating point type to another, - * - widening from byte, short, or char to a floating point type, - * - widening from int to double. + * - a narrowing primitive conversion + * - a widening and narrowing primitive conversion + * - a widening primitive conversion that is not exact + * + * @param source Source primitive or reference type, should be a numeric value + * @param target Target primitive or reference type + */ + public boolean isUnconditionallyExactValueBased(Type source, Type target) { + if (!(source.constValue() instanceof Number value) || !target.getTag().isNumeric()) return false; + + switch (source.getTag()) { + case BYTE: + switch (target.getTag()) { + case CHAR: return ExactConversionsSupport.isIntToCharExact(value.intValue()); + } + break; + case CHAR: + switch (target.getTag()) { + case BYTE: return ExactConversionsSupport.isIntToByteExact(value.intValue()); + case SHORT: return ExactConversionsSupport.isIntToShortExact(value.intValue()); + } + break; + case SHORT: + switch (target.getTag()) { + case BYTE: return ExactConversionsSupport.isIntToByteExact(value.intValue()); + case CHAR: return ExactConversionsSupport.isIntToCharExact(value.intValue()); + } + break; + case INT: + switch (target.getTag()) { + case BYTE: return ExactConversionsSupport.isIntToByteExact(value.intValue()); + case CHAR: return ExactConversionsSupport.isIntToCharExact(value.intValue()); + case SHORT: return ExactConversionsSupport.isIntToShortExact(value.intValue()); + case FLOAT: return ExactConversionsSupport.isIntToFloatExact(value.intValue()); + } + break; + case FLOAT: + switch (target.getTag()) { + case BYTE: return ExactConversionsSupport.isFloatToByteExact(value.floatValue()); + case CHAR: return ExactConversionsSupport.isFloatToCharExact(value.floatValue()); + case SHORT: return ExactConversionsSupport.isFloatToShortExact(value.floatValue()); + case INT: return ExactConversionsSupport.isFloatToIntExact(value.floatValue()); + case LONG: return ExactConversionsSupport.isFloatToLongExact(value.floatValue()); + } + break; + case LONG: + switch (target.getTag()) { + case BYTE: return ExactConversionsSupport.isLongToByteExact(value.longValue()); + case CHAR: return ExactConversionsSupport.isLongToCharExact(value.longValue()); + case SHORT: return ExactConversionsSupport.isLongToShortExact(value.longValue()); + case INT: return ExactConversionsSupport.isLongToIntExact(value.longValue()); + case FLOAT: return ExactConversionsSupport.isLongToFloatExact(value.longValue()); + case DOUBLE: return ExactConversionsSupport.isLongToDoubleExact(value.longValue()); + } + break; + case DOUBLE: + switch (target.getTag()) { + case BYTE: return ExactConversionsSupport.isDoubleToByteExact(value.doubleValue()); + case CHAR: return ExactConversionsSupport.isDoubleToCharExact(value.doubleValue()); + case SHORT: return ExactConversionsSupport.isDoubleToShortExact(value.doubleValue()); + case INT: return ExactConversionsSupport.isDoubleToIntExact(value.doubleValue()); + case FLOAT: return ExactConversionsSupport.isDoubleToFloatExact(value.doubleValue()); + case LONG: return ExactConversionsSupport.isDoubleToLongExact(value.doubleValue()); + } + break; + } + return true; + } + + /** Check both type or value-based unconditional exactness between any + * combination of reference or primitive types for the value of a constant + * expression according to JLS 5.7.2. * - * @param selectorType Type of selector - * @param targetType Target type + * @param source Source primitive or reference type, should be a numeric value + * @param target Target primitive or reference type */ - public boolean isUnconditionallyExactPrimitives(Type selectorType, Type targetType) { - return isSameType(selectorType, targetType) || - (selectorType.isPrimitive() && targetType.isPrimitive()) && - ((selectorType.getTag().isStrictSubRangeOf(targetType.getTag())) && - !((selectorType.hasTag(BYTE) && targetType.hasTag(CHAR)) || - (selectorType.hasTag(INT) && targetType.hasTag(FLOAT)) || - (selectorType.hasTag(LONG) && (targetType.hasTag(DOUBLE) || targetType.hasTag(FLOAT))))); + public boolean isUnconditionallyExactCombined(Type currentType, Type testType) { + return isUnconditionallyExactTypeBased(currentType, testType) || + (currentType.constValue() instanceof Number && isUnconditionallyExactValueBased(currentType, testType)); } // 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 a45f7466f9eae..ad41adcc135ea 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 @@ -1861,7 +1861,7 @@ private void handleSwitch(JCTree switchTree, boolean unconditional = unguarded && !patternType.isErroneous() && - types.isUnconditionallyExact(seltype, patternType); + types.isUnconditionallyExactTypeBased(seltype, patternType); if (unconditional) { if (hasUnconditionalPattern) { log.error(pat.pos(), Errors.DuplicateUnconditionalPattern); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 9098568f42ac0..94b14f3122f84 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -28,7 +28,6 @@ import java.util.*; import java.util.function.BiConsumer; import java.util.function.BiPredicate; -import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.ToIntBiFunction; @@ -167,6 +166,7 @@ protected Check(Context context) { allowModules = Feature.MODULES.allowedInSource(source); allowRecords = Feature.RECORDS.allowedInSource(source); allowSealed = Feature.SEALED_CLASSES.allowedInSource(source); + allowPrimitivePatterns = preview.isEnabled() && Feature.PRIMITIVE_PATTERNS.allowedInSource(source); } /** Character for synthetic names @@ -190,6 +190,10 @@ protected Check(Context context) { */ private final boolean allowSealed; + /** Are primitive patterns allowed + */ + private final boolean allowPrimitivePatterns; + /** Whether to force suppression of deprecation and preview warnings. * This happens when attributing import statements for JDK 9+. * @see Feature#DEPRECATION_ON_IMPORT @@ -4764,21 +4768,26 @@ void checkSwitchCaseLabelDominated(JCCaseLabel unconditionalCaseLabel, List newLabel; diff --git a/test/langtools/tools/javac/patterns/Domination.java b/test/langtools/tools/javac/patterns/Domination.java index a5cdb99b9c852..dd9d115369629 100644 --- a/test/langtools/tools/javac/patterns/Domination.java +++ b/test/langtools/tools/javac/patterns/Domination.java @@ -26,8 +26,8 @@ * @bug 8262891 8290709 * @summary Check the pattern domination error are reported correctly. * @compile/fail/ref=Domination.out -XDrawDiagnostics Domination.java + * @compile/fail/ref=DominationWithPP.out --enable-preview --source ${jdk.version} -XDrawDiagnostics Domination.java */ - public class Domination { int testDominatesError1(Object o) { switch (o) { @@ -218,4 +218,14 @@ int testNotDominates2(Integer x) { case null : return -1; } } + + int testCasePatternDominatedbyPreceedingUnconditionalCasePattern () { + interface A {} + interface B {} + A aa = new A() {}; + switch (aa) { + case A a : return 1; + case B b : return -1; + } + } } diff --git a/test/langtools/tools/javac/patterns/DominationWithPP.out b/test/langtools/tools/javac/patterns/DominationWithPP.out new file mode 100644 index 0000000000000..119cc003d071b --- /dev/null +++ b/test/langtools/tools/javac/patterns/DominationWithPP.out @@ -0,0 +1,14 @@ +Domination.java:35:18: compiler.err.pattern.dominated +Domination.java:43:18: compiler.err.pattern.dominated +Domination.java:51:18: compiler.err.pattern.dominated +Domination.java:67:18: compiler.err.pattern.dominated +Domination.java:88:18: compiler.err.pattern.dominated +Domination.java:113:18: compiler.err.pattern.dominated +Domination.java:144:18: compiler.err.pattern.dominated +Domination.java:153:18: compiler.err.pattern.dominated +Domination.java:184:18: compiler.err.pattern.dominated +Domination.java:193:18: compiler.err.pattern.dominated +Domination.java:202:18: compiler.err.pattern.dominated +Domination.java:211:18: compiler.err.pattern.dominated +Domination.java:228:18: compiler.err.pattern.dominated +13 errors diff --git a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.java b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.java new file mode 100644 index 0000000000000..2890b315e622a --- /dev/null +++ b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.java @@ -0,0 +1,71 @@ +/* + * @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 + */ +public class PrimitivePatternsSwitchConstants { + void testConstExpressions() { + switch (42) { // error: not exhaustive + case byte _ : + } + + switch (42l) { // error: not exhaustive + case byte _ : + } + + switch (123456) { // error: not exhaustive + case byte _ : + } + + switch (16_777_216) { // error: not exhaustive + case float _ : + } + + switch (16_777_217) { // error: not exhaustive + case float _ : + } + + switch (42d) { // error: not exhaustive + case float _ : + } + + switch (1) { // OK + case long _ : + } + + final int i = 42; + switch (i) { // OK + case long _ : + } + + switch (1) { // error: non-exhaustive + case Long _ : // error: widening primitive conversion and boxing is not supported + } + + switch (42) { + case byte bb -> {} + case int ii -> {} // OK + }; + + switch (42) { + case 42 -> {} + case int ii -> {} // OK + }; + + switch (42) { + case (byte) 42 -> {} + case int ii -> {} // OK + }; + + switch (42) { + case 42 -> {} + default -> {} // OK + }; + + switch (42) { + default -> {} // OK + case 42 -> {} + }; + } +} diff --git a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.out b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.out new file mode 100644 index 0000000000000..9a983a8643772 --- /dev/null +++ b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.out @@ -0,0 +1,11 @@ +PrimitivePatternsSwitchConstants.java:43:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.Long) +PrimitivePatternsSwitchConstants.java:9:9: compiler.err.not.exhaustive.statement +PrimitivePatternsSwitchConstants.java:13:9: compiler.err.not.exhaustive.statement +PrimitivePatternsSwitchConstants.java:17:9: compiler.err.not.exhaustive.statement +PrimitivePatternsSwitchConstants.java:21:9: compiler.err.not.exhaustive.statement +PrimitivePatternsSwitchConstants.java:25:9: compiler.err.not.exhaustive.statement +PrimitivePatternsSwitchConstants.java:29:9: compiler.err.not.exhaustive.statement +PrimitivePatternsSwitchConstants.java:42:9: compiler.err.not.exhaustive.statement +- compiler.note.preview.filename: PrimitivePatternsSwitchConstants.java, DEFAULT +- compiler.note.preview.recompile +8 errors diff --git a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.java b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.java index 3d47c4ac9bc72..dacd48f441cbd 100644 --- a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.java +++ b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.java @@ -59,7 +59,7 @@ public static int dominationBetweenBoxedAndPrimitive() { int i = 42; return switch (i) { case Integer ib -> ib; - case byte ip -> ip; // OK - not dominated! + case byte ip -> ip; // Error - dominated! }; } @@ -265,4 +265,69 @@ public static char disallowedUnboxingAndWidening(Short test) { public static boolean wideningReferenceConversionUnboxingAndNarrowingPrimitive(T i) { return i instanceof byte b; // not allowed as a conversion } + + public static void dominanceIntFloat() { + int ii = 42; + switch (ii) { + case int i -> {} + case float f -> {} // Error - dominated! + } + } + + public static void noDominanceIntFloat() { + int ii = 42; + switch (ii) { + case float f -> {} + case int i -> {} // ok + } + } + + public static void strengtheningDominance() { + byte x = 42; + switch (x) { + case short s -> {} + case 42 -> {} // error: dominated + } + + long l = 42l; + switch (l) { + case short s -> {} + case 42l -> {} // error: dominated + case long _ -> {} + } + + char c = 'a'; + switch (c) { + case short s -> {} + case 42 -> {} // error: dominated + case char _ -> {} + } + + int x2 = 42; + switch(x2) { + case float f -> {} + case 16_777_216 -> {} // error: dominated + default -> {} + } + + switch(x2) { + case float f -> {} + case 16_777_217 -> {} // OK + default -> {} + } + + switch(x2) { + case int ii -> {} + case float f -> {} // error: dominated + } + } + + public static void unconditionalFollowedByDefault() { + int ii = 42; + switch (ii) { + case int i -> {} + case float f -> {} // Error - dominated! + default -> {} // Error - unconditional and default + } + } } diff --git a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.out b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.out index 75fd62016a086..5a0822a9aef31 100644 --- a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.out +++ b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.out @@ -1,6 +1,7 @@ PrimitivePatternsSwitchErrors.java:15:18: compiler.err.pattern.dominated PrimitivePatternsSwitchErrors.java:24:18: compiler.err.pattern.dominated PrimitivePatternsSwitchErrors.java:31:24: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.Long) +PrimitivePatternsSwitchErrors.java:62:18: compiler.err.pattern.dominated PrimitivePatternsSwitchErrors.java:70:18: compiler.err.pattern.dominated PrimitivePatternsSwitchErrors.java:78:18: compiler.err.pattern.dominated PrimitivePatternsSwitchErrors.java:84:18: compiler.err.prob.found.req: (compiler.misc.possible.loss.of.precision: long, byte) @@ -29,6 +30,14 @@ PrimitivePatternsSwitchErrors.java:248:18: compiler.err.prob.found.req: (compile PrimitivePatternsSwitchErrors.java:255:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Long, int) PrimitivePatternsSwitchErrors.java:261:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Short, char) PrimitivePatternsSwitchErrors.java:266:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T, byte) +PrimitivePatternsSwitchErrors.java:273:18: compiler.err.pattern.dominated +PrimitivePatternsSwitchErrors.java:289:18: compiler.err.pattern.dominated +PrimitivePatternsSwitchErrors.java:295:18: compiler.err.pattern.dominated +PrimitivePatternsSwitchErrors.java:302:18: compiler.err.pattern.dominated +PrimitivePatternsSwitchErrors.java:309:18: compiler.err.pattern.dominated +PrimitivePatternsSwitchErrors.java:321:18: compiler.err.pattern.dominated +PrimitivePatternsSwitchErrors.java:330:13: compiler.err.unconditional.pattern.and.default +PrimitivePatternsSwitchErrors.java:329:18: compiler.err.pattern.dominated PrimitivePatternsSwitchErrors.java:30:16: compiler.err.not.exhaustive PrimitivePatternsSwitchErrors.java:37:16: compiler.err.not.exhaustive PrimitivePatternsSwitchErrors.java:44:16: compiler.err.not.exhaustive @@ -43,4 +52,4 @@ PrimitivePatternsSwitchErrors.java:254:16: compiler.err.not.exhaustive PrimitivePatternsSwitchErrors.java:260:16: compiler.err.not.exhaustive - compiler.note.preview.filename: PrimitivePatternsSwitchErrors.java, DEFAULT - compiler.note.preview.recompile -43 errors \ No newline at end of file +52 errors diff --git a/test/langtools/tools/javac/patterns/PrimitiveUnconditionallyExactInAssignability.java b/test/langtools/tools/javac/patterns/PrimitiveUnconditionallyExactInAssignability.java new file mode 100644 index 0000000000000..c75dff3f8adc9 --- /dev/null +++ b/test/langtools/tools/javac/patterns/PrimitiveUnconditionallyExactInAssignability.java @@ -0,0 +1,138 @@ +/* + * 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 + * @summary Check assignability of narrowing p.c. with constant expressions vs exact conversion methods + * @library /tools/lib/types + * @modules jdk.compiler/com.sun.tools.javac.code + * jdk.compiler/com.sun.tools.javac.comp + * jdk.compiler/com.sun.tools.javac.file + * jdk.compiler/com.sun.tools.javac.util + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.tree + * @build TypeHarness + * @compile PrimitiveUnconditionallyExactInAssignability.java + * @run main PrimitiveUnconditionallyExactInAssignability + */ + +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.TypeTag; + +import java.lang.runtime.ExactConversionsSupport; + +import static com.sun.tools.javac.code.TypeTag.ERROR; +import static com.sun.tools.javac.code.TypeTag.INT; + +public class PrimitiveUnconditionallyExactInAssignability extends TypeHarness { + PrimitiveUnconditionallyExactInAssignability() { + } + + void assertOriginalAssignmentNarrowingAndUnconditionality() { + // byte b = vs ExactConversionsSupport::isIntToByteExact + assertOriginaAndUpdatedAssignable(fac.Constant(Short.MIN_VALUE), predef.byteType, ExactConversionsSupport.isIntToByteExact(Short.MIN_VALUE)); + assertOriginaAndUpdatedAssignable(fac.Constant((short) (Byte.MIN_VALUE - 1)), predef.byteType, ExactConversionsSupport.isIntToByteExact((short) (Byte.MIN_VALUE - 1))); + assertOriginaAndUpdatedAssignable(fac.Constant((short) (Byte.MAX_VALUE + 1)), predef.byteType, ExactConversionsSupport.isIntToByteExact((short) (Byte.MAX_VALUE + 1))); + assertOriginaAndUpdatedAssignable(fac.Constant(Short.MAX_VALUE), predef.byteType, ExactConversionsSupport.isIntToByteExact(Short.MAX_VALUE)); + + // byte b = vs ExactConversionsSupport::isIntToByteExact + assertOriginaAndUpdatedAssignable(fac.Constant(Character.MIN_VALUE), predef.byteType, ExactConversionsSupport.isIntToByteExact(Character.MIN_VALUE)); + assertOriginaAndUpdatedAssignable(fac.Constant((char) (Byte.MAX_VALUE + 1)), predef.byteType, ExactConversionsSupport.isIntToByteExact((char) (Byte.MAX_VALUE + 1))); + assertOriginaAndUpdatedAssignable(fac.Constant(Character.MAX_VALUE), predef.byteType, ExactConversionsSupport.isIntToByteExact(Character.MAX_VALUE)); + + // byte b = vs ExactConversionsSupport::isIntToByteExact + assertOriginaAndUpdatedAssignable(fac.Constant(Integer.MIN_VALUE), predef.byteType, ExactConversionsSupport.isIntToByteExact(Integer.MIN_VALUE)); + assertOriginaAndUpdatedAssignable(fac.Constant((int) (Byte.MIN_VALUE - 1)), predef.byteType, ExactConversionsSupport.isIntToByteExact((int) (Byte.MIN_VALUE - 1))); + assertOriginaAndUpdatedAssignable(fac.Constant((int) (Byte.MAX_VALUE + 1)), predef.byteType, ExactConversionsSupport.isIntToByteExact((int) (Byte.MAX_VALUE + 1))); + assertOriginaAndUpdatedAssignable(fac.Constant(Integer.MAX_VALUE), predef.byteType, ExactConversionsSupport.isIntToByteExact(Integer.MAX_VALUE)); + + // char c = vs ExactConversionsSupport::isIntToCharExact + assertOriginaAndUpdatedAssignable(fac.Constant(Short.MIN_VALUE), predef.charType, ExactConversionsSupport.isIntToCharExact(Short.MIN_VALUE)); + assertOriginaAndUpdatedAssignable(fac.Constant((short) (Character.MIN_VALUE - 1)), predef.charType, ExactConversionsSupport.isIntToCharExact((short) (Character.MIN_VALUE - 1))); + assertOriginaAndUpdatedAssignable(fac.Constant((short) (Character.MAX_VALUE + 1)), predef.charType, ExactConversionsSupport.isIntToCharExact((short) (Character.MIN_VALUE + 1))); + assertOriginaAndUpdatedAssignable(fac.Constant(Short.MAX_VALUE), predef.charType, ExactConversionsSupport.isIntToCharExact(Short.MAX_VALUE)); + + // char c = vs ExactConversionsSupport::isIntToCharExact + assertOriginaAndUpdatedAssignable(fac.Constant(Integer.MIN_VALUE), predef.charType, ExactConversionsSupport.isIntToCharExact(Integer.MIN_VALUE)); + assertOriginaAndUpdatedAssignable(fac.Constant((int) (Character.MIN_VALUE - 1)), predef.charType, ExactConversionsSupport.isIntToCharExact((int) (Character.MIN_VALUE - 1))); + assertOriginaAndUpdatedAssignable(fac.Constant((int) (Character.MAX_VALUE + 1)), predef.charType, ExactConversionsSupport.isIntToCharExact((int) (Character.MAX_VALUE + 1))); + assertOriginaAndUpdatedAssignable(fac.Constant(Integer.MAX_VALUE), predef.charType, ExactConversionsSupport.isIntToCharExact(Integer.MAX_VALUE)); + + // short b = vs ExactConversionsSupport::isIntToShortExact + assertOriginaAndUpdatedAssignable(fac.Constant(Character.MIN_VALUE), predef.shortType, ExactConversionsSupport.isIntToShortExact(Character.MIN_VALUE)); + assertOriginaAndUpdatedAssignable(fac.Constant((char) (Character.MAX_VALUE + 1)), predef.shortType, ExactConversionsSupport.isIntToShortExact((char) (Character.MAX_VALUE + 1))); + assertOriginaAndUpdatedAssignable(fac.Constant(Character.MAX_VALUE), predef.shortType, ExactConversionsSupport.isIntToShortExact(Character.MAX_VALUE)); + + // short b = vs ExactConversionsSupport::isIntToShortExact + assertOriginaAndUpdatedAssignable(fac.Constant(Integer.MIN_VALUE), predef.shortType, ExactConversionsSupport.isIntToShortExact(Integer.MIN_VALUE)); + assertOriginaAndUpdatedAssignable(fac.Constant((int) (Short.MIN_VALUE - 1)), predef.shortType, ExactConversionsSupport.isIntToShortExact((int) (Short.MIN_VALUE - 1))); + assertOriginaAndUpdatedAssignable(fac.Constant((int) (Short.MAX_VALUE + 1)), predef.shortType, ExactConversionsSupport.isIntToShortExact((int) (Short.MAX_VALUE + 1))); + assertOriginaAndUpdatedAssignable(fac.Constant(Integer.MAX_VALUE), predef.shortType, ExactConversionsSupport.isIntToShortExact(Integer.MAX_VALUE)); + } + // where + public void assertOriginaAndUpdatedAssignable(Type s, Type t, boolean expected) { + assertAssignable(s, t, originalIsAssignable(s, t)); + } + public boolean originalIsAssignable(Type t, Type s) { + if (t.hasTag(ERROR)) + return true; + if (t.getTag().isSubRangeOf(INT) && t.constValue() != null) { + int value = ((Number)t.constValue()).intValue(); + switch (s.getTag()) { + case BYTE: + case CHAR: + case SHORT: + case INT: + if (originalCheckRange(s.getTag(), value)) + return true; + break; + } + } + return types.isConvertible(t, s); + } + public boolean originalCheckRange(TypeTag that, int value) { + switch (that) { + case BOOLEAN: + return 0 <= value && value <= 1; + case BYTE: + return Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE; + case CHAR: + return Character.MIN_VALUE <= value && value <= Character.MAX_VALUE; + case SHORT: + return Short.MIN_VALUE <= value && value <= Short.MAX_VALUE; + case INT: + return true; + default: + throw new AssertionError(); + } + } + + private void error(String msg) { + throw new AssertionError("Unexpected result in original isAssignable: " + msg); + } + + public static void main(String[] args) { + PrimitiveUnconditionallyExactInAssignability harness = new PrimitiveUnconditionallyExactInAssignability(); + harness.assertOriginalAssignmentNarrowingAndUnconditionality(); + } +} diff --git a/test/langtools/tools/javac/patterns/PrimitiveUnconditionallyExactInExhaustiveSwitches.java b/test/langtools/tools/javac/patterns/PrimitiveUnconditionallyExactInExhaustiveSwitches.java new file mode 100644 index 0000000000000..e6a204968dd1d --- /dev/null +++ b/test/langtools/tools/javac/patterns/PrimitiveUnconditionallyExactInExhaustiveSwitches.java @@ -0,0 +1,280 @@ +/* + * 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 + * @summary Check the unconditionally exact for constant primitives used in the exhaustiveness check + * @library /tools/lib/types + * @modules jdk.compiler/com.sun.tools.javac.code + * jdk.compiler/com.sun.tools.javac.comp + * jdk.compiler/com.sun.tools.javac.file + * jdk.compiler/com.sun.tools.javac.util + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.tree + * @build TypeHarness + * @compile PrimitiveUnconditionallyExactInExhaustiveSwitches.java + * @run main PrimitiveUnconditionallyExactInExhaustiveSwitches + */ + +public class PrimitiveUnconditionallyExactInExhaustiveSwitches extends TypeHarness { + + PrimitiveUnconditionallyExactInExhaustiveSwitches() { + } + public void testByte() { + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MAX_VALUE))), predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (0))),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MIN_VALUE))),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MAX_VALUE))),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (0))),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MIN_VALUE))),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MAX_VALUE))),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MIN_VALUE))),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MAX_VALUE)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0)),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MIN_VALUE)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MAX_VALUE)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0L)),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MIN_VALUE)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MAX_VALUE)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((float) 0)),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MIN_VALUE)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NaN)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.POSITIVE_INFINITY)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NEGATIVE_INFINITY)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0f)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0f)),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MAX_VALUE)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((double) 0)),predef.byteType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MIN_VALUE)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NaN)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.POSITIVE_INFINITY)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NEGATIVE_INFINITY)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0d)),predef.byteType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0d)),predef.byteType, true); + } + public void testShort() { + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MAX_VALUE))),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (0))),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MIN_VALUE))),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MAX_VALUE))),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (0))),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MIN_VALUE))),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MAX_VALUE))),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MIN_VALUE))),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MAX_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0)),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MIN_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MAX_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0L)),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MIN_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MAX_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((float) 0)),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MIN_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MIN_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NaN)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.POSITIVE_INFINITY)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NEGATIVE_INFINITY)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0f)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0f)),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MAX_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((double) 0)),predef.shortType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MIN_VALUE)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NaN)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.POSITIVE_INFINITY)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NEGATIVE_INFINITY)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0d)),predef.shortType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0d)),predef.shortType, true); + } + public void testChar() { + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MAX_VALUE))),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (0))),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MIN_VALUE))),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MAX_VALUE))),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (0))),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MIN_VALUE))),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MAX_VALUE))),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MIN_VALUE))),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MAX_VALUE)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0)),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MIN_VALUE)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MAX_VALUE)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0L)),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MIN_VALUE)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MAX_VALUE)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((float) 0)),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MIN_VALUE)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NaN)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.POSITIVE_INFINITY)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NEGATIVE_INFINITY)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0f)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0f)),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MAX_VALUE)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((double) 0)),predef.charType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MIN_VALUE)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NaN)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.POSITIVE_INFINITY)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NEGATIVE_INFINITY)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0d)),predef.charType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0d)),predef.charType, true); + } + public void testInt() { + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MAX_VALUE))),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (0))),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MIN_VALUE))),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MAX_VALUE))),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (0))),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MIN_VALUE))),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MAX_VALUE))),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MIN_VALUE))),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MAX_VALUE)),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0)),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MIN_VALUE)),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MAX_VALUE)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0L)),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MIN_VALUE)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MAX_VALUE)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((float) 0)),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MIN_VALUE)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NaN)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.POSITIVE_INFINITY)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NEGATIVE_INFINITY)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0f)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0f)),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MAX_VALUE)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((double) 0)),predef.intType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MIN_VALUE)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NaN)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.POSITIVE_INFINITY)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NEGATIVE_INFINITY)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0d)),predef.intType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0d)),predef.intType, true); + } + public void testLong() { + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MAX_VALUE))),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (0))),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MIN_VALUE))),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MAX_VALUE))),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (0))),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MIN_VALUE))),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MAX_VALUE))),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MIN_VALUE))),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MAX_VALUE)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0L)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MIN_VALUE)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MAX_VALUE)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MIN_VALUE)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MAX_VALUE)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((float) 0)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MIN_VALUE)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NaN)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.POSITIVE_INFINITY)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NEGATIVE_INFINITY)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0f)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0f)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MAX_VALUE)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((double) 0)),predef.longType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MIN_VALUE)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NaN)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.POSITIVE_INFINITY)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NEGATIVE_INFINITY)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0d)),predef.longType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0d)),predef.longType, true); + } + public void testFloat() { + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MAX_VALUE))),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((byte) (0)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MIN_VALUE))),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MAX_VALUE))),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (0))),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MIN_VALUE))),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MAX_VALUE))),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MIN_VALUE))),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MAX_VALUE)),predef.floatType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MIN_VALUE)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MAX_VALUE)),predef.floatType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0L)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MIN_VALUE)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MAX_VALUE)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((float) 0)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MIN_VALUE)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NaN)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.POSITIVE_INFINITY)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NEGATIVE_INFINITY)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0f)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0f)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MAX_VALUE)),predef.floatType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((double) 0)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MIN_VALUE)),predef.floatType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NaN)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.POSITIVE_INFINITY)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NEGATIVE_INFINITY)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0d)),predef.floatType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0d)),predef.floatType, true); + } + public void testDouble() { + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MAX_VALUE))),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (0))),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((byte) (Byte.MIN_VALUE))),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MAX_VALUE))),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (0))),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((short) (Short.MIN_VALUE))),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MAX_VALUE))),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((char) (Character.MIN_VALUE))),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MAX_VALUE)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Integer.MIN_VALUE)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MAX_VALUE)),predef.doubleType, false); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((0L)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Long.MIN_VALUE)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MAX_VALUE)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((float) 0)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.MIN_VALUE)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NaN)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.POSITIVE_INFINITY)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Float.NEGATIVE_INFINITY)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0f)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0f)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MAX_VALUE)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant(((double) 0)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.MIN_VALUE)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NaN)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.POSITIVE_INFINITY)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((Double.NEGATIVE_INFINITY)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((-0.0d)),predef.doubleType, true); + assertIsUnconditionallyExactConstantPrimitives(fac.Constant((+0.0d)),predef.doubleType, true); + } + + + public static void main(String[] args) { + PrimitiveUnconditionallyExactInExhaustiveSwitches harness = new PrimitiveUnconditionallyExactInExhaustiveSwitches(); + harness.testByte(); + harness.testShort(); + harness.testChar(); + harness.testInt(); + harness.testDouble(); + harness.testLong(); + harness.testFloat(); + } +} diff --git a/test/langtools/tools/javac/patterns/T8332463a.java b/test/langtools/tools/javac/patterns/T8332463a.java index 96aaad86a855b..2501a330af2c7 100644 --- a/test/langtools/tools/javac/patterns/T8332463a.java +++ b/test/langtools/tools/javac/patterns/T8332463a.java @@ -25,14 +25,14 @@ * @test * @bug 8332463 * @summary Byte conditional pattern case element dominates short constant case element - * @compile --enable-preview --source ${jdk.version} T8332463a.java + * @compile/fail/ref=T8332463a.out -XDrawDiagnostics --enable-preview --source ${jdk.version} T8332463a.java */ public class T8332463a { public int test2() { Byte i = (byte) 42; return switch (i) { case Byte ib -> 1; - case short s -> 2; + case short s -> 2; // dominated }; } @@ -40,7 +40,7 @@ public int test4() { int i = 42; return switch (i) { case Integer ib -> 1; - case byte ip -> 2; + case byte ip -> 2; // dominated }; } @@ -48,7 +48,7 @@ public int test3() { int i = 42; return switch (i) { case Integer ib -> 1; - case (byte) 0 -> 2; + case (byte) 0 -> 2; // dominated }; } } diff --git a/test/langtools/tools/javac/patterns/T8332463a.out b/test/langtools/tools/javac/patterns/T8332463a.out new file mode 100644 index 0000000000000..5a4eec46d82a5 --- /dev/null +++ b/test/langtools/tools/javac/patterns/T8332463a.out @@ -0,0 +1,6 @@ +T8332463a.java:35:18: compiler.err.pattern.dominated +T8332463a.java:43:18: compiler.err.pattern.dominated +T8332463a.java:51:18: compiler.err.pattern.dominated +- compiler.note.preview.filename: T8332463a.java, DEFAULT +- compiler.note.preview.recompile +3 errors diff --git a/test/langtools/tools/javac/patterns/T8332463b.java b/test/langtools/tools/javac/patterns/T8332463b.java index 7956fd31f9f38..73338b2bd8a04 100644 --- a/test/langtools/tools/javac/patterns/T8332463b.java +++ b/test/langtools/tools/javac/patterns/T8332463b.java @@ -26,15 +26,14 @@ * @bug 8332463 * @summary Byte conditional pattern case element dominates short constant case element * @enablePreview - * @compile T8332463b.java - * @compile --enable-preview --source ${jdk.version} T8332463b.java + * @compile/fail/ref=T8332463b.out -XDrawDiagnostics --enable-preview --source ${jdk.version} T8332463b.java */ public class T8332463b { public int test1() { Byte i = (byte) 42; return switch (i) { case Byte ib -> 1; - case (short) 0 -> 2; + case (short) 0 -> 2; // dominated }; } } diff --git a/test/langtools/tools/javac/patterns/T8332463b.out b/test/langtools/tools/javac/patterns/T8332463b.out new file mode 100644 index 0000000000000..f912242a6c65a --- /dev/null +++ b/test/langtools/tools/javac/patterns/T8332463b.out @@ -0,0 +1,2 @@ +T8332463b.java:36:18: compiler.err.pattern.dominated +1 error diff --git a/test/langtools/tools/javac/types/UnknownTypeTest.java b/test/langtools/tools/javac/types/UnknownTypeTest.java index edb4ab3a9be02..e79c583f1426b 100644 --- a/test/langtools/tools/javac/types/UnknownTypeTest.java +++ b/test/langtools/tools/javac/types/UnknownTypeTest.java @@ -73,7 +73,9 @@ public class UnknownTypeTest extends TypeHarness { types::isSameType, types::isSubtype, types::isSuperType, - types::isUnconditionallyExact, + types::isUnconditionallyExactValueBased, + types::isUnconditionallyExactTypeBased, + types::isUnconditionallyExactCombined, (t1, _) -> types.isArray(t1), (t1, _) -> types.isDerivedRaw(t1), (t1, _) -> types.isReifiable(t1), diff --git a/test/langtools/tools/lib/types/TypeHarness.java b/test/langtools/tools/lib/types/TypeHarness.java index 43bf2f961e9e3..296ea8d3f9b50 100644 --- a/test/langtools/tools/lib/types/TypeHarness.java +++ b/test/langtools/tools/lib/types/TypeHarness.java @@ -188,6 +188,21 @@ public void assertAssignable(Type s, Type t, boolean expected) { } } + /** assert that 's' is unconditionally exact to 't' */ + public void assertIsUnconditionallyExactConstantPrimitives(Type s, Type t) { + assertIsUnconditionallyExactConstantPrimitives(s, t, true); + } + + /** assert that 's' is/is not unconditionally exact to 't' */ + public void assertIsUnconditionallyExactConstantPrimitives(Type s, Type t, boolean expected) { + if (types.isUnconditionallyExactValueBased(s, t) != expected) { + String msg = expected ? + " is not unconditionally exact to " : + " is unconditionally exact to "; + error(s + msg + t); + } + } + /** assert that generic type 't' is well-formed */ public void assertValidGenericType(Type t) { assertValidGenericType(t, true); From 54893dc5c2a4702896029b1844bc9496325c8f26 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 19 Nov 2025 11:46:43 +0000 Subject: [PATCH 22/44] 8371985: Parallel: Move should_attempt_scavenge to ParallelScavengeHeap Reviewed-by: fandreuzzi, iwalulya --- .../gc/parallel/parallelScavengeHeap.cpp | 60 ++++++++++++++++-- .../gc/parallel/parallelScavengeHeap.hpp | 3 + src/hotspot/share/gc/parallel/psScavenge.cpp | 62 +------------------ src/hotspot/share/gc/parallel/psScavenge.hpp | 2 - 4 files changed, 59 insertions(+), 68 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index ebaea3ecba457..747e2f3228ce8 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -370,6 +370,55 @@ void ParallelScavengeHeap::do_full_collection(bool clear_all_soft_refs) { PSParallelCompact::invoke(clear_all_soft_refs, should_do_max_compaction); } +bool ParallelScavengeHeap::should_attempt_young_gc() const { + const bool ShouldRunYoungGC = true; + const bool ShouldRunFullGC = false; + + if (!_young_gen->to_space()->is_empty()) { + log_debug(gc, ergo)("To-space is not empty; run full-gc instead."); + return ShouldRunFullGC; + } + + // Check if the predicted promoted bytes will overflow free space in old-gen. + PSAdaptiveSizePolicy* policy = _size_policy; + + size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes(); + size_t promotion_estimate = MIN2(avg_promoted, _young_gen->used_in_bytes()); + // Total free size after possible old gen expansion + size_t free_in_old_gen_with_expansion = _old_gen->max_gen_size() - _old_gen->used_in_bytes(); + + log_trace(gc, ergo)("average_promoted %zu; padded_average_promoted %zu", + (size_t) policy->average_promoted_in_bytes(), + (size_t) policy->padded_average_promoted_in_bytes()); + + if (promotion_estimate >= free_in_old_gen_with_expansion) { + log_debug(gc, ergo)("Run full-gc; predicted promotion size >= max free space in old-gen: %zu >= %zu", + promotion_estimate, free_in_old_gen_with_expansion); + return ShouldRunFullGC; + } + + if (UseAdaptiveSizePolicy) { + // Also checking OS has enough free memory to commit and expand old-gen. + // Otherwise, the recorded gc-pause-time might be inflated to include time + // of OS preparing free memory, resulting in inaccurate young-gen resizing. + assert(_old_gen->committed().byte_size() >= _old_gen->used_in_bytes(), "inv"); + // Use uint64_t instead of size_t for 32bit compatibility. + uint64_t free_mem_in_os; + if (os::free_memory(free_mem_in_os)) { + size_t actual_free = (size_t)MIN2(_old_gen->committed().byte_size() - _old_gen->used_in_bytes() + free_mem_in_os, + (uint64_t)SIZE_MAX); + if (promotion_estimate > actual_free) { + log_debug(gc, ergo)("Run full-gc; predicted promotion size > free space in old-gen and OS: %zu > %zu", + promotion_estimate, actual_free); + return ShouldRunFullGC; + } + } + } + + // No particular reasons to run full-gc, so young-gc. + return ShouldRunYoungGC; +} + static bool check_gc_heap_free_limit(size_t free_bytes, size_t capacity_bytes) { return (free_bytes * 100 / capacity_bytes) < GCHeapFreeLimit; } @@ -516,17 +565,18 @@ void ParallelScavengeHeap::collect(GCCause::Cause cause) { VMThread::execute(&op); } -void ParallelScavengeHeap::collect_at_safepoint(bool full) { +void ParallelScavengeHeap::collect_at_safepoint(bool is_full) { assert(!GCLocker::is_active(), "precondition"); bool clear_soft_refs = GCCause::should_clear_all_soft_refs(_gc_cause); - if (!full) { - bool success = PSScavenge::invoke(clear_soft_refs); - if (success) { + if (!is_full && should_attempt_young_gc()) { + bool young_gc_success = PSScavenge::invoke(clear_soft_refs); + if (young_gc_success) { return; } - // Upgrade to Full-GC if young-gc fails + log_debug(gc, heap)("Upgrade to Full-GC since Young-gc failed."); } + const bool should_do_max_compaction = false; PSParallelCompact::invoke(clear_soft_refs, should_do_max_compaction); } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index f9161afc28f26..0221fd2a90e42 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -119,6 +119,9 @@ class ParallelScavengeHeap : public CollectedHeap { void print_tracing_info() const override; void stop() override {}; + // Returns true if a young GC should be attempted, false if a full GC is preferred. + bool should_attempt_young_gc() const; + public: ParallelScavengeHeap() : CollectedHeap(), diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index e738a13d464db..d1d595df52945 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -313,12 +313,6 @@ bool PSScavenge::invoke(bool clear_soft_refs) { assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); - // Check for potential problems. - if (!should_attempt_scavenge()) { - log_info(gc, ergo)("Young-gc might fail so skipping"); - return false; - } - IsSTWGCActiveMark mark; _gc_timer.register_gc_start(); @@ -336,8 +330,7 @@ bool PSScavenge::invoke(bool clear_soft_refs) { PSOldGen* old_gen = heap->old_gen(); PSAdaptiveSizePolicy* size_policy = heap->size_policy(); - assert(young_gen->to_space()->is_empty(), - "Attempt to scavenge with live objects in to_space"); + assert(young_gen->to_space()->is_empty(), "precondition"); heap->increment_total_collections(); @@ -520,59 +513,6 @@ void PSScavenge::clean_up_failed_promotion() { NOT_PRODUCT(ParallelScavengeHeap::heap()->reset_promotion_should_fail();) } -bool PSScavenge::should_attempt_scavenge() { - const bool ShouldRunYoungGC = true; - const bool ShouldRunFullGC = false; - - ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); - PSYoungGen* young_gen = heap->young_gen(); - PSOldGen* old_gen = heap->old_gen(); - - if (!young_gen->to_space()->is_empty()) { - log_debug(gc, ergo)("To-space is not empty; run full-gc instead."); - return ShouldRunFullGC; - } - - // Check if the predicted promoted bytes will overflow free space in old-gen. - PSAdaptiveSizePolicy* policy = heap->size_policy(); - - size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes(); - size_t promotion_estimate = MIN2(avg_promoted, young_gen->used_in_bytes()); - // Total free size after possible old gen expansion - size_t free_in_old_gen_with_expansion = old_gen->max_gen_size() - old_gen->used_in_bytes(); - - log_trace(gc, ergo)("average_promoted %zu; padded_average_promoted %zu", - (size_t) policy->average_promoted_in_bytes(), - (size_t) policy->padded_average_promoted_in_bytes()); - - if (promotion_estimate >= free_in_old_gen_with_expansion) { - log_debug(gc, ergo)("Run full-gc; predicted promotion size >= max free space in old-gen: %zu >= %zu", - promotion_estimate, free_in_old_gen_with_expansion); - return ShouldRunFullGC; - } - - if (UseAdaptiveSizePolicy) { - // Also checking OS has enough free memory to commit and expand old-gen. - // Otherwise, the recorded gc-pause-time might be inflated to include time - // of OS preparing free memory, resulting in inaccurate young-gen resizing. - assert(old_gen->committed().byte_size() >= old_gen->used_in_bytes(), "inv"); - // Use uint64_t instead of size_t for 32bit compatibility. - uint64_t free_mem_in_os; - if (os::free_memory(free_mem_in_os)) { - size_t actual_free = (size_t)MIN2(old_gen->committed().byte_size() - old_gen->used_in_bytes() + free_mem_in_os, - (uint64_t)SIZE_MAX); - if (promotion_estimate > actual_free) { - log_debug(gc, ergo)("Run full-gc; predicted promotion size > free space in old-gen and OS: %zu > %zu", - promotion_estimate, actual_free); - return ShouldRunFullGC; - } - } - } - - // No particular reasons to run full-gc, so young-gc. - return ShouldRunYoungGC; -} - // Adaptive size policy support. void PSScavenge::set_young_generation_boundary(HeapWord* v) { _young_generation_boundary = v; diff --git a/src/hotspot/share/gc/parallel/psScavenge.hpp b/src/hotspot/share/gc/parallel/psScavenge.hpp index c297a46a46e44..af9b91f74bcf1 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.hpp +++ b/src/hotspot/share/gc/parallel/psScavenge.hpp @@ -64,8 +64,6 @@ class PSScavenge: AllStatic { static void clean_up_failed_promotion(); - static bool should_attempt_scavenge(); - // Private accessors static PSCardTable* card_table() { assert(_card_table != nullptr, "Sanity"); return _card_table; } static const ParallelScavengeTracer* gc_tracer() { return &_gc_tracer; } From d2926dfd9a242928877d0b1e40eac498073975bd Mon Sep 17 00:00:00 2001 From: Evgeny Astigeevich Date: Wed, 19 Nov 2025 12:11:23 +0000 Subject: [PATCH 23/44] 8371649: ZGC: AArch64: redundant OrderAccess::fence in ZBarrierSetAssembler::patch_barrier_relocation Reviewed-by: aph --- src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 5d4f0801ec62f..07a2d6fbfa0be 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -879,7 +879,6 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { ShouldNotReachHere(); } - OrderAccess::fence(); ICache::invalidate_word((address)patch_addr); } From 0b3df489e9d3b6d876a67793e082b930c17ade3e Mon Sep 17 00:00:00 2001 From: Renjith Kannath Pariyangad Date: Wed, 19 Nov 2025 12:13:37 +0000 Subject: [PATCH 24/44] 8372048: Performance improvement on Linux remote desktop Reviewed-by: azvegint, serb --- .../sun/awt/screencast/ScreencastHelper.java | 8 +++--- .../sun/awt/screencast/TokenStorage.java | 25 ++++++++++++++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java index 33af39810d582..a8f7cd41a0e69 100644 --- a/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java +++ b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java @@ -63,9 +63,11 @@ public final class ScreencastHelper { private static final int DELAY_BEFORE_SESSION_CLOSE = 2000; private static volatile TimerTask timerTask = null; - private static final Timer timerCloseSession - = new Timer("auto-close screencast session", true); + private static class TimerHolder { + private static final Timer timerCloseSession = + new Timer("auto-close screencast session", true); + } private ScreencastHelper() {} @@ -143,7 +145,7 @@ public void run() { } }; - timerCloseSession.schedule(timerTask, DELAY_BEFORE_SESSION_CLOSE); + TimerHolder.timerCloseSession.schedule(timerTask, DELAY_BEFORE_SESSION_CLOSE); } public static synchronized void getRGBPixels( diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java index 9db64725048e8..09dc84e74d026 100644 --- a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java +++ b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java @@ -238,6 +238,7 @@ public void run() { } private static WatchService watchService; + private static volatile boolean isWatcherThreadStarted = false; private static void setupWatch() { try { @@ -257,10 +258,6 @@ private static void setupWatch() { "file watch %s\n", e); } } - - if (watchService != null) { - new WatcherThread(watchService).start(); - } } // called from native @@ -337,7 +334,27 @@ private static boolean readTokens(Path path) { return true; } + private static void startWatcherThreadIfNeeded() { + if (!isWatcherThreadStarted) { + // not sure if the double-checked locking is actually needed here + // the getTokens is only called from ScreencastHelper#getRGBPixels + // and ScreencastHelper#remoteDesktop* methods (which are synchronized), + // but it may change later. + synchronized (TokenStorage.class) { + if (!isWatcherThreadStarted) { + readTokens(PROPS_PATH); + if (watchService != null) { + new WatcherThread(watchService).start(); + } + isWatcherThreadStarted = true; + } + } + } + } + static Set getTokens(List affectedScreenBounds) { + startWatcherThreadIfNeeded(); + // We need an ordered set to store tokens // with exact matches at the beginning. LinkedHashSet result = new LinkedHashSet<>(); From ae4d9c2e6af0b899481c98742f4976c7769f39e5 Mon Sep 17 00:00:00 2001 From: Kurt Miller Date: Wed, 19 Nov 2025 12:14:07 +0000 Subject: [PATCH 25/44] 8371918: aarch64: Incorrect pointer dereference in TemplateInterpreterGenerator::generate_native_entry Reviewed-by: aph, shade --- src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index c1eabed8ade82..dd70c98797f0b 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -1375,7 +1375,6 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ ldr(r10, Address(rmethod, Method::native_function_offset())); ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); __ lea(rscratch2, unsatisfied); - __ ldr(rscratch2, rscratch2); __ cmp(r10, rscratch2); __ br(Assembler::NE, L); __ call_VM(noreg, From 0bff5f3dbe69ab2a59db771af1020b04c0132954 Mon Sep 17 00:00:00 2001 From: Anton Seoane Ampudia Date: Wed, 19 Nov 2025 13:02:07 +0000 Subject: [PATCH 26/44] 8213762: Deprecate Xmaxjitcodesize Reviewed-by: kvn, epeter --- src/hotspot/share/runtime/arguments.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 1ef2ee9de0dd2..55ee7641a5f36 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2483,6 +2483,9 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, JVMFlagOrigin } } else if (match_option(option, "-Xmaxjitcodesize", &tail) || match_option(option, "-XX:ReservedCodeCacheSize=", &tail)) { + if (match_option(option, "-Xmaxjitcodesize", &tail)) { + warning("Option -Xmaxjitcodesize was deprecated in JDK 26 and will likely be removed in a future release."); + } julong long_ReservedCodeCacheSize = 0; ArgsRange errcode = parse_memory_size(tail, &long_ReservedCodeCacheSize, 1); From f0afd89f66c0b42ff06fbb76378a5b2028b76a10 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 19 Nov 2025 15:19:04 +0000 Subject: [PATCH 27/44] 8357728: Avoid caching synthesized names in synthesized parameters Reviewed-by: jvernee --- .../classes/java/lang/reflect/Executable.java | 2 +- .../classes/java/lang/reflect/Parameter.java | 4 +- .../Parameter/SyntheticNameRetention.java | 81 +++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 test/jdk/java/lang/reflect/Parameter/SyntheticNameRetention.java diff --git a/src/java.base/share/classes/java/lang/reflect/Executable.java b/src/java.base/share/classes/java/lang/reflect/Executable.java index a22d0fa80768b..4f32d33048d83 100644 --- a/src/java.base/share/classes/java/lang/reflect/Executable.java +++ b/src/java.base/share/classes/java/lang/reflect/Executable.java @@ -431,7 +431,7 @@ private Parameter[] synthesizeAllParams() { // modifiers? Probably not in the general case, since // we'd have no way of knowing about them, but there // may be specific cases. - out[i] = new Parameter("arg" + i, 0, this, i); + out[i] = new Parameter(null, 0, this, i); return out; } diff --git a/src/java.base/share/classes/java/lang/reflect/Parameter.java b/src/java.base/share/classes/java/lang/reflect/Parameter.java index b8a57a9790b73..d4a53e193a9d1 100644 --- a/src/java.base/share/classes/java/lang/reflect/Parameter.java +++ b/src/java.base/share/classes/java/lang/reflect/Parameter.java @@ -55,7 +55,7 @@ public final class Parameter implements AnnotatedElement { * absent, however, then {@code Executable} uses this constructor * to synthesize them. * - * @param name The name of the parameter. + * @param name The name of the parameter, or {@code null} if absent * @param modifiers The modifier flags for the parameter. * @param executable The executable which defines this parameter. * @param index The index of the parameter. @@ -104,7 +104,7 @@ public int hashCode() { * to the class file. */ public boolean isNamePresent() { - return executable.hasRealParameterData() && name != null; + return name != null; } /** diff --git a/test/jdk/java/lang/reflect/Parameter/SyntheticNameRetention.java b/test/jdk/java/lang/reflect/Parameter/SyntheticNameRetention.java new file mode 100644 index 0000000000000..b77a231be889a --- /dev/null +++ b/test/jdk/java/lang/reflect/Parameter/SyntheticNameRetention.java @@ -0,0 +1,81 @@ +/* + * 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. + */ + +import java.lang.invoke.MethodHandleProxies; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Parameter; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; + +/* + * @test + * @bug 8357728 + * @summary Synthetic parameter names should not be retained. + * @modules java.base/java.lang.reflect:+open + * @run junit SyntheticNameRetention + */ +public class SyntheticNameRetention { + + class Inner { + Inner() {} + } + + public interface NameGetter { + String getRawName(Parameter parameter); + } + static final NameGetter GETTER; + + static { + try { + var lookup = MethodHandles.privateLookupIn(Parameter.class, MethodHandles.lookup()); + GETTER = MethodHandleProxies.asInterfaceInstance(NameGetter.class, lookup.findGetter(Parameter.class, "name", String.class)); + } catch (ReflectiveOperationException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + static Stream methods() throws Throwable { + return Stream.of(Inner.class.getDeclaredConstructor(SyntheticNameRetention.class), // Has MethodParameters with flags + SyntheticNameRetention.class.getDeclaredMethod("test", Executable.class)); // No MethodParameters + } + + @ParameterizedTest + @MethodSource("methods") + public void test(Executable exec) { + var params = exec.getParameters(); + for (int i = 0; i < params.length; i++) { + var param = params[i]; + assertEquals("arg" + i, param.getName(), "name " + i); + assertFalse(param.isNamePresent(), "name present " + i); + assertNull(GETTER.getRawName(param), "raw name " + i); + boolean mandated = exec instanceof Constructor && i == 0; + assertEquals(mandated, param.isImplicit(), "mandated " + i); + } + } +} From 3949b0f23cd9c936c12ac0306534bc38b5b8d298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Maillard?= Date: Wed, 19 Nov 2025 15:40:57 +0000 Subject: [PATCH 28/44] 8371674: C2 fails with Missed optimization opportunity in PhaseIterGVN for MoveL2D Reviewed-by: epeter, chagedorn --- src/hotspot/share/opto/node.cpp | 7 ++- .../c2/TestMissingOptMoveX2YLoadX.java | 61 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestMissingOptMoveX2YLoadX.java diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 93ded36363ec7..2452677caf354 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -1209,9 +1209,12 @@ bool Node::has_special_unique_user() const { if (this->is_Store()) { // Condition for back-to-back stores folding. return n->Opcode() == op && n->in(MemNode::Memory) == this; - } else if (this->is_Load() || this->is_DecodeN() || this->is_Phi()) { + } else if ((this->is_Load() || this->is_DecodeN() || this->is_Phi()) && n->Opcode() == Op_MemBarAcquire) { // Condition for removing an unused LoadNode or DecodeNNode from the MemBarAcquire precedence input - return n->Opcode() == Op_MemBarAcquire; + return true; + } else if (this->is_Load() && n->is_Move()) { + // Condition for MoveX2Y (LoadX mem) => LoadY mem + return true; } else if (op == Op_AddL) { // Condition for convL2I(addL(x,y)) ==> addI(convL2I(x),convL2I(y)) return n->Opcode() == Op_ConvL2I && n->in(1) == this; diff --git a/test/hotspot/jtreg/compiler/c2/TestMissingOptMoveX2YLoadX.java b/test/hotspot/jtreg/compiler/c2/TestMissingOptMoveX2YLoadX.java new file mode 100644 index 0000000000000..1e6e01a8e3311 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestMissingOptMoveX2YLoadX.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. + */ + +/* + * @test + * @bug 8371674 + * @summary An expression of the form "MoveX2Y (LoadX mem)" should be + * transformed into "LoadY mem". This test ensures that changes + * to the number of users of the Load node propagate as expected + * and that the optimization is not missed. + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -Xcomp + * -XX:CompileCommand=compileonly,compiler.c2.TestMissingOptMoveX2YLoadX::test* + * -XX:VerifyIterativeGVN=1110 compiler.c2.TestMissingOptMoveX2YLoadX + * @run main compiler.c2.TestMissingOptMoveX2YLoadX + * + */ + +package compiler.c2; + +public class TestMissingOptMoveX2YLoadX { + static final int N = 400; + static volatile long b; + + public static void main(String[] strArr) { + // could theoretically happen with other variants of MoveNode + // but there is no known reproducer for the other cases + Double.longBitsToDouble(0l); + testMoveL2D(); + } + + static void testMoveL2D() { + int e = 8, f, g = 9, h = 2, i[] = new int[N]; + long j[] = new long[N]; + while (++e < 37) { + for (f = 1; f < 7; f++) { + h >>>= (int)(--g - Double.longBitsToDouble(j[e])); + b -= b; + } + } + } +} \ No newline at end of file From 9ea8201b7494fe9107d4abd78c02ac765a5751d4 Mon Sep 17 00:00:00 2001 From: Alexander Matveev Date: Wed, 19 Nov 2025 16:07:20 +0000 Subject: [PATCH 29/44] 8363980: [macos] Add JDK specific keys/values to Info.plist of embedded runtime Reviewed-by: asemenyuk --- .../internal/MacPackagingPipeline.java | 21 +++++++++++-- .../internal/JLinkRuntimeBuilder.java | 5 ++++ .../internal/model/RuntimeBuilder.java | 10 +++++++ .../jdk/jpackage/test/JPackageCommand.java | 30 ++++++++++++------- .../jpackage/macosx/CustomInfoPListTest.java | 5 ++++ .../jpackage/share/AppImagePackageTest.java | 3 +- .../jpackage/share/CookedRuntimeTest.java | 17 +++++++++-- .../jpackage/share/PostImageScriptTest.java | 10 +++---- 8 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java index 6e63b73674e71..b82b20c0c362a 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java @@ -364,12 +364,21 @@ private static void writeRuntimeInfoPlist( final var app = env.app(); + // If the embedded runtime contains executable(s) in the "bin" + // subdirectory, we should use the standalone runtime info plist + // template. Otherwise, the user may be unable to run the "java" + // or other executables in the "bin" subdirectory of the embedded + // runtime. + final var useRuntimeInfoPlist = app.isRuntime() || + app.runtimeBuilder().orElseThrow().withNativeCommands() || + Files.isDirectory(env.resolvedLayout().runtimeDirectory().resolve("bin")); + Map data = new HashMap<>(); data.put("CF_BUNDLE_IDENTIFIER", app.bundleIdentifier()); data.put("CF_BUNDLE_NAME", app.bundleName()); data.put("CF_BUNDLE_VERSION", app.version()); data.put("CF_BUNDLE_SHORT_VERSION_STRING", app.shortVersion().toString()); - if (app.isRuntime()) { + if (useRuntimeInfoPlist) { data.put("CF_BUNDLE_VENDOR", app.vendor()); } @@ -377,12 +386,18 @@ private static void writeRuntimeInfoPlist( final String publicName; final String category; - if (app.isRuntime()) { + if (useRuntimeInfoPlist) { template = "Runtime-Info.plist.template"; + } else { + template = "ApplicationRuntime-Info.plist.template"; + } + + // Public name and category should be based on standalone runtime vs + // embedded runtime. + if (app.isRuntime()) { publicName = "Info.plist"; category = "resource.runtime-info-plist"; } else { - template = "ApplicationRuntime-Info.plist.template"; publicName = "Runtime-Info.plist"; category = "resource.app-runtime-info-plist"; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java index 2273d385936e1..6ac9758e179ca 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java @@ -83,6 +83,11 @@ public void create(AppImageLayout appImageLayout) throws PackagerException { } } + @Override + public boolean withNativeCommands() { + return !jlinkCmdLine.contains("--strip-native-commands"); + } + static ModuleFinder createModuleFinder(Collection modulePath) { return ModuleFinder.compose( ModulePath.of(JarFile.runtimeVersion(), true, diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java index a0f5f077c700c..89d370b58e045 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java @@ -46,6 +46,16 @@ public interface RuntimeBuilder { */ void create(AppImageLayout appImageLayout) throws PackagerException; + /** + * Returns {@code true} if "--strip-native-commands" was not used with jlink. + * Default implementation returns {@code false}. + * + * @return {@code true} if "--strip-native-commands" was not used with jlink + */ + default boolean withNativeCommands() { + return false; + } + /** * Gets the default set of paths where jlink should look up for system Java * modules. 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 57915b91d8bc9..98d991e38f375 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -306,24 +306,16 @@ public JPackageCommand setFakeRuntime() { TKit.trace(String.format("Init fake runtime in [%s] directory", fakeRuntimeDir)); - Files.createDirectories(fakeRuntimeDir); - - if (TKit.isLinux()) { - // Need to make the code in rpm spec happy as it assumes there is - // always something in application image. - fakeRuntimeDir.resolve("bin").toFile().mkdir(); - } - if (TKit.isOSX()) { // Make MacAppImageBuilder happy createBulkFile.accept(fakeRuntimeDir.resolve(Path.of( "lib/jli/libjli.dylib"))); } - // Mak sure fake runtime takes some disk space. + // Make sure fake runtime takes some disk space. // Package bundles with 0KB size are unexpected and considered // an error by PackageTest. - createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk"))); + createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("lib", "bulk"))); cmd.setArgumentValue("--runtime-image", fakeRuntimeDir); }); @@ -1227,6 +1219,24 @@ public static enum StandardAssert { MacHelper.verifyUnsignedBundleSignature(cmd); } }), + MAC_RUNTIME_PLIST_JDK_KEY(cmd -> { + if (TKit.isOSX()) { + var appLayout = cmd.appLayout(); + var plistPath = appLayout.runtimeDirectory().resolve("Contents/Info.plist"); + var keyName = "JavaVM"; + var keyValue = MacHelper.readPList(plistPath).findDictValue(keyName); + if (cmd.isRuntime() || Files.isDirectory(appLayout.runtimeHomeDirectory().resolve("bin"))) { + // There are native launchers in the runtime + TKit.assertTrue(keyValue.isPresent(), String.format( + "Check the runtime plist file [%s] contains '%s' key", + plistPath, keyName)); + } else { + TKit.assertTrue(keyValue.isEmpty(), String.format( + "Check the runtime plist file [%s] contains NO '%s' key", + plistPath, keyName)); + } + } + }), PREDEFINED_APP_IMAGE_COPY(cmd -> { Optional.ofNullable(cmd.getArgumentValue("--app-image")).filter(_ -> { return !TKit.isOSX() || !MacHelper.signPredefinedAppImage(cmd); diff --git a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java index b690b69269cd0..dd94330d03973 100644 --- a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java +++ b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java @@ -25,6 +25,7 @@ import static java.util.Map.entry; import jdk.jpackage.internal.util.Slot; import static jdk.jpackage.internal.util.PListWriter.writeDict; +import static jdk.jpackage.internal.util.PListWriter.writeKey; import static jdk.jpackage.internal.util.PListWriter.writePList; import static jdk.jpackage.internal.util.PListWriter.writeString; import static jdk.jpackage.internal.util.XmlUtils.createXml; @@ -431,6 +432,10 @@ void writeRuntimePlist(JPackageCommand cmd, XMLStreamWriter xml) throws XMLStrea writeString(xml, "CFBundleShortVersionString", value("CF_BUNDLE_SHORT_VERSION_STRING", cmd.version())); writeString(xml, "CFBundleVersion", value("CF_BUNDLE_VERSION", cmd.version())); writeString(xml, "CustomInfoPListFA", "DEPLOY_FILE_ASSOCIATIONS"); + writeKey(xml, "JavaVM"); + writeDict(xml, toXmlConsumer(() -> { + writeString(xml, "JVMVersion", value("CF_BUNDLE_VERSION", cmd.version())); + })); })); })); } diff --git a/test/jdk/tools/jpackage/share/AppImagePackageTest.java b/test/jdk/tools/jpackage/share/AppImagePackageTest.java index 94eb086e4c61d..c3faa1ffb6502 100644 --- a/test/jdk/tools/jpackage/share/AppImagePackageTest.java +++ b/test/jdk/tools/jpackage/share/AppImagePackageTest.java @@ -120,7 +120,8 @@ public static void testEmpty(boolean withIcon) throws IOException { StandardAssert.MAIN_JAR_FILE, StandardAssert.MAIN_LAUNCHER_FILES, StandardAssert.MAC_BUNDLE_STRUCTURE, - StandardAssert.RUNTIME_DIRECTORY); + StandardAssert.RUNTIME_DIRECTORY, + StandardAssert.MAC_RUNTIME_PLIST_JDK_KEY); }) .run(Action.CREATE_AND_UNPACK); } diff --git a/test/jdk/tools/jpackage/share/CookedRuntimeTest.java b/test/jdk/tools/jpackage/share/CookedRuntimeTest.java index 28fba111c8faa..e3304e01f84da 100644 --- a/test/jdk/tools/jpackage/share/CookedRuntimeTest.java +++ b/test/jdk/tools/jpackage/share/CookedRuntimeTest.java @@ -51,10 +51,11 @@ public final class CookedRuntimeTest { public CookedRuntimeTest(String javaAppDesc, String jlinkOutputSubdir, - String runtimeSubdir) { + String runtimeSubdir, boolean withNativeCommands) { this.javaAppDesc = javaAppDesc; this.jlinkOutputSubdir = Path.of(jlinkOutputSubdir); this.runtimeSubdir = Path.of(runtimeSubdir); + this.withNativeCommands = withNativeCommands; } @Test @@ -90,6 +91,10 @@ public void test() throws IOException { "--no-header-files", "--no-man-pages"); + if (!withNativeCommands) { + jlink.addArgument("--strip-native-commands"); + } + if (moduleName != null) { jlink.addArguments("--add-modules", moduleName, "--module-path", Path.of(cmd.getArgumentValue("--module-path")).resolve( @@ -127,7 +132,14 @@ public static Collection data() { List data = new ArrayList<>(); for (var javaAppDesc : javaAppDescs) { for (var pathCfg : paths) { - data.add(new Object[] { javaAppDesc, pathCfg[0], pathCfg[1] }); + if (TKit.isOSX()) { + // On OSX platform we need to test both runtime root and runtime home + // directories with and without "bin" folder. + data.add(new Object[] { javaAppDesc, pathCfg[0], pathCfg[1], true }); + data.add(new Object[] { javaAppDesc, pathCfg[0], pathCfg[1], false }); + } else { + data.add(new Object[] { javaAppDesc, pathCfg[0], pathCfg[1], true }); + } } } @@ -137,4 +149,5 @@ public static Collection data() { private final String javaAppDesc; private final Path jlinkOutputSubdir; private final Path runtimeSubdir; + private final boolean withNativeCommands; } diff --git a/test/jdk/tools/jpackage/share/PostImageScriptTest.java b/test/jdk/tools/jpackage/share/PostImageScriptTest.java index 5e9127d8fa8e3..655658f036d63 100644 --- a/test/jdk/tools/jpackage/share/PostImageScriptTest.java +++ b/test/jdk/tools/jpackage/share/PostImageScriptTest.java @@ -140,7 +140,7 @@ PackageTest initTest(PackageTest test) { runtimeDir = Path.of(""); } - final Path runtimeBinDir = runtimeDir.resolve("bin"); + final Path runtimeLibDir = runtimeDir.resolve("lib"); if (TKit.isWindows()) { final List script = new ArrayList<>(); @@ -148,16 +148,16 @@ PackageTest initTest(PackageTest test) { script.addAll(WinGlobals.JS_FS.expr()); script.addAll(List.of( "WScript.Echo('PWD: ' + fs.GetFolder(shell.CurrentDirectory).Path)", - String.format("WScript.Echo('Probe directory: %s')", runtimeBinDir), - String.format("fs.GetFolder('%s')", runtimeBinDir.toString().replace('\\', '/')) + String.format("WScript.Echo('Probe directory: %s')", runtimeLibDir), + String.format("fs.GetFolder('%s')", runtimeLibDir.toString().replace('\\', '/')) )); JPackageUserScript.POST_IMAGE.create(cmd, script); } else { JPackageUserScript.POST_IMAGE.create(cmd, List.of( "set -e", "printf 'PWD: %s\\n' \"$PWD\"", - String.format("printf 'Probe directory: %%s\\n' '%s'", runtimeBinDir), - String.format("[ -d '%s' ]", runtimeBinDir) + String.format("printf 'Probe directory: %%s\\n' '%s'", runtimeLibDir), + String.format("[ -d '%s' ]", runtimeLibDir) )); } }); From 223cc6451860f10fe8095705da07aaf7e882188f Mon Sep 17 00:00:00 2001 From: Matthew Donovan Date: Wed, 19 Nov 2025 19:14:33 +0000 Subject: [PATCH 30/44] 8343316: Review and update tests using explicit provider names Reviewed-by: rhalade --- .../KeyAgreement/DHGenSharedSecret.java | 11 ++++++---- .../KeyAgreement/DHKeyAgreement2.java | 21 ++++++++++--------- .../KeyAgreement/DHKeyAgreement3.java | 17 ++++++++------- .../Provider/ProviderVersionCheck.java | 7 ++++++- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/test/jdk/com/sun/crypto/provider/KeyAgreement/DHGenSharedSecret.java b/test/jdk/com/sun/crypto/provider/KeyAgreement/DHGenSharedSecret.java index 9fe96d967dcbb..a89efa7bddd81 100644 --- a/test/jdk/com/sun/crypto/provider/KeyAgreement/DHGenSharedSecret.java +++ b/test/jdk/com/sun/crypto/provider/KeyAgreement/DHGenSharedSecret.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -39,6 +39,9 @@ public class DHGenSharedSecret { + private static final String PROVIDER_NAME = + System.getProperty("test.provider.name", "SunJCE"); + public static void main(String[] args) throws Exception { DHGenSharedSecret test = new DHGenSharedSecret(); test.run(); @@ -57,7 +60,7 @@ public void run() throws Exception { // generate keyPairs using parameters KeyPairGenerator keyGen = - KeyPairGenerator.getInstance("DH", "SunJCE"); + KeyPairGenerator.getInstance("DH", PROVIDER_NAME); keyGen.initialize(spec); // Alice generates her key pairs @@ -77,11 +80,11 @@ public void run() throws Exception { // bob uses it to generate Secret X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(alicePubKeyEnc); - KeyFactory bobKeyFac = KeyFactory.getInstance("DH", "SunJCE"); + KeyFactory bobKeyFac = KeyFactory.getInstance("DH", PROVIDER_NAME); PublicKey alicePubKey = bobKeyFac.generatePublic(x509Spec); - KeyAgreement bobAlice = KeyAgreement.getInstance("DH", "SunJCE"); + KeyAgreement bobAlice = KeyAgreement.getInstance("DH", PROVIDER_NAME); start = System.currentTimeMillis(); bobAlice.init(keyB.getPrivate()); bobAlice.doPhase(alicePubKey, true); diff --git a/test/jdk/com/sun/crypto/provider/KeyAgreement/DHKeyAgreement2.java b/test/jdk/com/sun/crypto/provider/KeyAgreement/DHKeyAgreement2.java index da583c9dc2915..c972c8a696b58 100644 --- a/test/jdk/com/sun/crypto/provider/KeyAgreement/DHKeyAgreement2.java +++ b/test/jdk/com/sun/crypto/provider/KeyAgreement/DHKeyAgreement2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -53,7 +53,8 @@ public class DHKeyAgreement2 { - private static final String SUNJCE = "SunJCE"; + private static final String PROVIDER_NAME = System.getProperty( + "test.provider.name", "SunJCE"); // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX_FORMATTER = HexFormat.ofDelimiter(":").withUpperCase(); @@ -90,7 +91,7 @@ private void run(String mode) throws Exception { // Some central authority creates new DH parameters System.err.println("Creating Diffie-Hellman parameters ..."); AlgorithmParameterGenerator paramGen - = AlgorithmParameterGenerator.getInstance("DH", SUNJCE); + = AlgorithmParameterGenerator.getInstance("DH", PROVIDER_NAME); paramGen.init(primeSize); AlgorithmParameters params = paramGen.generateParameters(); dhParameterSpec = (DHParameterSpec)params.getParameterSpec @@ -108,7 +109,7 @@ private void run(String mode) throws Exception { * above */ System.err.println("ALICE: Generate DH keypair ..."); - KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH", SUNJCE); + KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH", PROVIDER_NAME); aliceKpairGen.initialize(dhParameterSpec); KeyPair aliceKpair = aliceKpairGen.generateKeyPair(); System.out.println("Alice DH public key:\n" + @@ -117,14 +118,14 @@ private void run(String mode) throws Exception { aliceKpair.getPrivate().toString()); DHParameterSpec dhParamSpec = ((DHPublicKey)aliceKpair.getPublic()).getParams(); - AlgorithmParameters algParams = AlgorithmParameters.getInstance("DH", SUNJCE); + AlgorithmParameters algParams = AlgorithmParameters.getInstance("DH", PROVIDER_NAME); algParams.init(dhParamSpec); System.out.println("Alice DH parameters:\n" + algParams.toString()); // Alice executes Phase1 of her version of the DH protocol System.err.println("ALICE: Execute PHASE1 ..."); - KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH", SUNJCE); + KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH", PROVIDER_NAME); aliceKeyAgree.init(aliceKpair.getPrivate()); // Alice encodes her public key, and sends it over to Bob. @@ -135,7 +136,7 @@ private void run(String mode) throws Exception { * in encoded format. * He instantiates a DH public key from the encoded key material. */ - KeyFactory bobKeyFac = KeyFactory.getInstance("DH", SUNJCE); + KeyFactory bobKeyFac = KeyFactory.getInstance("DH", PROVIDER_NAME); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec (alicePubKeyEnc); PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec); @@ -149,7 +150,7 @@ private void run(String mode) throws Exception { // Bob creates his own DH key pair System.err.println("BOB: Generate DH keypair ..."); - KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH", SUNJCE); + KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH", PROVIDER_NAME); bobKpairGen.initialize(dhParamSpec); KeyPair bobKpair = bobKpairGen.generateKeyPair(); System.out.println("Bob DH public key:\n" + @@ -159,7 +160,7 @@ private void run(String mode) throws Exception { // Bob executes Phase1 of his version of the DH protocol System.err.println("BOB: Execute PHASE1 ..."); - KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH", SUNJCE); + KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH", PROVIDER_NAME); bobKeyAgree.init(bobKpair.getPrivate()); // Bob encodes his public key, and sends it over to Alice. @@ -171,7 +172,7 @@ private void run(String mode) throws Exception { * Before she can do so, she has to instanticate a DH public key * from Bob's encoded key material. */ - KeyFactory aliceKeyFac = KeyFactory.getInstance("DH", SUNJCE); + KeyFactory aliceKeyFac = KeyFactory.getInstance("DH", PROVIDER_NAME); x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc); PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec); System.err.println("ALICE: Execute PHASE2 ..."); diff --git a/test/jdk/com/sun/crypto/provider/KeyAgreement/DHKeyAgreement3.java b/test/jdk/com/sun/crypto/provider/KeyAgreement/DHKeyAgreement3.java index d4f70ea256364..5315d62aee89f 100644 --- a/test/jdk/com/sun/crypto/provider/KeyAgreement/DHKeyAgreement3.java +++ b/test/jdk/com/sun/crypto/provider/KeyAgreement/DHKeyAgreement3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -50,6 +50,9 @@ public class DHKeyAgreement3 { + private static final String PROVIDER_NAME = + System.getProperty("test.provider.name", "SunJCE"); + // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX_FORMATTER = HexFormat.ofDelimiter(":").withUpperCase(); @@ -70,36 +73,36 @@ private void run() throws Exception { // Alice creates her own DH key pair System.err.println("ALICE: Generate DH keypair ..."); - KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH", "SunJCE"); + KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH", PROVIDER_NAME); aliceKpairGen.initialize(dhParamSpec); KeyPair aliceKpair = aliceKpairGen.generateKeyPair(); // Bob creates his own DH key pair System.err.println("BOB: Generate DH keypair ..."); - KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH", "SunJCE"); + KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH", PROVIDER_NAME); bobKpairGen.initialize(dhParamSpec); KeyPair bobKpair = bobKpairGen.generateKeyPair(); // Carol creates her own DH key pair System.err.println("CAROL: Generate DH keypair ..."); - KeyPairGenerator carolKpairGen = KeyPairGenerator.getInstance("DH", "SunJCE"); + KeyPairGenerator carolKpairGen = KeyPairGenerator.getInstance("DH", PROVIDER_NAME); carolKpairGen.initialize(dhParamSpec); KeyPair carolKpair = carolKpairGen.generateKeyPair(); // Alice initialize System.err.println("ALICE: Initialize ..."); - KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH", "SunJCE"); + KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH", PROVIDER_NAME); aliceKeyAgree.init(aliceKpair.getPrivate()); // Bob initialize System.err.println("BOB: Initialize ..."); - KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH", "SunJCE"); + KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH", PROVIDER_NAME); bobKeyAgree.init(bobKpair.getPrivate()); // Carol initialize System.err.println("CAROL: Initialize ..."); - KeyAgreement carolKeyAgree = KeyAgreement.getInstance("DH", "SunJCE"); + KeyAgreement carolKeyAgree = KeyAgreement.getInstance("DH", PROVIDER_NAME); carolKeyAgree.init(carolKpair.getPrivate()); diff --git a/test/jdk/java/security/Provider/ProviderVersionCheck.java b/test/jdk/java/security/Provider/ProviderVersionCheck.java index 43d1c502b3fe5..2af2be0a7ad89 100644 --- a/test/jdk/java/security/Provider/ProviderVersionCheck.java +++ b/test/jdk/java/security/Provider/ProviderVersionCheck.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, 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 @@ -42,6 +42,11 @@ public static void main(String arg[]) throws Exception{ for (Provider p: Security.getProviders()) { System.out.print(p.getName() + " "); + if (p.getName().equals(System.getProperty("test.provider.name"))) { + // Version numbers of non JDK-providers do not match JDK version number. + continue; + } + String specVersion = System.getProperty("java.specification.version"); if (p.getVersion() != Double.parseDouble(specVersion)) { System.out.println("failed. " + "Version received was " + From 6f1c5733ed4a1d1a1e099681f1f292acf827d9dc Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 19 Nov 2025 20:05:09 +0000 Subject: [PATCH 31/44] 8371923: Update LockFreeStack for Atomic Reviewed-by: iwalulya, dholmes --- src/hotspot/share/utilities/lockFreeStack.hpp | 51 +++++++++++++------ .../gtest/utilities/test_lockFreeStack.cpp | 34 ++++++------- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/hotspot/share/utilities/lockFreeStack.hpp b/src/hotspot/share/utilities/lockFreeStack.hpp index 43bc58fbc445b..3f63482a2681b 100644 --- a/src/hotspot/share/utilities/lockFreeStack.hpp +++ b/src/hotspot/share/utilities/lockFreeStack.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_UTILITIES_LOCKFREESTACK_HPP #define SHARE_UTILITIES_LOCKFREESTACK_HPP +#include "runtime/atomic.hpp" #include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -34,11 +35,14 @@ // a result, there is no allocation involved in adding objects to the stack // or removing them from the stack. // -// To be used in a LockFreeStack of objects of type T, an object of -// type T must have a list entry member of type T* volatile, with an -// non-member accessor function returning a pointer to that member. A -// LockFreeStack is associated with the class of its elements and an -// entry member from that class. +// To be used in a LockFreeStack of objects of type T, an object of type T +// must have a list entry member. A list entry member is a data member whose +// type is either (1) Atomic, or (2) T* volatile. There must be a +// non-member or static member function returning a pointer to that member, +// which is used to provide access to it by a LockFreeStack. A LockFreeStack +// is associated with the class of its elements and an entry member from that +// class by being specialized on the element class and a pointer to the +// function for accessing that entry member. // // An object can be in multiple stacks at the same time, so long as // each stack uses a different entry member. That is, the class of the @@ -52,12 +56,12 @@ // // \tparam T is the class of the elements in the stack. // -// \tparam next_ptr is a function pointer. Applying this function to +// \tparam next_accessor is a function pointer. Applying this function to // an object of type T must return a pointer to the list entry member // of the object associated with the LockFreeStack type. -template +template class LockFreeStack { - T* volatile _top; + Atomic _top; void prepend_impl(T* first, T* last) { T* cur = top(); @@ -65,12 +69,21 @@ class LockFreeStack { do { old = cur; set_next(*last, cur); - cur = AtomicAccess::cmpxchg(&_top, cur, first); + cur = _top.compare_exchange(cur, first); } while (old != cur); } NONCOPYABLE(LockFreeStack); + template + static constexpr void use_atomic_access_impl(NextAccessor) { + static_assert(DependentAlwaysFalse, "Invalid next accessor"); + } + static constexpr bool use_atomic_access_impl(T* volatile* (*)(T&)) { return true; } + static constexpr bool use_atomic_access_impl(Atomic* (*)(T&)) { return false; } + + static constexpr bool use_atomic_access = use_atomic_access_impl(next_accessor); + public: LockFreeStack() : _top(nullptr) {} ~LockFreeStack() { assert(empty(), "stack not empty"); } @@ -89,7 +102,7 @@ class LockFreeStack { new_top = next(*result); } // CAS even on empty pop, for consistent membar behavior. - result = AtomicAccess::cmpxchg(&_top, result, new_top); + result = _top.compare_exchange(result, new_top); } while (result != old); if (result != nullptr) { set_next(*result, nullptr); @@ -101,7 +114,7 @@ class LockFreeStack { // list of elements. Acts as a full memory barrier. // postcondition: empty() T* pop_all() { - return AtomicAccess::xchg(&_top, (T*)nullptr); + return _top.exchange(nullptr); } // Atomically adds value to the top of this stack. Acts as a full @@ -143,9 +156,9 @@ class LockFreeStack { // Return true if the stack is empty. bool empty() const { return top() == nullptr; } - // Return the most recently pushed element, or nullptr if the stack is empty. + // Return the most recently pushed element, or null if the stack is empty. // The returned element is not removed from the stack. - T* top() const { return AtomicAccess::load(&_top); } + T* top() const { return _top.load_relaxed(); } // Return the number of objects in the stack. There must be no concurrent // pops while the length is being determined. @@ -160,7 +173,11 @@ class LockFreeStack { // Return the entry following value in the list used by the // specialized LockFreeStack class. static T* next(const T& value) { - return AtomicAccess::load(next_ptr(const_cast(value))); + if constexpr (use_atomic_access) { + return AtomicAccess::load(next_accessor(const_cast(value))); + } else { + return next_accessor(const_cast(value))->load_relaxed(); + } } // Set the entry following value to new_next in the list used by the @@ -168,7 +185,11 @@ class LockFreeStack { // if value is in an instance of this specialization of LockFreeStack, // there must be no concurrent push or pop operations on that stack. static void set_next(T& value, T* new_next) { - AtomicAccess::store(next_ptr(value), new_next); + if constexpr (use_atomic_access) { + AtomicAccess::store(next_accessor(value), new_next); + } else { + next_accessor(value)->store_relaxed(new_next); + } } }; diff --git a/test/hotspot/gtest/utilities/test_lockFreeStack.cpp b/test/hotspot/gtest/utilities/test_lockFreeStack.cpp index 3a9d24ad61ec6..fac17e8701637 100644 --- a/test/hotspot/gtest/utilities/test_lockFreeStack.cpp +++ b/test/hotspot/gtest/utilities/test_lockFreeStack.cpp @@ -22,7 +22,7 @@ */ #include "memory/allocation.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/lockFreeStack.hpp" #include "threadHelper.inline.hpp" @@ -33,12 +33,12 @@ class LockFreeStackTestElement { typedef LockFreeStackTestElement Element; - Element* volatile _entry; - Element* volatile _entry1; + Atomic _entry; + Atomic _entry1; size_t _id; - static Element* volatile* entry_ptr(Element& e) { return &e._entry; } - static Element* volatile* entry1_ptr(Element& e) { return &e._entry1; } + static Atomic* entry_ptr(Element& e) { return &e._entry; } + static Atomic* entry1_ptr(Element& e) { return &e._entry1; } public: LockFreeStackTestElement(size_t id = 0) : _entry(), _entry1(), _id(id) {} @@ -202,17 +202,17 @@ class LockFreeStackTestThread : public JavaTestThread { uint _id; TestStack* _from; TestStack* _to; - volatile size_t* _processed; + Atomic* _processed; size_t _process_limit; size_t _local_processed; - volatile bool _ready; + Atomic _ready; public: LockFreeStackTestThread(Semaphore* post, uint id, TestStack* from, TestStack* to, - volatile size_t* processed, + Atomic* processed, size_t process_limit) : JavaTestThread(post), _id(id), @@ -225,21 +225,21 @@ class LockFreeStackTestThread : public JavaTestThread { {} virtual void main_run() { - AtomicAccess::release_store_fence(&_ready, true); + _ready.release_store_fence(true); while (true) { Element* e = _from->pop(); if (e != nullptr) { _to->push(*e); - AtomicAccess::inc(_processed); + _processed->fetch_then_add(1u); ++_local_processed; - } else if (AtomicAccess::load_acquire(_processed) == _process_limit) { + } else if (_processed->load_acquire() == _process_limit) { tty->print_cr("thread %u processed %zu", _id, _local_processed); return; } } } - bool ready() const { return AtomicAccess::load_acquire(&_ready); } + bool ready() const { return _ready.load_acquire(); } }; TEST_VM(LockFreeStackTest, stress) { @@ -248,8 +248,8 @@ TEST_VM(LockFreeStackTest, stress) { TestStack start_stack; TestStack middle_stack; TestStack final_stack; - volatile size_t stage1_processed = 0; - volatile size_t stage2_processed = 0; + Atomic stage1_processed{0}; + Atomic stage2_processed{0}; const size_t nelements = 10000; Element* elements = NEW_C_HEAP_ARRAY(Element, nelements, mtOther); @@ -272,7 +272,7 @@ TEST_VM(LockFreeStackTest, stress) { for (uint i = 0; i < ARRAY_SIZE(threads); ++i) { TestStack* from = &start_stack; TestStack* to = &middle_stack; - volatile size_t* processed = &stage1_processed; + Atomic* processed = &stage1_processed; if (i >= stage1_threads) { from = &middle_stack; to = &final_stack; @@ -293,8 +293,8 @@ TEST_VM(LockFreeStackTest, stress) { } // Verify expected state. - ASSERT_EQ(nelements, stage1_processed); - ASSERT_EQ(nelements, stage2_processed); + ASSERT_EQ(nelements, stage1_processed.load_relaxed()); + ASSERT_EQ(nelements, stage2_processed.load_relaxed()); ASSERT_EQ(0u, initial_stack.length()); ASSERT_EQ(0u, start_stack.length()); ASSERT_EQ(0u, middle_stack.length()); From f5bc6ee90d73da00cab5cad283b9517c692bc895 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 19 Nov 2025 20:56:21 +0000 Subject: [PATCH 32/44] 8369187: Add wrapper for that forbids use of global allocation and deallocation functions Reviewed-by: stefank, erikj, jrose --- make/hotspot/lib/CompileGtest.gmk | 1 + src/hotspot/share/code/relocInfo.cpp | 3 +- src/hotspot/share/code/relocInfo.hpp | 3 +- src/hotspot/share/cppstdlib/new.hpp | 154 ++++++++++++++++++ src/hotspot/share/gc/shared/bufferNode.cpp | 3 +- .../share/gc/shared/partialArrayState.cpp | 3 +- .../gc/z/zDeferredConstructed.inline.hpp | 3 +- src/hotspot/share/memory/allocation.hpp | 3 +- src/hotspot/share/memory/arena.cpp | 1 + src/hotspot/share/memory/arena.hpp | 2 - src/hotspot/share/utilities/debug.cpp | 2 +- .../share/utilities/deferredStatic.hpp | 3 +- src/hotspot/share/utilities/elfFile.cpp | 2 +- .../share/utilities/globalDefinitions.hpp | 21 +++ .../gtest/utilities/test_lockFreeStack.cpp | 3 +- 15 files changed, 187 insertions(+), 20 deletions(-) create mode 100644 src/hotspot/share/cppstdlib/new.hpp diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index d615e254f5a07..6091299213433 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -95,6 +95,7 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBJVM, \ EXTRA_OBJECT_FILES := $(BUILD_LIBJVM_ALL_OBJS), \ DEFAULT_CFLAGS := false, \ CFLAGS := $(JVM_CFLAGS) \ + -DHOTSPOT_GTEST \ -I$(GTEST_FRAMEWORK_SRC)/googletest/include \ -I$(GTEST_FRAMEWORK_SRC)/googlemock/include \ $(addprefix -I, $(GTEST_TEST_SRC)), \ diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 286d407c94b2c..2a6335e21182b 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -26,6 +26,7 @@ #include "code/compiledIC.hpp" #include "code/nmethod.hpp" #include "code/relocInfo.hpp" +#include "cppstdlib/new.hpp" #include "cppstdlib/type_traits.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -37,8 +38,6 @@ #include "utilities/checkedCast.hpp" #include "utilities/copy.hpp" -#include - const RelocationHolder RelocationHolder::none; // its type is relocInfo::none diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp index a6a08815d1055..6f1778ef479c4 100644 --- a/src/hotspot/share/code/relocInfo.hpp +++ b/src/hotspot/share/code/relocInfo.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_CODE_RELOCINFO_HPP #define SHARE_CODE_RELOCINFO_HPP +#include "cppstdlib/new.hpp" #include "memory/allocation.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/osInfo.hpp" @@ -32,8 +33,6 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" -#include - class CodeBlob; class Metadata; class NativeMovConstReg; diff --git a/src/hotspot/share/cppstdlib/new.hpp b/src/hotspot/share/cppstdlib/new.hpp new file mode 100644 index 0000000000000..3536ac13288f1 --- /dev/null +++ b/src/hotspot/share/cppstdlib/new.hpp @@ -0,0 +1,154 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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_CPPSTDLIB_NEW_HPP +#define SHARE_CPPSTDLIB_NEW_HPP + +#include "utilities/compilerWarnings.hpp" + +// HotSpot usage: +// Only the following may be used: +// * std::nothrow_t, std::nothrow +// * std::align_val_t +// * The non-allocating forms of `operator new` and `operator new[]` are +// implicitly used by the corresponding `new` and `new[]` expressions. +// - operator new(size_t, void*) noexcept +// - operator new[](size_t, void*) noexcept +// Note that the non-allocating forms of `operator delete` and `operator +// delete[]` are not used, since they are only invoked by a placement new +// expression that fails by throwing an exception. But they might still +// end up being referenced in such a situation. + +BEGIN_ALLOW_FORBIDDEN_FUNCTIONS +#include "utilities/vmassert_uninstall.hpp" + +#include + +#include "utilities/vmassert_reinstall.hpp" // don't reorder +END_ALLOW_FORBIDDEN_FUNCTIONS + +// Deprecation declarations to forbid use of the default global allocator. +// See C++17 21.6.1 Header synopsis. + +namespace std { + +#if 0 +// We could deprecate exception types, for completeness, but don't bother. We +// already have exceptions disabled, and run into compiler bugs when we try. +// +// gcc -Wattributes => type attributes ignored after type is already defined +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122167 +// +// clang -Wignored-attributes => attribute declaration must precede definition +// The clang warning is https://github.com/llvm/llvm-project/issues/135481, +// which should be fixed in clang 21. +class [[deprecated]] bad_alloc; +class [[deprecated]] bad_array_new_length; +#endif // #if 0 + +// Forbid new_handler manipulation by HotSpot code, leaving it untouched for +// use by application code. +[[deprecated]] new_handler get_new_handler() noexcept; +[[deprecated]] new_handler set_new_handler(new_handler) noexcept; + +// Prefer HotSpot mechanisms for padding. +// +// The syntax for redeclaring these for deprecation is tricky, and not +// supported by some versions of some compilers. Dispatch on compiler and +// version to decide whether to redeclare deprecated. + +#if defined(__clang__) +#if __clang_major__ >= 19 +// clang18 and earlier may accept the declaration but go wrong with uses. +// Different warnings and link-time failures are both possible. +#define CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES 1 +#endif // restrict clang version + +#elif defined(__GNUC__) +#if (__GNUC__ > 13) || (__GNUC__ == 13 && __GNUC_MINOR__ >= 2) +// g++11.5 accepts the declaration and reports deprecation for uses, but also +// has link-time failure for uses. Haven't tested intermediate versions. +#define CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES 1 +#endif // restrict gcc version + +#elif defined(_MSVC) +// VS2022-17.13.2 => error C2370: '...': redefinition; different storage class + +#endif // Compiler dispatch + +// Redeclare deprecated if such is supported. +#ifdef CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES +[[deprecated]] extern const size_t hardware_destructive_interference_size; +[[deprecated]] extern const size_t hardware_constructive_interference_size; +#undef CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES +#endif // CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES + +} // namespace std + +// Forbid using the global allocator by HotSpot code. +// This doesn't provide complete coverage. Some global allocation and +// deallocation functions are implicitly declared in all translation units, +// without needing to include ; see C++17 6.7.4. So this doesn't remove +// the need for the link-time verification that these functions aren't used. +// +// But don't poison them when compiling gtests. The gtest framework, the +// HotSpot wrapper around it (gtestMain.cpp), and even some tests, all have +// new/new[] and delete/delete[] expressions that use the default global +// allocator. We also don't apply the link-time check for gtests, for the +// same reason. +#ifndef HOTSPOT_GTEST + +[[deprecated]] void* operator new(std::size_t); +[[deprecated]] void* operator new(std::size_t, std::align_val_t); +[[deprecated]] void* operator new(std::size_t, const std::nothrow_t&) noexcept; +[[deprecated]] void* operator new(std::size_t, std::align_val_t, + const std::nothrow_t&) noexcept; + +[[deprecated]] void operator delete(void*) noexcept; +[[deprecated]] void operator delete(void*, std::size_t) noexcept; +[[deprecated]] void operator delete(void*, std::align_val_t) noexcept; +[[deprecated]] void operator delete(void*, std::size_t, std::align_val_t) noexcept; +[[deprecated]] void operator delete(void*, const std::nothrow_t&) noexcept; +[[deprecated]] void operator delete(void*, std::align_val_t, + const std::nothrow_t&) noexcept; + +[[deprecated]] void* operator new[](std::size_t); +[[deprecated]] void* operator new[](std::size_t, std::align_val_t); +[[deprecated]] void* operator new[](std::size_t, const std::nothrow_t&) noexcept; +[[deprecated]] void* operator new[](std::size_t, std::align_val_t, + const std::nothrow_t&) noexcept; + +[[deprecated]] void operator delete[](void*) noexcept; +[[deprecated]] void operator delete[](void*, std::size_t) noexcept; +[[deprecated]] void operator delete[](void*, std::align_val_t) noexcept; +[[deprecated]] void operator delete[](void*, std::size_t, std::align_val_t) noexcept; +[[deprecated]] void operator delete[](void*, const std::nothrow_t&) noexcept; +[[deprecated]] void operator delete[](void*, std::align_val_t, + const std::nothrow_t&) noexcept; + +#endif // HOTSPOT_GTEST + +// Allow (don't poison) the non-allocating forms from [new.delete.placement]. + +#endif // SHARE_CPPSTDLIB_NEW_HPP diff --git a/src/hotspot/share/gc/shared/bufferNode.cpp b/src/hotspot/share/gc/shared/bufferNode.cpp index b064f9c7efedb..90e50f52e84ad 100644 --- a/src/hotspot/share/gc/shared/bufferNode.cpp +++ b/src/hotspot/share/gc/shared/bufferNode.cpp @@ -22,12 +22,11 @@ * */ +#include "cppstdlib/new.hpp" #include "gc/shared/bufferNode.hpp" #include "memory/allocation.inline.hpp" #include "utilities/debug.hpp" -#include - BufferNode::AllocatorConfig::AllocatorConfig(size_t size) : _buffer_capacity(size) { diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index f913f3db4ba76..39c1fe4fc783e 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -22,6 +22,7 @@ * */ +#include "cppstdlib/new.hpp" #include "gc/shared/partialArrayState.hpp" #include "memory/allocation.inline.hpp" #include "memory/arena.hpp" @@ -33,8 +34,6 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" -#include - PartialArrayState::PartialArrayState(oop src, oop dst, size_t index, size_t length, size_t initial_refcount) diff --git a/src/hotspot/share/gc/z/zDeferredConstructed.inline.hpp b/src/hotspot/share/gc/z/zDeferredConstructed.inline.hpp index d6d35ecddcd82..f686bc78d1540 100644 --- a/src/hotspot/share/gc/z/zDeferredConstructed.inline.hpp +++ b/src/hotspot/share/gc/z/zDeferredConstructed.inline.hpp @@ -27,10 +27,9 @@ #include "gc/z/zDeferredConstructed.hpp" +#include "cppstdlib/new.hpp" #include "cppstdlib/type_traits.hpp" -#include - template inline ZDeferredConstructed::ZDeferredConstructed() DEBUG_ONLY(: _initialized(false)) { diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 35180fdba5e01..963ca04aadf96 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -25,14 +25,13 @@ #ifndef SHARE_MEMORY_ALLOCATION_HPP #define SHARE_MEMORY_ALLOCATION_HPP +#include "cppstdlib/new.hpp" #include "memory/allStatic.hpp" #include "nmt/memTag.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" -#include - class outputStream; class Thread; class JavaThread; diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index b9968083e0e85..2de3f837c001a 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -24,6 +24,7 @@ */ #include "compiler/compilationMemoryStatistic.hpp" +#include "cppstdlib/new.hpp" #include "memory/allocation.inline.hpp" #include "memory/arena.hpp" #include "memory/resourceArea.hpp" diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index b4a0546babf83..a8450b5543a68 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -31,8 +31,6 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" -#include - // The byte alignment to be used by Arena::Amalloc. #define ARENA_AMALLOC_ALIGNMENT BytesPerLong #define ARENA_ALIGN(x) (align_up((x), ARENA_AMALLOC_ALIGNMENT)) diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 89c0a1ebc0810..de39fe32dc1d6 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -29,6 +29,7 @@ #include "code/vtableStubs.hpp" #include "compiler/compileBroker.hpp" #include "compiler/disassembler.hpp" +#include "cppstdlib/new.hpp" #include "gc/shared/collectedHeap.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" @@ -63,7 +64,6 @@ #include "utilities/unsigned5.hpp" #include "utilities/vmError.hpp" -#include #include #include diff --git a/src/hotspot/share/utilities/deferredStatic.hpp b/src/hotspot/share/utilities/deferredStatic.hpp index 56bdb9b8e6bbf..3a32f920fe81b 100644 --- a/src/hotspot/share/utilities/deferredStatic.hpp +++ b/src/hotspot/share/utilities/deferredStatic.hpp @@ -25,11 +25,10 @@ #ifndef SHARE_UTILITIES_DEFERREDSTATIC_HPP #define SHARE_UTILITIES_DEFERREDSTATIC_HPP +#include "cppstdlib/new.hpp" #include "cppstdlib/type_traits.hpp" #include "utilities/globalDefinitions.hpp" -#include - // The purpose of this class is to provide control over the initialization // time for an object of type T with static storage duration. An instance of // this class provides storage for an object, sized and aligned for T. The diff --git a/src/hotspot/share/utilities/elfFile.cpp b/src/hotspot/share/utilities/elfFile.cpp index 9ea19b38276ca..0b7713e9ca9ca 100644 --- a/src/hotspot/share/utilities/elfFile.cpp +++ b/src/hotspot/share/utilities/elfFile.cpp @@ -25,6 +25,7 @@ #if !defined(_WINDOWS) && !defined(__APPLE__) +#include "cppstdlib/new.hpp" #include "jvm_io.h" #include "logging/log.hpp" #include "memory/allocation.inline.hpp" @@ -37,7 +38,6 @@ #include "utilities/ostream.hpp" #include -#include #include #include diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 1910759b43497..3284fd3bd15be 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1386,4 +1386,25 @@ template inline constexpr bool DependentAlwaysFalse = false; // handled. bool IEEE_subnormal_handling_OK(); +//---------------------------------------------------------------------------------------------------- +// Forbid using the global allocator by HotSpot code. +// +// This is a subset of allocator and deallocator functions. These are +// implicitly declared in all translation units, without needing to include +// ; see C++17 6.7.4. This isn't even the full set of those; implicit +// declarations involving std::align_val_t are not covered here, since that +// type is defined in . A translation unit that doesn't include is +// still likely to include this file. See cppstdlib/new.hpp for more details. +#ifndef HOTSPOT_GTEST + +[[deprecated]] void* operator new(std::size_t); +[[deprecated]] void operator delete(void*) noexcept; +[[deprecated]] void operator delete(void*, std::size_t) noexcept; + +[[deprecated]] void* operator new[](std::size_t); +[[deprecated]] void operator delete[](void*) noexcept; +[[deprecated]] void operator delete[](void*, std::size_t) noexcept; + +#endif // HOTSPOT_GTEST + #endif // SHARE_UTILITIES_GLOBALDEFINITIONS_HPP diff --git a/test/hotspot/gtest/utilities/test_lockFreeStack.cpp b/test/hotspot/gtest/utilities/test_lockFreeStack.cpp index fac17e8701637..bdba49b48c099 100644 --- a/test/hotspot/gtest/utilities/test_lockFreeStack.cpp +++ b/test/hotspot/gtest/utilities/test_lockFreeStack.cpp @@ -21,6 +21,7 @@ * questions. */ +#include "cppstdlib/new.hpp" #include "memory/allocation.inline.hpp" #include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" @@ -28,8 +29,6 @@ #include "threadHelper.inline.hpp" #include "unittest.hpp" -#include - class LockFreeStackTestElement { typedef LockFreeStackTestElement Element; From 1535d08f0ee5da42d9db9e196d6a620aabe9feea Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 19 Nov 2025 20:58:23 +0000 Subject: [PATCH 33/44] 8371944: AOT configuration is corrupted when app closes System.out Reviewed-by: kvn, iveresov --- src/hotspot/share/cds/aotMetaspace.cpp | 46 ++++++++--- src/hotspot/share/cds/aotMetaspace.hpp | 2 + src/hotspot/share/cds/dynamicArchive.cpp | 1 + src/hotspot/share/cds/filemap.cpp | 2 + src/hotspot/share/cds/filemap.hpp | 1 + .../cds/appcds/aotCache/CloseSystemOut.java | 82 +++++++++++++++++++ 6 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/CloseSystemOut.java diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 8642b1a6de880..42d41e6ae896c 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -114,6 +114,7 @@ intx AOTMetaspace::_relocation_delta; char* AOTMetaspace::_requested_base_address; Array* AOTMetaspace::_archived_method_handle_intrinsics = nullptr; bool AOTMetaspace::_use_optimized_module_handling = true; +FileMapInfo* AOTMetaspace::_output_mapinfo = nullptr; // The CDS archive is divided into the following regions: // rw - read-write metadata @@ -322,6 +323,24 @@ void AOTMetaspace::initialize_for_static_dump() { AOTMetaspace::unrecoverable_writing_error(); } _symbol_region.init(&_symbol_rs, &_symbol_vs); + if (CDSConfig::is_dumping_preimage_static_archive()) { + // We are in the AOT training run. User code is executed. + // + // On Windows, if the user code closes System.out and we open the AOT config file for output + // only at VM exit, we might get back the same file HANDLE as stdout, and the AOT config + // file may get corrupted by UL logs. By opening early, we ensure that the output + // HANDLE is different than stdout so we can avoid such corruption. + open_output_mapinfo(); + } else { + // No need for the above as we won't execute any user code. + } +} + +void AOTMetaspace::open_output_mapinfo() { + const char* static_archive = CDSConfig::output_archive_path(); + assert(static_archive != nullptr, "sanity"); + _output_mapinfo = new FileMapInfo(static_archive, true); + _output_mapinfo->open_as_output(); } // Called by universe_post_init() @@ -655,15 +674,14 @@ class VM_PopulateDumpSharedSpace : public VM_Operation { public: - VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b) : - VM_Operation(), _mapped_heap_info(), _streamed_heap_info(), _map_info(nullptr), _builder(b) {} + VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b, FileMapInfo* map_info) : + VM_Operation(), _mapped_heap_info(), _streamed_heap_info(), _map_info(map_info), _builder(b) {} 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; } - FileMapInfo* map_info() const { return _map_info; } void doit(); // outline because gdb sucks bool allow_nested_vm_operations() const { return true; } }; // class VM_PopulateDumpSharedSpace @@ -795,12 +813,6 @@ void VM_PopulateDumpSharedSpace::doit() { CppVtables::zero_archived_vtables(); // Write the archive file - if (CDSConfig::is_dumping_final_static_archive()) { - FileMapInfo::free_current_info(); // FIXME: should not free current info - } - const char* static_archive = CDSConfig::output_archive_path(); - assert(static_archive != nullptr, "sanity"); - _map_info = new FileMapInfo(static_archive, true); _map_info->populate_header(AOTMetaspace::core_region_alignment()); _map_info->set_early_serialized_data(early_serialized_data); _map_info->set_serialized_data(serialized_data); @@ -1138,7 +1150,14 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS } #endif - VM_PopulateDumpSharedSpace op(builder); + if (!CDSConfig::is_dumping_preimage_static_archive()) { + if (CDSConfig::is_dumping_final_static_archive()) { + FileMapInfo::free_current_info(); // FIXME: should not free current info + } + open_output_mapinfo(); + } + + VM_PopulateDumpSharedSpace op(builder, _output_mapinfo); VMThread::execute(&op); if (AOTCodeCache::is_on_for_dump() && CDSConfig::is_dumping_final_static_archive()) { @@ -1152,7 +1171,9 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS CDSConfig::disable_dumping_aot_code(); } - bool status = write_static_archive(&builder, op.map_info(), op.mapped_heap_info(), op.streamed_heap_info()); + bool status = write_static_archive(&builder, _output_mapinfo, op.mapped_heap_info(), op.streamed_heap_info()); + assert(!_output_mapinfo->is_open(), "Must be closed already"); + _output_mapinfo = nullptr; if (status && CDSConfig::is_dumping_preimage_static_archive()) { tty->print_cr("%s AOTConfiguration recorded: %s", CDSConfig::has_temp_aot_config_file() ? "Temporary" : "", AOTConfiguration); @@ -1173,11 +1194,10 @@ bool AOTMetaspace::write_static_archive(ArchiveBuilder* builder, // relocate the data so that it can be mapped to AOTMetaspace::requested_base_address() // without runtime relocation. builder->relocate_to_requested(); - - map_info->open_as_output(); if (!map_info->is_open()) { return false; } + map_info->prepare_for_writing(); builder->write_archive(map_info, mapped_heap_info, streamed_heap_info); return true; } diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index bfd9f4bcc7553..1712a7865adbf 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -60,6 +60,7 @@ class AOTMetaspace : AllStatic { static char* _requested_base_address; static bool _use_optimized_module_handling; static Array* _archived_method_handle_intrinsics; + static FileMapInfo* _output_mapinfo; public: enum { @@ -185,6 +186,7 @@ class AOTMetaspace : AllStatic { private: static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN; static void fork_and_dump_final_static_archive(TRAPS); + static void open_output_mapinfo(); static bool write_static_archive(ArchiveBuilder* builder, FileMapInfo* map_info, ArchiveMappedHeapInfo* mapped_heap_info, diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 85e59e23f8cc0..8fae8dabf8cd7 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -353,6 +353,7 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocatio assert(dynamic_info != nullptr, "Sanity"); dynamic_info->open_as_output(); + dynamic_info->prepare_for_writing(); ArchiveBuilder::write_archive(dynamic_info, nullptr, nullptr); address base = _requested_dynamic_archive_bottom; diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 61df0a86b413a..0eeb96bb2692f 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -779,7 +779,9 @@ void FileMapInfo::open_as_output() { } _fd = fd; _file_open = true; +} +void FileMapInfo::prepare_for_writing() { // Seek past the header. We will write the header after all regions are written // and their CRCs computed. size_t header_bytes = header()->header_size(); diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index 2a761843e473b..fbd3c8e16811f 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -365,6 +365,7 @@ class FileMapInfo : public CHeapObj { // File manipulation. bool open_as_input() NOT_CDS_RETURN_(false); void open_as_output(); + void prepare_for_writing(); void write_header(); void write_region(int region, char* base, size_t size, bool read_only, bool allow_exec); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/CloseSystemOut.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/CloseSystemOut.java new file mode 100644 index 0000000000000..1f4111ecfb1dd --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/CloseSystemOut.java @@ -0,0 +1,82 @@ +/* + * 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 + * @summary AOT configuration should not be corrupted even if the app closes System.out in the training run + * @bug 8371944 + * @library /test/jdk/lib/testlibrary /test/lib + * @build CloseSystemOut + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar CloseSystemOutApp + * @run driver CloseSystemOut + */ + +import java.io.PrintWriter; +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class CloseSystemOut { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = "CloseSystemOutApp"; + + public static void main(String[] args) throws Exception { + Tester tester = new Tester(); + tester.run(new String[] {"AOT", "--two-step-training"} ); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] {mainClass}; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) { + if (runMode != RunMode.ASSEMBLY) { + out.shouldContain("Hello Confused World"); + } + } + } +} + +class CloseSystemOutApp { + public static void main(String args[]) { + // Naive code that ends up closing System.out/err when we + // leave the "try" block + try (var err = new PrintWriter(System.err); + var out = new PrintWriter(System.out)) { + out.println("Hello Confused World"); + } + } +} From c8e64e7c33cabcc5c94616808b9c59ab5b7cd14e Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 19 Nov 2025 23:22:40 +0000 Subject: [PATCH 34/44] 8372118: Test tools/jpackage/macosx/DmgContentTest.java failed Reviewed-by: almatvee --- .../macosx/classes/jdk/jpackage/internal/MacDmgPackager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java index 6e13a2ff0c193..4ccc459109f86 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java @@ -30,6 +30,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; import java.text.MessageFormat; import java.util.Base64; @@ -288,7 +289,7 @@ private void buildDMG() throws IOException { // Copy app image, since we did not create DMG with it, but instead we created // empty one. if (copyAppImage) { - FileUtils.copyRecursive(srcFolder, mountedVolume); + FileUtils.copyRecursive(srcFolder, mountedVolume, LinkOption.NOFOLLOW_LINKS); } try { From 2acd8776f26686a93708eb9fc408ff4e2bbe287c Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Thu, 20 Nov 2025 01:29:49 +0000 Subject: [PATCH 35/44] 8371440: jpackage should exit with an error if it finds multiple matching signing certificates Reviewed-by: almatvee --- .../internal/SigningIdentityBuilder.java | 7 ++-- .../resources/MacResources.properties | 2 +- .../tools/jpackage/macosx/MacSignTest.java | 40 +++++++++++-------- 3 files changed, 29 insertions(+), 20 deletions(-) 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 bf8f1519fe157..f90e76bb23d19 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java @@ -156,9 +156,10 @@ private static X509Certificate selectSigningIdentity(List certs return certs.getFirst(); } default -> { - Log.error(I18N.format("error.multiple.certs.found", certificateSelector.signingIdentities().getFirst(), - keychain.map(Keychain::name).orElse(""))); - return certs.getFirst(); + throw I18N.buildConfigException("error.multiple.certs.found", + certificateSelector.signingIdentities().getFirst(), + keychain.map(Keychain::name).orElse("") + ).create(); } } } 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 58c3bbbc02551..afa71d84d5c39 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 @@ -36,7 +36,7 @@ error.must-sign-app-store=Mac App Store apps must be signed, and signing has bee error.must-sign-app-store.advice=Use --mac-sign option with appropriate user-name and keychain error.certificate.expired=Error: Certificate expired {0} error.cert.not.found=No certificate found matching [{0}] using keychain [{1}] -error.multiple.certs.found=WARNING: Multiple certificates found matching [{0}] using keychain [{1}], using first one +error.multiple.certs.found=Multiple certificates matching name [{0}] found in keychain [{1}] error.app-image.mac-sign.required=Error: --mac-sign option is required with predefined application image and with type [app-image] error.tool.failed.with.output=Error: "{0}" failed with following output: error.invalid-runtime-image-missing-file=Runtime image "{0}" is missing "{1}" file diff --git a/test/jdk/tools/jpackage/macosx/MacSignTest.java b/test/jdk/tools/jpackage/macosx/MacSignTest.java index 014fbc845489e..af7cf448bdc17 100644 --- a/test/jdk/tools/jpackage/macosx/MacSignTest.java +++ b/test/jdk/tools/jpackage/macosx/MacSignTest.java @@ -39,6 +39,7 @@ import jdk.jpackage.test.MacHelper; import jdk.jpackage.test.MacSign; import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.CertificateType; import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; @@ -155,20 +156,13 @@ public static void testExpiredCertificate(PackageType type, SignOption... option } @Test - // Case "--mac-signing-key-user-name": jpackage selects first certificate - // found with warning message. Certificate hash is pass to "codesign" in this - // case. - @Parameter({"IMAGE", "0", "GOOD_SIGNING_KEY_USER_NAME"}) - @Parameter({"MAC_DMG", "0", "GOOD_SIGNING_KEY_USER_NAME"}) - @Parameter({"MAC_PKG", "0", "GOOD_SIGNING_KEY_USER_NAME_PKG", "GOOD_SIGNING_KEY_USER_NAME"}) - - // Case "--mac-app-image-sign-identity": sign identity will be pass to - // "codesign" and "codesign" should fail due to multiple certificates with - // same common name found. - @Parameter({"IMAGE", "1", "GOOD_CODESIGN_SIGN_IDENTITY"}) - @Parameter({"MAC_PKG", "1", "GOOD_CODESIGN_SIGN_IDENTITY", "GOOD_PKG_SIGN_IDENTITY"}) - @Parameter({"MAC_PKG", "1", "GOOD_PKG_SIGN_IDENTITY"}) - public static void testMultipleCertificates(PackageType type, int jpackageExitCode, SignOption... options) { + @Parameter({"IMAGE", "GOOD_SIGNING_KEY_USER_NAME"}) + @Parameter({"MAC_DMG", "GOOD_SIGNING_KEY_USER_NAME"}) + @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) { MacSign.withKeychain(keychain -> { final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain) @@ -176,9 +170,19 @@ public static void testMultipleCertificates(PackageType type, int jpackageExitCo .addArguments(Stream.of(options).map(SignOption::args).flatMap(List::stream).toList()) .setPackageType(type); - SignOption.configureOutputValidation(cmd, List.of(options), opt -> { + 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; + } + }; + + SignOption.configureOutputValidation(cmd, Stream.of(options).filter(filter).toList(), opt -> { return JPackageStringBundle.MAIN.cannedFormattedString("error.multiple.certs.found", opt.identityName(), keychain.name()); - }).execute(jpackageExitCode); + }).execute(1); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.DUPLICATE.keychain()); } @@ -244,6 +248,10 @@ String identityName() { return cert.name(); } + CertificateType identityType() { + return cert.type(); + } + List args() { return List.of(option, shortName ? cert.shortName() : cert.name()); } From a3b1affbfb23eeef32749164aae316e5d55fffaa Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Thu, 20 Nov 2025 02:18:44 +0000 Subject: [PATCH 36/44] 8372046: compiler/floatingpoint/TestSubNodeFloatDoubleNegation.java fails IR verification Reviewed-by: mhaessig, epeter --- .../floatingpoint/TestSubNodeFloatDoubleNegation.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/floatingpoint/TestSubNodeFloatDoubleNegation.java b/test/hotspot/jtreg/compiler/floatingpoint/TestSubNodeFloatDoubleNegation.java index 4c7092ec65439..d96e64baa36b2 100644 --- a/test/hotspot/jtreg/compiler/floatingpoint/TestSubNodeFloatDoubleNegation.java +++ b/test/hotspot/jtreg/compiler/floatingpoint/TestSubNodeFloatDoubleNegation.java @@ -38,7 +38,13 @@ public class TestSubNodeFloatDoubleNegation { public static void main(String[] args) { - TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:CompileCommand=inline,jdk.incubator.vector.Float16::*"); + // Disable inlining for java.lang.Float::float16ToFloat and java.lang.Float::floatToFloat16. + // Otherwise, they could be inlined into testHalfFloat on platforms where there is no support + // for fp16, which causes unexpected IR graph. + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", + "-XX:CompileCommand=inline,jdk.incubator.vector.Float16::*", + "-XX:CompileCommand=dontinline,java.lang.Float::float16ToFloat", + "-XX:CompileCommand=dontinline,java.lang.Float::floatToFloat16"); } @Run(test = { "testHalfFloat", "testFloat", "testDouble" }) From 473471c1f1d3cd42a057dfd602d452196c53aa00 Mon Sep 17 00:00:00 2001 From: Henry Jen Date: Thu, 20 Nov 2025 05:30:40 +0000 Subject: [PATCH 37/44] 8369838: Likely invalid assert or function call in jimage.cpp Reviewed-by: dholmes --- src/hotspot/share/classfile/classLoader.cpp | 37 +++++++++---------- .../share/native/libjimage/jimage.cpp | 2 +- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 082c745f4c36a..12fbda899b9d1 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -412,31 +412,30 @@ ClassFileStream* ClassPathImageEntry::open_stream(JavaThread* current, const cha // ClassFileStream* ClassPathImageEntry::open_stream_for_loader(JavaThread* current, const char* name, ClassLoaderData* loader_data) { jlong size; - JImageLocationRef location = (*JImageFindResource)(jimage_non_null(), "", get_jimage_version_string(), name, &size); + JImageLocationRef location = 0; - if (location == 0) { - TempNewSymbol class_name = SymbolTable::new_symbol(name); - TempNewSymbol pkg_name = ClassLoader::package_from_class_name(class_name); + TempNewSymbol class_name = SymbolTable::new_symbol(name); + TempNewSymbol pkg_name = ClassLoader::package_from_class_name(class_name); - if (pkg_name != nullptr) { - if (!Universe::is_module_initialized()) { - location = (*JImageFindResource)(jimage_non_null(), JAVA_BASE_NAME, get_jimage_version_string(), name, &size); - } else { - PackageEntry* package_entry = ClassLoader::get_package_entry(pkg_name, loader_data); - if (package_entry != nullptr) { - ResourceMark rm(current); - // Get the module name - ModuleEntry* module = package_entry->module(); - assert(module != nullptr, "Boot classLoader package missing module"); - assert(module->is_named(), "Boot classLoader package is in unnamed module"); - const char* module_name = module->name()->as_C_string(); - if (module_name != nullptr) { - location = (*JImageFindResource)(jimage_non_null(), module_name, get_jimage_version_string(), name, &size); - } + if (pkg_name != nullptr) { + if (!Universe::is_module_initialized()) { + location = (*JImageFindResource)(jimage_non_null(), JAVA_BASE_NAME, get_jimage_version_string(), name, &size); + } else { + PackageEntry* package_entry = ClassLoader::get_package_entry(pkg_name, loader_data); + if (package_entry != nullptr) { + ResourceMark rm(current); + // Get the module name + ModuleEntry* module = package_entry->module(); + assert(module != nullptr, "Boot classLoader package missing module"); + assert(module->is_named(), "Boot classLoader package is in unnamed module"); + const char* module_name = module->name()->as_C_string(); + if (module_name != nullptr) { + location = (*JImageFindResource)(jimage_non_null(), module_name, get_jimage_version_string(), name, &size); } } } } + if (location != 0) { if (UsePerfData) { ClassLoader::perf_sys_classfile_bytes_read()->inc(size); diff --git a/src/java.base/share/native/libjimage/jimage.cpp b/src/java.base/share/native/libjimage/jimage.cpp index 10e85eb252012..91a86f992e6bd 100644 --- a/src/java.base/share/native/libjimage/jimage.cpp +++ b/src/java.base/share/native/libjimage/jimage.cpp @@ -110,7 +110,7 @@ JIMAGE_FindResource(JImageFile* image, size_t nameLen = strlen(name); size_t index; - // TBD: assert(moduleNameLen > 0 && "module name must be non-empty"); + assert(moduleNameLen > 0 && "module name must be non-empty"); assert(nameLen > 0 && "name must non-empty"); // If the concatenated string is too long for the buffer, return not found From 5d3e73b9e512b55cdf554158b19a4ec642dc1f1a Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 20 Nov 2025 06:14:40 +0000 Subject: [PATCH 38/44] 8371248: Crash in -Xdoclint with invalid @link Reviewed-by: hannesw, vromero --- .../com/sun/tools/javac/api/JavacTrees.java | 4 +++ .../com/sun/tools/javac/comp/Attr.java | 2 +- .../tools/javac/doctree/ReferenceTest.java | 30 +++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) 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 2eca26de83877..f933ef365656d 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 @@ -473,6 +473,10 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { if (memberName == null) return tsym; + if (tsym.type.isPrimitive()) { + return null; + } + final List paramTypes; if (ref.paramTypes == null) paramTypes = null; 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 ad41adcc135ea..c723caf1843e7 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 @@ -377,7 +377,7 @@ private class IdentAttributer extends SimpleTreeVisitor> @Override @DefinedBy(Api.COMPILER_TREE) public Symbol visitMemberSelect(MemberSelectTree node, Env env) { Symbol site = visit(node.getExpression(), env); - if (site.kind == ERR || site.kind == ABSENT_TYP || site.kind == HIDDEN) + if (site == null || site.kind == ERR || site.kind == ABSENT_TYP || site.kind == HIDDEN) return site; Name name = (Name)node.getIdentifier(); if (site.kind == PCK) { diff --git a/test/langtools/tools/javac/doctree/ReferenceTest.java b/test/langtools/tools/javac/doctree/ReferenceTest.java index 46c3d40e73a97..540cb9a6621c1 100644 --- a/test/langtools/tools/javac/doctree/ReferenceTest.java +++ b/test/langtools/tools/javac/doctree/ReferenceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 7021614 8278373 8164094 + * @bug 7021614 8278373 8164094 8371248 * @summary extend com.sun.source API to support parsing javadoc comments * @summary check references in at-see and {at-link} tags * @modules jdk.compiler @@ -43,6 +43,7 @@ import com.sun.source.util.TreePath; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.processing.AbstractProcessor; @@ -83,6 +84,31 @@ * {@link #trees Field} * {@link #getSupportedSourceVersion Method} * {@link #init(ProcessingEnvironment Method} + * {@link double Class} + * {@link double.NAN Bad} + * {@link double#NAN Bad} + * {@link double#double Bad} + * {@link java.base/double Bad} + * + * {@link List Interface} + * {@link List.add Bad} + * {@link List#add Method} + * {@link List#add(Object) Method} + * {@link Map.Entry Interface} + * {@link Map.Entry Interface} + * {@link Map.Entry.getKey Bad} + * {@link Map.Entry#getKey Method} + * {@link Map.Entry#setValue(Object) Method} + * + * {@link java.base/java.util.List Bad} + * {@link java.base/java.util.List.add Bad} + * {@link java.base/java.util.List#add Bad} + * {@link java.base/java.util.List#add(Object) Bad} + * {@link java.base/java.util.Map.Entry Bad} + * {@link java.base/java.util.Map.Entry Bad} + * {@link java.base/java.util.Map.Entry.getKey Bad} + * {@link java.base/java.util.Map.Entry#getKey Bad} + * {@link java.base/java.util.Map.Entry#setValue(Object) Bad} * * @see java.lang Package * @see java.lang.ERROR Bad From 72c45a4d923a294108995e24951bec24dfc70410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Thu, 20 Nov 2025 07:08:46 +0000 Subject: [PATCH 39/44] 8355225: Test gtest/AsyncLogGtest.java failed at droppingMessage_vm: apparent log corruption Reviewed-by: dholmes, syan --- test/hotspot/gtest/logging/test_asynclog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/hotspot/gtest/logging/test_asynclog.cpp b/test/hotspot/gtest/logging/test_asynclog.cpp index fdc3795e9db2a..2634b8dac770b 100644 --- a/test/hotspot/gtest/logging/test_asynclog.cpp +++ b/test/hotspot/gtest/logging/test_asynclog.cpp @@ -253,6 +253,8 @@ TEST_VM_F(AsyncLogTest, droppingMessage) { set_log_config(TestLogFileName, "logging=debug"); test_asynclog_drop_messages(); + AsyncLogWriter::flush(); + fflush(nullptr); bool messages_dropped = file_contains_substring(TestLogFileName, "messages dropped due to async logging"); if (!messages_dropped) { stringStream content; From 852141b9d42ada168a008aea63045deddca29190 Mon Sep 17 00:00:00 2001 From: Sean Coffey Date: Thu, 20 Nov 2025 07:32:06 +0000 Subject: [PATCH 40/44] 8372004: Have SSLLogger implement System.Logger Reviewed-by: dfuchs, weijun --- .../share/classes/sun/security/ssl/Alert.java | 2 +- .../sun/security/ssl/AlpnExtension.java | 18 +- .../security/ssl/CertSignAlgsExtension.java | 8 +- .../sun/security/ssl/CertStatusExtension.java | 32 +- .../ssl/CertificateAuthoritiesExtension.java | 16 +- .../sun/security/ssl/CertificateMessage.java | 32 +- .../sun/security/ssl/CertificateRequest.java | 22 +- .../sun/security/ssl/CertificateStatus.java | 6 +- .../sun/security/ssl/CertificateVerify.java | 26 +- .../sun/security/ssl/ChangeCipherSpec.java | 6 +- .../classes/sun/security/ssl/ClientHello.java | 42 +-- .../sun/security/ssl/CookieExtension.java | 10 +- .../sun/security/ssl/DHClientKeyExchange.java | 4 +- .../sun/security/ssl/DHServerKeyExchange.java | 4 +- .../sun/security/ssl/DTLSInputRecord.java | 76 ++--- .../sun/security/ssl/DTLSOutputRecord.java | 22 +- .../security/ssl/ECDHClientKeyExchange.java | 8 +- .../security/ssl/ECDHServerKeyExchange.java | 4 +- .../security/ssl/ECPointFormatsExtension.java | 6 +- .../sun/security/ssl/EncryptedExtensions.java | 4 +- .../ssl/ExtendedMasterSecretExtension.java | 10 +- .../classes/sun/security/ssl/Finished.java | 16 +- .../sun/security/ssl/HandshakeContext.java | 12 +- .../sun/security/ssl/HandshakeOutStream.java | 2 +- .../sun/security/ssl/HelloRequest.java | 10 +- .../sun/security/ssl/HelloVerifyRequest.java | 4 +- .../sun/security/ssl/KeyShareExtension.java | 32 +- .../classes/sun/security/ssl/KeyUpdate.java | 8 +- .../sun/security/ssl/MaxFragExtension.java | 18 +- .../classes/sun/security/ssl/NamedGroup.java | 8 +- .../sun/security/ssl/NewSessionTicket.java | 38 +-- .../sun/security/ssl/OutputRecord.java | 4 +- .../security/ssl/PreSharedKeyExtension.java | 34 +- .../ssl/PredefinedDHParameterSpecs.java | 6 +- .../ssl/PskKeyExchangeModesExtension.java | 8 +- .../security/ssl/QuicEngineOutputRecord.java | 8 +- .../sun/security/ssl/QuicKeyManager.java | 26 +- .../sun/security/ssl/QuicTLSEngineImpl.java | 6 +- .../security/ssl/RSAClientKeyExchange.java | 4 +- .../sun/security/ssl/RSAKeyExchange.java | 11 +- .../security/ssl/RSAServerKeyExchange.java | 4 +- .../sun/security/ssl/RenegoInfoExtension.java | 24 +- .../security/ssl/SSLAlgorithmConstraints.java | 2 +- .../classes/sun/security/ssl/SSLCipher.java | 44 +-- .../sun/security/ssl/SSLConfiguration.java | 8 +- .../sun/security/ssl/SSLContextImpl.java | 32 +- .../sun/security/ssl/SSLEngineImpl.java | 14 +- .../security/ssl/SSLEngineInputRecord.java | 10 +- .../security/ssl/SSLEngineOutputRecord.java | 26 +- .../sun/security/ssl/SSLExtension.java | 2 +- .../sun/security/ssl/SSLExtensions.java | 32 +- .../classes/sun/security/ssl/SSLLogger.java | 315 +++++++++--------- .../security/ssl/SSLMasterKeyDerivation.java | 3 +- .../security/ssl/SSLSessionContextImpl.java | 8 +- .../sun/security/ssl/SSLSessionImpl.java | 16 +- .../sun/security/ssl/SSLSocketImpl.java | 46 +-- .../security/ssl/SSLSocketInputRecord.java | 14 +- .../security/ssl/SSLSocketOutputRecord.java | 32 +- .../sun/security/ssl/SSLTransport.java | 8 +- .../classes/sun/security/ssl/ServerHello.java | 26 +- .../sun/security/ssl/ServerHelloDone.java | 4 +- .../sun/security/ssl/ServerNameExtension.java | 20 +- .../security/ssl/SessionTicketExtension.java | 24 +- .../ssl/SignatureAlgorithmsExtension.java | 4 +- .../sun/security/ssl/SignatureScheme.java | 20 +- .../security/ssl/StatusResponseManager.java | 62 ++-- .../security/ssl/SunX509KeyManagerImpl.java | 4 +- .../ssl/SupportedGroupsExtension.java | 20 +- .../ssl/SupportedVersionsExtension.java | 18 +- .../sun/security/ssl/TransportContext.java | 20 +- .../security/ssl/TrustManagerFactoryImpl.java | 10 +- .../sun/security/ssl/TrustStoreManager.java | 16 +- .../classes/sun/security/ssl/Utilities.java | 6 +- .../sun/security/ssl/X509Authentication.java | 26 +- .../ssl/X509KeyManagerCertChecking.java | 14 +- .../sun/security/ssl/X509KeyManagerImpl.java | 14 +- .../security/ssl/X509TrustManagerImpl.java | 12 +- .../classes/sun/security/util/DomainName.java | 9 +- .../sun/security/util/HostnameChecker.java | 10 +- .../SSLLogger/DebugPropertyValuesTest.java | 3 +- 80 files changed, 798 insertions(+), 797 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/Alert.java b/src/java.base/share/classes/sun/security/ssl/Alert.java index ed5e079bf44df..d172206326f1f 100644 --- a/src/java.base/share/classes/sun/security/ssl/Alert.java +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java @@ -238,7 +238,7 @@ public void consume(ConnectionContext context, TransportContext tc = (TransportContext)context; AlertMessage am = new AlertMessage(tc, m); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Received alert message", am); } diff --git a/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java b/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java index aa5933ddab014..f03a65c84103c 100644 --- a/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java @@ -157,7 +157,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Ignore client unavailable extension: " + SSLExtension.CH_ALPN.name); @@ -170,7 +170,7 @@ public byte[] produce(ConnectionContext context, String[] laps = chc.sslConfig.applicationProtocols; if ((laps == null) || (laps.length == 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "No available application protocols"); } @@ -183,7 +183,7 @@ public byte[] produce(ConnectionContext context, int length = ap.getBytes(alpnCharset).length; if (length == 0) { // log the configuration problem - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.severe( "Application protocol name cannot be empty"); } @@ -197,7 +197,7 @@ public byte[] produce(ConnectionContext context, listLength += (length + 1); } else { // log the configuration problem - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.severe( "Application protocol name (" + ap + ") exceeds the size limit (" + @@ -212,7 +212,7 @@ public byte[] produce(ConnectionContext context, if (listLength > MAX_AP_LIST_LENGTH) { // log the configuration problem - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.severe( "The configured application protocols (" + Arrays.toString(laps) + @@ -266,7 +266,7 @@ public void consume(ConnectionContext context, if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) { shc.applicationProtocol = ""; shc.conContext.applicationProtocol = ""; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Ignore server unavailable extension: " + SSLExtension.CH_ALPN.name); @@ -288,7 +288,7 @@ public void consume(ConnectionContext context, if (noAPSelector && noAlpnProtocols) { shc.applicationProtocol = ""; shc.conContext.applicationProtocol = ""; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore server unenabled extension: " + SSLExtension.CH_ALPN.name); @@ -378,7 +378,7 @@ public byte[] produce(ConnectionContext context, (AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN); if (requestedAlps == null) { // Ignore, this extension was not requested and accepted. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + SSLExtension.SH_ALPN.name); @@ -423,7 +423,7 @@ public byte[] produce(ConnectionContext context, // Ignore, no negotiated application layer protocol. shc.applicationProtocol = ""; shc.conContext.applicationProtocol = ""; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no negotiated application layer protocol"); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java index 2125a1481627e..2d03d5fef98f6 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java @@ -94,7 +94,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "signature_algorithms_cert extension"); @@ -144,7 +144,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "signature_algorithms_cert extension"); @@ -235,7 +235,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "signature_algorithms_cert extension"); @@ -283,7 +283,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "signature_algorithms_cert extension"); diff --git a/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java b/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java index 49713b6db11d7..d6c1cec5735e6 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java @@ -144,7 +144,7 @@ private CertStatusRequestSpec(HandshakeContext hc, if (statusType == CertStatusRequestType.OCSP.id) { this.statusRequest = new OCSPStatusRequest(statusType, encoded); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Unknown certificate status request " + "(status type: " + statusType + ")"); @@ -196,7 +196,7 @@ private CertStatusResponseSpec(HandshakeContext hc, if (type == CertStatusRequestType.OCSP.id) { this.statusResponse = new OCSPStatusResponse(type, respData); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Unknown certificate status response " + "(status type: " + type + ")"); @@ -557,7 +557,7 @@ public byte[] produce(ConnectionContext context, } if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + SSLExtension.CH_STATUS_REQUEST.name); @@ -598,7 +598,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + SSLExtension.CH_STATUS_REQUEST.name); } @@ -656,7 +656,7 @@ public byte[] produce(ConnectionContext context, shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST); if (spec == null) { // Ignore, no status_request extension requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Ignore unavailable extension: " + SSLExtension.CH_STATUS_REQUEST.name); } @@ -666,7 +666,7 @@ public byte[] produce(ConnectionContext context, // Is it a session resuming? if (shc.isResumption) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No status_request response for session resuming"); } @@ -839,7 +839,7 @@ private CertStatusRequestV2Spec(HandshakeContext hc, statusRequests.add( new OCSPStatusRequest(statusType, encoded)); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Unknown certificate status request " + "(status type: " + statusType + ")"); @@ -915,7 +915,7 @@ public byte[] produce(ConnectionContext context, } if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable status_request_v2 extension"); } @@ -957,7 +957,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable status_request_v2 extension"); } @@ -1017,7 +1017,7 @@ public byte[] produce(ConnectionContext context, shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2); if (spec == null) { // Ignore, no status_request_v2 extension requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable status_request_v2 extension"); } @@ -1027,7 +1027,7 @@ public byte[] produce(ConnectionContext context, // Is it a session resuming? if (shc.isResumption) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No status_request_v2 response for session resumption"); } @@ -1112,7 +1112,7 @@ public byte[] produce(ConnectionContext context, // Stapling needs to be active and have valid data to proceed if (shc.stapleParams == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Stapling is disabled for this connection"); } @@ -1121,7 +1121,7 @@ public byte[] produce(ConnectionContext context, // There needs to be a non-null CertificateEntry to proceed if (shc.currentCertEntry == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Found null CertificateEntry in context"); } return null; @@ -1139,7 +1139,7 @@ public byte[] produce(ConnectionContext context, byte[] respBytes = shc.stapleParams.responseMap.get(x509Cert); if (respBytes == null) { // We're done with this entry. Clear it from the context - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("No status response found for " + x509Cert.getSubjectX500Principal()); @@ -1149,7 +1149,7 @@ public byte[] produce(ConnectionContext context, } // Build a proper response buffer from the stapling information - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Found status response for " + x509Cert.getSubjectX500Principal() + ", response length: " + respBytes.length); @@ -1208,7 +1208,7 @@ public void consume(ConnectionContext context, respList.add(spec.statusResponse.encodedResponse); chc.handshakeSession.setStatusResponses(respList); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignoring stapled data on resumed session"); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java b/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java index 43bac16f0eace..cc513eb30ba2d 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java @@ -192,7 +192,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CH_CERTIFICATE_AUTHORITIES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "certificate_authorities extension"); @@ -205,7 +205,7 @@ public byte[] produce(ConnectionContext context, X509Certificate[] caCerts = chc.sslContext.getX509TrustManager().getAcceptedIssuers(); if (caCerts.length == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No available certificate authorities"); } @@ -216,7 +216,7 @@ public byte[] produce(ConnectionContext context, List encodedCAs = CertificateAuthoritiesSpec.getEncodedAuthorities(caCerts); if (encodedCAs.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "The number of CAs exceeds the maximum size " + "of the certificate_authorities extension"); @@ -270,7 +270,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CH_CERTIFICATE_AUTHORITIES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "certificate_authorities extension"); @@ -319,7 +319,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CR_CERTIFICATE_AUTHORITIES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "certificate_authorities extension"); @@ -332,7 +332,7 @@ public byte[] produce(ConnectionContext context, X509Certificate[] caCerts = shc.sslContext.getX509TrustManager().getAcceptedIssuers(); if (caCerts.length == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No available certificate authorities"); } @@ -343,7 +343,7 @@ public byte[] produce(ConnectionContext context, List encodedCAs = CertificateAuthoritiesSpec.getEncodedAuthorities(caCerts); if (encodedCAs.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Too many certificate authorities to use " + "the certificate_authorities extension"); @@ -397,7 +397,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CR_CERTIFICATE_AUTHORITIES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "certificate_authorities extension"); diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java index d4587d35ae987..2a2db34cab93b 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -265,7 +265,7 @@ private byte[] onProduceCertificate(ServerHandshakeContext shc, shc.handshakeSession.setLocalCertificates(x509Possession.popCerts); T12CertificateMessage cm = new T12CertificateMessage(shc, x509Possession.popCerts); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server Certificate handshake message", cm); } @@ -293,7 +293,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, // an empty cert chain instead. if (x509Possession == null) { if (chc.negotiatedProtocol.useTLS10PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 certificate for client authentication, " + "use empty Certificate message instead"); @@ -302,7 +302,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, x509Possession = new X509Possession(null, new X509Certificate[0]); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 certificate for client authentication, " + "send a no_certificate alert"); @@ -324,7 +324,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, } T12CertificateMessage cm = new T12CertificateMessage(chc, x509Possession.popCerts); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced client Certificate handshake message", cm); } @@ -360,13 +360,13 @@ public void consume(ConnectionContext context, T12CertificateMessage cm = new T12CertificateMessage(hc, message); if (hc.sslConfig.isClientMode) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server Certificate handshake message", cm); } onCertificate((ClientHandshakeContext)context, cm); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming client Certificate handshake message", cm); } @@ -501,7 +501,7 @@ private static boolean isIdentityEquivalent(X509Certificate thisCert, try { thisSubjectAltNames = thisCert.getSubjectAlternativeNames(); } catch (CertificateParsingException cpe) { - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.fine( "Attempt to obtain subjectAltNames extension failed!"); } @@ -511,7 +511,7 @@ private static boolean isIdentityEquivalent(X509Certificate thisCert, try { prevSubjectAltNames = prevCert.getSubjectAlternativeNames(); } catch (CertificateParsingException cpe) { - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.fine( "Attempt to obtain subjectAltNames extension failed!"); } @@ -980,7 +980,7 @@ private byte[] onProduceCertificate(ServerHandshakeContext shc, certEnt.extensions.produce(shc, enabledCTExts); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced server Certificate message", cm); } @@ -997,7 +997,7 @@ private static SSLPossession choosePossession( ClientHelloMessage clientHello) { if (hc.peerRequestedCertSignSchemes == null || hc.peerRequestedCertSignSchemes.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No signature_algorithms(_cert) in ClientHello"); } @@ -1021,7 +1021,7 @@ private static SSLPossession choosePossession( SSLPossession pos = X509Authentication .createPossession(hc, supportedKeyTypes); if (pos == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available authentication scheme"); } } @@ -1034,14 +1034,14 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, SSLPossession pos = choosePossession(chc, clientHello); X509Certificate[] localCerts; if (pos == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No available client authentication scheme"); } localCerts = new X509Certificate[0]; } else { chc.handshakePossessions.add(pos); if (!(pos instanceof X509Possession x509Possession)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 certificate for client authentication"); } @@ -1067,7 +1067,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "Failed to produce client Certificate message", ce); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced client Certificate message", cm); } @@ -1108,13 +1108,13 @@ public void consume(ConnectionContext context, T13CertificateMessage cm = new T13CertificateMessage(hc, message); if (hc.sslConfig.isClientMode) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server Certificate handshake message", cm); } onConsumeCertificate((ClientHandshakeContext)context, cm); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming client Certificate handshake message", cm); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java index 66b8c0487032f..a297d9d21b265 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java @@ -297,7 +297,7 @@ public byte[] produce(ConnectionContext context, shc.sslContext.getX509TrustManager().getAcceptedIssuers(); T10CertificateRequestMessage crm = new T10CertificateRequestMessage( shc, caCerts, shc.negotiatedCipherSuite.keyExchange); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateRequest handshake message", crm); } @@ -360,7 +360,7 @@ public void consume(ConnectionContext context, T10CertificateRequestMessage crm = new T10CertificateRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateRequest handshake message", crm); } @@ -400,7 +400,7 @@ public void consume(ConnectionContext context, } if (clientAlias == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available client authentication"); } return; @@ -408,7 +408,7 @@ public void consume(ConnectionContext context, PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias); if (clientPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available client private key"); } return; @@ -416,7 +416,7 @@ public void consume(ConnectionContext context, X509Certificate[] clientCerts = km.getCertificateChain(clientAlias); if ((clientCerts == null) || (clientCerts.length == 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available client certificate"); } return; @@ -655,7 +655,7 @@ public byte[] produce(ConnectionContext context, T12CertificateRequestMessage crm = new T12CertificateRequestMessage( shc, caCerts, shc.negotiatedCipherSuite.keyExchange, certReqSignAlgs); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateRequest handshake message", crm); } @@ -717,7 +717,7 @@ public void consume(ConnectionContext context, T12CertificateRequestMessage crm = new T12CertificateRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateRequest handshake message", crm); } @@ -784,7 +784,7 @@ private static SSLPossession choosePossession(HandshakeContext hc, T12CertificateRequestMessage crm) { if (hc.peerRequestedCertSignSchemes == null || hc.peerRequestedCertSignSchemes.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No signature and hash algorithms " + "in CertificateRequest"); } @@ -823,7 +823,7 @@ private static SSLPossession choosePossession(HandshakeContext hc, SSLPossession pos = X509Authentication .createPossession(hc, supportedKeyTypes); if (pos == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available authentication scheme"); } } @@ -933,7 +933,7 @@ public byte[] produce(ConnectionContext context, SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions( SSLHandshake.CERTIFICATE_REQUEST, shc.negotiatedProtocol); crm.extensions.produce(shc, extTypes); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced CertificateRequest message", crm); } @@ -985,7 +985,7 @@ public void consume(ConnectionContext context, T13CertificateRequestMessage crm = new T13CertificateRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateRequest handshake message", crm); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java b/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java index 11b2c5e587d43..a1048e423d129 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java @@ -281,7 +281,7 @@ public void consume(ConnectionContext context, new CertificateStatusMessage(chc, message); // Log the message - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server CertificateStatus handshake message", cst); @@ -325,7 +325,7 @@ public byte[] produce(ConnectionContext context, // Create the CertificateStatus message from info in the CertificateStatusMessage csm = new CertificateStatusMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server CertificateStatus handshake message", csm); } @@ -358,7 +358,7 @@ public void absent(ConnectionContext context, // status_request[_v2] extension. 2) The CertificateStatus // message was not sent. This means that cert path checking // was deferred, but must happen immediately. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Server did not send CertificateStatus, " + "checking cert chain without status info."); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java index 09d07d8e62d7a..18ea2b9c3de74 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java @@ -248,7 +248,7 @@ public byte[] produce(ConnectionContext context, if (x509Possession == null || x509Possession.popPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 credentials negotiated for CertificateVerify"); } @@ -258,7 +258,7 @@ public byte[] produce(ConnectionContext context, S30CertificateVerifyMessage cvm = new S30CertificateVerifyMessage(chc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateVerify handshake message", cvm); } @@ -300,7 +300,7 @@ public void consume(ConnectionContext context, S30CertificateVerifyMessage cvm = new S30CertificateVerifyMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateVerify handshake message", cvm); } @@ -503,7 +503,7 @@ public byte[] produce(ConnectionContext context, if (x509Possession == null || x509Possession.popPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 credentials negotiated for CertificateVerify"); } @@ -513,7 +513,7 @@ public byte[] produce(ConnectionContext context, T10CertificateVerifyMessage cvm = new T10CertificateVerifyMessage(chc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateVerify handshake message", cvm); } @@ -555,7 +555,7 @@ public void consume(ConnectionContext context, T10CertificateVerifyMessage cvm = new T10CertificateVerifyMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateVerify handshake message", cvm); } @@ -754,7 +754,7 @@ public byte[] produce(ConnectionContext context, if (x509Possession == null || x509Possession.popPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 credentials negotiated for CertificateVerify"); } @@ -764,7 +764,7 @@ public byte[] produce(ConnectionContext context, T12CertificateVerifyMessage cvm = new T12CertificateVerifyMessage(chc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateVerify handshake message", cvm); } @@ -806,7 +806,7 @@ public void consume(ConnectionContext context, T12CertificateVerifyMessage cvm = new T12CertificateVerifyMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateVerify handshake message", cvm); } @@ -1092,7 +1092,7 @@ public byte[] produce(ConnectionContext context, if (x509Possession == null || x509Possession.popPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 credentials negotiated for CertificateVerify"); } @@ -1113,7 +1113,7 @@ private byte[] onProduceCertificateVerify(ServerHandshakeContext shc, X509Possession x509Possession) throws IOException { T13CertificateVerifyMessage cvm = new T13CertificateVerifyMessage(shc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server CertificateVerify handshake message", cvm); } @@ -1130,7 +1130,7 @@ private byte[] onProduceCertificateVerify(ClientHandshakeContext chc, X509Possession x509Possession) throws IOException { T13CertificateVerifyMessage cvm = new T13CertificateVerifyMessage(chc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced client CertificateVerify handshake message", cvm); } @@ -1173,7 +1173,7 @@ public void consume(ConnectionContext context, T13CertificateVerifyMessage cvm = new T13CertificateVerifyMessage(hc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateVerify handshake message", cvm); } diff --git a/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java index 4ea61161c1d77..d3eac8f13af4c 100644 --- a/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java +++ b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java @@ -108,7 +108,7 @@ public byte[] produce(ConnectionContext context, ") and protocol version (" + hc.negotiatedProtocol + ")"); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ChangeCipherSpec message"); } @@ -142,7 +142,7 @@ public void consume(ConnectionContext context, throw tc.fatal(Alert.UNEXPECTED_MESSAGE, "Malformed or unexpected ChangeCipherSpec message"); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ChangeCipherSpec message"); } @@ -237,7 +237,7 @@ public void consume(ConnectionContext context, throw tc.fatal(Alert.UNEXPECTED_MESSAGE, "Malformed or unexpected ChangeCipherSpec message"); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ChangeCipherSpec message"); } diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHello.java b/src/java.base/share/classes/sun/security/ssl/ClientHello.java index c9432ea3979d2..421673d625d33 100644 --- a/src/java.base/share/classes/sun/security/ssl/ClientHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ClientHello.java @@ -430,7 +430,7 @@ public byte[] produce(ConnectionContext context) throws IOException { if (!session.isRejoinable()) { session = null; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, the session is not rejoinable"); @@ -443,7 +443,7 @@ public byte[] produce(ConnectionContext context) throws IOException { sessionSuite = session.getSuite(); if (!chc.isNegotiable(sessionSuite)) { session = null; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, unavailable session cipher suite"); @@ -456,7 +456,7 @@ public byte[] produce(ConnectionContext context) throws IOException { sessionVersion = session.getProtocolVersion(); if (!chc.isNegotiable(sessionVersion)) { session = null; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, unavailable protocol version"); @@ -513,7 +513,7 @@ public byte[] produce(ConnectionContext context) throws IOException { String sessionIdentityAlg = session.getIdentificationProtocol(); if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Can't resume, endpoint id" + " algorithm does not match, requested: " + @@ -524,7 +524,7 @@ public byte[] produce(ConnectionContext context) throws IOException { } if (session != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Try resuming session", session); } @@ -547,7 +547,7 @@ public byte[] produce(ConnectionContext context) throws IOException { cipherSuites = List.of(sessionSuite); } - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "No new session is allowed, so try to resume " + @@ -634,7 +634,7 @@ public byte[] produce(ConnectionContext context) throws IOException { SSLHandshake.CLIENT_HELLO, chc.activeProtocols); chm.extensions.produce(chc, extTypes); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ClientHello handshake message", chm); } @@ -700,7 +700,7 @@ public byte[] produce(ConnectionContext context, // // The HelloVerifyRequest consumer should have updated the // ClientHello handshake message with cookie. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ClientHello(cookie) handshake message", chc.initialClientHelloMsg); @@ -734,7 +734,7 @@ public byte[] produce(ConnectionContext context, // TLS 1.3 // The HelloRetryRequest consumer should have updated the // ClientHello handshake message with cookie. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ClientHello(HRR) handshake message", chc.initialClientHelloMsg); @@ -790,7 +790,7 @@ public void consume(ConnectionContext context, ClientHelloMessage chm = new ClientHelloMessage(shc, message, enabledExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ClientHello handshake message", chm); } @@ -820,7 +820,7 @@ private void onClientHello(ServerHandshakeContext context, negotiateProtocol(context, clientHello.clientVersion); } context.negotiatedProtocol = negotiatedProtocol; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Negotiated protocol version: " + negotiatedProtocol.name); } @@ -980,7 +980,7 @@ public void consume(ConnectionContext context, boolean resumingSession = (previous != null) && previous.isRejoinable(); if (!resumingSession) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -993,7 +993,7 @@ public void consume(ConnectionContext context, previous.getProtocolVersion(); if (sessionProtocol != shc.negotiatedProtocol) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, not the same protocol version"); @@ -1008,7 +1008,7 @@ public void consume(ConnectionContext context, previous.getPeerPrincipal(); } catch (SSLPeerUnverifiedException e) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -1023,7 +1023,7 @@ public void consume(ConnectionContext context, if ((!shc.isNegotiable(suite)) || (!clientHello.cipherSuites.contains(suite))) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -1039,7 +1039,7 @@ public void consume(ConnectionContext context, String sessionIdentityAlg = previous.getIdentificationProtocol(); if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Can't resume, endpoint id" + " algorithm does not match, requested: " + @@ -1054,7 +1054,7 @@ public void consume(ConnectionContext context, shc.isResumption = resumingSession; shc.resumingSession = resumingSession ? previous : null; - if (!resumingSession && SSLLogger.isOn && + if (!resumingSession && SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Session not resumed."); } @@ -1321,7 +1321,7 @@ public void consume(ConnectionContext context, boolean resumingSession = (previous != null) && previous.isRejoinable(); if (!resumingSession) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -1334,7 +1334,7 @@ public void consume(ConnectionContext context, previous.getProtocolVersion(); if (sessionProtocol != shc.negotiatedProtocol) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, not the same protocol version"); @@ -1350,7 +1350,7 @@ public void consume(ConnectionContext context, previous.getPeerPrincipal(); } catch (SSLPeerUnverifiedException e) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -1365,7 +1365,7 @@ public void consume(ConnectionContext context, if ((!shc.isNegotiable(suite)) || (!clientHello.cipherSuites.contains(suite))) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + diff --git a/src/java.base/share/classes/sun/security/ssl/CookieExtension.java b/src/java.base/share/classes/sun/security/ssl/CookieExtension.java index d54a1a3e63d5f..2c22dd121baaf 100644 --- a/src/java.base/share/classes/sun/security/ssl/CookieExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CookieExtension.java @@ -117,7 +117,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } @@ -154,7 +154,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } @@ -218,7 +218,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } @@ -253,7 +253,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } @@ -280,7 +280,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } diff --git a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java index fb5d6feef556b..53f9896a3e493 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java @@ -187,7 +187,7 @@ public byte[] produce(ConnectionContext context, chc.handshakePossessions.add(dhePossession); DHClientKeyExchangeMessage ckem = new DHClientKeyExchangeMessage(chc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced DH ClientKeyExchange handshake message", ckem); } @@ -268,7 +268,7 @@ public void consume(ConnectionContext context, DHClientKeyExchangeMessage ckem = new DHClientKeyExchangeMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming DH ClientKeyExchange handshake message", ckem); } diff --git a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java index 2df62d50fb845..744ff59f4027a 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java @@ -481,7 +481,7 @@ public byte[] produce(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; DHServerKeyExchangeMessage skem = new DHServerKeyExchangeMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced DH ServerKeyExchange handshake message", skem); } @@ -512,7 +512,7 @@ public void consume(ConnectionContext context, DHServerKeyExchangeMessage skem = new DHServerKeyExchangeMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming DH ServerKeyExchange handshake message", skem); } diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java index 4e82fd25a7b28..e880f36e846eb 100644 --- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java @@ -125,7 +125,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { return null; } - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw read", packet); } @@ -150,7 +150,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { int contentLen = ((packet.get() & 0xFF) << 8) | (packet.get() & 0xFF); // pos: 11, 12 - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine("READ: " + ProtocolVersion.nameOf(majorVersion, minorVersion) + " " + ContentType.nameOf(contentType) + ", length = " + @@ -162,7 +162,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { if (this.readEpoch > recordEpoch) { // Reset the position of the packet buffer. packet.position(recLim); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine("READ: discard this old record", recordEnS); } return null; @@ -181,7 +181,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { packet.position(recLim); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Premature record (epoch), discard it."); } @@ -223,7 +223,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { plaintextFragment = plaintext.fragment; contentType = plaintext.contentType; } catch (GeneralSecurityException gse) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + gse); } @@ -241,7 +241,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { // Cleanup the handshake reassembler if necessary. if ((reassembler != null) && (reassembler.handshakeEpoch < recordEpoch)) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Cleanup the handshake reassembler"); } @@ -273,7 +273,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { if (hsFrag == null) { // invalid, discard this record - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Invalid handshake message, discard it."); } @@ -296,7 +296,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { return pt == null ? null : new Plaintext[] { pt }; } - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("The reassembler is not initialized yet."); } @@ -356,7 +356,7 @@ private static HandshakeFragment parseHandshakeMessage( int remaining = plaintextFragment.remaining(); if (remaining < handshakeHeaderSize) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + "too small record to hold a handshake fragment"); } @@ -368,7 +368,7 @@ private static HandshakeFragment parseHandshakeMessage( // Fail fast for unknown handshake message. byte handshakeType = plaintextFragment.get(); // pos: 0 if (!SSLHandshake.isKnown(handshakeType)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + "unknown handshake type size, Handshake.msg_type = " + (handshakeType & 0xFF)); @@ -404,7 +404,7 @@ private static HandshakeFragment parseHandshakeMessage( ((plaintextFragment.get() & 0xFF) << 8) | (plaintextFragment.get() & 0xFF); // pos: 9-11 if ((remaining - handshakeHeaderSize) < fragmentLength) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + "not a complete handshake fragment in the record"); } @@ -748,7 +748,7 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { // It's OK to discard retransmission as the handshake hash // is computed as if each handshake message had been sent // as a single fragment. - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Have got the full message, discard it."); } @@ -769,7 +769,7 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { // The ranges SHOULD NOT overlap. if (hole.offset > hsf.fragmentOffset || hole.limit < fragmentLimit) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + "handshake fragment ranges are overlapping"); } @@ -837,7 +837,7 @@ private HandshakeFragment valHello(HandshakeFragment hsf, } // Read the random (32 bytes) if (fragmentData.remaining() < 32) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Rejected client hello fragment (bad random len) " + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); } @@ -861,7 +861,7 @@ private HandshakeFragment valHello(HandshakeFragment hsf, // Cookie byte[] cookie = Record.getBytes8(fragmentData); if (firstHello && cookie.length != 0) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); } @@ -897,7 +897,7 @@ private HandshakeFragment valHello(HandshakeFragment hsf, } } } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Rejected client hello fragment " + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); } @@ -1029,7 +1029,7 @@ private boolean isDesirable(RecordFragment rf) throws SSLProtocolException { int previousEpoch = nextRecordEpoch - 1; if (rf.recordEpoch < previousEpoch) { // Too old to use, discard this record. - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Too old epoch to use this record, discard it."); } @@ -1075,7 +1075,7 @@ private boolean isDesirable(RecordFragment rf) throws SSLProtocolException { if (!isDesired) { // Too old to use, discard this retransmitted record - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Too old retransmission to use, discard it."); } @@ -1088,7 +1088,7 @@ private boolean isDesirable(RecordFragment rf) throws SSLProtocolException { // Previously disordered record for the current epoch. // // Should have been retransmitted. Discard this record. - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Lagging behind record (sequence), discard it."); } @@ -1126,7 +1126,7 @@ private boolean isEmpty() { Plaintext acquirePlaintext() throws SSLProtocolException { if (bufferedFragments.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("No received handshake messages"); } return null; @@ -1147,7 +1147,7 @@ Plaintext acquirePlaintext() throws SSLProtocolException { // Reset the next handshake flight. resetHandshakeFlight(precedingFlight); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Received a retransmission flight."); } @@ -1159,7 +1159,7 @@ Plaintext acquirePlaintext() throws SSLProtocolException { } if (!flightIsReady) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "The handshake flight is not ready to use: " + handshakeFlight.handshakeType); @@ -1244,7 +1244,7 @@ private Plaintext acquireCachedMessage() throws SSLProtocolException { if (readEpoch != rFrag.recordEpoch) { if (readEpoch > rFrag.recordEpoch) { // discard old records - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Discard old buffered ciphertext fragments."); } @@ -1256,7 +1256,7 @@ private Plaintext acquireCachedMessage() throws SSLProtocolException { flightIsReady = false; } - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Not yet ready to decrypt the cached fragments."); } @@ -1273,7 +1273,7 @@ private Plaintext acquireCachedMessage() throws SSLProtocolException { plaintextFragment = plaintext.fragment; rFrag.contentType = plaintext.contentType; } catch (GeneralSecurityException gse) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Discard invalid record: ", gse); } @@ -1295,7 +1295,7 @@ private Plaintext acquireCachedMessage() throws SSLProtocolException { if (hsFrag == null) { // invalid, discard this record - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Invalid handshake fragment, discard it", plaintextFragment); @@ -1446,7 +1446,7 @@ boolean flightIsReady() { if (expectCCSFlight) { // Have the ChangeCipherSpec/Finished flight been received? boolean isReady = hasFinishedMessage(); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Has the final flight been received? " + isReady); } @@ -1454,7 +1454,7 @@ boolean flightIsReady() { return isReady; } - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("No flight is received yet."); } @@ -1467,7 +1467,7 @@ boolean flightIsReady() { // single handshake message flight boolean isReady = hasCompleted(flightType); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Is the handshake message completed? " + isReady); } @@ -1481,7 +1481,7 @@ boolean flightIsReady() { if (flightType == SSLHandshake.SERVER_HELLO.id) { // Firstly, check the first flight handshake message. if (!hasCompleted(flightType)) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "The ServerHello message is not completed yet."); } @@ -1493,7 +1493,7 @@ boolean flightIsReady() { // an abbreviated handshake // if (hasFinishedMessage()) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("It's an abbreviated handshake."); } @@ -1507,7 +1507,7 @@ boolean flightIsReady() { SSLHandshake.SERVER_HELLO_DONE.id); if ((holes == null) || !holes.isEmpty()) { // Not yet got the final message of the flight. - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Not yet got the ServerHelloDone message"); } @@ -1519,7 +1519,7 @@ boolean flightIsReady() { boolean isReady = hasCompleted(bufferedFragments, handshakeFlight.minMessageSeq, handshakeFlight.maxMessageSeq); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Is the ServerHello flight (message " + handshakeFlight.minMessageSeq + "-" + @@ -1542,7 +1542,7 @@ boolean flightIsReady() { // Firstly, check the first flight handshake message. if (!hasCompleted(flightType)) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "The ClientKeyExchange or client Certificate " + "message is not completed yet."); @@ -1556,7 +1556,7 @@ boolean flightIsReady() { if (needClientVerify(bufferedFragments) && !hasCompleted(SSLHandshake.CERTIFICATE_VERIFY.id)) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Not yet have the CertificateVerify message"); } @@ -1567,7 +1567,7 @@ boolean flightIsReady() { if (!hasFinishedMessage()) { // not yet have the ChangeCipherSpec/Finished messages - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Not yet have the ChangeCipherSpec and " + "Finished messages"); @@ -1580,7 +1580,7 @@ boolean flightIsReady() { boolean isReady = hasCompleted(bufferedFragments, handshakeFlight.minMessageSeq, handshakeFlight.maxMessageSeq); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Is the ClientKeyExchange flight (message " + handshakeFlight.minMessageSeq + "-" + @@ -1594,7 +1594,7 @@ boolean flightIsReady() { // // Otherwise, need to receive more handshake messages. // - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Need to receive more handshake messages"); } diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java index 691ac32c26b82..162dbb58eec5b 100644 --- a/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java @@ -92,7 +92,7 @@ void finishHandshake() { void changeWriteCiphers(SSLWriteCipher writeCipher, boolean useChangeCipherSpec) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -120,7 +120,7 @@ void changeWriteCiphers(SSLWriteCipher writeCipher, @Override void encodeAlert(byte level, byte description) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "alert message: " + Alert.nameOf(description)); } @@ -137,7 +137,7 @@ void encodeAlert(byte level, byte description) { @Override void encodeChangeCipherSpec() { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -154,7 +154,7 @@ void encodeChangeCipherSpec() { void encodeHandshake(byte[] source, int offset, int length) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake message", ByteBuffer.wrap(source, offset, length)); @@ -179,14 +179,14 @@ Ciphertext encode( ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { if (isClosed) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "application data or cached messages"); } return null; } else if (isCloseWaiting) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "application data"); } @@ -201,7 +201,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, ByteBuffer destination) throws IOException { if (writeCipher.authenticator.seqNumOverflow()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "sequence number extremely close to overflow " + "(2^64-1 packets). Closing connection."); @@ -269,7 +269,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, destination.limit(destination.position()); destination.position(dstContent); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.APPLICATION_DATA.name + @@ -282,7 +282,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, dstPos, dstLim, headerSize, protocolVersion); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer temporary = destination.duplicate(); temporary.limit(temporary.position()); temporary.position(dstPos); @@ -497,7 +497,7 @@ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { dstBuf.limit(dstBuf.position()); dstBuf.position(dstContent); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.nameOf(memo.contentType) + @@ -511,7 +511,7 @@ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { ProtocolVersion.valueOf(memo.majorVersion, memo.minorVersion)); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer temporary = dstBuf.duplicate(); temporary.limit(temporary.position()); temporary.position(dstPos); diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java index e1c1b1377ad25..a626f6f34d06e 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java @@ -199,7 +199,7 @@ public byte[] produce(ConnectionContext context, ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage( chc, sslPossession.encode()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ECDH ClientKeyExchange handshake message", cke); } @@ -308,7 +308,7 @@ public void consume(ConnectionContext context, // parse either handshake message containing either EC/XEC. ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming ECDH ClientKeyExchange handshake message", cke); } @@ -397,7 +397,7 @@ public byte[] produce(ConnectionContext context, new ECDHClientKeyExchangeMessage( chc, sslPossession.encode()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ECDHE ClientKeyExchange handshake message", cke); } @@ -490,7 +490,7 @@ public void consume(ConnectionContext context, // parse the EC/XEC handshake message ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming ECDHE ClientKeyExchange handshake message", cke); } diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java index b31c0ba9cb9de..a02a8438163df 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java @@ -489,7 +489,7 @@ public byte[] produce(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; ECDHServerKeyExchangeMessage skem = new ECDHServerKeyExchangeMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ECDH ServerKeyExchange handshake message", skem); } @@ -522,7 +522,7 @@ public void consume(ConnectionContext context, // AlgorithmConstraints are checked during decoding ECDHServerKeyExchangeMessage skem = new ECDHServerKeyExchangeMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming ECDH ServerKeyExchange handshake message", skem); } diff --git a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java index 580e1d416deab..72b7c950374cb 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java @@ -171,7 +171,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable ec_point_formats extension"); } @@ -193,7 +193,7 @@ public byte[] produce(ConnectionContext context, return extData; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Need no ec_point_formats extension"); } @@ -221,7 +221,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable ec_point_formats extension"); } diff --git a/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java b/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java index d1975b5caa48d..b5be927f0aa22 100644 --- a/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java +++ b/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java @@ -134,7 +134,7 @@ public byte[] produce(ConnectionContext context, SSLHandshake.ENCRYPTED_EXTENSIONS, shc.negotiatedProtocol); eem.extensions.produce(shc, extTypes); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced EncryptedExtensions message", eem); } @@ -168,7 +168,7 @@ public void consume(ConnectionContext context, EncryptedExtensionsMessage eem = new EncryptedExtensionsMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming EncryptedExtensions handshake message", eem); } diff --git a/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java index ff4694c8c7caa..6bacbfbd1d8e1 100644 --- a/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java @@ -119,7 +119,7 @@ public byte[] produce(ConnectionContext context, if (!chc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) || !SSLConfiguration.useExtendedMasterSecret || !chc.conContext.protocolVersion.useTLS10PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extended_master_secret extension"); } @@ -162,7 +162,7 @@ public void consume(ConnectionContext context, if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) || !SSLConfiguration.useExtendedMasterSecret || !shc.negotiatedProtocol.useTLS10PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + CH_EXTENDED_MASTER_SECRET.name); } @@ -182,7 +182,7 @@ public void consume(ConnectionContext context, // with a full handshake. shc.isResumption = false; shc.resumingSession = null; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption which did not use " + "Extended Master Secret extension"); @@ -213,7 +213,7 @@ public void absent(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) || !SSLConfiguration.useExtendedMasterSecret) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + CH_EXTENDED_MASTER_SECRET.name); } @@ -252,7 +252,7 @@ public void absent(ConnectionContext context, } else { // Otherwise, continue with a full handshake. shc.isResumption = false; shc.resumingSession = null; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "missing Extended Master Secret extension"); diff --git a/src/java.base/share/classes/sun/security/ssl/Finished.java b/src/java.base/share/classes/sun/security/ssl/Finished.java index 04fe61760d02c..4238ced8f01f1 100644 --- a/src/java.base/share/classes/sun/security/ssl/Finished.java +++ b/src/java.base/share/classes/sun/security/ssl/Finished.java @@ -390,7 +390,7 @@ private byte[] onProduceFinished(ClientHandshakeContext chc, // Change write cipher and delivery ChangeCipherSpec message. ChangeCipherSpec.t10Producer.produce(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced client Finished handshake message", fm); } @@ -453,7 +453,7 @@ private byte[] onProduceFinished(ServerHandshakeContext shc, // Change write cipher and delivery ChangeCipherSpec message. ChangeCipherSpec.t10Producer.produce(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server Finished handshake message", fm); } @@ -542,7 +542,7 @@ public void consume(ConnectionContext context, private void onConsumeFinished(ClientHandshakeContext chc, ByteBuffer message) throws IOException { FinishedMessage fm = new FinishedMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server Finished handshake message", fm); } @@ -602,7 +602,7 @@ private void onConsumeFinished(ServerHandshakeContext shc, } FinishedMessage fm = new FinishedMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming client Finished handshake message", fm); } @@ -681,7 +681,7 @@ private byte[] onProduceFinished(ClientHandshakeContext chc, chc.handshakeHash.update(); FinishedMessage fm = new FinishedMessage(chc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced client Finished handshake message", fm); } @@ -778,7 +778,7 @@ private byte[] onProduceFinished(ServerHandshakeContext shc, shc.handshakeHash.update(); FinishedMessage fm = new FinishedMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server Finished handshake message", fm); } @@ -930,7 +930,7 @@ private void onConsumeFinished(ClientHandshakeContext chc, } FinishedMessage fm = new FinishedMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server Finished handshake message", fm); } @@ -1073,7 +1073,7 @@ private void onConsumeFinished(ServerHandshakeContext shc, } FinishedMessage fm = new FinishedMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming client Finished handshake message", fm); } diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java index 8455ddfc65d3f..a5f340d520343 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -284,14 +284,14 @@ private static List getActiveProtocols( found = true; break; } - } else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Ignore unsupported cipher suite: " + suite + " for " + protocol.name); } } - if (!found && (SSLLogger.isOn) && SSLLogger.isOn("handshake")) { + if (!found && (SSLLogger.isOn()) && SSLLogger.isOn("handshake")) { SSLLogger.fine( "No available cipher suite for " + protocol.name); } @@ -335,7 +335,7 @@ private static List getActiveCipherSuites( } if (!isSupported && - SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.finest( "Ignore unsupported cipher suite: " + suite); } @@ -556,7 +556,7 @@ private static boolean isActivatable( cachedStatus.put(groupType, groupAvailable); if (!groupAvailable && - SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "No activated named group in " + groupType); } @@ -570,13 +570,13 @@ private static boolean isActivatable( } } - if (!retval && SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (!retval && SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("No active named group(s), ignore " + suite); } return retval; - } else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Ignore disabled cipher suite: " + suite); } diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java b/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java index 619364425027a..2a05881180d50 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java @@ -61,7 +61,7 @@ void complete() throws IOException { if (!outputRecord.isClosed()) { outputRecord.encodeHandshake(buf, 0, count); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake messages", ByteBuffer.wrap(buf, 0, count)); } diff --git a/src/java.base/share/classes/sun/security/ssl/HelloRequest.java b/src/java.base/share/classes/sun/security/ssl/HelloRequest.java index f4da66b5dd3b7..39464992db59e 100644 --- a/src/java.base/share/classes/sun/security/ssl/HelloRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/HelloRequest.java @@ -101,7 +101,7 @@ public byte[] produce(ConnectionContext context) throws IOException { ServerHandshakeContext shc = (ServerHandshakeContext)context; HelloRequestMessage hrm = new HelloRequestMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced HelloRequest handshake message", hrm); } @@ -137,7 +137,7 @@ public byte[] produce(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; HelloRequestMessage hrm = new HelloRequestMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced HelloRequest handshake message", hrm); } @@ -177,7 +177,7 @@ public void consume(ConnectionContext context, // be sent by the server at any time. Please don't clean up this // handshake consumer. HelloRequestMessage hrm = new HelloRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming HelloRequest handshake message", hrm); } @@ -190,7 +190,7 @@ public void consume(ConnectionContext context, } if (!chc.conContext.secureRenegotiation) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Continue with insecure renegotiation"); } @@ -206,7 +206,7 @@ public void consume(ConnectionContext context, // SSLHandshake.CLIENT_HELLO.produce(context, hrm); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore HelloRequest, handshaking is in progress"); } diff --git a/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java b/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java index f28ae16de8872..8d3f1048c91bc 100644 --- a/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java @@ -140,7 +140,7 @@ public byte[] produce(ConnectionContext context, HelloVerifyRequestMessage hvrm = new HelloVerifyRequestMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced HelloVerifyRequest handshake message", hvrm); } @@ -197,7 +197,7 @@ public void consume(ConnectionContext context, HelloVerifyRequestMessage hvrm = new HelloVerifyRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming HelloVerifyRequest handshake message", hvrm); } diff --git a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java index 98e4693e9170c..8d785f7515a0c 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java @@ -90,7 +90,7 @@ private byte[] getEncoded() { Record.putInt16(m, namedGroupId); Record.putBytes16(m, keyExchange); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Unlikely IOException", ioe); } @@ -222,7 +222,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable key_share extension"); } @@ -237,7 +237,7 @@ public byte[] produce(ConnectionContext context, namedGroups = chc.clientRequestedNamedGroups; if (namedGroups == null || namedGroups.isEmpty()) { // No supported groups. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore key_share extension, no supported groups"); } @@ -287,7 +287,7 @@ private static byte[] getShare(ClientHandshakeContext chc, NamedGroup ng) { SSLKeyExchange ke = SSLKeyExchange.valueOf(ng); if (ke == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No key exchange for named group " + ng.name); } @@ -323,7 +323,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; if (shc.handshakeExtensions.containsKey(SSLExtension.CH_KEY_SHARE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "The key_share extension has been loaded"); } @@ -332,7 +332,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable key_share extension"); } @@ -346,7 +346,7 @@ public void consume(ConnectionContext context, NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId); if (ng == null || !NamedGroup.isActivatable(shc.sslConfig, shc.algorithmConstraints, ng)) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unsupported named group: " + @@ -364,7 +364,7 @@ public void consume(ConnectionContext context, if (!shc.algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), namedGroupCredentials.getPublicKey())) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "key share entry of " + ng + " does not " + @@ -379,7 +379,7 @@ public void consume(ConnectionContext context, credentials.add(kaCred); } } catch (GeneralSecurityException ex) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Cannot decode named group: " + NamedGroup.nameOf(entry.namedGroupId)); @@ -522,7 +522,7 @@ public byte[] produce(ConnectionContext context, SSLExtension.CH_KEY_SHARE); if (kss == null) { // Unlikely, no key_share extension requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no client key_share extension"); } @@ -531,7 +531,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no available server key_share extension"); } @@ -542,7 +542,7 @@ public byte[] produce(ConnectionContext context, if ((shc.handshakeCredentials == null) || shc.handshakeCredentials.isEmpty()) { // Unlikely, HelloRetryRequest should be used earlier. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No available client key share entries"); } @@ -562,7 +562,7 @@ public byte[] produce(ConnectionContext context, SSLKeyExchange ke = SSLKeyExchange.valueOf(ng); if (ke == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No key exchange for named group " + ng.name); } @@ -597,7 +597,7 @@ public byte[] produce(ConnectionContext context, if (keyShare == null) { // Unlikely, HelloRetryRequest should be used instead earlier. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No available server key_share extension"); } @@ -708,7 +708,7 @@ public void absent(ConnectionContext context, ClientHandshakeContext chc = (ClientHandshakeContext)context; // Cannot use the previous requested key shares anymore. - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.fine( "No key_share extension in ServerHello, " + "cleanup the key shares if necessary"); @@ -801,7 +801,7 @@ public byte[] produce(ConnectionContext context, for (NamedGroup ng : shc.clientRequestedNamedGroups) { if (NamedGroup.isActivatable(shc.sslConfig, shc.algorithmConstraints, ng)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "HelloRetryRequest selected named group: " + ng.name); diff --git a/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java index 2b17c7406a345..4e5a9683079dd 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java @@ -191,7 +191,7 @@ public void consume(ConnectionContext context, // The consuming happens in client side only. PostHandshakeContext hc = (PostHandshakeContext)context; KeyUpdateMessage km = new KeyUpdateMessage(hc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming KeyUpdate post-handshake message", km); } @@ -235,7 +235,7 @@ public void consume(ConnectionContext context, rc.baseSecret = nplus1; hc.conContext.inputRecord.changeReadCiphers(rc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("KeyUpdate: read key updated"); } } catch (GeneralSecurityException gse) { @@ -276,7 +276,7 @@ public byte[] produce(ConnectionContext context, return null; } KeyUpdateMessage km = (KeyUpdateMessage)message; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced KeyUpdate post-handshake message", km); } @@ -328,7 +328,7 @@ public byte[] produce(ConnectionContext context, // changeWriteCiphers() implementation. wc.baseSecret = nplus1; hc.conContext.outputRecord.changeWriteCiphers(wc, km.status.id); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("KeyUpdate: write key updated"); } diff --git a/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java b/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java index a07e81be914cd..25500c7ac573d 100644 --- a/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java @@ -176,7 +176,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable max_fragment_length extension"); } @@ -213,7 +213,7 @@ public byte[] produce(ConnectionContext context, } else { // log and ignore, no MFL extension. chc.maxFragmentLength = -1; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No available max_fragment_length extension can " + "be used for fragment size of " + @@ -243,7 +243,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; if (!shc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable max_fragment_length extension"); } @@ -288,7 +288,7 @@ public byte[] produce(ConnectionContext context, MaxFragLenSpec spec = (MaxFragLenSpec) shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH); if (spec == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable max_fragment_length extension"); } @@ -305,7 +305,7 @@ public byte[] produce(ConnectionContext context, // For better interoperability, abort the maximum // fragment length negotiation, rather than terminate // the connection with a fatal alert. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Abort the maximum fragment length negotiation, " + "may overflow the maximum packet size limit."); @@ -413,7 +413,7 @@ public void consume(ConnectionContext context, // For better interoperability, abort the maximum // fragment length negotiation, rather than terminate // the connection with a fatal alert. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Abort the maximum fragment length negotiation, " + "may overflow the maximum packet size limit."); @@ -455,7 +455,7 @@ public byte[] produce(ConnectionContext context, MaxFragLenSpec spec = (MaxFragLenSpec) shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH); if (spec == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable max_fragment_length extension"); } @@ -472,7 +472,7 @@ public byte[] produce(ConnectionContext context, // For better interoperability, abort the maximum // fragment length negotiation, rather than terminate // the connection with a fatal alert. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Abort the maximum fragment length negotiation, " + "may overflow the maximum packet size limit."); @@ -578,7 +578,7 @@ public void consume(ConnectionContext context, // For better interoperability, abort the maximum // fragment length negotiation, rather than terminate // the connection with a fatal alert. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Abort the maximum fragment length negotiation, " + "may overflow the maximum packet size limit."); diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java index 46280a0535516..0c708b194cb89 100644 --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -273,7 +273,7 @@ enum NamedGroup { | NoSuchAlgorithmException exp) { if (namedGroupSpec != NamedGroupSpec.NAMED_GROUP_XDH) { mediator = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No AlgorithmParameters for " + name, exp); } @@ -294,7 +294,7 @@ enum NamedGroup { // AlgorithmParameters.getInstance(name); } catch (NoSuchAlgorithmException nsae) { mediator = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No AlgorithmParameters for " + name, nsae); } @@ -382,7 +382,7 @@ public static List namesOf(String[] namedGroups) { for (String ss : namedGroups) { NamedGroup ng = NamedGroup.nameOf(ss); if (ng == null || !ng.isAvailable) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore the named group (" + ss @@ -811,7 +811,7 @@ static final class SupportedGroups { } if (groupList.isEmpty() && - SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("No default named groups"); } } diff --git a/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java index 4c879e0dc4d76..89b0a72bb32d0 100644 --- a/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java +++ b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java @@ -202,7 +202,7 @@ static final class T13NewSessionTicketMessage extends NewSessionTicketMessage { this.ticket = Record.getBytes16(m); if (ticket.length == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No ticket in the NewSessionTicket handshake message"); } @@ -329,7 +329,7 @@ public byte[] produce(ConnectionContext context) throws IOException { if (hc instanceof ServerHandshakeContext) { // Is this session resumable? if (!hc.handshakeSession.isRejoinable()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session ticket produced: " + "session is not resumable"); } @@ -347,7 +347,7 @@ public byte[] produce(ConnectionContext context) throws IOException { SSLExtension.PSK_KEY_EXCHANGE_MODES); if (pkemSpec == null || !pkemSpec.contains(PskKeyExchangeMode.PSK_DHE_KE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session ticket produced: " + "client does not support psk_dhe_ke"); } @@ -358,7 +358,7 @@ public byte[] produce(ConnectionContext context) throws IOException { // Check if we have sent a PSK already, then we know it is // using an allowable PSK exchange key mode. if (!hc.handshakeSession.isPSKable()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session ticket produced: " + "No session ticket allowed in this session"); } @@ -372,7 +372,7 @@ public byte[] produce(ConnectionContext context) throws IOException { hc.sslContext.engineGetServerSessionContext(); int sessionTimeoutSeconds = sessionCache.getSessionTimeout(); if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session ticket produced: " + "session timeout is too long"); } @@ -459,7 +459,7 @@ private NewSessionTicketMessage generateNST(HandshakeContext hc, if (!nstm.isValid()) { hc.statelessResumption = false; } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced NewSessionTicket stateless " + "post-handshake message", nstm); } @@ -474,7 +474,7 @@ private NewSessionTicketMessage generateNST(HandshakeContext hc, sessionCache.getSessionTimeout(), hc.sslContext.getSecureRandom(), nonce, newId.getId()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced NewSessionTicket " + "post-handshake message", nstm); } @@ -488,7 +488,7 @@ private NewSessionTicketMessage generateNST(HandshakeContext hc, return nstm; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No NewSessionTicket created"); } @@ -526,7 +526,7 @@ public byte[] produce(ConnectionContext context, shc.sslContext.engineGetServerSessionContext(); int sessionTimeoutSeconds = sessionCache.getSessionTimeout(); if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Session timeout is too long. No ticket sent."); } @@ -540,7 +540,7 @@ public byte[] produce(ConnectionContext context, NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(shc, sessionTimeoutSeconds, new SessionTicketSpec().encrypt(shc, sessionCopy)); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced NewSessionTicket stateless handshake message", nstm); @@ -579,7 +579,7 @@ public void consume(ConnectionContext context, HandshakeContext hc = (HandshakeContext)context; NewSessionTicketMessage nstm = new T13NewSessionTicketMessage(hc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming NewSessionTicket message", nstm); } @@ -590,7 +590,7 @@ public void consume(ConnectionContext context, // discard tickets with timeout 0 if (nstm.ticketLifetime <= 0 || nstm.ticketLifetime > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Discarding NewSessionTicket with lifetime " + nstm.ticketLifetime, nstm); @@ -599,7 +599,7 @@ public void consume(ConnectionContext context, } if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Session cache lifetime is too long. " + "Discarding ticket."); @@ -611,7 +611,7 @@ public void consume(ConnectionContext context, SecretKey resumptionMasterSecret = sessionToSave.getResumptionMasterSecret(); if (resumptionMasterSecret == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Session has no resumption master secret. " + "Ignoring ticket."); @@ -637,7 +637,7 @@ public void consume(ConnectionContext context, sessionCopy.setPskIdentity(nstm.ticket); sessionCache.put(sessionCopy, sessionCopy.isPSK()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("MultiNST PSK (Server): " + Utilities.toHexString(Arrays.copyOf(nstm.ticket, 16))); } @@ -665,7 +665,7 @@ public void consume(ConnectionContext context, NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(hc, message); if (nstm.ticket.length == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("NewSessionTicket ticket was empty"); } return; @@ -674,7 +674,7 @@ public void consume(ConnectionContext context, // discard tickets with timeout 0 if (nstm.ticketLifetime <= 0 || nstm.ticketLifetime > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Discarding NewSessionTicket with lifetime " + nstm.ticketLifetime, nstm); @@ -686,7 +686,7 @@ public void consume(ConnectionContext context, hc.sslContext.engineGetClientSessionContext(); if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Session cache lifetime is too long. " + "Discarding ticket."); @@ -695,7 +695,7 @@ public void consume(ConnectionContext context, } hc.handshakeSession.setPskIdentity(nstm.ticket); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming NewSessionTicket\n" + nstm); } } diff --git a/src/java.base/share/classes/sun/security/ssl/OutputRecord.java b/src/java.base/share/classes/sun/security/ssl/OutputRecord.java index f2c30b3ff72a9..416d5d1b5ef9d 100644 --- a/src/java.base/share/classes/sun/security/ssl/OutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/OutputRecord.java @@ -188,7 +188,7 @@ void changeWriteCiphers(SSLWriteCipher writeCipher, recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -222,7 +222,7 @@ void changeWriteCiphers(SSLWriteCipher writeCipher, recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "key_update handshake message"); } diff --git a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java index 819fdd589cb4f..b99c017583807 100644 --- a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java @@ -341,7 +341,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable pre_shared_key extension"); } @@ -393,7 +393,7 @@ public void consume(ConnectionContext context, } } if (b == null || s == null) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Stateless session ticket invalid"); @@ -402,7 +402,7 @@ public void consume(ConnectionContext context, } if (s != null && canRejoin(clientHello, shc, s)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Resuming session: ", s); } @@ -435,7 +435,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, // Check protocol version if (result && s.getProtocolVersion() != shc.negotiatedProtocol) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Can't resume, incorrect protocol version"); @@ -449,7 +449,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, try { s.getPeerPrincipal(); } catch (SSLPeerUnverifiedException e) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -466,7 +466,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, if (result && !shc.localSupportedCertSignAlgs.containsAll(sessionSigAlgs)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Can't resume. Session uses different " + "signature algorithms"); } @@ -480,7 +480,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, if (result && identityAlg != null) { String sessionIdentityAlg = s.getIdentificationProtocol(); if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Can't resume, endpoint id" + @@ -494,7 +494,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, // Ensure cipher suite can be negotiated if (result && (!shc.isNegotiable(s.getSuite()) || !clientHello.cipherSuites.contains(s.getSuite()))) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, unavailable session cipher suite"); @@ -653,7 +653,7 @@ public byte[] produce(ConnectionContext context, // The producing happens in client side only. ClientHandshakeContext chc = (ClientHandshakeContext)context; if (!chc.isResumption || chc.resumingSession == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session to resume."); } return null; @@ -663,7 +663,7 @@ public byte[] produce(ConnectionContext context, Collection sessionSigAlgs = chc.resumingSession.getLocalSupportedSignatureSchemes(); if (!chc.localSupportedCertSignAlgs.containsAll(sessionSigAlgs)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Existing session uses different " + "signature algorithms"); } @@ -673,7 +673,7 @@ public byte[] produce(ConnectionContext context, // The session must have a pre-shared key SecretKey psk = chc.resumingSession.getPreSharedKey(); if (psk == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Existing session has no PSK."); } return null; @@ -687,7 +687,7 @@ public byte[] produce(ConnectionContext context, } if (chc.pskIdentity == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "PSK has no identity, or identity was already used"); } @@ -699,7 +699,7 @@ public byte[] produce(ConnectionContext context, chc.sslContext.engineGetClientSessionContext(); sessionCache.remove(chc.resumingSession.getSessionId(), true); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Found resumable session. Preparing PSK message."); SSLLogger.fine( @@ -836,7 +836,7 @@ class CHPreSharedKeyOnLoadAbsence implements HandshakeAbsence { public void absent(ConnectionContext context, HandshakeMessage message) throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Handling pre_shared_key absence."); } @@ -901,7 +901,7 @@ public void consume(ConnectionContext context, } SHPreSharedKeySpec shPsk = new SHPreSharedKeySpec(chc, buffer); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Received pre_shared_key extension: ", shPsk); } @@ -911,7 +911,7 @@ public void consume(ConnectionContext context, "Selected identity index is not in correct range."); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Resuming session: ", chc.resumingSession); } @@ -925,7 +925,7 @@ public void absent(ConnectionContext context, HandshakeMessage message) throws IOException { ClientHandshakeContext chc = (ClientHandshakeContext)context; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Handling pre_shared_key absence."); } diff --git a/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java b/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java index e510fe92b0ef3..c826d9c89e33c 100644 --- a/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java +++ b/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java @@ -246,7 +246,7 @@ final class PredefinedDHParameterSpecs { Matcher spacesMatcher = spacesPattern.matcher(property); property = spacesMatcher.replaceAll(""); - if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("sslctx")) { SSLLogger.fine( "The Security Property " + PROPERTY_NAME + ": " + property); @@ -262,7 +262,7 @@ final class PredefinedDHParameterSpecs { String primeModulus = paramsFinder.group(1); BigInteger p = new BigInteger(primeModulus, 16); if (!p.isProbablePrime(PRIME_CERTAINTY)) { - if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("sslctx")) { SSLLogger.fine( "Prime modulus p in Security Property, " + PROPERTY_NAME + ", is not a prime: " + @@ -279,7 +279,7 @@ final class PredefinedDHParameterSpecs { DHParameterSpec spec = new DHParameterSpec(p, g); defaultParams.put(primeLen, spec); } - } else if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("sslctx")) { SSLLogger.fine("Invalid Security Property, " + PROPERTY_NAME + ", definition"); } diff --git a/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java b/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java index 07707c5163a45..a4f343ccb06c9 100644 --- a/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java @@ -184,7 +184,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.PSK_KEY_EXCHANGE_MODES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable psk_key_exchange_modes extension"); } @@ -216,7 +216,7 @@ public void consume(ConnectionContext context, if (!spec.contains(PskKeyExchangeMode.PSK_DHE_KE)) { shc.isResumption = false; shc.resumingSession = null; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "no supported psk_dhe_ke PSK key exchange mode"); @@ -247,7 +247,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.PSK_KEY_EXCHANGE_MODES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unavailable psk_key_exchange_modes extension"); } @@ -287,7 +287,7 @@ public void absent(ConnectionContext context, if (shc.isResumption) { // resumingSession may not be set shc.isResumption = false; shc.resumingSession = null; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "no supported psk_dhe_ke PSK key exchange mode"); diff --git a/src/java.base/share/classes/sun/security/ssl/QuicEngineOutputRecord.java b/src/java.base/share/classes/sun/security/ssl/QuicEngineOutputRecord.java index 893eb2821169b..7e307ba9d2759 100644 --- a/src/java.base/share/classes/sun/security/ssl/QuicEngineOutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/QuicEngineOutputRecord.java @@ -75,14 +75,14 @@ void encodeAlert(byte level, byte description) throws IOException { recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "alert message: " + Alert.nameOf(description)); } return; } if (level == Alert.Level.WARNING.level) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Suppressing warning-level " + "alert message: " + Alert.nameOf(description)); } @@ -90,7 +90,7 @@ void encodeAlert(byte level, byte description) throws IOException { } if (alert != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Suppressing subsequent alert: " + description + ", original: " + alert.id); } @@ -109,7 +109,7 @@ void encodeHandshake(byte[] source, recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake message", ByteBuffer.wrap(source, offset, length)); diff --git a/src/java.base/share/classes/sun/security/ssl/QuicKeyManager.java b/src/java.base/share/classes/sun/security/ssl/QuicKeyManager.java index fb9077af022d1..4613dcf96ff58 100644 --- a/src/java.base/share/classes/sun/security/ssl/QuicKeyManager.java +++ b/src/java.base/share/classes/sun/security/ssl/QuicKeyManager.java @@ -244,7 +244,7 @@ void discardKeys() { if (toDiscard == null) { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("discarding keys (keyphase=" + toDiscard.writeCipher.getKeyPhase() + ") of " + this.keySpace + " key space"); @@ -389,7 +389,7 @@ void discardKeys() { if (toDiscard == null) { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("discarding keys (keyphase=" + toDiscard.writeCipher.getKeyPhase() + ") of " + this.keySpace + " key space"); @@ -570,7 +570,7 @@ void discardKeys() { if (series == null) { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("discarding key (series) of " + this.keySpace + " key space"); } @@ -611,7 +611,7 @@ void decryptPacket(final long packetNumber, final int keyPhase, if (series.canUseOldDecryptKey(packetNumber)) { final QuicReadCipher oldReadCipher = series.old; assert oldReadCipher != null : "old key is unexpectedly null"; - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("using old read key to decrypt packet: " + packetNumber + ", with incoming key phase: " + keyPhase + ", current key phase: " + @@ -633,7 +633,7 @@ void decryptPacket(final long packetNumber, final int keyPhase, // KEY_UPDATE_ERROR. This indicates that a peer has // received and acknowledged a packet that initiates a key // update, but has not updated keys in response. - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("peer used incorrect key, was" + " expected to use updated key of" + " key phase: " + currentKeyPhase + @@ -646,7 +646,7 @@ void decryptPacket(final long packetNumber, final int keyPhase, } return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("detected ONE_RTT key update, current key " + "phase: " + currentKeyPhase + ", incoming key phase: " + keyPhase @@ -717,7 +717,7 @@ private boolean maybeInitiateKeyUpdate(final KeySeries currentSeries, } final long numEncrypted = cipher.getNumEncrypted(); if (numEncrypted >= 0.8 * confidentialityLimit) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("about to reach confidentiality limit, " + "attempting to initiate a 1-RTT key update," + " packet number: " + @@ -732,7 +732,7 @@ private boolean maybeInitiateKeyUpdate(final KeySeries currentSeries, : "key phase of updated key unexpectedly matches " + "the key phase " + cipher.getKeyPhase() + " of current keys"; - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "1-RTT key update initiated, new key phase: " + newKeyPhase); @@ -755,7 +755,7 @@ private boolean initiateKeyUpdate(final KeySeries series) { // current key phase. This ensures that keys are // available to both peers before // another key update can be initiated. - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "skipping key update initiation because peer " + "hasn't yet sent us a packet encrypted with " + @@ -803,7 +803,7 @@ private void decryptUsingNextKeys( // (we avoid timing attacks by not generating // keys during decryption, our key generation // only happens during encryption) - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("next keys unavailable," + " won't decrypt a packet which appears to be" + " a key update"); @@ -815,7 +815,7 @@ private void decryptUsingNextKeys( // use the next keys to attempt decrypting currentKeySeries.next.readCipher.decryptPacket(packetNumber, packet, headerLength, output); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "decrypted using next keys for peer-initiated" + " key update; will now switch to new key phase: " + @@ -1025,14 +1025,14 @@ private KeySeries rolloverKeys(final QuicVersion version, // update the key series this.keySeries = newSeries; if (oldReadCipher != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "discarding old read key of key phase: " + oldReadCipher.getKeyPhase()); } oldReadCipher.discard(false); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("discarding write key of key phase: " + writeCipherToDiscard.getKeyPhase()); } diff --git a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java index 6765f554fcc67..18790a58c11a3 100644 --- a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java @@ -560,7 +560,7 @@ public void consumeHandshakeBytes(KeySpace keySpace, ByteBuffer payload) // incoming crypto buffer is null. Validate message type, // check if size is available byte messageType = payload.get(payload.position()); - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine("Received message of type 0x" + Integer.toHexString(messageType & 0xFF)); } @@ -835,7 +835,7 @@ public boolean tryMarkHandshakeDone() { final boolean confirmed = HANDSHAKE_STATE_HANDLE.compareAndSet(this, NEED_SEND_HANDSHAKE_DONE, HANDSHAKE_CONFIRMED); if (confirmed) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine("QuicTLSEngine (server) marked handshake " + "state as HANDSHAKE_CONFIRMED"); } @@ -853,7 +853,7 @@ public boolean tryReceiveHandshakeDone() { final boolean confirmed = HANDSHAKE_STATE_HANDLE.compareAndSet(this, NEED_RECV_HANDSHAKE_DONE, HANDSHAKE_CONFIRMED); if (confirmed) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine( "QuicTLSEngine (client) received HANDSHAKE_DONE," + " marking state as HANDSHAKE_DONE"); diff --git a/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java index 701ba35174e8f..ec91cb4509a6f 100644 --- a/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java @@ -190,7 +190,7 @@ public byte[] produce(ConnectionContext context, throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, "Cannot generate RSA premaster secret", gse); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced RSA ClientKeyExchange handshake message", ckem); } @@ -270,7 +270,7 @@ public void consume(ConnectionContext context, RSAClientKeyExchangeMessage ckem = new RSAClientKeyExchangeMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming RSA ClientKeyExchange handshake message", ckem); } diff --git a/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java index 311ac97e744db..d176d7311d04b 100644 --- a/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java @@ -35,7 +35,6 @@ import java.security.PublicKey; import java.security.SecureRandom; import java.security.interfaces.RSAPublicKey; -import java.security.spec.AlgorithmParameterSpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; @@ -150,7 +149,7 @@ static RSAPremasterSecret decode(ServerHandshakeContext shc, needFailover = !KeyUtil.isOracleJCEProvider( cipher.getProvider().getName()); } catch (InvalidKeyException | UnsupportedOperationException iue) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("The Cipher provider " + safeProviderName(cipher) + " caused exception: " + iue.getMessage()); @@ -197,7 +196,7 @@ private static String safeProviderName(Cipher cipher) { try { return cipher.getProvider().toString(); } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Retrieving The Cipher provider name" + " caused exception ", e); } @@ -205,7 +204,7 @@ private static String safeProviderName(Cipher cipher) { try { return cipher.toString() + " (provider name not available)"; } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Retrieving The Cipher name" + " caused exception ", e); } @@ -220,7 +219,7 @@ private static SecretKey generatePremasterSecret( int clientVersion, int serverVersion, byte[] encodedSecret, SecureRandom generator) throws GeneralSecurityException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Generating a premaster secret"); } @@ -235,7 +234,7 @@ private static SecretKey generatePremasterSecret( } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException iae) { // unlikely to happen, otherwise, must be a provider exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("RSA premaster secret generation error", iae); } diff --git a/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java index 8633b9458cea9..43f2ef95ba87c 100644 --- a/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java @@ -264,7 +264,7 @@ public byte[] produce(ConnectionContext context, RSAServerKeyExchangeMessage skem = new RSAServerKeyExchangeMessage( shc, x509Possession, rsaPossession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced RSA ServerKeyExchange handshake message", skem); } @@ -296,7 +296,7 @@ public void consume(ConnectionContext context, RSAServerKeyExchangeMessage skem = new RSAServerKeyExchangeMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming RSA ServerKeyExchange handshake message", skem); } diff --git a/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java b/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java index e1348badd3097..90b9e99992594 100644 --- a/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java @@ -138,7 +138,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable renegotiation_info extension"); } @@ -182,7 +182,7 @@ public byte[] produce(ConnectionContext context, return extData; } else { // not secure renegotiation if (HandshakeContext.allowUnsafeRenegotiation) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Using insecure renegotiation"); } @@ -216,7 +216,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + CH_RENEGOTIATION_INFO.name); } @@ -280,7 +280,7 @@ public void absent(ConnectionContext context, for (int id : clientHello.cipherSuiteIds) { if (id == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.id) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Safe renegotiation, using the SCSV signaling"); } @@ -294,7 +294,7 @@ public void absent(ConnectionContext context, "Failed to negotiate the use of secure renegotiation"); } // otherwise, allow legacy hello message - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Warning: No renegotiation " + "indication in ClientHello, allow legacy ClientHello"); } @@ -306,13 +306,13 @@ public void absent(ConnectionContext context, "Inconsistent secure renegotiation indication"); } else { // renegotiation, not secure if (HandshakeContext.allowUnsafeRenegotiation) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Using insecure renegotiation"); } } else { // Unsafe renegotiation should have been aborted in // earlier processes. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Terminate insecure renegotiation"); } throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, @@ -345,7 +345,7 @@ public byte[] produce(ConnectionContext context, if (requestedSpec == null && !shc.conContext.secureRenegotiation) { // Ignore, no renegotiation_info extension or SCSV signaling // requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable renegotiation_info extension"); } @@ -354,7 +354,7 @@ public byte[] produce(ConnectionContext context, if (!shc.conContext.secureRenegotiation) { // Ignore, no secure renegotiation is negotiated. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No secure renegotiation has been negotiated"); } @@ -515,7 +515,7 @@ public void absent(ConnectionContext context, "Failed to negotiate the use of secure renegotiation"); } // otherwise, allow legacy hello message - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Warning: No renegotiation " + "indication in ServerHello, allow legacy ServerHello"); } @@ -527,13 +527,13 @@ public void absent(ConnectionContext context, "Inconsistent secure renegotiation indication"); } else { // renegotiation, not secure if (HandshakeContext.allowUnsafeRenegotiation) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Using insecure renegotiation"); } } else { // Unsafe renegotiation should have been aborted in // earlier processes. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Terminate insecure renegotiation"); } throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, diff --git a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java index 1d5a4c4e73d45..594766ea0fd8c 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java @@ -454,7 +454,7 @@ private boolean checkRsaSsaPssParams( .equalsIgnoreCase(paramDigestAlg)); } catch (InvalidParameterSpecException e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid AlgorithmParameters: " + parameters + "; Error: " + e.getMessage()); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLCipher.java b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java index 4a52a2ea58340..5dfa5be3420ed 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLCipher.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java @@ -392,7 +392,7 @@ enum SSLCipher { if (values[1].contains(tag[0])) { index = 0; } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("jdk.tls.keyLimits: Unknown action: " + entry); } @@ -413,13 +413,13 @@ enum SSLCipher { "Length exceeded limits"); } } catch (NumberFormatException e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("jdk.tls.keyLimits: " + e.getMessage() + ": " + entry); } continue; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("jdk.tls.keyLimits: entry = " + entry + ". " + values[0] + ":" + tag[index] + " = " + size); } @@ -468,7 +468,7 @@ private static boolean isTransformationAvailable(String transformation) { Cipher.getInstance(transformation); return true; } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Transformation " + transformation + " is" + " not available."); } @@ -860,7 +860,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, "JCE provider " + cipher.getProvider().getName(), sbe); } pt.position(pos); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -930,7 +930,7 @@ public int encrypt(byte contentType, ByteBuffer bb) { authenticator.increaseSequenceNumber(); } - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.finest( "Padded plaintext before ENCRYPTION", bb.duplicate()); } @@ -1050,7 +1050,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, "JCE provider " + cipher.getProvider().getName(), sbe); } - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Padded plaintext after DECRYPTION", pt.duplicate().position(pos)); @@ -1182,7 +1182,7 @@ public int encrypt(byte contentType, ByteBuffer bb) { int len = addPadding(bb, blockSize); bb.position(pos); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Padded plaintext before ENCRYPTION", bb.duplicate()); @@ -1326,7 +1326,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, "JCE provider " + cipher.getProvider().getName(), sbe); } - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine("Padded plaintext after DECRYPTION", pt.duplicate().position(pos)); } @@ -1478,7 +1478,7 @@ public int encrypt(byte contentType, ByteBuffer bb) { int len = addPadding(bb, blockSize); bb.position(pos); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Padded plaintext before ENCRYPTION", bb.duplicate()); @@ -1650,7 +1650,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, pt.position(pos); pt.limit(pos + len); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -1737,7 +1737,7 @@ public int encrypt(byte contentType, // DON'T encrypt the nonce for AEAD mode. int len, pos = bb.position(); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext before ENCRYPTION", bb.duplicate()); @@ -1823,7 +1823,7 @@ static final class GcmReadCipher extends SSLReadCipher { keyLimitCountdown = cipherLimits.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH) + ":" + tag[0], 0L); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("KeyLimit read side: algorithm = " + algorithm + ":" + tag[0] + "\ncountdown value = " + keyLimitCountdown); @@ -1932,7 +1932,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, contentType = pt.get(i); pt.limit(i); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -1984,7 +1984,7 @@ private static final class GcmWriteCipher extends SSLWriteCipher { keyLimitCountdown = cipherLimits.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH) + ":" + tag[0], 0L); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("KeyLimit write side: algorithm = " + algorithm + ":" + tag[0] + "\ncountdown value = " + keyLimitCountdown); @@ -2026,7 +2026,7 @@ public int encrypt(byte contentType, cipher.updateAAD(aad); int len, pos = bb.position(); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext before ENCRYPTION", bb.duplicate()); @@ -2182,7 +2182,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, pt.position(pos); pt.limit(pos + len); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -2231,7 +2231,7 @@ private static final class CC20P1305WriteCipher extends SSLWriteCipher { keyLimitCountdown = cipherLimits.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH) + ":" + tag[0], 0L); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("algorithm = " + algorithm + ":" + tag[0] + "\ncountdown value = " + keyLimitCountdown); @@ -2273,7 +2273,7 @@ public int encrypt(byte contentType, // DON'T encrypt the nonce for AEAD mode. int pos = bb.position(); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext before ENCRYPTION", bb.duplicate()); @@ -2450,7 +2450,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, contentType = pt.get(i); pt.limit(i); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -2499,7 +2499,7 @@ private static final class CC20P1305WriteCipher extends SSLWriteCipher { keyLimitCountdown = cipherLimits.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH) + ":" + tag[0], 0L); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("algorithm = " + algorithm + ":" + tag[0] + "\ncountdown value = " + keyLimitCountdown); @@ -2541,7 +2541,7 @@ public int encrypt(byte contentType, cipher.updateAAD(aad); int pos = bb.position(); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext before ENCRYPTION", bb.duplicate()); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index 6d1834ad2b721..ace60e41af9b0 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -204,7 +204,7 @@ final class SSLConfiguration implements Cloneable { if (nstServerCount == null || nstServerCount < 0 || nstServerCount > 10) { serverNewSessionTicketCount = SERVER_NST_DEFAULT; - if (nstServerCount != null && SSLLogger.isOn && + if (nstServerCount != null && SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "jdk.tls.server.newSessionTicketCount defaults to " + @@ -213,7 +213,7 @@ final class SSLConfiguration implements Cloneable { } } else { serverNewSessionTicketCount = nstServerCount; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "jdk.tls.server.newSessionTicketCount set to " + serverNewSessionTicketCount); @@ -586,7 +586,7 @@ private static String[] getCustomizedSignatureScheme(String propertyName) { String property = System.getProperty(propertyName); // this method is called from class initializer; logging here // will occasionally pin threads and deadlock if called from a virtual thread - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx") + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx") && !Thread.currentThread().isVirtual()) { SSLLogger.fine( "System property " + propertyName + " is set to '" + @@ -615,7 +615,7 @@ private static String[] getCustomizedSignatureScheme(String propertyName) { if (scheme != null && scheme.isAvailable) { signatureSchemes.add(schemeName); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx") + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx") && !Thread.currentThread().isVirtual()) { SSLLogger.fine( "The current installed providers do not " + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java index d50a9a10b76d1..be324eb0949eb 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java @@ -104,11 +104,11 @@ protected void engineInit(KeyManager[] km, TrustManager[] tm, * first connection to time out and fail. Make sure it is * primed and ready by getting some initial output from it. */ - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.finest("trigger seeding of SecureRandom"); } secureRandom.nextInt(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.finest("done seeding of SecureRandom"); } @@ -143,7 +143,7 @@ private X509ExtendedKeyManager chooseKeyManager(KeyManager[] kms) { return (X509ExtendedKeyManager)km; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.warning( "X509KeyManager passed to SSLContext.init(): need an " + "X509ExtendedKeyManager for SSLEngine use"); @@ -246,7 +246,7 @@ StatusResponseManager getStatusResponseManager() { contextLock.lock(); try { if (statusResponseManager == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.finest( "Initializing StatusResponseManager"); } @@ -383,7 +383,7 @@ private static List getApplicableCipherSuites( suite.name, null)) { suites.add(suite); isSupported = true; - } else if (SSLLogger.isOn && + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx,verbose")) { SSLLogger.fine( "Ignore disabled cipher suite: " + suite.name); @@ -392,7 +392,7 @@ private static List getApplicableCipherSuites( break; } - if (!isSupported && SSLLogger.isOn && + if (!isSupported && SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx,verbose")) { SSLLogger.finest( "Ignore unsupported cipher suite: " + suite); @@ -410,7 +410,7 @@ private static Collection getCustomizedCipherSuites( String propertyName) { String property = System.getProperty(propertyName); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.fine( "System property " + propertyName + " is set to '" + property + "'"); @@ -437,7 +437,7 @@ private static Collection getCustomizedCipherSuites( try { suite = CipherSuite.nameOf(cipherSuiteNames[i]); } catch (IllegalArgumentException iae) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.fine( "Unknown or unsupported cipher suite name: " + cipherSuiteNames[i]); @@ -449,7 +449,7 @@ private static Collection getCustomizedCipherSuites( if (suite != null && suite.isAvailable()) { cipherSuites.add(suite); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.fine( "The current installed providers do not " + "support cipher suite: " + cipherSuiteNames[i]); @@ -907,7 +907,7 @@ private static final class DefaultManagersHolder { tmMediator = getTrustManagers(); } catch (Exception e) { reserved = e; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.warning( "Failed to load default trust managers", e); } @@ -919,7 +919,7 @@ private static final class DefaultManagersHolder { kmMediator = getKeyManagers(); } catch (Exception e) { reserved = e; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.warning( "Failed to load default key managers", e); } @@ -977,7 +977,7 @@ private static KeyManager[] getKeyManagers() throws Exception { String defaultKeyStore = props.get("keyStore"); String defaultKeyStoreType = props.get("keyStoreType"); String defaultKeyStoreProvider = props.get("keyStoreProvider"); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.fine("keyStore is : " + defaultKeyStore); SSLLogger.fine("keyStore type is : " + defaultKeyStoreType); @@ -1007,7 +1007,7 @@ private static KeyManager[] getKeyManagers() throws Exception { // Try to initialize key store. if ((defaultKeyStoreType.length()) != 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.finest("init keystore"); } if (defaultKeyStoreProvider.isEmpty()) { @@ -1030,7 +1030,7 @@ private static KeyManager[] getKeyManagers() throws Exception { /* * Try to initialize key manager. */ - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.fine("init keymanager of type " + KeyManagerFactory.getDefaultAlgorithm()); } @@ -1068,7 +1068,7 @@ private static final class DefaultSSLContextHolder { // exception object, which may be not garbage collection // friendly as 'reservedException' is a static filed. reserved = new KeyManagementException(e.getMessage()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.warning( "Failed to load default SSLContext", e); } @@ -1097,7 +1097,7 @@ public DefaultSSLContext() throws Exception { super.engineInit(DefaultManagersHolder.keyManagers, DefaultManagersHolder.trustManagers, null); } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.fine("default context init failed: ", e); } throw e; diff --git a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java index 4b19f5a9d7bba..5e23e6ee37b5c 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -330,7 +330,7 @@ private Ciphertext encode( // application data may be discarded accordingly. As could // be an issue for some applications. This impact can be // mitigated by sending the last flight twice. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,verbose")) { SSLLogger.finest("retransmit the last flight messages"); } @@ -397,7 +397,7 @@ private HandshakeStatus tryKeyUpdate( if ((conContext.handshakeContext == null) && !conContext.isOutboundClosed() && !conContext.isBroken) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger key update"); } beginHandshake(); @@ -419,7 +419,7 @@ private HandshakeStatus tryNewSessionTicket( !conContext.isOutboundClosed() && !conContext.isInboundClosed() && !conContext.isBroken) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger NST"); } conContext.conSession.updateNST = false; @@ -612,7 +612,7 @@ private SSLEngineResult readRecord( } catch (SSLException ssle) { // Need to discard invalid records for DTLS protocols. if (sslContext.isDTLS()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,verbose")) { SSLLogger.finest("Discard invalid DTLS records", ssle); } @@ -780,7 +780,7 @@ public void closeInbound() throws SSLException { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("Closing inbound of SSLEngine"); } @@ -819,7 +819,7 @@ public void closeOutbound() { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("Closing outbound of SSLEngine"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java index 1bfbd9f51bf00..6e08fc71664a6 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -172,7 +172,7 @@ private Plaintext[] decode(ByteBuffer packet) return null; } - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw read", packet); } @@ -209,7 +209,7 @@ private Plaintext[] decodeInputRecord(ByteBuffer packet) byte minorVersion = packet.get(); // pos: 2 int contentLen = Record.getInt16(packet); // pos: 3, 4 - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "READ: " + ProtocolVersion.nameOf(majorVersion, minorVersion) + @@ -388,7 +388,7 @@ private Plaintext[] handleUnknownRecord(ByteBuffer packet) * error message, one that's treated as fatal by * clients (Otherwise we'll hang.) */ - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "Requested to negotiate unsupported SSLv2!"); } @@ -410,7 +410,7 @@ private Plaintext[] handleUnknownRecord(ByteBuffer packet) ByteBuffer converted = convertToClientHello(packet); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine( "[Converted] ClientHello", converted); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java index 4a689a84d5f54..1c8751e66fe75 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -73,7 +73,7 @@ boolean isClosed() { @Override void encodeAlert(byte level, byte description) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "alert message: " + Alert.nameOf(description)); } @@ -91,7 +91,7 @@ void encodeAlert(byte level, byte description) { void encodeHandshake(byte[] source, int offset, int length) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake message", ByteBuffer.wrap(source, offset, length)); @@ -138,7 +138,7 @@ void encodeHandshake(byte[] source, @Override void encodeChangeCipherSpec() { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -171,14 +171,14 @@ Ciphertext encode( ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { if (isClosed) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "application data or cached messages"); } return null; } else if (isCloseWaiting) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "application data"); } @@ -193,7 +193,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, ByteBuffer destination) throws IOException { if (writeCipher.authenticator.seqNumOverflow()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "sequence number extremely close to overflow " + "(2^64-1 packets). Closing connection."); @@ -275,7 +275,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, destination.limit(destination.position()); destination.position(dstContent); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.APPLICATION_DATA.name + @@ -288,7 +288,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, dstPos, dstLim, headerSize, protocolVersion); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer temporary = destination.duplicate(); temporary.limit(temporary.position()); temporary.position(dstPos); @@ -317,7 +317,7 @@ private Ciphertext acquireCiphertext( // // Please don't change the limit of the destination buffer. destination.put(SSLRecord.v2NoCipher); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); } @@ -331,7 +331,7 @@ private Ciphertext acquireCiphertext( // deliver the SSLv2 format ClientHello message // // Please don't change the limit of the destination buffer. - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { if (SSLLogger.isOn("record")) { SSLLogger.fine(Thread.currentThread().getName() + ", WRITE: SSLv2 ClientHello message" + @@ -525,7 +525,7 @@ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { dstBuf.limit(dstBuf.position()); dstBuf.position(dstContent); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.nameOf(memo.contentType) + @@ -543,7 +543,7 @@ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { memo.encodeCipher.dispose(); } - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer temporary = dstBuf.duplicate(); temporary.limit(temporary.position()); temporary.position(dstPos); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index fb0490d70f11e..47a0d0b0e4407 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -844,7 +844,7 @@ private static Collection getDisabledExtensions( String property = System.getProperty(propertyName); // this method is called from class initializer; logging here // will occasionally pin threads and deadlock if called from a virtual thread - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx") + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx") && !Thread.currentThread().isVirtual()) { SSLLogger.fine( "System property " + propertyName + " is set to '" + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java b/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java index 5ad93cfc836fd..66f6293302e9a 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -43,7 +43,7 @@ final class SSLExtensions { // Extension map for debug logging private final Map logMap = - SSLLogger.isOn ? new LinkedHashMap<>() : null; + SSLLogger.isOn() ? new LinkedHashMap<>() : null; SSLExtensions(HandshakeMessage handshakeMessage) { this.handshakeMessage = handshakeMessage; @@ -93,7 +93,7 @@ final class SSLExtensions { // However, the implementation of the limit is complicated // and inefficient, and may not worthy the maintenance. isSupported = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Received buggy supported_groups extension " + "in the ServerHello handshake message"); @@ -143,7 +143,7 @@ final class SSLExtensions { m.get(extData); logMap.put(extId, extData); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unknown or unsupported extension", toString(extId, extData)); @@ -171,7 +171,7 @@ void consumeOnLoad(HandshakeContext context, for (SSLExtension extension : extensions) { if (context.negotiatedProtocol != null && !extension.isAvailable(context.negotiatedProtocol)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unsupported extension: " + extension.name); } @@ -181,7 +181,7 @@ void consumeOnLoad(HandshakeContext context, if (!extMap.containsKey(extension)) { if (extension.onLoadAbsence != null) { extension.absentOnLoad(context, handshakeMessage); - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + extension.name); } @@ -190,7 +190,7 @@ void consumeOnLoad(HandshakeContext context, if (extension.onLoadConsumer == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unsupported extension: " + extension.name); } @@ -200,7 +200,7 @@ void consumeOnLoad(HandshakeContext context, ByteBuffer m = ByteBuffer.wrap(extMap.get(extension)); extension.consumeOnLoad(context, handshakeMessage, m); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consumed extension: " + extension.name); } } @@ -215,7 +215,7 @@ void consumeOnTrade(HandshakeContext context, if (!extMap.containsKey(extension)) { if (extension.onTradeAbsence != null) { extension.absentOnTrade(context, handshakeMessage); - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + extension.name); } @@ -223,7 +223,7 @@ void consumeOnTrade(HandshakeContext context, } if (extension.onTradeConsumer == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore impact of unsupported extension: " + extension.name); @@ -232,7 +232,7 @@ void consumeOnTrade(HandshakeContext context, } extension.consumeOnTrade(context, handshakeMessage); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Populated with extension: " + extension.name); } } @@ -245,7 +245,7 @@ void produce(HandshakeContext context, SSLExtension[] extensions) throws IOException { for (SSLExtension extension : extensions) { if (extMap.containsKey(extension)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore, duplicated extension: " + extension.name); @@ -254,7 +254,7 @@ void produce(HandshakeContext context, } if (extension.networkProducer == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no extension producer defined: " + extension.name); @@ -267,7 +267,7 @@ void produce(HandshakeContext context, extMap.put(extension, encoded); encodedLength += encoded.length + 4; // extension_type (2) // extension_data length(2) - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { // The extension is not available in the context. SSLLogger.fine( "Ignore, context unavailable extension: " + @@ -284,7 +284,7 @@ void reproduce(HandshakeContext context, SSLExtension[] extensions) throws IOException { for (SSLExtension extension : extensions) { if (extension.networkProducer == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no extension producer defined: " + extension.name); @@ -305,7 +305,7 @@ void reproduce(HandshakeContext context, encodedLength += encoded.length + 4; // extension_type (2) // extension_data length(2) - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { // The extension is not available in the context. SSLLogger.fine( "Ignore, context unavailable extension: " + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java index f55ab27d297bf..7fa6fbf91b574 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java @@ -29,8 +29,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.security.cert.Extension; @@ -41,6 +39,7 @@ import java.time.format.DateTimeFormatter; import java.util.*; +import jdk.internal.vm.annotation.ForceInline; import sun.security.util.HexDumpEncoder; import sun.security.util.Debug; import sun.security.x509.*; @@ -58,10 +57,13 @@ * logging mechanisms. If the system property "javax.net.debug" is defined * and non-empty, a private debug logger implemented in this class is used. */ -public final class SSLLogger { +public final class SSLLogger implements System.Logger { private static final System.Logger logger; private static final String property; - public static final boolean isOn; + private static final boolean isOn; + + private final String loggerName; + private final boolean useCompactFormat; static { @@ -76,7 +78,7 @@ public final class SSLLogger { help(); } - logger = new SSLConsoleLogger("javax.net.ssl", p); + logger = new SSLLogger("javax.net.ssl", p); } isOn = true; } else { @@ -86,35 +88,10 @@ public final class SSLLogger { } } - private static void help() { - System.err.println(); - System.err.println("help print the help messages"); - System.err.println("expand expand debugging information"); - System.err.println(); - System.err.println("all turn on all debugging"); - System.err.println("ssl turn on ssl debugging"); - System.err.println(); - System.err.println("The following can be used with ssl:"); - System.err.println("\trecord enable per-record tracing"); - System.err.println("\thandshake print each handshake message"); - System.err.println("\tkeygen print key generation data"); - System.err.println("\tsession print session activity"); - System.err.println("\tdefaultctx print default SSL initialization"); - System.err.println("\tsslctx print SSLContext tracing"); - System.err.println("\tsessioncache print session cache tracing"); - System.err.println("\tkeymanager print key manager tracing"); - System.err.println("\ttrustmanager print trust manager tracing"); - System.err.println("\tpluggability print pluggability tracing"); - System.err.println(); - System.err.println("\thandshake debugging can be widened with:"); - System.err.println("\tdata hex dump of each handshake message"); - System.err.println("\tverbose verbose handshake message printing"); - System.err.println(); - System.err.println("\trecord debugging can be widened with:"); - System.err.println("\tplaintext hex dump of record plaintext"); - System.err.println("\tpacket print raw SSL/TLS packets"); - System.err.println(); - System.exit(0); + private SSLLogger(String loggerName, String options) { + this.loggerName = loggerName; + options = options.toLowerCase(Locale.ENGLISH); + this.useCompactFormat = !options.contains("expand"); } /** @@ -139,6 +116,11 @@ public static boolean isOn(String checkPoints) { return true; } + @ForceInline + public static boolean isOn() { + return isOn; + } + private static boolean hasOption(String option) { option = option.toLowerCase(Locale.ENGLISH); if (property.contains("all")) { @@ -161,30 +143,30 @@ private static boolean hasOption(String option) { } public static void severe(String msg, Object... params) { - SSLLogger.log(Level.ERROR, msg, params); + SSLLogger.log0(Level.ERROR, msg, params); } public static void warning(String msg, Object... params) { - SSLLogger.log(Level.WARNING, msg, params); + SSLLogger.log0(Level.WARNING, msg, params); } public static void info(String msg, Object... params) { - SSLLogger.log(Level.INFO, msg, params); + SSLLogger.log0(Level.INFO, msg, params); } public static void fine(String msg, Object... params) { - SSLLogger.log(Level.DEBUG, msg, params); + SSLLogger.log0(Level.DEBUG, msg, params); } public static void finer(String msg, Object... params) { - SSLLogger.log(Level.TRACE, msg, params); + SSLLogger.log0(Level.TRACE, msg, params); } public static void finest(String msg, Object... params) { - SSLLogger.log(Level.TRACE, msg, params); + SSLLogger.log0(Level.TRACE, msg, params); } - private static void log(Level level, String msg, Object... params) { + private static void log0(Level level, String msg, Object... params) { if (logger != null && logger.isLoggable(level)) { if (params == null || params.length == 0) { logger.log(level, msg); @@ -192,8 +174,8 @@ private static void log(Level level, String msg, Object... params) { try { String formatted = SSLSimpleFormatter.formatParameters(params); - // use the customized log method for SSLConsoleLogger - if (logger instanceof SSLConsoleLogger) { + // use the customized log method for SSLLogger + if (logger instanceof SSLLogger) { logger.log(level, msg, formatted); } else { logger.log(level, msg + ":" + LINE_SEP + formatted); @@ -205,6 +187,37 @@ private static void log(Level level, String msg, Object... params) { } } + private static void help() { + System.err.println(); + System.err.println("help print the help messages"); + System.err.println("expand expand debugging information"); + System.err.println(); + System.err.println("all turn on all debugging"); + System.err.println("ssl turn on ssl debugging"); + System.err.println(); + System.err.println("The following can be used with ssl:"); + System.err.println("\trecord enable per-record tracing"); + System.err.println("\thandshake print each handshake message"); + System.err.println("\tkeygen print key generation data"); + System.err.println("\tsession print session activity"); + System.err.println("\tdefaultctx print default SSL initialization"); + System.err.println("\tsslctx print SSLContext tracing"); + System.err.println("\tsessioncache print session cache tracing"); + System.err.println("\tkeymanager print key manager tracing"); + System.err.println("\ttrustmanager print trust manager tracing"); + System.err.println("\tpluggability print pluggability tracing"); + System.err.println(); + System.err.println("\thandshake debugging can be widened with:"); + System.err.println("\tdata hex dump of each handshake message"); + System.err.println("\tverbose verbose handshake message printing"); + System.err.println(); + System.err.println("\trecord debugging can be widened with:"); + System.err.println("\tplaintext hex dump of record plaintext"); + System.err.println("\tpacket print raw SSL/TLS packets"); + System.err.println(); + System.exit(0); + } + static String toString(Object... params) { try { return SSLSimpleFormatter.formatParameters(params); @@ -216,65 +229,55 @@ static String toString(Object... params) { // Logs a warning message and always returns false. This method // can be used as an OR Predicate to add a log in a stream filter. public static boolean logWarning(String option, String s) { - if (SSLLogger.isOn && SSLLogger.isOn(option)) { + if (SSLLogger.isOn() && SSLLogger.isOn(option)) { SSLLogger.warning(s); } return false; } - private static class SSLConsoleLogger implements Logger { - private final String loggerName; - private final boolean useCompactFormat; - - SSLConsoleLogger(String loggerName, String options) { - this.loggerName = loggerName; - options = options.toLowerCase(Locale.ENGLISH); - this.useCompactFormat = !options.contains("expand"); - } - - @Override - public String getName() { - return loggerName; - } + @Override + public String getName() { + return loggerName; + } - @Override - public boolean isLoggable(Level level) { - return level != Level.OFF; - } + @Override + public boolean isLoggable(Level level) { + return level != Level.OFF; + } - @Override - public void log(Level level, - ResourceBundle rb, String message, Throwable thrwbl) { - if (isLoggable(level)) { - try { - String formatted = + @Override + public void log(Level level, + ResourceBundle rb, String message, Throwable thrwbl) { + if (isLoggable(level)) { + try { + String formatted = SSLSimpleFormatter.format(this, level, message, thrwbl); - System.err.write(formatted.getBytes(UTF_8)); - } catch (Exception exp) { - // ignore it, just for debugging. - } + System.err.write(formatted.getBytes(UTF_8)); + } catch (Exception exp) { + // ignore it, just for debugging. } } + } - @Override - public void log(Level level, - ResourceBundle rb, String message, Object... params) { - if (isLoggable(level)) { - try { - String formatted = + @Override + public void log(Level level, + ResourceBundle rb, String message, Object... params) { + if (isLoggable(level)) { + try { + String formatted = SSLSimpleFormatter.format(this, level, message, params); - System.err.write(formatted.getBytes(UTF_8)); - } catch (Exception exp) { - // ignore it, just for debugging. - } + System.err.write(formatted.getBytes(UTF_8)); + } catch (Exception exp) { + // ignore it, just for debugging. } } } private static class SSLSimpleFormatter { private static final String PATTERN = "yyyy-MM-dd kk:mm:ss.SSS z"; - private static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern(PATTERN, Locale.ENGLISH) - .withZone(ZoneId.systemDefault()); + private static final DateTimeFormatter dateTimeFormat = + DateTimeFormatter.ofPattern(PATTERN, Locale.ENGLISH) + .withZone(ZoneId.systemDefault()); private static final MessageFormat basicCertFormat = new MessageFormat( """ @@ -290,68 +293,68 @@ private static class SSLSimpleFormatter { Locale.ENGLISH); private static final MessageFormat extendedCertFormat = - new MessageFormat( - """ - "version" : "v{0}", - "serial number" : "{1}", - "signature algorithm": "{2}", - "issuer" : "{3}", - "not before" : "{4}", - "not after" : "{5}", - "subject" : "{6}", - "subject public key" : "{7}", - "extensions" : [ - {8} - ] - """, - Locale.ENGLISH); + new MessageFormat( + """ + "version" : "v{0}", + "serial number" : "{1}", + "signature algorithm": "{2}", + "issuer" : "{3}", + "not before" : "{4}", + "not after" : "{5}", + "subject" : "{6}", + "subject public key" : "{7}", + "extensions" : [ + {8} + ] + """, + Locale.ENGLISH); private static final MessageFormat messageFormatNoParas = - new MessageFormat( - """ - '{' - "logger" : "{0}", - "level" : "{1}", - "thread id" : "{2}", - "thread name" : "{3}", - "time" : "{4}", - "caller" : "{5}", - "message" : "{6}" - '}' - """, - Locale.ENGLISH); + new MessageFormat( + """ + '{' + "logger" : "{0}", + "level" : "{1}", + "thread id" : "{2}", + "thread name" : "{3}", + "time" : "{4}", + "caller" : "{5}", + "message" : "{6}" + '}' + """, + Locale.ENGLISH); private static final MessageFormat messageCompactFormatNoParas = - new MessageFormat( - "{0}|{1}|{2}|{3}|{4}|{5}|{6}" + LINE_SEP, - Locale.ENGLISH); + new MessageFormat( + "{0}|{1}|{2}|{3}|{4}|{5}|{6}" + LINE_SEP, + Locale.ENGLISH); private static final MessageFormat messageFormatWithParas = - new MessageFormat( - """ - '{' - "logger" : "{0}", - "level" : "{1}", - "thread id" : "{2}", - "thread name" : "{3}", - "time" : "{4}", - "caller" : "{5}", - "message" : "{6}", - "specifics" : [ - {7} - ] - '}' - """, - Locale.ENGLISH); + new MessageFormat( + """ + '{' + "logger" : "{0}", + "level" : "{1}", + "thread id" : "{2}", + "thread name" : "{3}", + "time" : "{4}", + "caller" : "{5}", + "message" : "{6}", + "specifics" : [ + {7} + ] + '}' + """, + Locale.ENGLISH); private static final MessageFormat messageCompactFormatWithParas = - new MessageFormat( - """ - {0}|{1}|{2}|{3}|{4}|{5}|{6} ( - {7} - ) - """, - Locale.ENGLISH); + new MessageFormat( + """ + {0}|{1}|{2}|{3}|{4}|{5}|{6} ( + {7} + ) + """, + Locale.ENGLISH); private static final MessageFormat keyObjectFormat = new MessageFormat( """ @@ -364,8 +367,8 @@ private static class SSLSimpleFormatter { // log message // log message // ... - private static String format(SSLConsoleLogger logger, Level level, - String message, Object ... parameters) { + private static String format(SSLLogger logger, Level level, + String message, Object... parameters) { if (parameters == null || parameters.length == 0) { Object[] messageFields = { @@ -394,9 +397,9 @@ private static String format(SSLConsoleLogger logger, Level level, formatCaller(), message, (logger.useCompactFormat ? - formatParameters(parameters) : - Utilities.indent(formatParameters(parameters))) - }; + formatParameters(parameters) : + Utilities.indent(formatParameters(parameters))) + }; if (logger.useCompactFormat) { return messageCompactFormatWithParas.format(messageFields); @@ -414,7 +417,7 @@ private static String formatCaller() { .findFirst().orElse("unknown caller")); } - private static String formatParameters(Object ... parameters) { + private static String formatParameters(Object... parameters) { StringBuilder builder = new StringBuilder(512); boolean isFirst = true; for (Object parameter : parameters) { @@ -425,21 +428,21 @@ private static String formatParameters(Object ... parameters) { } if (parameter instanceof Throwable) { - builder.append(formatThrowable((Throwable)parameter)); + builder.append(formatThrowable((Throwable) parameter)); } else if (parameter instanceof Certificate) { - builder.append(formatCertificate((Certificate)parameter)); + builder.append(formatCertificate((Certificate) parameter)); } else if (parameter instanceof ByteArrayInputStream) { builder.append(formatByteArrayInputStream( - (ByteArrayInputStream)parameter)); + (ByteArrayInputStream) parameter)); } else if (parameter instanceof ByteBuffer) { - builder.append(formatByteBuffer((ByteBuffer)parameter)); + builder.append(formatByteBuffer((ByteBuffer) parameter)); } else if (parameter instanceof byte[]) { builder.append(formatByteArrayInputStream( - new ByteArrayInputStream((byte[])parameter))); + new ByteArrayInputStream((byte[]) parameter))); } else if (parameter instanceof Map.Entry) { @SuppressWarnings("unchecked") Map.Entry mapParameter = - (Map.Entry)parameter; + (Map.Entry) parameter; builder.append(formatMapEntry(mapParameter)); } else { builder.append(formatObject(parameter)); @@ -462,7 +465,7 @@ private static String formatThrowable(Throwable throwable) { Object[] fields = { "throwable", builder.toString() - }; + }; return keyObjectFormat.format(fields); } @@ -479,7 +482,7 @@ private static String formatCertificate(Certificate certificate) { StringBuilder builder = new StringBuilder(512); try { X509CertImpl x509 = - X509CertImpl.toImpl((X509Certificate)certificate); + X509CertImpl.toImpl((X509Certificate) certificate); X509CertInfo certInfo = x509.getInfo(); CertificateExtensions certExts = certInfo.getExtensions(); if (certExts == null) { @@ -528,7 +531,7 @@ private static String formatCertificate(Certificate certificate) { Object[] fields = { "certificate", builder.toString() - }; + }; return Utilities.indent(keyObjectFormat.format(fields)); } @@ -591,13 +594,13 @@ private static String formatMapEntry(Map.Entry entry) { formatted = builder.toString(); } else if (value instanceof byte[]) { formatted = "\"" + key + "\": \"" + - Utilities.toHexString((byte[])value) + "\""; + Utilities.toHexString((byte[]) value) + "\""; } else if (value instanceof Byte) { formatted = "\"" + key + "\": \"" + - HexFormat.of().toHexDigits((byte)value) + "\""; + HexFormat.of().toHexDigits((byte) value) + "\""; } else { formatted = "\"" + key + "\": " + - "\"" + value.toString() + "\""; + "\"" + value.toString() + "\""; } return Utilities.indent(formatted); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java index db5887c5e8e48..4de29b7570af3 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java @@ -29,7 +29,6 @@ import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.ProviderException; -import java.security.spec.AlgorithmParameterSpec; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import sun.security.internal.spec.TlsMasterSecretParameterSpec; @@ -152,7 +151,7 @@ public SecretKey deriveKey(String typeNotUsed) throws IOException { // // For RSA premaster secrets, do not signal a protocol error // due to the Bleichenbacher attack. See comments further down. - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.fine("RSA master secret generation error.", iae); } throw new ProviderException(iae); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java index 4baa3304fee1f..f713f723ea0ce 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java @@ -339,7 +339,7 @@ private int getDefaults(boolean server) { if (t < 0 || t > NewSessionTicket.MAX_TICKET_LIFETIME) { timeout = DEFAULT_SESSION_TIMEOUT; - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid timeout given " + "jdk.tls.server.sessionTicketTimeout: " + t + ". Set to default value " + timeout); @@ -349,7 +349,7 @@ private int getDefaults(boolean server) { } } catch (NumberFormatException e) { setSessionTimeout(DEFAULT_SESSION_TIMEOUT); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid timeout for " + "jdk.tls.server.sessionTicketTimeout: " + s + ". Set to default value " + timeout); @@ -363,7 +363,7 @@ private int getDefaults(boolean server) { if (defaultCacheLimit >= 0) { return defaultCacheLimit; - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "invalid System Property javax.net.ssl.sessionCacheSize, " + "use the default session cache size (" + @@ -371,7 +371,7 @@ private int getDefaults(boolean server) { } } catch (Exception e) { // unlikely, log it for safe - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "the System Property javax.net.ssl.sessionCacheSize is " + "not available, use the default value (" + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java index 1bf561c47e622..f3a4b96415850 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java @@ -223,7 +223,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { this.identificationProtocol = hc.sslConfig.identificationProtocol; this.boundValues = new ConcurrentHashMap<>(); - if (SSLLogger.isOn && SSLLogger.isOn("session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("session")) { SSLLogger.finest("Session initialized: " + this); } } @@ -256,7 +256,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { this.maximumPacketSize = baseSession.maximumPacketSize; this.boundValues = baseSession.boundValues; - if (SSLLogger.isOn && SSLLogger.isOn("session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("session")) { SSLLogger.finest("Session initialized: " + this); } } @@ -455,7 +455,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { if (same) { this.localCerts = ((X509Possession) pos).popCerts; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,session")) { SSLLogger.fine("Restored " + len + " local certificates from session ticket" + " for algorithms " + Arrays.toString(certAlgs)); @@ -463,7 +463,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { } else { this.localCerts = null; this.invalidated = true; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,session")) { SSLLogger.warning("Local certificates can not be restored " + "from session ticket " + "for algorithms " + Arrays.toString(certAlgs)); @@ -482,7 +482,7 @@ boolean isStatelessable() { // If there is no getMasterSecret with TLS1.2 or under, do not resume. if (!protocolVersion.useTLS13PlusSpec() && getMasterSecret().getEncoded() == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("No MasterSecret, cannot make stateless" + " ticket"); } @@ -490,7 +490,7 @@ boolean isStatelessable() { } if (boundValues != null && boundValues.size() > 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("There are boundValues, cannot make" + " stateless ticket"); } @@ -862,7 +862,7 @@ CipherSuite getSuite() { void setSuite(CipherSuite suite) { cipherSuite = suite; - if (SSLLogger.isOn && SSLLogger.isOn("session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("session")) { SSLLogger.finest("Negotiating session: " + this); } } @@ -1132,7 +1132,7 @@ public void invalidate() { return; } invalidated = true; - if (SSLLogger.isOn && SSLLogger.isOn("session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("session")) { SSLLogger.finest("Invalidated session: " + this); } for (SSLSessionImpl child : childSessions) { diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index be95a09006f66..ab01a9d85be70 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -370,7 +370,7 @@ public SSLSession getSession() { // start handshaking, if failed, the connection will be closed. ensureNegotiated(false); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.severe("handshake failed", ioe); } @@ -573,7 +573,7 @@ public void close() throws IOException { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("duplex close of SSLSocket"); } @@ -591,7 +591,7 @@ public void close() throws IOException { } } catch (IOException ioe) { // ignore the exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("SSLSocket duplex close failed. Debug info only. Exception details:", ioe); } } finally { @@ -601,7 +601,7 @@ public void close() throws IOException { closeSocket(false); } catch (IOException ioe) { // ignore the exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("SSLSocket close failed. Debug info only. Exception details:", ioe); } } finally { @@ -696,7 +696,7 @@ void closeNotify(boolean useUserCanceled) throws IOException { "close_notify message cannot be sent."); } else { super.shutdownOutput(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "SSLSocket output duplex close failed: " + "SO_LINGER timeout, " + @@ -717,7 +717,7 @@ void closeNotify(boolean useUserCanceled) throws IOException { // failed to send the close_notify message. // conContext.conSession.invalidate(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Invalidate the session: SO_LINGER timeout, " + "close_notify message cannot be sent."); @@ -832,7 +832,7 @@ private void shutdownInput( return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("close inbound of SSLSocket"); } @@ -868,7 +868,7 @@ public void shutdownOutput() throws IOException { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("close outbound of SSLSocket"); } conContext.closeOutbound(); @@ -1027,7 +1027,7 @@ public int read(byte[] b, int off, int len) throws IOException { // filed is checked here, in case the closing process is // still in progress. if (hasDepleted) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("The input stream has been depleted"); } @@ -1048,7 +1048,7 @@ public int read(byte[] b, int off, int len) throws IOException { // Double check if the input stream has been depleted. if (hasDepleted) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("The input stream is closing"); } @@ -1134,7 +1134,7 @@ public long skip(long n) throws IOException { @Override public void close() throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("Closing input stream"); } @@ -1142,7 +1142,7 @@ public void close() throws IOException { SSLSocketImpl.this.close(); } catch (IOException ioe) { // ignore the exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("input stream close failed. Debug info only. Exception details:", ioe); } } @@ -1218,7 +1218,7 @@ private void readLockedDeplete() { socketInputRecord.deplete( conContext.isNegotiated && (getSoTimeout() > 0)); } catch (Exception ex) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "input stream close depletion failed", ex); } @@ -1327,7 +1327,7 @@ public void write(byte[] b, @Override public void close() throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("Closing output stream"); } @@ -1335,7 +1335,7 @@ public void close() throws IOException { SSLSocketImpl.this.close(); } catch (IOException ioe) { // ignore the exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("output stream close failed. Debug info only. Exception details:", ioe); } } @@ -1543,7 +1543,7 @@ private void tryKeyUpdate() throws IOException { if ((conContext.handshakeContext == null) && !conContext.isOutboundClosed() && !conContext.isBroken) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger key update"); } startHandshake(); @@ -1562,7 +1562,7 @@ private void tryNewSessionTicket() throws IOException { !conContext.isOutboundClosed() && !conContext.isInboundClosed() && !conContext.isBroken) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger new session ticket"); } conContext.conSession.updateNST = false; @@ -1670,7 +1670,7 @@ public void setHost(String host) { * This method never returns normally, it always throws an IOException. */ private void handleException(Exception cause) throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("handling exception", cause); } @@ -1747,7 +1747,7 @@ public boolean useDelegatedTask() { @Override public void shutdown() throws IOException { if (!isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("close the underlying socket"); } @@ -1773,7 +1773,7 @@ public String toString() { } private void closeSocket(boolean selfInitiated) throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("close the SSL connection " + (selfInitiated ? "(initiative)" : "(passive)")); } @@ -1828,7 +1828,7 @@ private void closeSocket(boolean selfInitiated) throws IOException { * transport without waiting for the responding close_notify. */ private void waitForClose() throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("wait for close_notify or alert"); } @@ -1838,7 +1838,7 @@ private void waitForClose() throws IOException { try { Plaintext plainText = decode(null); // discard and continue - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "discard plaintext while waiting for close", plainText); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java index 09223a4485d97..ce7ab63073048 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -210,7 +210,7 @@ private Plaintext[] decodeInputRecord() throws IOException, BadPaddingException int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF); // pos: 3, 4 - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "READ: " + ProtocolVersion.nameOf(majorVersion, minorVersion) + @@ -243,7 +243,7 @@ private Plaintext[] decodeInputRecord() throws IOException, BadPaddingException readFully(contentLen); recordBody.flip(); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "READ: " + ProtocolVersion.nameOf(majorVersion, minorVersion) + @@ -406,7 +406,7 @@ private Plaintext[] handleUnknownRecord() throws IOException { */ os.write(SSLRecord.v2NoCipher); // SSLv2Hello - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { if (SSLLogger.isOn("record")) { SSLLogger.fine( "Requested to negotiate unsupported SSLv2!"); @@ -445,7 +445,7 @@ private Plaintext[] handleUnknownRecord() throws IOException { ByteBuffer converted = convertToClientHello(recordBody); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine( "[Converted] ClientHello", converted); } @@ -488,13 +488,13 @@ private int readHeader() throws IOException { private static int read(InputStream is, byte[] buf, int off, int len) throws IOException { int readLen = is.read(buf, off, len); if (readLen < 0) { - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw read: EOF"); } throw new EOFException("SSL peer shut down incorrectly"); } - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer bb = ByteBuffer.wrap(buf, off, readLen); SSLLogger.fine("Raw read", bb); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java index a7809754ed039..e83ad15db22c8 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -55,7 +55,7 @@ void encodeAlert(byte level, byte description) throws IOException { recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "alert message: " + Alert.nameOf(description)); } @@ -67,7 +67,7 @@ void encodeAlert(byte level, byte description) throws IOException { write(level); write(description); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine("WRITE: " + protocolVersion.name + " " + ContentType.ALERT.name + "(" + Alert.nameOf(description) + ")" + @@ -81,7 +81,7 @@ void encodeAlert(byte level, byte description) throws IOException { deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } @@ -99,7 +99,7 @@ void encodeHandshake(byte[] source, recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake message", ByteBuffer.wrap(source, offset, length)); @@ -127,7 +127,7 @@ void encodeHandshake(byte[] source, int limit = v2ClientHello.limit(); handshakeHash.deliver(record, 2, (limit - 2)); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: SSLv2 ClientHello message" + ", length = " + limit); @@ -141,7 +141,7 @@ void encodeHandshake(byte[] source, deliverStream.write(record, 0, limit); deliverStream.flush(); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(record, 0, limit))); } @@ -177,7 +177,7 @@ void encodeHandshake(byte[] source, return; } - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.HANDSHAKE.name + @@ -191,7 +191,7 @@ void encodeHandshake(byte[] source, deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } @@ -212,7 +212,7 @@ void encodeChangeCipherSpec() throws IOException { recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -231,7 +231,7 @@ void encodeChangeCipherSpec() throws IOException { deliverStream.write(buf, 0, count); // may throw IOException // deliverStream.flush(); // flush in Finished - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } @@ -257,7 +257,7 @@ public void flush() throws IOException { return; } - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.HANDSHAKE.name + @@ -271,7 +271,7 @@ public void flush() throws IOException { deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } @@ -293,7 +293,7 @@ void deliver(byte[] source, int offset, int length) throws IOException { } if (writeCipher.authenticator.seqNumOverflow()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "sequence number extremely close to overflow " + "(2^64-1 packets). Closing connection."); @@ -330,7 +330,7 @@ void deliver(byte[] source, int offset, int length) throws IOException { count = position; write(source, offset, fragLen); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.APPLICATION_DATA.name + @@ -345,7 +345,7 @@ void deliver(byte[] source, int offset, int length) throws IOException { deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLTransport.java b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java index c248b48fb0a14..9298e016f6309 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLTransport.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -113,7 +113,7 @@ static Plaintext decode(TransportContext context, // Code to deliver SSLv2 error message for SSL/TLS connections. if (!context.sslContext.isDTLS()) { context.outputRecord.encodeV2NoCipher(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("may be talking to SSLv2"); } } @@ -161,7 +161,7 @@ static Plaintext decode(TransportContext context, if (context.handshakeContext != null && context.handshakeContext.sslConfig.enableRetransmissions && context.sslContext.isDTLS()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,verbose")) { SSLLogger.finest("retransmitted handshake flight"); } @@ -181,7 +181,7 @@ static Plaintext decode(TransportContext context, // Note that JDK does not support 0-RTT yet. Otherwise, it is // needed to check early_data. if (!context.isNegotiated) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,verbose")) { SSLLogger.warning("unexpected application data " + "before handshake completion"); } diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 1d2faa5351f33..76c266a628a68 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -365,7 +365,7 @@ public byte[] produce(ConnectionContext context, shc.sslConfig.getEnabledExtensions( SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol); shm.extensions.produce(shc, serverHelloExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ServerHello handshake message", shm); } @@ -440,7 +440,7 @@ private static KeyExchangeProperties chooseCipherSuite( } // The cipher suite has been negotiated. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("use cipher suite " + cs.name); } @@ -453,7 +453,7 @@ private static KeyExchangeProperties chooseCipherSuite( if (ke != null) { SSLPossession[] hcds = ke.createPossessions(shc); if ((hcds != null) && (hcds.length != 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "use legacy cipher suite " + cs.name); } @@ -570,7 +570,7 @@ public byte[] produce(ConnectionContext context, shc.sslConfig.getEnabledExtensions( SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol); shm.extensions.produce(shc, serverHelloExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ServerHello handshake message", shm); } @@ -723,14 +723,14 @@ private static CipherSuite chooseCipherSuite( } // The cipher suite has been negotiated. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("use cipher suite " + cs.name); } return cs; } if (legacySuite != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "use legacy cipher suite " + legacySuite.name); } @@ -783,7 +783,7 @@ public byte[] produce(ConnectionContext context, shc.sslConfig.getEnabledExtensions( SSLHandshake.HELLO_RETRY_REQUEST, shc.negotiatedProtocol); hhrm.extensions.produce(shc, serverHelloExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced HelloRetryRequest handshake message", hhrm); } @@ -845,7 +845,7 @@ public byte[] produce(ConnectionContext context, shc.sslConfig.getEnabledExtensions( SSLHandshake.MESSAGE_HASH, shc.negotiatedProtocol); hhrm.extensions.produce(shc, serverHelloExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Reproduced HelloRetryRequest handshake message", hhrm); } @@ -886,7 +886,7 @@ public void consume(ConnectionContext context, } ServerHelloMessage shm = new ServerHelloMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ServerHello handshake message", shm); } @@ -931,7 +931,7 @@ private void onHelloRetryRequest(ClientHandshakeContext chc, } chc.negotiatedProtocol = serverVersion; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Negotiated protocol version: " + serverVersion.name); } @@ -986,7 +986,7 @@ private void onServerHello(ClientHandshakeContext chc, chc.conContext.protocolVersion = chc.negotiatedProtocol; chc.conContext.outputRecord.setVersion(chc.negotiatedProtocol); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Negotiated protocol version: " + serverVersion.name); } @@ -1132,7 +1132,7 @@ public void consume(ConnectionContext context, chc.handshakeSession = new SSLSessionImpl(chc, chc.negotiatedCipherSuite, newId); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Locally assigned Session Id: " + newId.toString()); } @@ -1204,7 +1204,7 @@ public void consume(ConnectionContext context, private static void setUpPskKD(HandshakeContext hc, SecretKey psk) throws SSLHandshakeException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Using PSK to derive early secret"); } diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java b/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java index 7136b36ffc2be..a86257ab3d0ff 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java @@ -93,7 +93,7 @@ public byte[] produce(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; ServerHelloDoneMessage shdm = new ServerHelloDoneMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ServerHelloDone handshake message", shdm); } @@ -147,7 +147,7 @@ public void consume(ConnectionContext context, ServerHelloDoneMessage shdm = new ServerHelloDoneMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming ServerHelloDone handshake message", shdm); } diff --git a/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java b/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java index 96c3fe2fa6abd..1b3c8ec3ebaf2 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java @@ -216,7 +216,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_SERVER_NAME)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unavailable server_name extension"); } @@ -261,7 +261,7 @@ public byte[] produce(ConnectionContext context, return extData; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Unable to indicate server name"); } return null; @@ -287,7 +287,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_SERVER_NAME)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + CH_SERVER_NAME.name); } @@ -305,7 +305,7 @@ public void consume(ConnectionContext context, if (!shc.sslConfig.sniMatchers.isEmpty()) { sni = chooseSni(shc.sslConfig.sniMatchers, spec.serverNames); if (sni != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "server name indication (" + sni + ") is accepted"); @@ -322,7 +322,7 @@ public void consume(ConnectionContext context, // connection with a "missing_extension" alert. // // We do not reject client without SNI extension currently. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "no server name matchers, " + "ignore server name indication"); @@ -347,7 +347,7 @@ public void consume(ConnectionContext context, // so don't include the pre-shared key in the // ServerHello handshake message shc.handshakeExtensions.remove(SH_PRE_SHARED_KEY); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "different server name indication used"); @@ -441,7 +441,7 @@ public byte[] produce(ConnectionContext context, CHServerNamesSpec spec = (CHServerNamesSpec) shc.handshakeExtensions.get(CH_SERVER_NAME); if (spec == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable extension: " + SH_SERVER_NAME.name); } @@ -451,7 +451,7 @@ public byte[] produce(ConnectionContext context, // When resuming a session, the server MUST NOT include a // server_name extension in the server hello. if (shc.isResumption || shc.negotiatedServerName == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No expected server name indication response"); } @@ -528,7 +528,7 @@ public byte[] produce(ConnectionContext context, CHServerNamesSpec spec = (CHServerNamesSpec) shc.handshakeExtensions.get(CH_SERVER_NAME); if (spec == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable extension: " + EE_SERVER_NAME.name); } @@ -538,7 +538,7 @@ public byte[] produce(ConnectionContext context, // When resuming a session, the server MUST NOT include a // server_name extension in the server hello. if (shc.isResumption || shc.negotiatedServerName == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No expected server name indication response"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java index 9a84bbad8fda3..1a0283cf85995 100644 --- a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java @@ -93,7 +93,7 @@ final class SessionTicketExtension { kt = Integer.parseInt(s) * 1000; // change to ms if (kt < 0 || kt > NewSessionTicket.MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid timeout for " + "jdk.tls.server.statelessKeyTimeout: " + kt + ". Set to default value " + @@ -103,7 +103,7 @@ final class SessionTicketExtension { } } catch (NumberFormatException e) { kt = TIMEOUT_DEFAULT; - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid timeout for " + "jdk.tls.server.statelessKeyTimeout: " + s + ". Set to default value " + TIMEOUT_DEFAULT + @@ -252,7 +252,7 @@ byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) { Integer.BYTES + iv.length + 1, encrypted.length); return result; } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Encryption failed." + e); } return new byte[0]; @@ -294,7 +294,7 @@ ByteBuffer decrypt(HandshakeContext hc) { return out; } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Decryption failed." + e); } } @@ -309,7 +309,7 @@ private static byte[] compress(byte[] input) throws IOException { gos.write(input, 0, decompressedLen); gos.finish(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("decompressed bytes: " + decompressedLen + "; compressed bytes: " + baos.size()); } @@ -328,7 +328,7 @@ private static ByteBuffer decompress(ByteBuffer input) new ByteArrayInputStream(bytes))) { byte[] out = gis.readAllBytes(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("compressed bytes: " + compressedLen + "; decompressed bytes: " + out.length); } @@ -394,7 +394,7 @@ public byte[] produce(ConnectionContext context, // If the context does not allow stateless tickets, exit if (!((SSLSessionContextImpl)chc.sslContext. engineGetClientSessionContext()).statelessEnabled()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Stateless resumption not supported"); } return null; @@ -406,7 +406,7 @@ public byte[] produce(ConnectionContext context, if (!chc.isResumption || chc.resumingSession == null || chc.resumingSession.getPskIdentity() == null || chc.resumingSession.getProtocolVersion().useTLS13PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Stateless resumption supported"); } return new byte[0]; @@ -450,7 +450,7 @@ public void consume(ConnectionContext context, shc.statelessResumption = true; if (buffer.remaining() == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Client accepts session tickets."); } return; @@ -462,11 +462,11 @@ public void consume(ConnectionContext context, if (b != null) { shc.resumingSession = new SSLSessionImpl(shc, b); shc.isResumption = true; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Valid stateless session ticket found"); } } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Invalid stateless session ticket found"); } } @@ -546,7 +546,7 @@ public void absent(ConnectionContext context, // Disable stateless resumption if server doesn't send the extension. if (chc.statelessResumption) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Server doesn't support stateless resumption"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java index b298da05e9a0e..dddeb5235160a 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java @@ -182,7 +182,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable signature_algorithms extension"); } @@ -218,7 +218,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable signature_algorithms extension"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java index 5a8f103082b14..b91fc17fd2939 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java @@ -204,7 +204,7 @@ enum SigAlgParamSpec { // support RSASSA-PSS only now NoSuchAlgorithmException | RuntimeException exp) { // Signature.getParameters() may throw RuntimeException. mediator = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "RSASSA-PSS signature with " + hash + " is not supported by the underlying providers", exp); @@ -297,7 +297,7 @@ enum SigAlgParamSpec { // support RSASSA-PSS only now Signature.getInstance(algorithm); } catch (Exception e) { mediator = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Signature algorithm, " + algorithm + ", is not supported by the underlying providers"); @@ -432,7 +432,7 @@ private static List getSupportedAlgorithms( for (SignatureScheme ss: schemesToCheck) { if (!ss.isAvailable) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore unsupported signature scheme: " + ss.name); @@ -451,12 +451,12 @@ private static List getSupportedAlgorithms( if (isMatch) { if (ss.isPermitted(constraints, scopes)) { supported.add(ss); - } else if (SSLLogger.isOn && + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore disabled signature scheme: " + ss.name); } - } else if (SSLLogger.isOn && + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore inactive signature scheme: " + ss.name); @@ -476,7 +476,7 @@ static List getSupportedAlgorithms( for (int ssid : algorithmIds) { SignatureScheme ss = SignatureScheme.valueOf(ssid); if (ss == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Unsupported signature scheme: " + SignatureScheme.nameOf(ssid)); @@ -486,7 +486,7 @@ static List getSupportedAlgorithms( && ss.isAllowed(constraints, protocolVersion, scopes)) { supported.add(ss); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Unsupported signature scheme: " + ss.name); } @@ -545,7 +545,7 @@ static Map.Entry getSignerOfPreferableAlgorithm( } } - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore the signature algorithm (" + ss + @@ -574,7 +574,7 @@ static Map.Entry getSignerOfPreferableAlgorithm( } } - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore the legacy signature algorithm (" + ss + @@ -660,7 +660,7 @@ private Signature getSigner(PrivateKey privateKey) { return signer; } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException nsae) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore unsupported signature algorithm (" + diff --git a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java index ec200c6e49564..d8c4a8ccc3e1a 100644 --- a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java +++ b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java @@ -119,13 +119,13 @@ URI getURI(X509Certificate cert) { if (cert.getExtensionValue( PKIXExtensions.OCSPNoCheck_Id.toString()) != null) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "OCSP NoCheck extension found. OCSP will be skipped"); } return null; } else if (defaultResponder != null && respOverride) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Responder override: URI is " + defaultResponder); } @@ -165,7 +165,7 @@ Map get(CertStatusRequestType type, Map responseMap = new HashMap<>(); List requestList = new ArrayList<>(); - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Beginning check: Type = " + type + ", Chain length = " + chain.length); @@ -192,7 +192,7 @@ Map get(CertStatusRequestType type, requestList.add(new OCSPFetchCall(sInfo, ocspReq)); } } catch (IOException exc) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Exception during CertId creation: ", exc); } @@ -219,14 +219,14 @@ Map get(CertStatusRequestType type, requestList.add(new OCSPFetchCall(sInfo, ocspReq)); } } catch (IOException exc) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Exception during CertId creation: ", exc); } } } } else { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Unsupported status request type: " + type); } } @@ -257,7 +257,7 @@ Map get(CertStatusRequestType type, // that, otherwise just log the ExecutionException Throwable cause = Optional.ofNullable( exc.getCause()).orElse(exc); - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Exception during OCSP fetch: " + cause); } @@ -266,13 +266,13 @@ Map get(CertStatusRequestType type, if (info != null && info.responseData != null) { responseMap.put(info.cert, info.responseData.ocspBytes); - } else if (SSLLogger.isOn && + } else if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Completed task had no response data"); } } else { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Found cancelled task"); } } @@ -280,7 +280,7 @@ Map get(CertStatusRequestType type, } catch (InterruptedException intex) { // Log and reset the interrupted state Thread.currentThread().interrupt(); - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Interrupt occurred while fetching: " + intex); } @@ -308,7 +308,7 @@ private ResponseCacheEntry getFromCache(CertId cid, for (Extension ext : ocspRequest.extensions) { if (ext.getId().equals( PKIXExtensions.OCSPNonce_Id.toString())) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Nonce extension found, skipping cache check"); } @@ -323,14 +323,14 @@ private ResponseCacheEntry getFromCache(CertId cid, // and do not return it as a cache hit. if (respEntry != null && respEntry.nextUpdate != null && respEntry.nextUpdate.before(new Date())) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "nextUpdate threshold exceeded, purging from cache"); } respEntry = null; } - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Check cache for SN" + Debug.toString(cid.getSerialNumber()) + ": " + (respEntry != null ? "HIT" : "MISS")); @@ -493,7 +493,7 @@ public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) { */ @Override public StatusInfo call() { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Starting fetch for SN " + Debug.toString(statInfo.cid.getSerialNumber())); @@ -505,13 +505,13 @@ public StatusInfo call() { if (statInfo.responder == null) { // If we have no URI then there's nothing to do // but return. - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Null URI detected, OCSP fetch aborted"); } return statInfo; } else { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Attempting fetch from " + statInfo.responder); } @@ -541,7 +541,7 @@ public StatusInfo call() { statInfo.cid); // Get the response status and act on it appropriately - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("OCSP Status: " + cacheEntry.status + " (" + respBytes.length + " bytes)"); } @@ -554,7 +554,7 @@ public StatusInfo call() { addToCache(statInfo.cid, cacheEntry); } } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Caught exception: ", ioe); } } @@ -573,12 +573,12 @@ private void addToCache(CertId certId, ResponseCacheEntry entry) { // If no cache lifetime has been set on entries then // don't cache this response if there is no nextUpdate field if (entry.nextUpdate == null && cacheLifetime == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Not caching this OCSP response"); } } else { responseCache.put(certId, entry); - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Added response for SN " + Debug.toString(certId.getSerialNumber()) + @@ -600,7 +600,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { // is necessary. Also, we will only staple if we're doing a full // handshake. if (!shc.sslContext.isStaplingEnabled(false) || shc.isResumption) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Staping disabled or is a resumed session"); } return null; @@ -623,7 +623,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { // selection yet, only accept a request if the ResponderId field // is empty. Finally, we'll only do this in (D)TLS 1.2 or earlier. if (statReqV2 != null && !shc.negotiatedProtocol.useTLS13PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.fine("SH Processing status_request_v2 extension"); } // RFC 6961 stapling @@ -660,7 +660,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { req = reqItems[ocspIdx]; type = CertStatusRequestType.valueOf(req.statusType); } else { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: No suitable request " + "found in the status_request_v2 extension."); @@ -678,7 +678,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { // we will try processing an asserted status_request. if ((statReq != null) && (ext == null || type == null || req == null)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.fine("SH Processing status_request extension"); } ext = SSLExtension.CH_STATUS_REQUEST; @@ -692,7 +692,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { if (ocspReq.responderIds.isEmpty()) { req = ocspReq; } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: No suitable request " + "found in the status_request extension."); } @@ -704,7 +704,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { // find a suitable StatusRequest, then stapling is disabled. // The ext, type and req variables must have been set to continue. if (type == null || req == null || ext == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No suitable status_request or " + "status_request_v2, stapling is disabled"); } @@ -721,7 +721,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { } if (x509Possession == null) { // unlikely - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: no X.509 certificates found. " + "Stapling is disabled."); } @@ -743,7 +743,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { responses = statRespMgr.get(fetchType, req, certs, shc.statusRespTimeout, TimeUnit.MILLISECONDS); if (!responses.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Response manager returned " + responses.size() + " entries."); } @@ -752,7 +752,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { if (type == CertStatusRequestType.OCSP) { byte[] respDER = responses.get(certs[0]); if (respDER == null || respDER.length == 0) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: Null or zero-length " + "response found for leaf certificate. " + @@ -763,7 +763,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { } params = new StaplingParameters(ext, type, req, responses); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: no OCSP responses obtained. " + "Stapling is disabled."); } @@ -771,7 +771,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { } else { // This should not happen, but if lazy initialization of the // StatusResponseManager doesn't occur we should turn off stapling. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: lazy initialization " + "of the StatusResponseManager failed. " + "Stapling is disabled."); diff --git a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java index 6bf138f4e4502..1fa2356d1de08 100644 --- a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java @@ -129,7 +129,7 @@ private static class X509Credentials { X509Credentials cred = new X509Credentials((PrivateKey) key, (X509Certificate[]) certs); credentialsMap.put(alias, cred); - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("found key for : " + alias, (Object[]) certs); } } @@ -315,7 +315,7 @@ private String[] getAliases(List keyTypes, Principal[] issuers, } if (results == null) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("KeyMgr: no matching key found"); } return null; diff --git a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java index 57e5f8c90932b..67cb37988a11d 100644 --- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java @@ -164,7 +164,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable supported_groups extension"); } @@ -177,7 +177,7 @@ public byte[] produce(ConnectionContext context, for (String name : chc.sslConfig.namedGroups) { NamedGroup ng = NamedGroup.nameOf(name); if (ng == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unspecified named group: " + name); } @@ -193,14 +193,14 @@ public byte[] produce(ConnectionContext context, ng.isSupported(chc.activeCipherSuites) && ng.isPermitted(chc.algorithmConstraints)) { namedGroups.add(ng); - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore inactive or disabled named group: " + ng.name); } } if (namedGroups.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("no available named group"); } @@ -244,7 +244,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable supported_groups extension"); } @@ -319,7 +319,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable supported_groups extension"); } @@ -335,7 +335,7 @@ public byte[] produce(ConnectionContext context, for (String name : shc.sslConfig.namedGroups) { NamedGroup ng = NamedGroup.nameOf(name); if (ng == null) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unspecified named group: " + name); @@ -352,14 +352,14 @@ public byte[] produce(ConnectionContext context, ng.isSupported(shc.activeCipherSuites) && ng.isPermitted(shc.algorithmConstraints)) { namedGroups.add(ng); - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore inactive or disabled named group: " + ng.name); } } if (namedGroups.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("no available named group"); } @@ -399,7 +399,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable supported_groups extension"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java b/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java index 6efdcef0d294f..58597c3008c4c 100644 --- a/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, 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 @@ -168,7 +168,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + CH_SUPPORTED_VERSIONS.name); @@ -216,7 +216,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + CH_SUPPORTED_VERSIONS.name); @@ -308,7 +308,7 @@ public byte[] produce(ConnectionContext context, shc.handshakeExtensions.get(CH_SUPPORTED_VERSIONS); if (svs == null) { // Unlikely, no key_share extension requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unavailable supported_versions extension"); } @@ -317,7 +317,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + SH_SUPPORTED_VERSIONS.name); @@ -356,7 +356,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + SH_SUPPORTED_VERSIONS.name); @@ -399,7 +399,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + HRR_SUPPORTED_VERSIONS.name); @@ -441,7 +441,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + HRR_SUPPORTED_VERSIONS.name); @@ -483,7 +483,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "[Reproduce] Ignore unavailable extension: " + HRR_SUPPORTED_VERSIONS.name); diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java index 9595dc8b3fd49..980d9c4a6cebb 100644 --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java @@ -270,7 +270,7 @@ void warning(Alert alert) { try { outputRecord.encodeAlert(Alert.Level.WARNING.level, alert.id); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Warning: failed to send warning alert " + alert, ioe); } @@ -330,7 +330,7 @@ SSLException fatal(Alert alert, String diagnostic, // so we'll do it here. if (closeReason != null) { if (cause == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Closed transport, general or untracked problem"); } @@ -341,7 +341,7 @@ SSLException fatal(Alert alert, String diagnostic, if (cause instanceof SSLException) { throw (SSLException)cause; } else { // unlikely, but just in case. - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Closed transport, unexpected rethrowing", cause); } @@ -364,7 +364,7 @@ SSLException fatal(Alert alert, String diagnostic, } // shutdown the transport - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.severe("Fatal (" + alert + "): " + diagnostic, cause); } @@ -380,7 +380,7 @@ SSLException fatal(Alert alert, String diagnostic, try { inputRecord.close(); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Fatal: input record closure failed", ioe); } @@ -411,7 +411,7 @@ SSLException fatal(Alert alert, String diagnostic, try { outputRecord.encodeAlert(Alert.Level.FATAL.level, alert.id); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Fatal: failed to send fatal alert " + alert, ioe); } @@ -424,7 +424,7 @@ SSLException fatal(Alert alert, String diagnostic, try { outputRecord.close(); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Fatal: output record closure failed", ioe); } @@ -440,7 +440,7 @@ SSLException fatal(Alert alert, String diagnostic, try { transport.shutdown(); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Fatal: transport closure failed", ioe); } @@ -526,7 +526,7 @@ void closeInbound() { passiveInboundClose(); } } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("inbound closure failed", ioe); } } @@ -583,7 +583,7 @@ void closeOutbound() { try { initiateOutboundClose(); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound closure failed", ioe); } } diff --git a/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java b/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java index bac7735de072c..8b89fecefa88b 100644 --- a/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -48,24 +48,24 @@ protected void engineInit(KeyStore ks) throws KeyStoreException { trustManager = getInstance(TrustStoreManager.getTrustedCerts()); } catch (SecurityException se) { // eat security exceptions but report other throwables - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "SunX509: skip default keystore", se); } } catch (Error err) { - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "SunX509: skip default keystore", err); } throw err; } catch (RuntimeException re) { - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "SunX509: skip default keystore", re); } throw re; } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "SunX509: skip default keystore", e); } diff --git a/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java b/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java index e2c353847c74e..a44f3a4e32dab 100644 --- a/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java +++ b/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java @@ -108,7 +108,7 @@ private TrustStoreDescriptor(String storeName, String storeType, this.storeFile = storeFile; this.lastModified = lastModified; - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "trustStore is: " + storeName + "\n" + "trustStore type is: " + storeType + "\n" + @@ -151,7 +151,7 @@ static TrustStoreDescriptor createInstance() { } // Not break, the file is inaccessible. - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "Inaccessible trust store: " + @@ -267,7 +267,7 @@ KeyStore getKeyStore( } // Reload a new key store. - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("Reload the trust store"); } @@ -321,7 +321,7 @@ Set getTrustedCerts( // Reload the trust store if needed. if (ks == null) { - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("Reload the trust store"); } ks = loadKeyStore(descriptor); @@ -329,12 +329,12 @@ Set getTrustedCerts( } // Reload trust certs from the key store. - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("Reload trust certs"); } certs = loadTrustedCerts(ks); - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("Reloaded " + certs.size() + " trust certs"); } @@ -355,7 +355,7 @@ private static KeyStore loadKeyStore( descriptor.storeFile == null) { // No file available, no KeyStore available. - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("No available key store"); } @@ -382,7 +382,7 @@ private static KeyStore loadKeyStore( ks.load(bis, password); } catch (FileNotFoundException fnfe) { // No file available, no KeyStore available. - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "Not available key store: " + descriptor.storeName); } diff --git a/src/java.base/share/classes/sun/security/ssl/Utilities.java b/src/java.base/share/classes/sun/security/ssl/Utilities.java index 50cd3bee75106..458551b9d8a83 100644 --- a/src/java.base/share/classes/sun/security/ssl/Utilities.java +++ b/src/java.base/share/classes/sun/security/ssl/Utilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -70,7 +70,7 @@ static List addToSNIServerNameList( SNIServerName serverName = sniList.get(i); if (serverName.getType() == StandardConstants.SNI_HOST_NAME) { sniList.set(i, sniHostName); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "the previous server name in SNI (" + serverName + ") was replaced with (" + sniHostName + ")"); @@ -116,7 +116,7 @@ private static SNIHostName rawToSNIHostName(String hostname) { return new SNIHostName(hostname); } catch (IllegalArgumentException iae) { // don't bother to handle illegal host_name - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine(hostname + "\" " + "is not a legal HostName for server name indication"); } diff --git a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java index 5abc2cb1bf4e8..6aedff02c3424 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java +++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java @@ -201,7 +201,7 @@ public static SSLPossession createPossession( private static SSLPossession createClientPossession( ClientHandshakeContext chc, String[] keyTypes) { X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("X509KeyManager class: " + km.getClass().getName()); } @@ -243,7 +243,7 @@ private static SSLPossession createClientPossession( } if (clientAlias == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("No X.509 cert selected for " + Arrays.toString(keyTypes)); } @@ -252,7 +252,7 @@ private static SSLPossession createClientPossession( PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias); if (clientPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( clientAlias + " is not a private key entry"); } @@ -261,7 +261,7 @@ private static SSLPossession createClientPossession( X509Certificate[] clientCerts = km.getCertificateChain(clientAlias); if ((clientCerts == null) || (clientCerts.length == 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest(clientAlias + " is a private key entry with no cert chain stored"); } @@ -270,7 +270,7 @@ private static SSLPossession createClientPossession( String privateKeyAlgorithm = clientPrivateKey.getAlgorithm(); if (!Arrays.asList(keyTypes).contains(privateKeyAlgorithm)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( clientAlias + " private key algorithm " + privateKeyAlgorithm + " not in request list"); @@ -280,7 +280,7 @@ private static SSLPossession createClientPossession( String publicKeyAlgorithm = clientCerts[0].getPublicKey().getAlgorithm(); if (!privateKeyAlgorithm.equals(publicKeyAlgorithm)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( clientAlias + " private or public key is not of " + "same algorithm: " + @@ -296,7 +296,7 @@ private static SSLPossession createClientPossession( private static SSLPossession createServerPossession( ServerHandshakeContext shc, String[] keyTypes) { X509ExtendedKeyManager km = shc.sslContext.getX509KeyManager(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("X509KeyManager class: " + km.getClass().getName()); } @@ -337,7 +337,7 @@ private static SSLPossession createServerPossession( } if (serverAlias == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("No X.509 cert selected for " + keyType); } continue; @@ -345,7 +345,7 @@ private static SSLPossession createServerPossession( PrivateKey serverPrivateKey = km.getPrivateKey(serverAlias); if (serverPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( serverAlias + " is not a private key entry"); } @@ -354,7 +354,7 @@ private static SSLPossession createServerPossession( X509Certificate[] serverCerts = km.getCertificateChain(serverAlias); if ((serverCerts == null) || (serverCerts.length == 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( serverAlias + " is not a certificate entry"); } @@ -364,7 +364,7 @@ private static SSLPossession createServerPossession( PublicKey serverPublicKey = serverCerts[0].getPublicKey(); if ((!serverPrivateKey.getAlgorithm().equals(keyType)) || (!serverPublicKey.getAlgorithm().equals(keyType))) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( serverAlias + " private or public key is not of " + keyType + " algorithm"); @@ -379,7 +379,7 @@ private static SSLPossession createServerPossession( if (!shc.negotiatedProtocol.useTLS13PlusSpec() && keyType.equals("EC")) { if (!(serverPublicKey instanceof ECPublicKey)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning(serverAlias + " public key is not an instance of ECPublicKey"); } @@ -398,7 +398,7 @@ private static SSLPossession createServerPossession( ((shc.clientRequestedNamedGroups != null) && !shc.clientRequestedNamedGroups.contains(namedGroup))) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Unsupported named group (" + namedGroup + ") used in the " + serverAlias + " certificate"); diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java index 9484ab4f830b4..2a1f01273bdc1 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java @@ -116,7 +116,7 @@ protected EntryStatus checkAlias(int keyStoreIndex, String alias, } if (keyIndex == -1) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("Ignore alias " + alias + ": key algorithm does not match"); } @@ -134,7 +134,7 @@ protected EntryStatus checkAlias(int keyStoreIndex, String alias, } } if (!found) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine( "Ignore alias " + alias + ": issuers do not match"); @@ -150,7 +150,7 @@ protected EntryStatus checkAlias(int keyStoreIndex, String alias, !conformsToAlgorithmConstraints(constraints, chain, checkType.getValidator())) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("Ignore alias " + alias + ": certificate chain does not conform to " + "algorithm constraints"); @@ -219,7 +219,7 @@ private boolean conformsToAlgorithmConstraints( checker.init(false); } catch (CertPathValidatorException cpve) { // unlikely to happen - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine( "Cannot initialize algorithm constraints checker", cpve); @@ -235,7 +235,7 @@ private boolean conformsToAlgorithmConstraints( // We don't care about the unresolved critical extensions. checker.check(cert, Collections.emptySet()); } catch (CertPathValidatorException cpve) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("Certificate does not conform to " + "algorithm constraints", cert, cpve); } @@ -392,7 +392,7 @@ CheckResult check(X509Certificate cert, Date date, serverName.getEncoded()); } catch (IllegalArgumentException iae) { // unlikely to happen, just in case ... - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("Illegal server name: " + serverName); @@ -408,7 +408,7 @@ CheckResult check(X509Certificate cert, Date date, X509TrustManagerImpl.checkIdentity(hostname, cert, idAlgorithm); } catch (CertificateException e) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine( "Certificate identity does not match " diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java index c607fe0f25d8e..72f079db17582 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java @@ -228,7 +228,7 @@ private PrivateKeyEntry getEntry(String alias) { || (secondDot - firstDot < 2) || (alias.length() - secondDot < 2)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.warning("Invalid alias format: " + alias); } return null; @@ -255,7 +255,7 @@ private PrivateKeyEntry getEntry(String alias) { NoSuchAlgorithmException | IndexOutOfBoundsException e) { // ignore and only log exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.warning("Exception thrown while getting an alias " + alias + ": " + e); } @@ -295,7 +295,7 @@ private String chooseAlias(List keyTypeList, Principal[] issuers, if (results != null) { for (EntryStatus status : results) { if (status.checkResult == CheckResult.OK) { - if (SSLLogger.isOn + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine("Choosing key: " + status); } @@ -312,13 +312,13 @@ private String chooseAlias(List keyTypeList, Principal[] issuers, } } if (allResults == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine("No matching key found"); } return null; } Collections.sort(allResults); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine( "No good matching key found, " + "returning best match out of", allResults); @@ -358,13 +358,13 @@ private String[] getAliases( } } if (allResults == null || allResults.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine("No matching alias found"); } return null; } Collections.sort(allResults); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine("Getting aliases", allResults); } return toAliases(allResults); diff --git a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java index d82b94a1d7d4e..1bbe0bfb9c788 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java @@ -81,7 +81,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager this.trustedCerts = trustedCerts; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("adding as trusted certificates", (Object[])trustedCerts.toArray(new X509Certificate[0])); } @@ -98,7 +98,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager trustedCerts = v.getTrustedCertificates(); serverValidator = v; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("adding as trusted certificates", (Object[])trustedCerts.toArray(new X509Certificate[0])); } @@ -242,7 +242,7 @@ private void checkTrusted(X509Certificate[] chain, null, checkClientTrusted ? null : authType); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("Found trusted certificate", trustedChain[trustedChain.length - 1]); } @@ -288,7 +288,7 @@ private void checkTrusted(X509Certificate[] chain, null, checkClientTrusted ? null : authType); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("Found trusted certificate", trustedChain[trustedChain.length - 1]); } @@ -331,7 +331,7 @@ private void checkTrusted(X509Certificate[] chain, null, checkClientTrusted ? null : authType); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("Found trusted certificate", trustedChain[trustedChain.length - 1]); } @@ -365,7 +365,7 @@ private static String getHostNameInSNI(List sniNames) { hostname = new SNIHostName(sniName.getEncoded()); } catch (IllegalArgumentException iae) { // unlikely to happen, just in case ... - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("Illegal server name: " + sniName); } } diff --git a/src/java.base/share/classes/sun/security/util/DomainName.java b/src/java.base/share/classes/sun/security/util/DomainName.java index 4f577f1114ccd..465c155ab8794 100644 --- a/src/java.base/share/classes/sun/security/util/DomainName.java +++ b/src/java.base/share/classes/sun/security/util/DomainName.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 @@ -45,7 +45,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; -import jdk.internal.util.StaticProperty; import sun.security.ssl.SSLLogger; /** @@ -193,7 +192,7 @@ private static Rules createRules(String tld) { } return getRules(tld, new ZipInputStream(pubSuffixStream)); } catch (IOException e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "cannot parse public suffix data for " + tld + ": " + e.getMessage()); @@ -210,7 +209,7 @@ private static InputStream getPubSuffixStream() { is = new FileInputStream(f); } catch (FileNotFoundException e) { } if (is == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl") && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl") && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "lib/security/public_suffix_list.dat not found"); @@ -231,7 +230,7 @@ private static Rules getRules(String tld, } } if (!found) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Domain " + tld + " not found"); } return null; diff --git a/src/java.base/share/classes/sun/security/util/HostnameChecker.java b/src/java.base/share/classes/sun/security/util/HostnameChecker.java index 1374bc6d53521..65115c9aeaf9f 100644 --- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java +++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, 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 @@ -271,7 +271,7 @@ private boolean isMatched(String name, String template, name = IDN.toUnicode(IDN.toASCII(name)); template = IDN.toUnicode(IDN.toASCII(template)); } catch (RuntimeException re) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine("Failed to normalize to Unicode: " + re); } @@ -308,7 +308,7 @@ private static boolean hasIllegalWildcard( String template, boolean chainsToPublicCA) { // not ok if it is a single wildcard character or "*." if (template.equals("*") || template.equals("*.")) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine( "Certificate domain name has illegal single " + "wildcard character: " + template); @@ -328,7 +328,7 @@ private static boolean hasIllegalWildcard( // not ok if there is no dot after wildcard (ex: "*com") if (firstDotIndex == -1) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine( "Certificate domain name has illegal wildcard, " + "no dot after wildcard character: " + template); @@ -353,7 +353,7 @@ private static boolean hasIllegalWildcard( // Is it a top-level domain? if (wildcardedDomain.equalsIgnoreCase(templateDomainSuffix)) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine( "Certificate domain name has illegal " + "wildcard for top-level public suffix: " + template); diff --git a/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java b/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java index fbd20e588d1dd..f32d0e381a6e2 100644 --- a/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java +++ b/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8350582 8340312 8369995 + * @bug 8350582 8340312 8369995 8372004 * @library /test/lib /javax/net/ssl/templates * @summary Correct the parsing of the ssl value in javax.net.debug * @run junit DebugPropertyValuesTest @@ -91,6 +91,7 @@ public class DebugPropertyValuesTest extends SSLSocketTemplate { List.of("FINE: adding as trusted certificates:" + System.lineSeparator() + " \"certificate\" : \\{", + "sun.security.ssl.SSLSocketImpl close", "FINE: Produced ClientHello handshake message:" + System.lineSeparator() + "\"ClientHello\": \\{", From 6fc8e4998019a2f3ef05ff3e73a4c855c0366d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Casta=C3=B1eda=20Lozano?= Date: Thu, 20 Nov 2025 09:13:57 +0000 Subject: [PATCH 41/44] 8372097: C2: PhasePrintLevel requires setting PrintPhaseLevel explicitly to be active Reviewed-by: mhaessig, chagedorn --- src/hotspot/share/opto/c2_globals.hpp | 2 +- src/hotspot/share/opto/compile.cpp | 2 +- .../compiler/oracle/TestPhasePrintLevel.java | 110 ++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/oracle/TestPhasePrintLevel.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 0a4f231c49b36..2b2b4db47b134 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -428,7 +428,7 @@ "0=print nothing except PhasePrintLevel directives, " \ "6=all details printed. " \ "Level of detail of printouts can be set on a per-method level " \ - "as well by using CompileCommand=PrintPhaseLevel.") \ + "as well by using CompileCommand=PhasePrintLevel.") \ range(-1, 6) \ \ develop(bool, PrintIdealGraph, false, \ diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 6babc13e1b315..89b5e36b120f1 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -5233,7 +5233,7 @@ void Compile::end_method() { #ifndef PRODUCT bool Compile::should_print_phase(const int level) const { - return PrintPhaseLevel > 0 && directive()->PhasePrintLevelOption >= level && + return PrintPhaseLevel >= 0 && directive()->PhasePrintLevelOption >= level && _method != nullptr; // Do not print phases for stubs. } diff --git a/test/hotspot/jtreg/compiler/oracle/TestPhasePrintLevel.java b/test/hotspot/jtreg/compiler/oracle/TestPhasePrintLevel.java new file mode 100644 index 0000000000000..cf60c5c9be225 --- /dev/null +++ b/test/hotspot/jtreg/compiler/oracle/TestPhasePrintLevel.java @@ -0,0 +1,110 @@ +/* + * 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 compiler.oracle; + +import java.util.ArrayList; +import java.util.List; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import compiler.lib.ir_framework.CompilePhase; + +/** + * @test + * @bug 8372097 + * @summary Checks that -XX:CompileCommand=PhasePrintLevel,... interacts with + * -XX:PrintPhaseLevel as expected. + * @library /test/lib / + * @requires vm.debug & vm.compiler2.enabled & vm.flagless + * @run driver compiler.oracle.TestPhasePrintLevel + */ + +public class TestPhasePrintLevel { + + static final String level1Phase = CompilePhase.FINAL_CODE.getName(); + static final String level2Phase = CompilePhase.GLOBAL_CODE_MOTION.getName(); + + public static void main(String[] args) throws Exception { + // Test flag level < 0: nothing should be printed regardless of the compile command level. + test(-1, -1, null, level1Phase); + test(-1, 0, null, level1Phase); + test(-1, 1, null, level1Phase); + + // Test flag level = 0: the compile command level should determine what is printed. + test(0, -1, null, level1Phase); + test(0, 0, null, level1Phase); + test(0, 1, level1Phase, null); + test(0, 2, level2Phase, null); + + // Test flag level > 0: the compile command level should take precedence. + test(1, -1, null, level1Phase); + test(1, 0, null, level1Phase); + test(1, 1, level1Phase, null); + test(2, 1, level1Phase, level2Phase); + test(1, 2, level2Phase, null); + } + + static void test(int flagLevel, int compileCommandLevel, String expectedPhase, String unexpectedPhase) throws Exception { + List options = new ArrayList(); + options.add("-Xbatch"); + options.add("-XX:CompileOnly=" + getTestName()); + options.add("-XX:PrintPhaseLevel=" + flagLevel); + options.add("-XX:CompileCommand=PhasePrintLevel," + getTestName() + "," + compileCommandLevel); + options.add(getTestClass()); + OutputAnalyzer oa = ProcessTools.executeTestJava(options); + oa.shouldHaveExitValue(0) + .shouldContain("CompileCommand: PhasePrintLevel compiler/oracle/TestPhasePrintLevel$TestMain.test intx PhasePrintLevel = " + compileCommandLevel) + .shouldNotContain("CompileCommand: An error occurred during parsing") + .shouldNotContain("# A fatal error has been detected by the Java Runtime Environment"); + if (expectedPhase != null) { + oa.shouldContain(expectedPhase); + } + if (unexpectedPhase != null) { + oa.shouldNotContain(unexpectedPhase); + } + } + + static String getTestClass() { + return TestMain.class.getName(); + } + + static String getTestName() { + return getTestClass() + "::test"; + } + + static class TestMain { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(i); + } + } + + static void test(int i) { + if ((i % 1000) == 0) { + System.out.println("Hello World!"); + } + } + } +} From b41146cd1e5d412f69b893bfb2fd65b6206bb0d2 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 20 Nov 2025 09:32:57 +0000 Subject: [PATCH 42/44] 8367531: Template Framework: use scopes and tokens instead of misbehaving immediate-return-queries Co-authored-by: Christian Hagedorn Reviewed-by: rcastanedalo, mhaessig, chagedorn --- .../arguments/TestMethodArguments.java | 4 +- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 16 +- .../lib/template_framework/AddNameToken.java | 4 + .../lib/template_framework/CodeFrame.java | 123 +- .../lib/template_framework/DataName.java | 207 +- .../compiler/lib/template_framework/Hook.java | 92 +- .../template_framework/HookAnchorToken.java | 5 +- .../template_framework/HookInsertToken.java | 6 +- .../HookIsAnchoredToken.java | 37 + .../lib/template_framework/LetToken.java | 38 + .../template_framework/NameCountToken.java | 39 + .../template_framework/NameForEachToken.java | 41 + .../template_framework/NameHasAnyToken.java | 39 + .../template_framework/NameSampleToken.java | 43 + .../lib/template_framework/NameSet.java | 1 + .../template_framework/NamesToListToken.java | 41 + .../lib/template_framework/Renderer.java | 187 +- .../{TemplateBody.java => ScopeToken.java} | 10 +- .../template_framework/ScopeTokenImpl.java | 42 + ...othingToken.java => SetFuelCostToken.java} | 5 +- .../template_framework/StructuralName.java | 147 +- .../lib/template_framework/Template.java | 510 ++-- .../lib/template_framework/TemplateFrame.java | 109 +- .../lib/template_framework/TemplateToken.java | 10 +- .../lib/template_framework/Token.java | 29 +- .../lib/template_framework/TokenParser.java | 2 +- .../library/Expression.java | 4 +- .../library/PrimitiveType.java | 4 +- .../library/TestFrameworkClass.java | 10 +- .../superword/TestAliasingFuzzer.java | 66 +- .../examples/TestAdvanced.java | 6 +- .../examples/TestExpressions.java | 4 +- .../examples/TestPrimitiveTypes.java | 31 +- .../examples/TestSimple.java | 4 +- .../examples/TestTutorial.java | 836 +++++-- .../examples/TestWithTestFrameworkClass.java | 6 +- .../tests/TestExpression.java | 8 +- .../template_framework/tests/TestFormat.java | 6 +- .../tests/TestTemplate.java | 2207 ++++++++++++++--- 39 files changed, 3974 insertions(+), 1005 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/HookIsAnchoredToken.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/NameCountToken.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/NameForEachToken.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/NameHasAnyToken.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/NameSampleToken.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/NamesToListToken.java rename test/hotspot/jtreg/compiler/lib/template_framework/{TemplateBody.java => ScopeToken.java} (78%) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/ScopeTokenImpl.java rename test/hotspot/jtreg/compiler/lib/template_framework/{NothingToken.java => SetFuelCostToken.java} (89%) diff --git a/test/hotspot/jtreg/compiler/arguments/TestMethodArguments.java b/test/hotspot/jtreg/compiler/arguments/TestMethodArguments.java index 6ff830e85c6f3..306d0176aad3a 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestMethodArguments.java +++ b/test/hotspot/jtreg/compiler/arguments/TestMethodArguments.java @@ -60,7 +60,7 @@ public static Template.ZeroArgs generateTest(PrimitiveType type, int numberOfArg : IntStream.range(0, numberOfArguments) .mapToObj(i -> "x" + i) .collect(Collectors.joining(" + ")); - return Template.make(() -> Template.body( + return Template.make(() -> Template.scope( Template.let("type", type.name()), Template.let("boxedType", type.boxedTypeName()), Template.let("arguments", arguments), @@ -115,7 +115,7 @@ public static String generate(CompileFramework comp) { tests.add(generateTest(CodeGenerationDataNameType.longs(), i / 2).asToken()); tests.add(generateTest(CodeGenerationDataNameType.doubles(), i / 2).asToken()); } - return Template.make(() -> Template.body( + return Template.make(() -> Template.scope( Template.let("classpath", comp.getEscapedClassPathOfCompiledClasses()), """ import java.util.Arrays; diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 60b11e8ffbc44..40bfb2e431941 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -45,7 +45,7 @@ import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import compiler.lib.template_framework.library.CodeGenerationDataNameType; @@ -99,7 +99,7 @@ public static String generate(CompileFramework comp) { // Create the body for the test. We use it twice: compiled and reference. // Execute the expression and catch expected Exceptions. - var bodyTemplate = Template.make("expression", "arguments", "checksum", (Expression expression, List arguments, String checksum) -> body( + var bodyTemplate = Template.make("expression", "arguments", "checksum", (Expression expression, List arguments, String checksum) -> scope( """ try { """, @@ -167,14 +167,14 @@ public static String generate(CompileFramework comp) { default -> throw new RuntimeException("not handled: " + type.name()); }; StringPair cmp = cmps.get(RANDOM.nextInt(cmps.size())); - return body( + return scope( ", ", cmp.s0(), type.con(), cmp.s1() ); }); // Checksum method: returns not just the value, but also does some range / bit checks. // This gives us enhanced verification on the range / bits of the result type. - var checksumTemplate = Template.make("expression", "checksum", (Expression expression, String checksum) -> body( + var checksumTemplate = Template.make("expression", "checksum", (Expression expression, String checksum) -> scope( let("returnType", expression.returnType), """ @ForceInline @@ -201,7 +201,7 @@ public static String generate(CompileFramework comp) { // We need to prepare some random values to pass into the test method. We generate the values // once, and pass the same values into both the compiled and reference method. - var valueTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( + var valueTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> scope( "#type #name = ", (type instanceof PrimitiveType pt) ? pt.callLibraryRNG() : type.con(), ";\n" @@ -213,7 +213,7 @@ public static String generate(CompileFramework comp) { // // To ensure that both the compiled and reference method use the same constraint, we put // the computation in a ForceInline method. - var constrainArgumentMethodTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( + var constrainArgumentMethodTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> scope( """ @ForceInline public static #type constrain_#name(#type v) { @@ -247,7 +247,7 @@ public static String generate(CompileFramework comp) { """ )); - var constrainArgumentTemplate = Template.make("name", (String name) -> body( + var constrainArgumentTemplate = Template.make("name", (String name) -> scope( """ #name = constrain_#name(#name); """ @@ -279,7 +279,7 @@ public static String generate(CompileFramework comp) { } } } - return body( + return scope( let("methodArguments", methodArguments.stream().map(ma -> ma.name).collect(Collectors.joining(", "))), let("methodArgumentsWithTypes", diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/AddNameToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/AddNameToken.java index 4f1f7e569bf0f..ceb1cc263fc61 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/AddNameToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/AddNameToken.java @@ -23,4 +23,8 @@ package compiler.lib.template_framework; +/** + * Represents the addition of the specified {@link Name} to the current scope, + * or an outer scope if the inner scope is transparent to {@link Name}s. + */ record AddNameToken(Name name) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java index 5c4ff55614fa5..765e9bc42ba95 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/CodeFrame.java @@ -29,22 +29,96 @@ import java.util.List; /** - * The {@link CodeFrame} represents a frame (i.e. scope) of code, appending {@link Code} to the {@code 'codeList'} + * The {@link CodeFrame} represents a frame (i.e. scope) of generated code by appending {@link Code} to the {@link #codeList} * as {@link Token}s are rendered, and adding names to the {@link NameSet}s with {@link Template#addStructuralName}/ - * {@link Template#addDataName}. {@link Hook}s can be added to a frame, which allows code to be inserted at that - * location later. When a {@link Hook} is {@link Hook#anchor}ed, it separates the Template into an outer and inner - * {@link CodeFrame}, ensuring that names that are added inside the inner frame are only available inside that frame. + * {@link Template#addDataName}. {@link Hook}s can be added to a code frame, which allows code to be inserted at that + * location later. * *

    - * On the other hand, each {@link TemplateFrame} represents the frame (or scope) of exactly one use of a - * Template. + * The {@link CodeFrame} thus implements the {@link Name} non-transparency aspect of {@link ScopeToken}. * *

    - * For simple Template nesting, the {@link CodeFrame}s and {@link TemplateFrame}s overlap exactly. - * However, when using {@link Hook#insert}, we simply nest {@link TemplateFrame}s, going further "in", - * but we jump to an outer {@link CodeFrame}, ensuring that we insert {@link Code} at the outer frame, - * and operating on the names of the outer frame. Once the {@link Hook#insert}ion is complete, we jump - * back to the caller {@link TemplateFrame} and {@link CodeFrame}. + * The {@link CodeFrame}s are nested relative to the order of the final rendered code. This can + * diverge from the nesting order of the {@link Template} when using {@link Hook#insert}, where + * the execution jumps from the current (caller) {@link CodeFrame} scope to the scope of the + * {@link Hook#anchor}. This ensures that the {@link Name}s of the anchor scope are accessed, + * and not the ones from the caller scope. Once the {@link Hook#insert}ion is complete, we + * jump back to the caller {@link CodeFrame}. + * + *

    + * Note, that {@link CodeFrame}s and {@link TemplateFrame}s often go together. But they do diverge when + * we call {@link Hook#insert}. On the {@link CodeFrame} side, the inserted scope is nested in the anchoring + * scope, so that the inserted scope has access to the Names of the anchoring scope, and not the caller + * scope. But the {@link TemplateFrame} of the inserted scope is nested in the caller scope, so + * that the inserted scope has access to hashtag replacements of the caller scope, and not the + * anchoring scope. + */ + +/* + * Below, we look at an example, and show the use of CodeFrames (c) and TemplateFrames (t). + * + * Explanations: + * - Generally, every scope has a CodeFrame and a TemplateFrame. There can be multiple + * scopes inside a Template, and so there can be multiple CodeFrames and TemplateFrames. + * In the drawing below, we draw the frames vertically, and give each a unique id. + * - When we nest scopes inside scopes, we create a new CodeFrame and a new TemplateFrame, + * and so they grow the same nested structure. Example: t3 is nested inside t2 and + * c3 is nested inside c2b. + * - The exception to this: + * - At a hook.anchor, there are two CodeFrames. The first one (e.g. c2a) we call the + * hook CodeFrame, it is kept empty until we insert code to the hook. The second + * (e.g. c2b) we call the inner CodeFrame of the anchoring, into which we keep + * generating the code that is inside the scope of the hook.anchor. + * - At a hook.insert, the TemplateFrame (e.g. t4) is nested into the caller (e.g. t3), + * while the CodeFrame (e.g. c4) is nested into the anchoring CodeFrame (e.g. c2a). + * + * Template( + * t1 c1 + * t1 c1 + * t1 c1 Anchoring Scope + * t1 c1 hook.anchor(scope( + * t1 c1 t2 c2a + * t1 c1 t2 c2a <------ CodeFrame nesting--------+ + * t1 c1 t2 c2a with generated code | + * t1 c1 t2 and Names | + * t1 c1 t2 ^ | + * t1 c1 t2 +- Two CodeFramees | + * t1 c1 t2 v | + * t1 c1 t2 | + * t1 c1 t2 c2b | + * t1 c1 t2 c2b | + * t1 c1 t2 c2b Caller Scope | + * t1 c1 t2 c2b ... scope( | + * t1 c1 t2 c2b ... t3 c3 | Insertion Scope + * t1 c1 t2 c2b ... t3 c3 | hook.insert(transparentScope( + * t1 c1 t2 c2b ... t3 c3 | t4 c4 + * t1 c1 t2 c2b ... t3 c3 +---- t4 ----c4 + * t1 c1 t2 c2b ... t3 c3 t4 c4 + * t1 c1 t2 c2b ... t3 c3 <-- TemplateFrame nesting ---t4 c4 + * t1 c1 t2 c2b ... t3 c3 with hashtag t4 c4 // t: Concerns Template Frame + * t1 c1 t2 c2b ... t3 c3 and setFuelCost t4 c4 // c: Concerns Code Frame + * t1 c1 t2 c2b ... t3 c3 t4 c4 "use hashtag #x" -> t: hashtag queried in Insertion (t4) and Caller Scope (t3) + * t1 c1 t2 c2b ... t3 c3 t4 c4 c: code added to Anchoring Scope (c2a) + * t1 c1 t2 c2b ... t3 c3 t4 c4 + * t1 c1 t2 c2b ... t3 c3 t4 c4 let("x", 42) -> t: hashtag definition escapes to Caller Scope (t3) because + * t1 c1 t2 c2b ... t3 c3 t4 c4 Insertion Scope is transparent + * t1 c1 t2 c2b ... t3 c3 t4 c4 + * t1 c1 t2 c2b ... t3 c3 t4 c4 dataNames(...)...sample() -> c: sample from Insertion (c4) and Anchoring Scope (c2a) + * t1 c1 t2 c2b ... t3 c3 t4 c4 (CodeFrame nesting: c2a -> c4) + * t1 c1 t2 c2b ... t3 c3 t4 c4 addDataName(...) -> c: names escape to the Caller Scope (c3) because + * t1 c1 t2 c2b ... t3 c3 t4 c4 Insertion Scope is transparent + * t1 c1 t2 c2b ... t3 c3 t4 c4 + * t1 c1 t2 c2b ... t3 c3 )) + * t1 c1 t2 c2b ... t3 c3 + * t1 c1 t2 c2b ... t3 c3 + * t1 c1 t2 c2b ... ) + * t1 c1 t2 c2b + * t1 c1 t2 c2b + * t1 c1 )) + * t1 c1 + * t1 c1 + * ) + * */ class CodeFrame { public final CodeFrame parent; @@ -78,25 +152,16 @@ public static CodeFrame makeBase() { } /** - * Creates a normal frame, which has a {@link #parent} and which defines an inner - * {@link NameSet}, for the names that are generated inside this frame. Once this - * frame is exited, the name from inside this frame are not available anymore. - */ - public static CodeFrame make(CodeFrame parent) { - return new CodeFrame(parent, false); - } - - /** - * Creates a special frame, which has a {@link #parent} but uses the {@link NameSet} - * from the parent frame, allowing {@link Template#addDataName}/ - * {@link Template#addStructuralName} to persist in the outer frame when the current frame - * is exited. This is necessary for {@link Hook#insert}, where we would possibly want to - * make field or variable definitions during the insertion that are not just local to the - * insertion but affect the {@link CodeFrame} that we {@link Hook#anchor} earlier and are - * now {@link Hook#insert}ing into. + * Creates a normal frame, which has a {@link #parent}. It can either be + * transparent for names, meaning that names are added and accessed to and + * from an outer frame. Names that are added in a transparent frame are + * still available in the outer frames, as far out as the next non-transparent + * frame. If a frame is non-transparent, this frame defines an inner + * {@link NameSet}, for the names that are generated inside this frame. Once + * this frame is exited, the names from inside this frame are not available. */ - public static CodeFrame makeTransparentForNames(CodeFrame parent) { - return new CodeFrame(parent, true); + public static CodeFrame make(CodeFrame parent, boolean isTransparentForNames) { + return new CodeFrame(parent, isTransparentForNames); } void addString(String s) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/DataName.java b/test/hotspot/jtreg/compiler/lib/template_framework/DataName.java index f45a4db8a1ee3..4a82608567f4d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/DataName.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/DataName.java @@ -24,6 +24,7 @@ package compiler.lib.template_framework; import java.util.List; +import java.util.function.Function; /** * {@link DataName}s represent things like fields and local variables, and can be added to the local @@ -114,18 +115,36 @@ public static final class FilteredSet { this(mutability, null, null); } + // Wrap the FilteredSet as a Predicate. + private record DataNamePredicate(FilteredSet fs) implements NameSet.Predicate { + public boolean check(Name type) { + return fs.check(type); + } + public String toString() { + return fs.toString(); + } + } + NameSet.Predicate predicate() { if (subtype == null && supertype == null) { throw new UnsupportedOperationException("Must first call 'subtypeOf', 'supertypeOf', or 'exactOf'."); } - return (Name name) -> { - if (!(name instanceof DataName dataName)) { return false; } - if (mutability == Mutability.MUTABLE && !dataName.mutable()) { return false; } - if (mutability == Mutability.IMMUTABLE && dataName.mutable()) { return false; } - if (subtype != null && !dataName.type().isSubtypeOf(subtype)) { return false; } - if (supertype != null && !supertype.isSubtypeOf(dataName.type())) { return false; } - return true; - }; + return new DataNamePredicate(this); + } + + boolean check(Name name) { + if (!(name instanceof DataName dataName)) { return false; } + if (mutability == Mutability.MUTABLE && !dataName.mutable()) { return false; } + if (mutability == Mutability.IMMUTABLE && dataName.mutable()) { return false; } + if (subtype != null && !dataName.type().isSubtypeOf(subtype)) { return false; } + if (supertype != null && !supertype.isSubtypeOf(dataName.type())) { return false; } + return true; + } + + public String toString() { + String msg1 = (subtype == null) ? "" : ", subtypeOf(" + subtype + ")"; + String msg2 = (supertype == null) ? "" : ", supertypeOf(" + supertype + ")"; + return "DataName.FilterdSet(" + mutability + msg1 + msg2 + ")"; } /** @@ -173,55 +192,179 @@ public FilteredSet exactOf(DataName.Type type) { /** * Samples a random {@link DataName} from the filtered set, according to the weights - * of the contained {@link DataName}s. + * of the contained {@link DataName}s, making the sampled {@link DataName} + * available to an inner scope. * - * @return The sampled {@link DataName}. + * @param function The {@link Function} that creates the inner {@link ScopeToken} given + * the sampled {@link DataName}. + * @return a token that represents the sampling and inner scope. * @throws UnsupportedOperationException If the type was not constrained with either of * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. - * @throws RendererException If the set was empty. - */ - public DataName sample() { - DataName n = (DataName)Renderer.getCurrent().sampleName(predicate()); - if (n == null) { - String msg1 = (subtype == null) ? "" : ", subtypeOf(" + subtype + ")"; - String msg2 = (supertype == null) ? "" : ", supertypeOf(" + supertype + ")"; - throw new RendererException("No variable: " + mutability + msg1 + msg2 + "."); - } - return n; + */ + public Token sample(Function function) { + return new NameSampleToken<>(predicate(), null, null, function); } /** - * Counts the number of {@link DataName}s in the filtered set. + * Samples a random {@link DataName} from the filtered set, according to the weights + * of the contained {@link DataName}s, and makes a hashtag replacement for both + * the name and type of the {@link DataName}, in the current scope. + * + *

    + * Note, that the following two do the equivalent: * - * @return The number of {@link DataName}s in the filtered set. + *

    + * {@snippet lang=java : + * var template = Template.make(() -> scope( + * dataNames(MUTABLE).subtypeOf(type).sampleAndLetAs("name", "type"), + * """ + * #name #type + * """ + * )); + * } + * + *

    + * {@snippet lang=java : + * var template = Template.make(() -> scope( + * dataNames(MUTABLE).subtypeOf(type).sample((DataName dn) -> transparentScope( + * // The "let" hashtag definitions escape the "transparentScope". + * let("name", dn.name()), + * let("type", dn.type()) + * )), + * """ + * #name #type + * """ + * )); + * } + * + * @param name the key of the hashtag replacement for the {@link DataName} name. + * @param type the key of the hashtag replacement for the {@link DataName} type. + * @return a token that represents the sampling and hashtag replacement definition. * @throws UnsupportedOperationException If the type was not constrained with either of * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. */ - public int count() { - return Renderer.getCurrent().countNames(predicate()); + public Token sampleAndLetAs(String name, String type) { + return new NameSampleToken(predicate(), name, type, n -> Template.transparentScope()); } /** - * Checks if there are any {@link DataName}s in the filtered set. + * Samples a random {@link DataName} from the filtered set, according to the weights + * of the contained {@link DataName}s, and makes a hashtag replacement for the + * name of the {@link DataName}, in the current scope. + * + *

    + * Note, that the following two do the equivalent: + * + *

    + * {@snippet lang=java : + * var template = Template.make(() -> scope( + * dataNames(MUTABLE).subtypeOf(type).sampleAndLetAs("name"), + * """ + * #name + * """ + * )); + * } + * + *

    + * {@snippet lang=java : + * var template = Template.make(() -> scope( + * dataNames(MUTABLE).subtypeOf(type).sample((DataName dn) -> transparentScope( + * // The "let" hashtag definition escape the "transparentScope". + * let("name", dn.name()) + * )), + * """ + * #name + * """ + * )); + * } * - * @return Returns {@code true} iff there is at least one {@link DataName} in the filtered set. + * @param name the key of the hashtag replacement for the {@link DataName} name. + * @return a token that represents the sampling and hashtag replacement definition. * @throws UnsupportedOperationException If the type was not constrained with either of * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. */ - public boolean hasAny() { - return Renderer.getCurrent().hasAnyNames(predicate()); + public Token sampleAndLetAs(String name) { + return new NameSampleToken(predicate(), name, null, n -> Template.transparentScope()); } /** - * Collects all {@link DataName}s in the filtered set. + * Counts the number of {@link DataName}s in the filtered set, making the count + * available to an inner scope. * + * @param function The {@link Function} that creates the inner {@link ScopeToken} given + * the count. + * @return a token that represents the counting and inner scope. + * @throws UnsupportedOperationException If the type was not constrained with either of + * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. + */ + public Token count(Function function) { + return new NameCountToken(predicate(), function); + } + + /** + * Checks if there are any {@link DataName}s in the filtered set, making the resulting boolean + * available to an inner scope. + * + * @param function The {@link Function} that creates the inner {@link ScopeToken} given + * the boolean indicating iff there are any {@link DataName}s in the filtered set. + * @return a token that represents the checking and inner scope. + * @throws UnsupportedOperationException If the type was not constrained with either of + * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. + */ + public Token hasAny(Function function) { + return new NameHasAnyToken(predicate(), function); + } + + /** + * Collects all {@link DataName}s in the filtered set, making the collected list + * available to an inner scope. + * + * @param function The {@link Function} that creates the inner {@link ScopeToken} given + * the list of {@link DataName}. * @return A {@link List} of all {@link DataName}s in the filtered set. * @throws UnsupportedOperationException If the type was not constrained with either of * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. */ - public List toList() { - List list = Renderer.getCurrent().listNames(predicate()); - return list.stream().map(n -> (DataName)n).toList(); + public Token toList(Function, ScopeToken> function) { + return new NamesToListToken<>(predicate(), function); + } + + /** + * Calls the provided {@code function} for each {@link DataName}s in the filtered set, + * making each of these {@link DataName}s available to a separate inner scope. + * + * @param function The {@link Function} that is called to create the inner {@link ScopeToken}s + * for each of the {@link DataName}s in the filtered set. + * @return The token representing the for-each execution and the respective inner scopes. + * @throws UnsupportedOperationException If the type was not constrained with either of + * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. + */ + public Token forEach(Function function) { + return new NameForEachToken<>(predicate(), null, null, function); + } + + /** + * Calls the provided {@code function} for each {@link DataName}s in the filtered set, + * making each of these {@link DataName}s available to a separate inner scope, and additionally + * setting hashtag replacements for the {@code name} and {@code type} of the respective + * {@link DataName}s. + * + *

    + * Note, to avoid duplication of the {@code name} and {@code type} + * hashtag replacements, the scope created by the provided {@code function} should be + * non-transparent to hashtag replacements, for example {@link Template#scope} or + * {@link Template#hashtagScope}. + * + * @param name the key of the hashtag replacement for each individual {@link DataName} name. + * @param type the key of the hashtag replacement for each individual {@link DataName} type. + * @param function The {@link Function} that is called to create the inner {@link ScopeToken}s + * for each of the {@link DataName}s in the filtereds set. + * @return The token representing the for-each execution and the respective inner scopes. + * @throws UnsupportedOperationException If the type was not constrained with either of + * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. + */ + public Token forEach(String name, String type, Function function) { + return new NameForEachToken<>(predicate(), name, type, function); } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java index 8ee2689eb2fce..ef5a5df6ce0ac 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Hook.java @@ -23,59 +23,84 @@ package compiler.lib.template_framework; +import java.util.function.Function; + /** - * {@link Hook}s can be {@link #anchor}ed for a certain scope in a Template, and all nested - * Templates in this scope, and then from within this scope, any Template can - * {@link #insert} code to where the {@link Hook} was {@link #anchor}ed. This can be useful to reach - * "back" or to some outer scope, e.g. while generating code for a method, one can reach out - * to the class scope to insert fields. + * A {@link Hook} can be {@link #anchor}ed for a certain scope ({@link ScopeToken}), and that + * anchoring stays active for any nested scope or nested {@link Template}. With {@link #insert}, + * one can insert a template ({@link TemplateToken}) or scope ({@link ScopeToken}) to where the + * {@link Hook} was {@link #anchor}'ed. If the hook was anchored for multiple outer scopes, the + * innermost is chosen for insertion. + * + *

    + * This can be useful to reach "back" or to some outer scope, e.g. while generating code for a + * method, one can reach out to the class scope to insert fields. Or one may want to reach back + * to the beginning of a method to insert local variables that should be live for the whole method. * *

    + * The choice of {@link ScopeToken} is very important and powerful. + * For example, if you want to insert a {@link DataName} to the scope of an anchor, + * it is important that the scope of the insertion is transparent for {@link DataName}s, + * e.g. using {@link Template#transparentScope}. In most cases, we want {@link DataName}s to escape + * the inserted scope but not the anchor scope, so the anchor scope should be + * non-transparent for {@link DataName}s, e.g. using {@link Template#scope}. * Example: + * + *

    * {@snippet lang=java : * var myHook = new Hook("MyHook"); * - * var template1 = Template.make("name", (String name) -> body( - * """ - * public static int #name = 42; - * """ - * )); - * - * var template2 = Template.make(() -> body( + * var template = Template.make(() -> scope( * """ * public class Test { * """, * // Anchor the hook here. - * myHook.anchor( + * myHook.anchor(scope( * """ * public static void main(String[] args) { * System.out.println("$field: " + $field) * """, - * // Reach out to where the hook was anchored, and insert the code of template1. - * myHook.insert(template1.asToken($("field"))), + * // Reach out to where the hook was anchored, and insert some code. + * myHook.insert(transparentScope( + * // The field (DataName) escapes because the inserted scope is "transparentScope" + * addDataName($("field"), Primitives.INTS, MUTABLE), + * """ + * public static int $field = 42; + * """ + * )), * """ * } * """ - * ), + * )), * """ * } * """ * )); * } * + *

    + * Note that if we use {@link #insert} with {@link Template#transparentScope}, then + * {@link DataName}s and {@link StructuralName}s escape from the inserted scope to the + * anchor scope, but hashtag replacements and {@link Template#setFuelCost} escape to + * the caller, i.e. from where we inserted the scope. This makes sense if we consider + * {@link DataName}s belonging to the structure of the generated code and the inserted + * scope belonging to the anchor scope. On the other hand, hashtag replacements and + * {@link Template#setFuelCost} rather belong to the code generation that happens + * within the context of a template. + * * @param name The name of the Hook, for debugging purposes only. */ public record Hook(String name) { /** - * Anchor this {@link Hook} for the scope of the provided {@code 'tokens'}. + * Anchor this {@link Hook} for the provided inner scope. * From anywhere inside this scope, even in nested Templates, code can be * {@link #insert}ed back to the location where this {@link Hook} was {@link #anchor}ed. * - * @param tokens A list of tokens, which have the same restrictions as {@link Template#body}. - * @return A {@link Token} that captures the anchoring of the scope and the list of validated {@link Token}s. + * @param innerScope An inner scope, for which the {@link Hook} is anchored. + * @return A {@link Token} that captures the anchoring and the inner scope. */ - public Token anchor(Object... tokens) { - return new HookAnchorToken(this, TokenParser.parse(tokens)); + public Token anchor(ScopeToken innerScope) { + return new HookAnchorToken(this, innerScope); } /** @@ -83,18 +108,31 @@ public Token anchor(Object... tokens) { * This could be in the same Template, or one nested further out. * * @param templateToken The Template with applied arguments to be inserted at the {@link Hook}. - * @return The {@link Token} which when used inside a {@link Template#body} performs the code insertion into the {@link Hook}. + * @return The {@link Token} which represents the code insertion into the {@link Hook}. */ public Token insert(TemplateToken templateToken) { - return new HookInsertToken(this, templateToken); + return new HookInsertToken(this, Template.transparentScope(templateToken)); + } + + /** + * Inserts a scope ({@link ScopeToken}) to the innermost location where this {@link Hook} was {@link #anchor}ed. + * This could be in the same Template, or one nested further out. + * + * @param scopeToken The scope to be inserted at the {@link Hook}. + * @return The {@link Token} which represents the code insertion into the {@link Hook}. + */ + public Token insert(ScopeToken scopeToken) { + return new HookInsertToken(this, scopeToken); } /** - * Checks if the {@link Hook} was {@link Hook#anchor}ed for the current scope or an outer scope. + * Checks if the {@link Hook} was {@link Hook#anchor}ed for the current scope or an outer scope, + * and makes the boolean result available to an inner scope. * - * @return If the {@link Hook} was {@link Hook#anchor}ed for the current scope or an outer scope. + * @param function the function that generates the inner scope given the boolean result. + * @return the token that represents the check and inner scope. */ - public boolean isAnchored() { - return Renderer.getCurrent().isAnchored(this); + public Token isAnchored(Function function) { + return new HookIsAnchoredToken(this, function); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookAnchorToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookAnchorToken.java index b025c5ff0415d..4979365b3d0ee 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/HookAnchorToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookAnchorToken.java @@ -25,4 +25,7 @@ import java.util.List; -record HookAnchorToken(Hook hook, List tokens) implements Token {} +/** + * Represents the {@link Hook#anchor} with its inner scope. + */ +record HookAnchorToken(Hook hook, ScopeToken innerScope) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookInsertToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookInsertToken.java index de8b60bbf243f..a433d472a6e7b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/HookInsertToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookInsertToken.java @@ -23,4 +23,8 @@ package compiler.lib.template_framework; -record HookInsertToken(Hook hook, TemplateToken templateToken) implements Token {} +/** + * Represents the {@link Hook#insert} with the {@link ScopeToken} of the + * scope that is to be inserted. + */ +record HookInsertToken(Hook hook, ScopeToken scopeToken) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/HookIsAnchoredToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/HookIsAnchoredToken.java new file mode 100644 index 0000000000000..5c7b92ec1fe54 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/HookIsAnchoredToken.java @@ -0,0 +1,37 @@ +/* + * 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 compiler.lib.template_framework; + +import java.util.function.Function; + +/** + * Represents an {@link Hook#isAnchored} query with the function that creates an inner scope + * given the boolean answer. + */ +record HookIsAnchoredToken(Hook hook, Function function) implements Token { + + ScopeToken getScopeToken(boolean isAnchored) { + return function().apply(isAnchored); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java new file mode 100644 index 0000000000000..ee18dd440b732 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/LetToken.java @@ -0,0 +1,38 @@ +/* + * 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 compiler.lib.template_framework; + +import java.util.function.Function; + +/** + * Represents a let (aka hashtag) definition. The hashtag replacement is active for the + * scope ({@link ScopeToken}) that the {@code function} creates, but can escape that + * scope if it is transparent to hashtags. + */ +record LetToken(String key, T value, Function function) implements Token { + + ScopeToken getScopeToken() { + return function().apply(value); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameCountToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameCountToken.java new file mode 100644 index 0000000000000..f0344efdd082b --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameCountToken.java @@ -0,0 +1,39 @@ +/* + * 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 compiler.lib.template_framework; + +import java.util.function.Function; + +/** + * Represents the counting of {@link Name}s, and the function that is called + * to create an inner scope given the count. + */ +record NameCountToken( + NameSet.Predicate predicate, + Function function) implements Token { + + ScopeToken getScopeToken(int count) { + return function().apply(count); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameForEachToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameForEachToken.java new file mode 100644 index 0000000000000..0e629740be119 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameForEachToken.java @@ -0,0 +1,41 @@ +/* + * 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 compiler.lib.template_framework; + +import java.util.function.Function; + +/** + * Represents the for-each execution of the provided function and (optional) hashtag replacement + * keys for name and type of each name. + */ +record NameForEachToken( + NameSet.Predicate predicate, + String name, + String type, + Function function) implements Token { + + ScopeToken getScopeToken(Name n) { + return function().apply((N)n); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameHasAnyToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameHasAnyToken.java new file mode 100644 index 0000000000000..a31990af21074 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameHasAnyToken.java @@ -0,0 +1,39 @@ +/* + * 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 compiler.lib.template_framework; + +import java.util.function.Function; + +/** + * Represents the check if there is any name and the function that is to + * be called given the boolean value (true iff there are any names). + */ +record NameHasAnyToken( + NameSet.Predicate predicate, + Function function) implements Token { + + ScopeToken getScopeToken(boolean hasAny) { + return function().apply(hasAny); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSampleToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSampleToken.java new file mode 100644 index 0000000000000..0b01f00fcd9fc --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSampleToken.java @@ -0,0 +1,43 @@ +/* + * 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 compiler.lib.template_framework; + +import java.util.function.Function; + +/** + * Represents the sampling of {@link Name}s, and the function that is called given + * the sampled name, as well as the (optional) hashtag replacement keys for the + * name and type of the sampled name, which are then available in the inner scope + * created by the provided function. + */ +record NameSampleToken( + NameSet.Predicate predicate, + String name, + String type, + Function function) implements Token { + + ScopeToken getScopeToken(Name n) { + return function().apply((N)n); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java index ef79c33d48a76..403dbdc694fe0 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NameSet.java @@ -43,6 +43,7 @@ class NameSet { interface Predicate { boolean check(Name type); + String toString(); // used when sampling fails. } NameSet(NameSet parent) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NamesToListToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/NamesToListToken.java new file mode 100644 index 0000000000000..40710a012979f --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/NamesToListToken.java @@ -0,0 +1,41 @@ +/* + * 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 compiler.lib.template_framework; + +import java.util.function.Function; +import java.util.List; + +/** + * Represents the {@code toList} on a filtered name set, including the collection of the + * names and the creation of the inner scope with the function. + */ +record NamesToListToken( + NameSet.Predicate predicate, + Function, ScopeToken> function) implements Token { + + ScopeToken getScopeToken(List names) { + List castNames = names.stream().map(n -> (N)n).toList(); + return function().apply(castNames); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 14adfc81d3fab..61ab9ab343c8c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java @@ -76,7 +76,7 @@ static boolean isValidHashtagOrDollarName(String name) { *

    * When using nested templates, the user of the Template Framework may be tempted to first render * the nested template to a {@link String}, and then use this {@link String} as a token in an outer - * {@link Template#body}. This would be a bad pattern: the outer and nested {@link Template} would + * {@link Template#scope}. This would be a bad pattern: the outer and nested {@link Template} would * be rendered separately, and could not interact. For example, the nested {@link Template} would * not have access to the scopes of the outer {@link Template}. The inner {@link Template} could * not access {@link Name}s and {@link Hook}s from the outer {@link Template}. The user might assume @@ -84,8 +84,8 @@ static boolean isValidHashtagOrDollarName(String name) { * be separated. This could lead to unexpected behavior or even bugs. * *

    - * Instead, the user should create a {@link TemplateToken} from the inner {@link Template}, and - * use that {@link TemplateToken} in the {@link Template#body} of the outer {@link Template}. + * Instead, the user must create a {@link TemplateToken} from the inner {@link Template}, and + * use that {@link TemplateToken} in the {@link Template#scope} of the outer {@link Template}. * This way, the inner and outer {@link Template}s get rendered together, and the inner {@link Template} * has access to the {@link Name}s and {@link Hook}s of the outer {@link Template}. * @@ -113,7 +113,7 @@ private Renderer(float fuel) { static Renderer getCurrent() { if (renderer == null) { - throw new RendererException("A Template method such as '$', 'let', 'sample', 'count' etc. was called outside a template rendering."); + throw new RendererException("A Template method such as '$', 'fuel', etc. was called outside a template rendering call."); } return renderer; } @@ -171,26 +171,6 @@ float fuel() { return currentTemplateFrame.fuel; } - void setFuelCost(float fuelCost) { - currentTemplateFrame.setFuelCost(fuelCost); - } - - Name sampleName(NameSet.Predicate predicate) { - return currentCodeFrame.sampleName(predicate); - } - - int countNames(NameSet.Predicate predicate) { - return currentCodeFrame.countNames(predicate); - } - - boolean hasAnyNames(NameSet.Predicate predicate) { - return currentCodeFrame.hasAnyNames(predicate); - } - - List listNames(NameSet.Predicate predicate) { - return currentCodeFrame.listNames(predicate); - } - /** * Formats values to {@link String} with the goal of using them in Java code. * By default, we use the overrides of {@link Object#toString}. @@ -243,12 +223,16 @@ private static String formatDouble(Double d) { } private void renderTemplateToken(TemplateToken templateToken) { + // We need a TemplateFrame in all cases, this ensures that the outermost scope of the template + // is not transparent for hashtags and setFuelCost, and also that the id of the template is + // unique. TemplateFrame templateFrame = TemplateFrame.make(currentTemplateFrame, nextTemplateFrameId++); currentTemplateFrame = templateFrame; templateToken.visitArguments((name, value) -> addHashtagReplacement(name, format(value))); - TemplateBody body = templateToken.instantiate(); - renderTokenList(body.tokens()); + + // If the ScopeToken is transparent to Names, then the Template is transparent to names. + renderScopeToken(templateToken.instantiate()); if (currentTemplateFrame != templateFrame) { throw new RuntimeException("Internal error: TemplateFrame mismatch!"); @@ -256,29 +240,76 @@ private void renderTemplateToken(TemplateToken templateToken) { currentTemplateFrame = currentTemplateFrame.parent; } + private void renderScopeToken(ScopeToken st) { + renderScopeToken(st, () -> {}); + } + + private void renderScopeToken(ScopeToken st, Runnable preamble) { + if (!(st instanceof ScopeTokenImpl(List tokens, + boolean isTransparentForNames, + boolean isTransparentForHashtags, + boolean isTransparentForSetFuelCost))) { + throw new RuntimeException("Internal error: could not unpack ScopeTokenImpl."); + } + + // We need the CodeFrame for local names. + CodeFrame outerCodeFrame = currentCodeFrame; + if (!isTransparentForNames) { + currentCodeFrame = CodeFrame.make(currentCodeFrame, false); + } + + // We need to be able to define local hashtag replacements, but still + // see the outer ones. We also need to have the same id for dollar + // replacement as the outer frame. And we need to be able to allow + // local setFuelCost definitions. + TemplateFrame innerTemplateFrame = null; + if (!isTransparentForHashtags || !isTransparentForSetFuelCost) { + innerTemplateFrame = TemplateFrame.makeInnerScope(currentTemplateFrame, + isTransparentForHashtags, + isTransparentForSetFuelCost); + currentTemplateFrame = innerTemplateFrame; + } + + // Allow definition of hashtags and variables to be placed in the nested frames. + preamble.run(); + + // Now render the nested code. + renderTokenList(tokens); + + if (!isTransparentForHashtags || !isTransparentForSetFuelCost) { + if (currentTemplateFrame != innerTemplateFrame) { + throw new RuntimeException("Internal error: TemplateFrame mismatch!"); + } + currentTemplateFrame = currentTemplateFrame.parent; + } + + // Tear down CodeFrame nesting. If no nesting happened, the code is already + // in the currentCodeFrame. + if (!isTransparentForNames) { + outerCodeFrame.addCode(currentCodeFrame.getCode()); + currentCodeFrame = outerCodeFrame; + } + } + private void renderToken(Token token) { switch (token) { case StringToken(String s) -> { renderStringWithDollarAndHashtagReplacements(s); } - case NothingToken() -> { - // Nothing. - } - case HookAnchorToken(Hook hook, List tokens) -> { + case HookAnchorToken(Hook hook, ScopeTokenImpl innerScope) -> { CodeFrame outerCodeFrame = currentCodeFrame; - // We need a CodeFrame to which the hook can insert code. That way, name - // definitions at the hook cannot escape the hookCodeFrame. - CodeFrame hookCodeFrame = CodeFrame.make(outerCodeFrame); + // We need a CodeFrame to which the hook can insert code. If the nested names + // are to be local, the CodeFrame must be non-transparent for names. + CodeFrame hookCodeFrame = CodeFrame.make(outerCodeFrame, innerScope.isTransparentForNames()); hookCodeFrame.addHook(hook); - // We need a CodeFrame where the tokens can be rendered. That way, name - // definitions from the tokens cannot escape the innerCodeFrame to the - // hookCodeFrame. - CodeFrame innerCodeFrame = CodeFrame.make(hookCodeFrame); + // We need a CodeFrame where the tokens can be rendered for code that is + // generated inside the anchor scope, but not inserted directly to the hook. + CodeFrame innerCodeFrame = CodeFrame.make(hookCodeFrame, innerScope.isTransparentForNames()); currentCodeFrame = innerCodeFrame; - renderTokenList(tokens); + renderScopeToken(innerScope); // Close the hookCodeFrame and innerCodeFrame. hookCodeFrame code comes before the // innerCodeFrame code from the tokens. @@ -286,20 +317,20 @@ case HookAnchorToken(Hook hook, List tokens) -> { currentCodeFrame.addCode(hookCodeFrame.getCode()); currentCodeFrame.addCode(innerCodeFrame.getCode()); } - case HookInsertToken(Hook hook, TemplateToken templateToken) -> { + case HookInsertToken(Hook hook, ScopeTokenImpl scopeToken) -> { // Switch to hook CodeFrame. CodeFrame callerCodeFrame = currentCodeFrame; CodeFrame hookCodeFrame = codeFrameForHook(hook); // Use a transparent nested CodeFrame. We need a CodeFrame so that the code generated - // by the TemplateToken can be collected, and hook insertions from it can still - // be made to the hookCodeFrame before the code from the TemplateToken is added to + // by the scopeToken can be collected, and hook insertions from it can still + // be made to the hookCodeFrame before the code from the scopeToken is added to // the hookCodeFrame. // But the CodeFrame must be transparent, so that its name definitions go out to - // the hookCodeFrame, and are not limited to the CodeFrame for the TemplateToken. - currentCodeFrame = CodeFrame.makeTransparentForNames(hookCodeFrame); + // the hookCodeFrame, and are not limited to the CodeFrame for the scopeToken. + currentCodeFrame = CodeFrame.make(hookCodeFrame, true); - renderTemplateToken(templateToken); + renderScopeToken(scopeToken); hookCodeFrame.addCode(currentCodeFrame.getCode()); @@ -307,18 +338,68 @@ case HookInsertToken(Hook hook, TemplateToken templateToken) -> { currentCodeFrame = callerCodeFrame; } case TemplateToken templateToken -> { - // Use a nested CodeFrame. - CodeFrame callerCodeFrame = currentCodeFrame; - currentCodeFrame = CodeFrame.make(currentCodeFrame); - renderTemplateToken(templateToken); - - callerCodeFrame.addCode(currentCodeFrame.getCode()); - currentCodeFrame = callerCodeFrame; } case AddNameToken(Name name) -> { currentCodeFrame.addName(name); } + case ScopeToken scopeToken -> { + renderScopeToken(scopeToken); + } + case NameSampleToken nameScopeToken -> { + Name name = currentCodeFrame.sampleName(nameScopeToken.predicate()); + if (name == null) { + throw new RendererException("No Name found for " + nameScopeToken.predicate().toString()); + } + ScopeToken scopeToken = nameScopeToken.getScopeToken(name); + renderScopeToken(scopeToken, () -> { + if (nameScopeToken.name() != null) { + addHashtagReplacement(nameScopeToken.name(), name.name()); + } + if (nameScopeToken.type() != null) { + addHashtagReplacement(nameScopeToken.type(), name.type()); + } + }); + } + case NameForEachToken nameForEachToken -> { + List list = currentCodeFrame.listNames(nameForEachToken.predicate()); + list.stream().forEach(name -> { + ScopeToken scopeToken = nameForEachToken.getScopeToken(name); + renderScopeToken(scopeToken, () -> { + if (nameForEachToken.name() != null) { + addHashtagReplacement(nameForEachToken.name(), name.name()); + } + if (nameForEachToken.type() != null) { + addHashtagReplacement(nameForEachToken.type(), name.type()); + } + }); + }); + } + case NamesToListToken nameToListToken -> { + List list = currentCodeFrame.listNames(nameToListToken.predicate()); + renderScopeToken(nameToListToken.getScopeToken(list)); + } + case NameCountToken nameCountToken -> { + int count = currentCodeFrame.countNames(nameCountToken.predicate()); + renderScopeToken(nameCountToken.getScopeToken(count)); + } + case NameHasAnyToken nameHasAnyToken -> { + boolean hasAny = currentCodeFrame.hasAnyNames(nameHasAnyToken.predicate()); + renderScopeToken(nameHasAnyToken.getScopeToken(hasAny)); + } + case SetFuelCostToken(float fuelCost) -> { + currentTemplateFrame.setFuelCost(fuelCost); + } + case LetToken letToken -> { + ScopeToken scopeToken = letToken.getScopeToken(); + renderScopeToken(scopeToken, () -> { + addHashtagReplacement(letToken.key(), letToken.value()); + }); + } + case HookIsAnchoredToken hookIsAnchoredToken -> { + boolean isAnchored = currentCodeFrame.codeFrameForHook(hookIsAnchoredToken.hook()) != null; + renderScopeToken(hookIsAnchoredToken.getScopeToken(isAnchored)); + } } } @@ -423,10 +504,6 @@ private void renderStringWithDollarAndHashtagReplacementsPart(final String s, fi )); } - boolean isAnchored(Hook hook) { - return currentCodeFrame.codeFrameForHook(hook) != null; - } - private CodeFrame codeFrameForHook(Hook hook) { CodeFrame codeFrame = currentCodeFrame.codeFrameForHook(hook); if (codeFrame == null) { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java b/test/hotspot/jtreg/compiler/lib/template_framework/ScopeToken.java similarity index 78% rename from test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java rename to test/hotspot/jtreg/compiler/lib/template_framework/ScopeToken.java index 440766b3f79ff..f81215da36b87 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateBody.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/ScopeToken.java @@ -23,12 +23,8 @@ package compiler.lib.template_framework; -import java.util.List; - /** - * A Template generates a {@link TemplateBody}, which is a list of {@link Token}s, - * which are then later rendered to {@link String}s. - * - * @param tokens The list of {@link Token}s that are later rendered to {@link String}s. + * A {@link ScopeToken} represents a scope in a {@link Template}, which can be + * created with {@link Template#scope}, {@link Template#transparentScope}, and other related methods. */ -public record TemplateBody(List tokens) {} +public sealed interface ScopeToken extends Token permits ScopeTokenImpl {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/ScopeTokenImpl.java b/test/hotspot/jtreg/compiler/lib/template_framework/ScopeTokenImpl.java new file mode 100644 index 0000000000000..df95bd5672265 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/ScopeTokenImpl.java @@ -0,0 +1,42 @@ +/* + * 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 compiler.lib.template_framework; + +import java.util.List; + +/** + * Represents a scope with its tokens. Boolean flags indicate if names, + * hashtag replacements and {@link Template#setFuelCost} are local, or escape to + * outer scopes. + * + *

    + * Note: We want the {@link ScopeToken} to be public, but the internals of the + * record should be private. One way to solve this is with a public interface + * that exposes nothing but its name, and a private implementation via a + * record that allows easy destructuring with pattern matching. + */ +record ScopeTokenImpl(List tokens, + boolean isTransparentForNames, + boolean isTransparentForHashtags, + boolean isTransparentForSetFuelCost) implements ScopeToken, Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/NothingToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/SetFuelCostToken.java similarity index 89% rename from test/hotspot/jtreg/compiler/lib/template_framework/NothingToken.java rename to test/hotspot/jtreg/compiler/lib/template_framework/SetFuelCostToken.java index 540eaf1e14c9e..08e219b2cd90b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/NothingToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/SetFuelCostToken.java @@ -23,4 +23,7 @@ package compiler.lib.template_framework; -record NothingToken() implements Token {} +/** + * Represents the setting of the fuel cost in the current scope. + */ +record SetFuelCostToken(float fuelCost) implements Token {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/StructuralName.java b/test/hotspot/jtreg/compiler/lib/template_framework/StructuralName.java index 866ac6dbfb8a6..8a1090bc5ab2d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/StructuralName.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/StructuralName.java @@ -24,6 +24,7 @@ package compiler.lib.template_framework; import java.util.List; +import java.util.function.Function; /** * {@link StructuralName}s represent things like method and class names, and can be added to the local @@ -89,16 +90,34 @@ public static final class FilteredSet { this(null, null); } + // Wrap the FilteredSet as a Predicate. + private record StructuralNamePredicate(FilteredSet fs) implements NameSet.Predicate { + public boolean check(Name type) { + return fs.check(type); + } + public String toString() { + return fs.toString(); + } + } + NameSet.Predicate predicate() { if (subtype == null && supertype == null) { throw new UnsupportedOperationException("Must first call 'subtypeOf', 'supertypeOf', or 'exactOf'."); } - return (Name name) -> { - if (!(name instanceof StructuralName structuralName)) { return false; } - if (subtype != null && !structuralName.type().isSubtypeOf(subtype)) { return false; } - if (supertype != null && !supertype.isSubtypeOf(structuralName.type())) { return false; } - return true; - }; + return new StructuralNamePredicate(this); + } + + boolean check(Name name) { + if (!(name instanceof StructuralName structuralName)) { return false; } + if (subtype != null && !structuralName.type().isSubtypeOf(subtype)) { return false; } + if (supertype != null && !supertype.isSubtypeOf(structuralName.type())) { return false; } + return true; + } + + public String toString() { + String msg1 = (subtype == null) ? "" : " subtypeOf(" + subtype + ")"; + String msg2 = (supertype == null) ? "" : " supertypeOf(" + supertype + ")"; + return "StructuralName.FilteredSet(" + msg1 + msg2 + ")"; } /** @@ -146,55 +165,125 @@ public FilteredSet exactOf(StructuralName.Type type) { /** * Samples a random {@link StructuralName} from the filtered set, according to the weights - * of the contained {@link StructuralName}s. + * of the contained {@link StructuralName}s, making the sampled {@link StructuralName} + * available to an inner scope. * - * @return The sampled {@link StructuralName}. + * @param function The {@link Function} that creates the inner {@link ScopeToken} given + * the sampled {@link StructuralName}. + * @return a token that represents the sampling and inner scope. * @throws UnsupportedOperationException If the type was not constrained with either of * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. - * @throws RendererException If the set was empty. */ - public StructuralName sample() { - StructuralName n = (StructuralName)Renderer.getCurrent().sampleName(predicate()); - if (n == null) { - String msg1 = (subtype == null) ? "" : " subtypeOf(" + subtype + ")"; - String msg2 = (supertype == null) ? "" : " supertypeOf(" + supertype + ")"; - throw new RendererException("No variable:" + msg1 + msg2 + "."); - } - return n; + public Token sample(Function function) { + return new NameSampleToken<>(predicate(), null, null, function); + } + + /** + * Samples a random {@link StructuralName} from the filtered set, according to the weights + * of the contained {@link StructuralName}s, and makes a hashtag replacement for both + * the name and type of the {@link StructuralName}, in the current scope. + * + * @param name the key of the hashtag replacement for the {@link StructuralName} name. + * @param type the key of the hashtag replacement for the {@link StructuralName} type. + * @return a token that represents the sampling and hashtag replacement definition. + * @throws UnsupportedOperationException If the type was not constrained with either of + * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. + */ + public Token sampleAndLetAs(String name, String type) { + return new NameSampleToken(predicate(), name, type, n -> Template.transparentScope()); } /** - * Counts the number of {@link StructuralName}s in the filtered set. + * Samples a random {@link StructuralName} from the filtered set, according to the weights + * of the contained {@link StructuralName}s, and makes a hashtag replacement for the + * name of the {@link StructuralName}, in the current scope. * - * @return The number of {@link StructuralName}s in the filtered set. + * @param name the key of the hashtag replacement for the {@link StructuralName} name. + * @return a token that represents the sampling and hashtag replacement definition. * @throws UnsupportedOperationException If the type was not constrained with either of * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. */ - public int count() { - return Renderer.getCurrent().countNames(predicate()); + public Token sampleAndLetAs(String name) { + return new NameSampleToken(predicate(), name, null, n -> Template.transparentScope()); } /** - * Checks if there are any {@link StructuralName}s in the filtered set. + * Counts the number of {@link StructuralName}s in the filtered set, making the count + * available to an inner scope. * - * @return Returns {@code true} iff there is at least one {@link StructuralName} in the filtered set. + * @param function The {@link Function} that creates the inner {@link ScopeToken} given + * the count. + * @return a token that represents the counting and inner scope. * @throws UnsupportedOperationException If the type was not constrained with either of * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. */ - public boolean hasAny() { - return Renderer.getCurrent().hasAnyNames(predicate()); + public Token count(Function function) { + return new NameCountToken(predicate(), function); } /** - * Collects all {@link StructuralName}s in the filtered set. + * Checks if there are any {@link StructuralName}s in the filtered set, making the resulting boolean + * available to an inner scope. * + * @param function The {@link Function} that creates the inner {@link ScopeToken} given + * the boolean indicating iff there are any {@link StructuralName}s in the filtered set. + * @return a token that represents the checking and inner scope. + * @throws UnsupportedOperationException If the type was not constrained with either of + * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. + */ + public Token hasAny(Function function) { + return new NameHasAnyToken(predicate(), function); + } + /** + * Collects all {@link StructuralName}s in the filtered set, making the collected list + * available to an inner scope. + * + * @param function The {@link Function} that creates the inner {@link ScopeToken} given + * the list of {@link StructuralName}. * @return A {@link List} of all {@link StructuralName}s in the filtered set. * @throws UnsupportedOperationException If the type was not constrained with either of * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. */ - public List toList() { - List list = Renderer.getCurrent().listNames(predicate()); - return list.stream().map(n -> (StructuralName)n).toList(); + public Token toList(Function, ScopeToken> function) { + return new NamesToListToken<>(predicate(), function); + } + + /** + * Calls the provided {@code function} for each {@link StructuralName}s in the filtered set, + * making each of these {@link StructuralName}s available to a separate inner scope. + * + * @param function The {@link Function} that is called to create the inner {@link ScopeToken}s + * for each of the {@link StructuralName}s in the filtereds set. + * @return The token representing the for-each execution and the respective inner scopes. + * @throws UnsupportedOperationException If the type was not constrained with either of + * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. + */ + public Token forEach(Function function) { + return new NameForEachToken<>(predicate(), null, null, function); + } + + /** + * Calls the provided {@code function} for each {@link StructuralName}s in the filtered set, + * making each of these {@link StructuralName}s available to a separate inner scope, and additionally + * setting hashtag replacements for the {@code name} and {@code type} of the respective + * {@link StructuralName}s. + * + *

    + * Note, to avoid duplication of the {@code name} and {@code type} + * hashtag replacements, the scope created by the provided {@code function} should be + * non-transparent to hashtag replacements, for example {@link Template#scope} or + * {@link Template#hashtagScope}. + * + * @param name the key of the hashtag replacement for each individual {@link StructuralName} name. + * @param type the key of the hashtag replacement for each individual {@link StructuralName} type. + * @param function The {@link Function} that is called to create the inner {@link ScopeToken}s + * for each of the {@link StructuralName}s in the filtereds set. + * @return The token representing the for-each execution and the respective inner scopes. + * @throws UnsupportedOperationException If the type was not constrained with either of + * {@link #subtypeOf}, {@link #supertypeOf} or {@link #exactOf}. + */ + public Token forEach(String name, String type, Function function) { + return new NameForEachToken<>(predicate(), name, type, function); } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index 57d06e732bb1f..f245cda050118 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -65,7 +65,7 @@ * *

    * {@snippet lang=java : - * var testTemplate = Template.make("typeName", "operator", "generator", (String typeName, String operator, MyGenerator generator) -> body( + * var testTemplate = Template.make("typeName", "operator", "generator", (String typeName, String operator, MyGenerator generator) -> scope( * let("con1", generator.next()), * let("con2", generator.next()), * """ @@ -86,13 +86,13 @@ * } * *

    - * To get an executable test, we define a {@link Template} that produces a class body with a main method. The Template + * To get an executable test, we define a {@link Template} that produces a class scope with a main method. The Template * takes a list of types, and calls the {@code testTemplate} defined above for each type and operator. We use * the {@link TestFramework} to call our {@code @Test} methods. * *

    * {@snippet lang=java : - * var classTemplate = Template.make("types", (List types) -> body( + * var classTemplate = Template.make("types", (List types) -> scope( * let("classpath", comp.getEscapedClassPathOfCompiledClasses()), * """ * package p.xyz; @@ -148,12 +148,12 @@ * {@link Template#make(String, Function)}. For each number of arguments there is an implementation * (e.g. {@link Template.TwoArgs} for two arguments). This allows the use of generics for the * {@link Template} argument types which enables type checking of the {@link Template} arguments. - * It is currently only allowed to use up to three arguments. + * It is currently only allowed to use up to three arguments. * *

    * A {@link Template} can be rendered to a {@link String} (e.g. {@link Template.ZeroArgs#render()}). * Alternatively, we can generate a {@link Token} (more specifically, a {@link TemplateToken}) with {@code asToken()} - * (e.g. {@link Template.ZeroArgs#asToken()}), and use the {@link Token} inside another {@link Template#body}. + * (e.g. {@link Template.ZeroArgs#asToken()}), and use the {@link Token} inside another {@link Template#scope}. * *

    * Ideally, we would have used string templates to inject these Template @@ -161,6 +161,11 @@ * hashtag replacements in the {@link String}s: the Template argument names are captured, and * the argument values automatically replace any {@code "#name"} in the {@link String}s. See the different overloads * of {@link #make} for examples. Additional hashtag replacements can be defined with {@link #let}. + * We have decided to keep hashtag replacements constrained to the scope of one Template. They + * do not escape to outer or inner Template uses. If one needs to pass values to inner Templates, + * this can be done with Template arguments. Keeping hashtag replacements local to Templates + * has the benefit that there is no conflict in recursive templates, where outer and inner Templates + * define the same hashtag replacement. * *

    * When using nested Templates, there can be collisions with identifiers (e.g. variable names and method names). @@ -176,25 +181,6 @@ * {@code #{name}}. * *

    - * A {@link TemplateToken} cannot just be used in {@link Template#body}, but it can also be - * {@link Hook#insert}ed to where a {@link Hook} was {@link Hook#anchor}ed earlier (in some outer scope of the code). - * For example, while generating code in a method, one can reach out to the scope of the class, and insert a - * new field, or define a utility method. - * - *

    - * A {@link TemplateBinding} allows the recursive use of Templates. With the indirection of such a binding, - * a Template can reference itself. - * - *

    - * The writer of recursive {@link Template}s must ensure that this recursion terminates. To unify the - * approach across {@link Template}s, we introduce the concept of {@link #fuel}. Templates are rendered starting - * with a limited amount of {@link #fuel} (default: 100, see {@link #DEFAULT_FUEL}), which is decreased at each - * Template nesting by a certain amount (default: 10, see {@link #DEFAULT_FUEL_COST}). The default fuel for a - * template can be changed when we {@code render()} it (e.g. {@link ZeroArgs#render(float)}) and the default - * fuel cost with {@link #setFuelCost}) when defining the {@link #body(Object...)}. Recursive templates are - * supposed to terminate once the {@link #fuel} is depleted (i.e. reaches zero). - * - *

    * Code generation can involve keeping track of fields and variables, as well as the scopes in which they * are available, and if they are mutable or immutable. We model fields and variables with {@link DataName}s, * which we can add to the current scope with {@link #addDataName}. We can access the {@link DataName}s with @@ -211,61 +197,70 @@ * are not concerned about mutability. * *

    - * When working with {@link DataName}s and {@link StructuralName}s, it is important to be aware of the - * relevant scopes, as well as the execution order of the {@link Template} lambdas and the evaluation - * of the {@link Template#body} tokens. When a {@link Template} is rendered, its lambda is invoked. In the - * lambda, we generate the tokens, and create the {@link Template#body}. Once the lambda returns, the - * tokens are evaluated one by one. While evaluating the tokens, the {@link Renderer} might encounter a nested - * {@link TemplateToken}, which in turn triggers the evaluation of that nested {@link Template}, i.e. - * the evaluation of its lambda and later the evaluation of its tokens. It is important to keep in mind - * that the lambda is always executed first, and the tokens are evaluated afterwards. A method like - * {@code dataNames(MUTABLE).exactOf(type).count()} is a method that is executed during the evaluation - * of the lambda. But a method like {@link #addDataName} returns a token, and does not immediately add - * the {@link DataName}. This ensures that the {@link DataName} is only inserted when the tokens are - * evaluated, so that it is inserted at the exact scope where we would expect it. + * Code generation can involve keeping track of scopes in the code (e.g. liveness and availability of + * {@link DataName}s) and of the hashtag replacements in the templates. The {@link ScopeToken} serves + * this purpose, and allows the definition of transparent scopes (e.g. {@link #transparentScope}) and + * non-transparent scopes (e.g. {@link #scope}). + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Scopes and (non-)transparency
    hashtag {@link DataName} and {@link StructuralName} {@link #setFuelCost}
    {@link #scope} non-transparent non-transparent non-transparent
    {@link #hashtagScope} non-transparent transparent transparent
    {@link #nameScope} transparent non-transparent transparent
    {@link #setFuelCostScope} transparent transparent non-transparent
    {@link #transparentScope} transparent transparent transparent
    + * + *

    + * In some cases, we may be deeper nested in templates and scopes, and would like to reach "back" or + * to outer scopes. This is possible with {@link Hook#anchor}ing in some outer scope, and later + * {@link Hook#insert}ing from an inner scope to the scope of the anchoring. For example, while + * generating code in a method, one can reach out to the scope of the class, and insert a new field, + * or define a utility method. * *

    - * Let us look at the following example to better understand the execution order. + * A {@link TemplateBinding} allows the recursive use of Templates. With the indirection of such a binding, + * a Template can reference itself. * *

    - * {@snippet lang=java : - * var testTemplate = Template.make(() -> body( - * // The lambda has just been invoked. - * // We count the DataNames and assign the count to the hashtag replacement "c1". - * let("c1", dataNames(MUTABLE).exactOf(someType).count()), - * // We want to define a DataName "v1", and create a token for it. - * addDataName($("v1"), someType, MUTABLE), - * // We count the DataNames again, but the count does NOT change compared to "c1". - * // This is because the token for "v1" is only evaluated later. - * let("c2", dataNames(MUTABLE).exactOf(someType).count()), - * // Create a nested scope. - * METHOD_HOOK.anchor( - * // We want to define a DataName "v2", which is only valid inside this - * // nested scope. - * addDataName($("v2"), someType, MUTABLE), - * // The count is still not different to "c1". - * let("c3", dataNames(MUTABLE).exactOf(someType).count()), - * // We nest a Template. This creates a TemplateToken, which is later evaluated. - * // By the time the TemplateToken is evaluated, the tokens from above will - * // be already evaluated. Hence, "v1" and "v2" are added by then, and if the - * // "otherTemplate" were to count the DataNames, the count would be increased - * // by 2 compared to "c1". - * otherTemplate.asToken() - * ), - * // After closing the scope, "v2" is no longer available. - * // The count is still the same as "c1", since "v1" is still only a token. - * let("c4", dataNames(MUTABLE).exactOf(someType).count()), - * // We nest another Template. Again, this creates a TemplateToken, which is only - * // evaluated later. By that time, the token for "v1" is evaluated, and so the - * // nested Template would observe an increment in the count. - * anotherTemplate.asToken() - * // By this point, all methods are called, and the tokens generated. - * // The lambda returns the "body", which is all of the tokens that we just - * // generated. After returning from the lambda, the tokens will be evaluated - * // one by one. - * )); - * } - + * The writer of recursive {@link Template}s must ensure that this recursion terminates. To unify the + * approach across {@link Template}s, we introduce the concept of {@link #fuel}. Templates are rendered starting + * with a limited amount of {@link #fuel} (default: 100, see {@link #DEFAULT_FUEL}), which is decreased at each + * Template nesting by a certain amount (default: 10, see {@link #DEFAULT_FUEL_COST}). The default fuel for a + * template can be changed when we {@code render()} it (e.g. {@link ZeroArgs#render(float)}) and the default + * fuel cost with {@link #setFuelCost}) when defining the {@link #scope(Object...)}. Recursive templates are + * supposed to terminate once the {@link #fuel} is depleted (i.e. reaches zero). + * + *

    + * A note from the implementor to the user: We have decided to implement the Template Framework using + * a functional (lambdas) and data-oriented (tokens) model. The consequence is that there are three + * orders in template rendering: (1) the execution order in lambdas, where we usually assemble the + * tokens and pass them to some scope ({@link ScopeToken}) as arguments. (2) the token evaluation + * order, which occurs in the order of how tokens are listed in a scope. By design, the token order + * is the same order as execution in lambdas. To keep the lambda and token order in sync, most of the + * queries about the state of code generation, such as {@link DataName}s and {@link Hook}s cannot + * return the values immediately, but have to be expressed as tokens. If we had a mix of tokens and + * immediate queries, then the immediate queries would "float" by the tokens, because the immediate + * queries are executed during the lambda execution, but the tokens are only executed later. Having + * to express everything as tokens can be a little more cumbersome (e.g. sample requires a lambda + * that captures the {@link DataName}, and sample does not return the {@link DataName} directly). + * But this ensures that reasoning about execution order is relatively straight forward, namely in + * the order of the specified tokens. (3) the final code order is the same as the lambda and token + * order, except when using {@link Hook#insert}, which places the code at the innermost {@link Hook#anchor}. + * *

    * More examples for these functionalities can be found in {@code TestTutorial.java}, {@code TestSimple.java}, * and {@code TestAdvanced.java}, which all produce compilable Java code. Additional examples can be found in @@ -281,10 +276,10 @@ public sealed interface Template permits Template.ZeroArgs, /** * A {@link Template} with no arguments. * - * @param function The {@link Supplier} that creates the {@link TemplateBody}. + * @param function The {@link Supplier} that creates the {@link ScopeToken}. */ - record ZeroArgs(Supplier function) implements Template { - TemplateBody instantiate() { + record ZeroArgs(Supplier function) implements Template { + ScopeToken instantiate() { return function.get(); } @@ -324,10 +319,10 @@ public String render(float fuel) { * * @param arg1Name The name of the (first) argument, used for hashtag replacements in the {@link Template}. * @param The type of the (first) argument. - * @param function The {@link Function} that creates the {@link TemplateBody} given the template argument. + * @param function The {@link Function} that creates the {@link ScopeToken} given the template argument. */ - record OneArg(String arg1Name, Function function) implements Template { - TemplateBody instantiate(T1 arg1) { + record OneArg(String arg1Name, Function function) implements Template { + ScopeToken instantiate(T1 arg1) { return function.apply(arg1); } @@ -372,10 +367,10 @@ public String render(float fuel, T1 arg1) { * @param arg2Name The name of the second argument, used for hashtag replacements in the {@link Template}. * @param The type of the first argument. * @param The type of the second argument. - * @param function The {@link BiFunction} that creates the {@link TemplateBody} given the template arguments. + * @param function The {@link BiFunction} that creates the {@link ScopeToken} given the template arguments. */ - record TwoArgs(String arg1Name, String arg2Name, BiFunction function) implements Template { - TemplateBody instantiate(T1 arg1, T2 arg2) { + record TwoArgs(String arg1Name, String arg2Name, BiFunction function) implements Template { + ScopeToken instantiate(T1 arg1, T2 arg2) { return function.apply(arg1, arg2); } @@ -447,10 +442,10 @@ interface TriFunction { * @param The type of the first argument. * @param The type of the second argument. * @param The type of the third argument. - * @param function The function with three arguments that creates the {@link TemplateBody} given the template arguments. + * @param function The function with three arguments that creates the {@link ScopeToken} given the template arguments. */ - record ThreeArgs(String arg1Name, String arg2Name, String arg3Name, TriFunction function) implements Template { - TemplateBody instantiate(T1 arg1, T2 arg2, T3 arg3) { + record ThreeArgs(String arg1Name, String arg2Name, String arg3Name, TriFunction function) implements Template { + ScopeToken instantiate(T1 arg1, T2 arg2, T3 arg3) { return function.apply(arg1, arg2, arg3); } @@ -496,28 +491,28 @@ public String render(float fuel, T1 arg1, T2 arg2, T3 arg3) { /** * Creates a {@link Template} with no arguments. - * See {@link #body} for more details about how to construct a Template with {@link Token}s. + * See {@link #scope} for more details about how to construct a Template with {@link Token}s. * *

    * Example: * {@snippet lang=java : - * var template = Template.make(() -> body( + * var template = Template.make(() -> scope( * """ * Multi-line string or other tokens. * """ * )); * } * - * @param body The {@link TemplateBody} created by {@link Template#body}. + * @param scope The {@link ScopeToken} created by {@link Template#scope}. * @return A {@link Template} with zero arguments. */ - static Template.ZeroArgs make(Supplier body) { - return new Template.ZeroArgs(body); + static Template.ZeroArgs make(Supplier scope) { + return new Template.ZeroArgs(scope); } /** * Creates a {@link Template} with one argument. - * See {@link #body} for more details about how to construct a Template with {@link Token}s. + * See {@link #scope} for more details about how to construct a Template with {@link Token}s. * Good practice but not enforced but not enforced: {@code arg1Name} should match the lambda argument name. * *

    @@ -525,7 +520,7 @@ static Template.ZeroArgs make(Supplier body) { * for use in hashtag replacements, and captured once as lambda argument with the corresponding type * of the generic argument. * {@snippet lang=java : - * var template = Template.make("a", (Integer a) -> body( + * var template = Template.make("a", (Integer a) -> scope( * """ * Multi-line string or other tokens. * We can use the hashtag replacement #a to directly insert the String value of a. @@ -534,18 +529,18 @@ static Template.ZeroArgs make(Supplier body) { * )); * } * - * @param body The {@link TemplateBody} created by {@link Template#body}. + * @param scope The {@link ScopeToken} created by {@link Template#scope}. * @param Type of the (first) argument. * @param arg1Name The name of the (first) argument for hashtag replacement. * @return A {@link Template} with one argument. */ - static Template.OneArg make(String arg1Name, Function body) { - return new Template.OneArg<>(arg1Name, body); + static Template.OneArg make(String arg1Name, Function scope) { + return new Template.OneArg<>(arg1Name, scope); } /** * Creates a {@link Template} with two arguments. - * See {@link #body} for more details about how to construct a Template with {@link Token}s. + * See {@link #scope} for more details about how to construct a Template with {@link Token}s. * Good practice but not enforced: {@code arg1Name} and {@code arg2Name} should match the lambda argument names. * *

    @@ -553,7 +548,7 @@ static Template.OneArg make(String arg1Name, Function * for use in hashtag replacements, and captured once as lambda arguments with the corresponding types * of the generic arguments. * {@snippet lang=java : - * var template = Template.make("a", "b", (Integer a, String b) -> body( + * var template = Template.make("a", "b", (Integer a, String b) -> scope( * """ * Multi-line string or other tokens. * We can use the hashtag replacement #a and #b to directly insert the String value of a and b. @@ -562,23 +557,23 @@ static Template.OneArg make(String arg1Name, Function * )); * } * - * @param body The {@link TemplateBody} created by {@link Template#body}. + * @param scope The {@link ScopeToken} created by {@link Template#scope}. * @param Type of the first argument. * @param arg1Name The name of the first argument for hashtag replacement. * @param Type of the second argument. * @param arg2Name The name of the second argument for hashtag replacement. * @return A {@link Template} with two arguments. */ - static Template.TwoArgs make(String arg1Name, String arg2Name, BiFunction body) { - return new Template.TwoArgs<>(arg1Name, arg2Name, body); + static Template.TwoArgs make(String arg1Name, String arg2Name, BiFunction scope) { + return new Template.TwoArgs<>(arg1Name, arg2Name, scope); } /** * Creates a {@link Template} with three arguments. - * See {@link #body} for more details about how to construct a Template with {@link Token}s. + * See {@link #scope} for more details about how to construct a Template with {@link Token}s. * Good practice but not enforced: {@code arg1Name}, {@code arg2Name}, and {@code arg3Name} should match the lambda argument names. * - * @param body The {@link TemplateBody} created by {@link Template#body}. + * @param scope The {@link ScopeToken} created by {@link Template#scope}. * @param Type of the first argument. * @param arg1Name The name of the first argument for hashtag replacement. * @param Type of the second argument. @@ -587,18 +582,35 @@ static Template.TwoArgs make(String arg1Name, String arg2Name, * @param arg3Name The name of the third argument for hashtag replacement. * @return A {@link Template} with three arguments. */ - static Template.ThreeArgs make(String arg1Name, String arg2Name, String arg3Name, Template.TriFunction body) { - return new Template.ThreeArgs<>(arg1Name, arg2Name, arg3Name, body); + static Template.ThreeArgs make(String arg1Name, String arg2Name, String arg3Name, Template.TriFunction scope) { + return new Template.ThreeArgs<>(arg1Name, arg2Name, arg3Name, scope); } /** - * Creates a {@link TemplateBody} from a list of tokens, which can be {@link String}s, - * boxed primitive types (for example {@link Integer} or auto-boxed {@code int}), any {@link Token}, - * or {@link List}s of any of these. + * Creates a {@link ScopeToken} that represents a scope that is completely + * non-transparent, not allowing anything to escape. This + * means that no {@link DataName}, {@link StructuralName}s, hashtag-replacement + * or {@link #setFuelCost} defined inside the scope is available outside. All + * these usages are only local to the defining scope here. + * + *

    + * The scope is formed from a list of tokens, which can be {@link String}s, + * boxed primitive types (for example {@link Integer} or auto-boxed {@code int}), + * any {@link Token}, or {@link List}s of any of these. + * + *

    + * If you require a scope that is either fully transparent (i.e. everything escapes) + * or only restricts a specific kind to not escape, consider using one of the other + * provided scopes: {@link #transparentScope}, {@link #nameScope}, {@link #hashtagScope}, + * or {@link #setFuelCostScope}. A "scope-transparency-matrix" can also be found in + * the interface comment for {@link Template}. + * + *

    + * The most common use of {@link #scope} is in the construction of templates: * *

    * {@snippet lang=java : - * var template = Template.make(() -> body( + * var template = Template.make(() -> scope( * """ * Multi-line string * """, @@ -608,14 +620,200 @@ static Template.ThreeArgs make(String arg1Name, String * )); * } * + *

    + * Note that regardless of the chosen scope for {@code Template.make}, + * hashtag-replacements and {@link #setFuelCost} are always implicitly + * non-transparent (i.e. non-escaping). For example, {@link #let} will + * not escape the template scope even when using {@link #transparentScope}. + * As a default, it is recommended to use {@link #scope} for + * {@code Template.make} since in most cases template scopes align with + * code scopes that are non-transparent for fields, variables, etc. In + * rare cases, where the scope of the template needs to be transparent + * (e.g. because we need to insert a variable or field into an outer scope), + * it is recommended to use {@link #transparentScope}. This allows to make + * {@link DataName}s and {@link StructuralName}s available outside this + * template crossing the template boundary. + * + *

    + * We can also use nested scopes inside of templates: + * + *

    + * {@snippet lang=java : + * var template = Template.make(() -> scope( + * // CODE1: some code in the outer scope + * scope( + * // CODE2: some code in the inner scope. Names, hashtags and setFuelCost + * // do not escape the inner scope. + * ), + * // CODE3: more code in the outer scope, names and hashtags from CODE2 are + * // not available anymore because of the non-transparent "scope". + * transparentScope( + * // CODE4: some code in the inner "transparentScope". Names, hashtags and setFuelCost + * // escape the "transparentScope" and are still available after the "transparentScope" + * // closes. + * ) + * // CODE5: we still have access to names and hashtags from CODE4. + * )); + * } + * * @param tokens A list of tokens, which can be {@link String}s, boxed primitive types * (for example {@link Integer}), any {@link Token}, or {@link List}s * of any of these. - * @return The {@link TemplateBody} which captures the list of validated {@link Token}s. + * @return The {@link ScopeToken} which captures the list of validated {@link Token}s. * @throws IllegalArgumentException if the list of tokens contains an unexpected object. */ - static TemplateBody body(Object... tokens) { - return new TemplateBody(TokenParser.parse(tokens)); + static ScopeToken scope(Object... tokens) { + return new ScopeTokenImpl(TokenParser.parse(tokens), false, false, false); + } + + /** + * Creates a {@link ScopeToken} that represents a completely transparent scope. + * This means that {@link DataName}s, {@link StructuralName}s, + * hashtag-replacements and {@link #setFuelCost} declared inside the scope will be available + * in the outer scope. + * The scope is formed from a list of tokens, which can be {@link String}s, + * boxed primitive types (for example {@link Integer} or auto-boxed {@code int}), + * any {@link Token}, or {@link List}s of any of these. + * + *

    + * If you require a scope that is non-transparent (i.e. nothing escapes) or only restricts + * a specific kind to not escape, consider using one of the other provided scopes: + * {@link #scope}, {@link #nameScope}, {@link #hashtagScope}, or {@link #setFuelCostScope}. + * A "scope-transparency-matrix" can also be found in the interface comment for {@link Template}. + * + * @param tokens A list of tokens, which can be {@link String}s, boxed primitive types + * (for example {@link Integer}), any {@link Token}, or {@link List}s + * of any of these. + * @return The {@link ScopeToken} which captures the list of validated {@link Token}s. + * @throws IllegalArgumentException if the list of tokens contains an unexpected object. + */ + static ScopeToken transparentScope(Object... tokens) { + return new ScopeTokenImpl(TokenParser.parse(tokens), true, true, true); + } + + /** + * Creates a {@link ScopeToken} that represents a scope that is non-transparent for + * {@link DataName}s and {@link StructuralName}s (i.e. cannot escape), but + * transparent for hashtag-replacements and {@link #setFuelCost} (i.e. available + * in outer scope). + * + *

    + * The scope is formed from a list of tokens, which can be {@link String}s, + * boxed primitive types (for example {@link Integer} or auto-boxed {@code int}), + * any {@link Token}, or {@link List}s of any of these. + * + *

    + * If you require a scope that is transparent or uses a different restriction, consider + * using one of the other provided scopes: {@link #scope}, {@link #transparentScope}, + * {@link #hashtagScope}, or {@link #setFuelCostScope}. A "scope-transparency-matrix" can + * also be found in the interface comment for {@link Template}. + * + * @param tokens A list of tokens, which can be {@link String}s, boxed primitive types + * (for example {@link Integer}), any {@link Token}, or {@link List}s + * of any of these. + * @return The {@link ScopeToken} which captures the list of validated {@link Token}s. + * @throws IllegalArgumentException if the list of tokens contains an unexpected object. + */ + static ScopeToken nameScope(Object... tokens) { + return new ScopeTokenImpl(TokenParser.parse(tokens), false, true, true); + } + + /** + * Creates a {@link ScopeToken} that represents a scope that is non-transparent for + * hashtag-replacements (i.e. cannot escape), but transparent for {@link DataName}s + * and {@link StructuralName}s and {@link #setFuelCost} (i.e. available in outer scope). + * + *

    + * The scope is formed from a list of tokens, which can be {@link String}s, + * boxed primitive types (for example {@link Integer} or auto-boxed {@code int}), + * any {@link Token}, or {@link List}s of any of these. + * + *

    + * If you require a scope that is transparent or uses a different restriction, consider + * using one of the other provided scopes: {@link #scope}, {@link #transparentScope}, + * {@link #nameScope}, or {@link #setFuelCostScope}. A "scope-transparency-matrix" can + * also be found in the interface comment for {@link Template}. + * + *

    + * Keeping hashtag-replacements local but letting {@link DataName}s escape can be + * useful in cases like the following, where we may want to reuse the hashtag + * multiple times: + * + *

    + * {@snippet lang=java : + * var template = Template.make(() -> scope( + * List.of("a", "b", "c").stream().map(name -> hashtagScope( + * let("name", name), // assumes values: a, b, c + * addDataName(name, PrimitiveType.INTS, MUTABLE), // escapes + * """ + * int #name = 42; + * """ + * )) + * // We still have access to the three DataNames. + * )); + * } + * + * @param tokens A list of tokens, which can be {@link String}s, boxed primitive types + * (for example {@link Integer}), any {@link Token}, or {@link List}s + * of any of these. + * @return The {@link ScopeToken} which captures the list of validated {@link Token}s. + * @throws IllegalArgumentException if the list of tokens contains an unexpected object. + */ + static ScopeToken hashtagScope(Object... tokens) { + return new ScopeTokenImpl(TokenParser.parse(tokens), true, false, true); + } + + /** + * Creates a {@link ScopeToken} that represents a scope that is non-transparent for + * {@link #setFuelCost} (i.e. cannot escape), but transparent for hashtag-replacements, + * {@link DataName}s and {@link StructuralName}s (i.e. available in outer scope). + * The scope is formed from a list of tokens, which can be {@link String}s, + * boxed primitive types (for example {@link Integer} or auto-boxed {@code int}), + * any {@link Token}, or {@link List}s of any of these. + * + *

    + * If you require a scope that is transparent or uses a different restriction, consider + * using one of the other provided scopes: {@link #scope}, {@link #transparentScope}, + * {@link #hashtagScope}, or {@link #nameScope}. A "scope-transparency-matrix" can + * also be found in the interface comment for {@link Template}. + * + *

    + * In some cases, it can be helpful to have different {@link #setFuelCost} within + * a single template, depending on the code nesting depth. Example: + * + *

    + * {@snippet lang=java : + * var template = Template.make(() -> scope( + * setFuelCost(1), + * // CODE1: some shallow code, allowing recursive template uses here + * // to use more fuel. + * """ + * for (int i = 0; i < 1000; i++) { + * """, + * setFuelCostScope( + * setFuelCost(100) + * // CODE2: with the for-loop, we already have a deeper nesting + * // depth, and recursive template uses should not get + * // as much fuel as in CODE1. + * ), + * """ + * } + * """ + * // CODE3: we are back in the outer scope of CODE1, and can use + * // more fuel again in nested template uses. setFuelCost + * // is automatically restored to what was set before the + * // inner scope. + * )); + * } + * + * @param tokens A list of tokens, which can be {@link String}s, boxed primitive types + * (for example {@link Integer}), any {@link Token}, or {@link List}s + * of any of these. + * @return The {@link ScopeToken} which captures the list of validated {@link Token}s. + * @throws IllegalArgumentException if the list of tokens contains an unexpected object. + */ + static ScopeToken setFuelCostScope(Object... tokens) { + return new ScopeTokenImpl(TokenParser.parse(tokens), true, true, false); } /** @@ -628,7 +826,7 @@ static TemplateBody body(Object... tokens) { * with an implicit dollar replacement, and then captures that dollar replacement * using {@link #$} for the use inside a nested template. * {@snippet lang=java : - * var template = Template.make(() -> body( + * var template = Template.make(() -> scope( * """ * int $var = 42; * """, @@ -640,6 +838,9 @@ static TemplateBody body(Object... tokens) { * @return The dollar replacement for the {@code 'name'}. */ static String $(String name) { + // Note, since the dollar replacements do not change within a template + // and the retrieval has no side effects, we can return the value immediately, + // and do not need a token. return Renderer.getCurrent().$(name); } @@ -648,7 +849,7 @@ static TemplateBody body(Object... tokens) { * *

    * {@snippet lang=java : - * var template = Template.make("a", (Integer a) -> body( + * var template = Template.make("a", (Integer a) -> scope( * let("b", a * 5), * """ * System.out.println("Use a and b with hashtag replacement: #a and #b"); @@ -656,41 +857,50 @@ static TemplateBody body(Object... tokens) { * )); * } * + *

    + * Note that a {@code let} definition makes the hashtag replacement available + * for anything that follows it, until the the end of the next outer scope + * that is non-transparent for hashtag replacements. Additionally, hashtag + * replacements are limited to the template they were defined in. + * If you want to pass values from an outer to an inner template, this cannot + * be done with hashtags directly. Instead, one has to pass the values via + * template arguments. + * * @param key Name for the hashtag replacement. * @param value The value that the hashtag is replaced with. - * @return A token that does nothing, so that the {@link #let} can easily be put in a list of tokens - * inside a {@link Template#body}. - * @throws RendererException if there is a duplicate hashtag {@code key}. + * @return A token that represents the hashtag replacement definition. */ static Token let(String key, Object value) { - Renderer.getCurrent().addHashtagReplacement(key, value); - return new NothingToken(); + return new LetToken(key, value, v -> transparentScope()); } /** * Define a hashtag replacement for {@code "#key"}, with a specific value, which is also captured - * by the provided {@code function} with type {@code }. + * by the provided {@code function} with type {@code }. While the argument of the lambda that + * captures the value is naturally bounded to the scope of the lambda, the hashtag replacement + * may be bound to the scope or escape it, depending on the choice of scope, see {@link #scope} + * and {@link #transparentScope}. * *

    * {@snippet lang=java : - * var template = Template.make("a", (Integer a) -> let("b", a * 2, (Integer b) -> body( - * """ - * System.out.println("Use a and b with hashtag replacement: #a and #b"); - * """, - * "System.out.println(\"Use a and b as capture variables:\"" + a + " and " + b + ");\n" - * ))); + * var template = Template.make("a", (Integer a) -> scope( + * let("b", a * 2, (Integer b) -> scope( + * """ + * System.out.println("Use a and b with hashtag replacement: #a and #b"); + * """, + * "System.out.println(\"Use a and b as capture variables:\"" + a + " and " + b + ");\n" + * )) + * )); * } * * @param key Name for the hashtag replacement. * @param value The value that the hashtag is replaced with. * @param The type of the value. * @param function The function that is applied with the provided {@code value}. - * @return A {@link TemplateBody}. - * @throws RendererException if there is a duplicate hashtag {@code key}. + * @return A {@link Token} representing the hashtag replacement definition and inner scope. */ - static TemplateBody let(String key, T value, Function function) { - Renderer.getCurrent().addHashtagReplacement(key, value); - return function.apply(value); + static Token let(String key, T value, Function function) { + return new LetToken(key, value, function); } /** @@ -702,7 +912,7 @@ static TemplateBody let(String key, T value, Function funct /** * The default amount of fuel spent per Template. It is subtracted from the current {@link #fuel} at every * nesting level, and once the {@link #fuel} reaches zero, the nesting is supposed to terminate. Can be changed - * with {@link #setFuelCost(float)} inside {@link #body(Object...)}. + * with {@link #setFuelCost(float)} inside {@link #scope(Object...)}. */ float DEFAULT_FUEL_COST = 10.0f; @@ -721,7 +931,7 @@ static TemplateBody let(String key, T value, Function funct *

    * {@snippet lang=java : * var binding = new TemplateBinding>(); - * var template = Template.make("depth", (Integer depth) -> body( + * var template = Template.make("depth", (Integer depth) -> scope( * setFuelCost(5.0f), * let("fuel", fuel()), * """ @@ -737,6 +947,9 @@ static TemplateBody let(String key, T value, Function funct * @return The amount of fuel left for nested Template use. */ static float fuel() { + // Note, since the fuel amount does not change within a template + // and the retrieval has no side effects, we can return the value immediately, + // and do not need a token. return Renderer.getCurrent().fuel(); } @@ -745,16 +958,17 @@ static float fuel() { * {@link Template#DEFAULT_FUEL_COST}. * * @param fuelCost The amount of fuel used for the current Template. - * @return A token for convenient use in {@link Template#body}. + * @return A token for convenient use in {@link Template#scope}. */ static Token setFuelCost(float fuelCost) { - Renderer.getCurrent().setFuelCost(fuelCost); - return new NothingToken(); + return new SetFuelCostToken(fuelCost); } /** - * Add a {@link DataName} in the current scope, that is the innermost of either - * {@link Template#body} or {@link Hook#anchor}. + * Add a {@link DataName} in the current {@link #scope}. + * If the current scope is transparent to {@link DataName}s, it escapes to the next + * outer scope that is non-transparent, and is available for everything that follows + * the {@code addDataName} until the end of that non-transparent scope. * * @param name The name of the {@link DataName}, i.e. the {@link String} used in code. * @param type The type of the {@link DataName}. @@ -779,8 +993,10 @@ static Token addDataName(String name, DataName.Type type, DataName.Mutability mu } /** - * Add a {@link DataName} in the current scope, that is the innermost of either - * {@link Template#body} or {@link Hook#anchor}, with a {@code weight} of 1. + * Add a {@link DataName} in the current {@link #scope}, with a {@code weight} of 1. + * If the current scope is transparent to {@link DataName}s, it escapes to the next + * outer scope that is non-transparent, and is available for everything that follows + * the {@code addDataName} until the end of that non-transparent scope. * * @param name The name of the {@link DataName}, i.e. the {@link String} used in code. * @param type The type of the {@link DataName}. @@ -804,8 +1020,10 @@ static DataName.FilteredSet dataNames(DataName.Mutability mutability) { } /** - * Add a {@link StructuralName} in the current scope, that is the innermost of either - * {@link Template#body} or {@link Hook#anchor}. + * Add a {@link StructuralName} in the current {@link #scope}. + * If the current scope is transparent to {@link StructuralName}s, it escapes to the next + * outer scope that is non-transparent, and is available for everything that follows + * the {@code addStructuralName} until the end of that non-transparent scope. * * @param name The name of the {@link StructuralName}, i.e. the {@link String} used in code. * @param type The type of the {@link StructuralName}. @@ -822,8 +1040,10 @@ static Token addStructuralName(String name, StructuralName.Type type, int weight } /** - * Add a {@link StructuralName} in the current scope, that is the innermost of either - * {@link Template#body} or {@link Hook#anchor}, with a {@code weight} of 1. + * Add a {@link StructuralName} in the current {@link #scope}, with a {@code weight} of 1. + * If the current scope is transparent to {@link StructuralName}s, it escapes to the next + * outer scope that is non-transparent, and is available for everything that follows + * the {@code addStructuralName} until the end of that non-transparent scope. * * @param name The name of the {@link StructuralName}, i.e. the {@link String} used in code. * @param type The type of the {@link StructuralName}. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java index cf8c4afb321ff..04305dff02fb1 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateFrame.java @@ -27,38 +27,96 @@ import java.util.Map; /** - * The {@link TemplateFrame} is the frame for a {@link Template}, i.e. the corresponding - * {@link TemplateToken}. It ensures that each template use has its own unique {@link #id} - * used to deconflict names using {@link Template#$}. It also has a set of hashtag - * replacements, which combine the key-value pairs from the template argument and the - * {@link Template#let} definitions. The {@link #parent} relationship provides a trace - * for the use chain of templates. The {@link #fuel} is reduced over this chain, to give - * a heuristic on how much time is spent on the code from the template corresponding to - * the frame, and to give a termination criterion to avoid nesting templates too deeply. + * The {@link TemplateFrame} keeps track of the nested hashtag replacements available + * inside the {@link Template}, as well as the unique id of the {@link Template} use, + * and how much fuel is available for recursive {@link Template} calls. The name of + * the {@link TemplateFrame} indicates that it corresponds to the structure of the + * {@link Template}, whereas the {@link CodeFrame} corresponds to the structure of + * the generated code. * *

    - * See also {@link CodeFrame} for more explanations about the frames. + * The unique id is used to deconflict names using {@link Template#$}. + * + *

    + * A {@link Template} can have multiple {@link TemplateFrame}s, if there are nested + * scopes. The outermost {@link TemplateFrame} determines the id of the {@link Template} + * use and performs the subtraction of fuel from the outer {@link Template}. Inner + * {@link TemplateFrame}s ensure the correct availability of hashtag replacement and + * {@link Template#setFuelCost} definitions, so that they are local to their scope and + * nested scopes, and only escape if the scope is transparent. + * + *

    + * The hashtag replacements are a set of key-value pairs from the template arguments + * and queries such as {@link Template#let} definitions. Each {@link TemplateFrame} + * has such a set of hashtag replacements, and implicitly provides access to the + * hashtag replacements of the outer {@link TemplateFrame}s, up to the outermost + * of the current {@link Template}. If a hashtag replacement is added in a scope, + * we have to traverse to outer scopes until we find one that is not transparent + * for hashtags (at most it is the frame of the Template), and insert it there. + * The hashtag replacent is local to that frame, and accessible for any frames nested + * inside it, but not inside other Templates. The hashtag replacement disappears once + * the corresponding scope is exited, i.e. the frame removed. + * + *

    + * The {@link #parent} relationship provides a trace for the use chain of templates and + * their inner scopes. The {@link #fuel} is reduced over this chain to give a heuristic + * on how deeply nested the code is at a given point, correlating to the runtime that + * would be spent if the code was executed. The idea is that once the fuel is depleated, + * we do not want to nest more deeply, so that there is a reasonable chance that the + * execution of the generated code can terminate. + * + *

    + * The {@link TemplateFrame} thus implements the hashtag and {@link Template#setFuelCost} + * non-transparency aspect of {@link ScopeToken}. + * + *

    + * See also {@link CodeFrame} for more explanations about the frames. Note, that while + * {@link TemplateFrame} always nests inward, even with {@link Hook#insert}, the + * {@link CodeFrame} can also jump to the {@link Hook#anchor} {@link CodeFrame} when + * using {@link Hook#insert}. */ class TemplateFrame { final TemplateFrame parent; + private final boolean isInnerScope; private final int id; private final Map hashtagReplacements = new HashMap<>(); final float fuel; private float fuelCost; + private final boolean isTransparentForHashtag; + private final boolean isTransparentForFuel; public static TemplateFrame makeBase(int id, float fuel) { - return new TemplateFrame(null, id, fuel, 0.0f); + return new TemplateFrame(null, false, id, fuel, 0.0f, false, false); } public static TemplateFrame make(TemplateFrame parent, int id) { - return new TemplateFrame(parent, id, parent.fuel - parent.fuelCost, Template.DEFAULT_FUEL_COST); + float fuel = parent.fuel - parent.fuelCost; + return new TemplateFrame(parent, false, id, fuel, Template.DEFAULT_FUEL_COST, false, false); } - private TemplateFrame(TemplateFrame parent, int id, float fuel, float fuelCost) { + public static TemplateFrame makeInnerScope(TemplateFrame parent, + boolean isTransparentForHashtag, + boolean isTransparentForFuel) { + // We keep the id of the parent, so that we have the same dollar replacements. + // And we subtract no fuel, but forward the cost. + return new TemplateFrame(parent, true, parent.id, parent.fuel, parent.fuelCost, + isTransparentForHashtag, isTransparentForFuel); + } + + private TemplateFrame(TemplateFrame parent, + boolean isInnerScope, + int id, + float fuel, + float fuelCost, + boolean isTransparentForHashtag, + boolean isTransparentForFuel) { this.parent = parent; + this.isInnerScope = isInnerScope; this.id = id; this.fuel = fuel; this.fuelCost = fuelCost; + this.isTransparentForHashtag = isTransparentForHashtag; + this.isTransparentForFuel = isTransparentForFuel; } public String $(String name) { @@ -78,8 +136,15 @@ void addHashtagReplacement(String key, String value) { if (!Renderer.isValidHashtagOrDollarName(key)) { throw new RendererException("Is not a valid hashtag replacement name: '" + key + "'."); } - if (hashtagReplacements.putIfAbsent(key, value) != null) { - throw new RendererException("Duplicate hashtag replacement for #" + key); + String previous = findHashtagReplacementInScopes(key); + if (previous != null) { + throw new RendererException("Duplicate hashtag replacement for #" + key + ". " + + "previous: " + previous + ", new: " + value); + } + if (isTransparentForHashtag) { + parent.addHashtagReplacement(key, value); + } else { + hashtagReplacements.put(key, value); } } @@ -87,13 +152,27 @@ String getHashtagReplacement(String key) { if (!Renderer.isValidHashtagOrDollarName(key)) { throw new RendererException("Is not a valid hashtag replacement name: '" + key + "'."); } + String value = findHashtagReplacementInScopes(key); + if (value != null) { + return value; + } + throw new RendererException("Missing hashtag replacement for #" + key); + } + + private String findHashtagReplacementInScopes(String key) { if (hashtagReplacements.containsKey(key)) { return hashtagReplacements.get(key); } - throw new RendererException("Missing hashtag replacement for #" + key); + if (!isInnerScope) { + return null; + } + return parent.findHashtagReplacementInScopes(key); } void setFuelCost(float fuelCost) { this.fuelCost = fuelCost; + if (isTransparentForFuel) { + parent.setFuelCost(fuelCost); + } } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateToken.java b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateToken.java index 47262f152d4ab..ffbfcfdf2d089 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TemplateToken.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TemplateToken.java @@ -49,7 +49,7 @@ static final class ZeroArgs extends TemplateToken implements Token { } @Override - public TemplateBody instantiate() { + public ScopeToken instantiate() { return zeroArgs.instantiate(); } @@ -74,7 +74,7 @@ static final class OneArg extends TemplateToken implements Token { } @Override - public TemplateBody instantiate() { + public ScopeToken instantiate() { return oneArgs.instantiate(arg1); } @@ -104,7 +104,7 @@ static final class TwoArgs extends TemplateToken implements Token { } @Override - public TemplateBody instantiate() { + public ScopeToken instantiate() { return twoArgs.instantiate(arg1, arg2); } @@ -138,7 +138,7 @@ static final class ThreeArgs extends TemplateToken implements Token } @Override - public TemplateBody instantiate() { + public ScopeToken instantiate() { return threeArgs.instantiate(arg1, arg2, arg3); } @@ -150,7 +150,7 @@ public void visitArguments(ArgumentVisitor visitor) { } } - abstract TemplateBody instantiate(); + abstract ScopeToken instantiate(); @FunctionalInterface interface ArgumentVisitor { diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java index 0e9f9b272c54f..6e9d5f7650a37 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Token.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Token.java @@ -24,16 +24,25 @@ package compiler.lib.template_framework; /** - * The {@link Template#body} and {@link Hook#anchor} are given a list of tokens, which are either + * The {@link Template#scope} and {@link Hook#anchor} are given a list of tokens, which are either * {@link Token}s or {@link String}s or some permitted boxed primitives. */ public sealed interface Token permits StringToken, - TemplateToken, - TemplateToken.ZeroArgs, - TemplateToken.OneArg, - TemplateToken.TwoArgs, - TemplateToken.ThreeArgs, - HookAnchorToken, - HookInsertToken, - AddNameToken, - NothingToken {} + TemplateToken, + TemplateToken.ZeroArgs, + TemplateToken.OneArg, + TemplateToken.TwoArgs, + TemplateToken.ThreeArgs, + HookAnchorToken, + HookInsertToken, + HookIsAnchoredToken, + AddNameToken, + NameSampleToken, + NameForEachToken, + NamesToListToken, + NameCountToken, + NameHasAnyToken, + LetToken, + ScopeToken, + ScopeTokenImpl, + SetFuelCostToken {} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/TokenParser.java b/test/hotspot/jtreg/compiler/lib/template_framework/TokenParser.java index 0c335bd4fb89e..bee6246bdc52d 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/TokenParser.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/TokenParser.java @@ -31,7 +31,7 @@ * Helper class for {@link Token}, to keep the parsing methods package private. * *

    - * The {@link Template#body} and {@link Hook#anchor} are given a list of tokens, which are either + * The {@link Template#scope} and {@link Hook#anchor} are given a list of tokens, which are either * {@link Token}s or {@link String}s or some permitted boxed primitives. These are then parsed * and all non-{@link Token}s are converted to {@link StringToken}s. The parsing also flattens * {@link List}s. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 360937c8f7fc8..43ab16af415b8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -33,7 +33,7 @@ import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; /** * {@link Expression}s model Java expressions, that have a list of arguments with specified @@ -357,7 +357,7 @@ public TemplateToken asToken(List arguments) { } tokens.add(strings.getLast()); - var template = Template.make(() -> body( + var template = Template.make(() -> scope( tokens )); return template.asToken(); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index 46a9d5bbabe67..c0db3d51545c3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -33,7 +33,7 @@ import compiler.lib.template_framework.DataName; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; /** * The {@link PrimitiveType} models Java's primitive types, and provides a set @@ -190,7 +190,7 @@ public Object callLibraryRNG() { * @return a TemplateToken that holds all the {@code LibraryRNG} class. */ public static TemplateToken generateLibraryRNG() { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ public static class LibraryRNG { private static final Random RANDOM = Utils.getRandomInstance(); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/TestFrameworkClass.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/TestFrameworkClass.java index 5194b75af43d7..a9db9285b78dc 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/TestFrameworkClass.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/TestFrameworkClass.java @@ -30,7 +30,7 @@ import compiler.lib.compile_framework.CompileFramework; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; import static compiler.lib.template_framework.Template.let; /** @@ -51,7 +51,7 @@ public final class TestFrameworkClass { private TestFrameworkClass() {} /** - * This method renders a list of {@code testTemplateTokens} into the body of a class + * This method renders a list of {@code testTemplateTokens} into the scope of a class * and generates a {@code main} method which launches the {@link TestFramework} * to run the generated tests. * @@ -81,7 +81,7 @@ public static String render(final String packageName, final Set imports, final String classpath, final List testTemplateTokens) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("packageName", packageName), let("className", className), let("classpath", classpath), @@ -96,7 +96,7 @@ public static String render(final String packageName, public class #className { // --- CLASS_HOOK insertions start --- """, - Hooks.CLASS_HOOK.anchor( + Hooks.CLASS_HOOK.anchor(scope( """ // --- CLASS_HOOK insertions end --- public static void main(String[] vmFlags) { @@ -108,7 +108,7 @@ public static void main(String[] vmFlags) { // --- LIST OF TESTS start --- """, testTemplateTokens - ), + )), """ // --- LIST OF TESTS end --- } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java index 62e474ecb2c67..5d20ce659b9de 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java @@ -61,7 +61,7 @@ import compiler.lib.generators.Generators; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; @@ -333,7 +333,7 @@ public String generate() { } public TemplateToken index(String invar0, String[] invarRest) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("con", con), let("ivScale", ivScale), let("invar0Scale", invar0Scale), @@ -349,7 +349,7 @@ public TemplateToken index(String invar0, String[] invarRest) { // MemorySegment need to be long-addressed, otherwise there can be int-overflow // in the index, and that prevents RangeCheck Elimination and Vectorization. public TemplateToken indexLong(String invar0, String[] invarRest) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("con", con), let("ivScale", ivScale), let("invar0Scale", invar0Scale), @@ -365,7 +365,7 @@ public TemplateToken indexLong(String invar0, String[] invarRest) { // Mirror the IndexForm from the generator to the test. public static TemplateToken generateIndexForm() { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ private static final Random RANDOM = Utils.getRandomInstance(); @@ -610,7 +610,7 @@ public TemplateToken generate() { for (int i = 0; i < indexFormNames.length; i++) { indexFormNames[i] = $("index" + i); } - return body( + return scope( """ // --- $test start --- """, @@ -662,7 +662,7 @@ public TemplateToken generate() { } private TemplateToken generateArrayField(String name, MyType type) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("size", containerByteSize / type.byteSize()), let("name", name), let("type", type), @@ -676,7 +676,7 @@ private TemplateToken generateArrayField(String name, MyType type) { } private TemplateToken generateMemorySegmentField(String name, MyType type) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("size", containerByteSize / type.byteSize()), let("byteSize", containerByteSize), let("name", name), @@ -698,7 +698,7 @@ private TemplateToken generateMemorySegmentField(String name, MyType type) { } private TemplateToken generateIndexField(String name, IndexForm form) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("name", name), let("form", form.generate()), """ @@ -709,7 +709,7 @@ private TemplateToken generateIndexField(String name, IndexForm form) { } private TemplateToken generateTestFields(String[] invarRest, String[] containerNames, String[] indexFormNames) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("ivType", isLongIvType ? "long" : "int"), """ // invarRest fields: @@ -741,7 +741,7 @@ private TemplateToken generateTestFields(String[] invarRest, String[] containerN } private TemplateToken generateContainerInitArray(String name) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("size", containerByteSize / containerElementType.byteSize()), let("name", name), """ @@ -753,7 +753,7 @@ private TemplateToken generateContainerInitArray(String name) { } private TemplateToken generateContainerInitMemorySegment(String name) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("size", containerByteSize / containerElementType.byteSize()), let("name", name), """ @@ -765,7 +765,7 @@ private TemplateToken generateContainerInitMemorySegment(String name) { } private TemplateToken generateContainerInit(String[] containerNames) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ // Init containers from original data: """, @@ -784,7 +784,7 @@ private TemplateToken generateContainerInit(String[] containerNames) { } private TemplateToken generateContainerAliasingAssignment(int i, String name1, String name2, String iterations) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("i", i), let("name1", name1), let("name2", name2), @@ -798,7 +798,7 @@ private TemplateToken generateContainerAliasingAssignment(int i, String name1, S } private TemplateToken generateContainerAliasing(String[] containerNames, String iterations) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ // Container aliasing: """, @@ -832,7 +832,7 @@ private TemplateToken generateRanges() { if (accessIndexForm.length != 2) { throw new RuntimeException("not yet implemented"); } - var templateSplitRanges = Template.make(() -> body( + var templateSplitRanges = Template.make(() -> scope( let("size", size), """ int middle = RANDOM.nextInt(#size / 3, #size * 2 / 3); @@ -865,7 +865,7 @@ private TemplateToken generateRanges() { """ )); - var templateWholeRanges = Template.make(() -> body( + var templateWholeRanges = Template.make(() -> scope( let("size", size), """ var r0 = new IndexForm.Range(0, #size); @@ -873,7 +873,7 @@ private TemplateToken generateRanges() { """ )); - var templateRandomRanges = Template.make(() -> body( + var templateRandomRanges = Template.make(() -> scope( let("size", size), """ int lo0 = RANDOM.nextInt(0, #size * 3 / 4); @@ -883,7 +883,7 @@ private TemplateToken generateRanges() { """ )); - var templateSmallOverlapRanges = Template.make(() -> body( + var templateSmallOverlapRanges = Template.make(() -> scope( // Idea: same size ranges, with size "range". A small overlap, // so that bad runtime checks would create wrong results. let("size", size), @@ -907,7 +907,7 @@ private TemplateToken generateRanges() { // -> safe with rnd = size/10 )); - var templateAnyRanges = Template.make(() -> body( + var templateAnyRanges = Template.make(() -> scope( switch(RANDOM.nextInt(4)) { case 0 -> templateSplitRanges.asToken(); case 1 -> templateWholeRanges.asToken(); @@ -917,7 +917,7 @@ private TemplateToken generateRanges() { } )); - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ // Generate ranges: """, @@ -941,7 +941,7 @@ private TemplateToken generateBoundsAndInvariants(String[] indexFormNames, Strin // We want there to be at least 1000 iterations. final int minIvRange = ivStrideAbs * 1000; - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("containerByteSize", containerByteSize), """ // Compute loop bounds and loop invariants. @@ -949,7 +949,7 @@ private TemplateToken generateBoundsAndInvariants(String[] indexFormNames, Strin int ivHi = ivLo + #containerByteSize; """, IntStream.range(0, indexFormNames.length).mapToObj(i -> - Template.make(() -> body( + Template.make(() -> scope( let("i", i), let("form", indexFormNames[i]), """ @@ -990,7 +990,7 @@ private TemplateToken generateBoundsAndInvariants(String[] indexFormNames, Strin """, IntStream.range(0, indexFormNames.length).mapToObj(i1 -> IntStream.range(0, i1).mapToObj(i2 -> - Template.make(() -> body( + Template.make(() -> scope( let("i1", i1), let("i2", i2), // i1 < i2 or i1 > i2 @@ -1021,7 +1021,7 @@ private TemplateToken generateBoundsAndInvariants(String[] indexFormNames, Strin private TemplateToken generateCallMethod(String output, String methodName, String containerPrefix) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("output", output), let("methodName", methodName), "var #output = #methodName(", @@ -1034,7 +1034,7 @@ private TemplateToken generateCallMethod(String output, String methodName, Strin } private TemplateToken generateIRRules() { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( switch (containerKind) { case ContainerKind.ARRAY -> generateIRRulesArray(); @@ -1094,7 +1094,7 @@ private TemplateToken generateIRRules() { // Regular array-accesses are vectorized quite predictably, and we can create nice // IR rules - even for cases where we do not expect vectorization. private TemplateToken generateIRRulesArray() { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("T", containerElementType.letter()), switch (accessScenario) { case COPY_LOAD_STORE -> @@ -1145,7 +1145,7 @@ private TemplateToken generateIRRulesArray() { } private TemplateToken generateIRRulesMemorySegmentAtIndex() { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ // Unfortunately, there are some issues that prevent RangeCheck elimination. // The cases are currently quite unpredictable, so we cannot create any IR @@ -1158,7 +1158,7 @@ private TemplateToken generateIRRulesMemorySegmentAtIndex() { } private TemplateToken generateIRRulesMemorySegmentLongAdrStride() { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ // Unfortunately, there are some issues that prevent RangeCheck elimination. // The cases are currently quite unpredictable, so we cannot create any IR @@ -1169,7 +1169,7 @@ private TemplateToken generateIRRulesMemorySegmentLongAdrStride() { } private TemplateToken generateIRRulesMemorySegmentLongAdrScale() { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ // Unfortunately, there are some issues that prevent RangeCheck elimination. // The cases are currently quite unpredictable, so we cannot create any IR @@ -1180,7 +1180,7 @@ private TemplateToken generateIRRulesMemorySegmentLongAdrScale() { } private TemplateToken generateTestMethod(String methodName, String[] invarRest) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("methodName", methodName), let("containerElementType", containerElementType), let("ivStrideAbs", ivStrideAbs), @@ -1230,7 +1230,7 @@ private TemplateToken generateTestMethod(String methodName, String[] invarRest) } private TemplateToken generateTestLoopIterationArray(String[] invarRest) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("type", containerElementType), switch (accessScenario) { case COPY_LOAD_STORE -> @@ -1245,7 +1245,7 @@ private TemplateToken generateTestLoopIterationArray(String[] invarRest) { } private TemplateToken generateTestLoopIterationMemorySegmentAtIndex(String[] invarRest) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("type0", accessType[0]), let("type1", accessType[1]), let("type0Layout", accessType[0].layout()), @@ -1265,7 +1265,7 @@ private TemplateToken generateTestLoopIterationMemorySegmentAtIndex(String[] inv } private TemplateToken generateTestLoopIterationMemorySegmentLongAdr(String[] invarRest) { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( let("type0", accessType[0]), let("type1", accessType[1]), let("type0Layout", accessType[0].layout()), diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java index c5a4528f63d47..784f1ded06555 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestAdvanced.java @@ -43,7 +43,7 @@ import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; import static compiler.lib.template_framework.Template.let; /** @@ -96,7 +96,7 @@ public static String generate(CompileFramework comp) { // - The GOLD value is computed at the beginning, hopefully by the interpreter. // - The test method is eventually compiled, and the values are verified by the // check method. - var testTemplate = Template.make("typeName", "operator", "generator", (String typeName, String operator, MyGenerator generator) -> body( + var testTemplate = Template.make("typeName", "operator", "generator", (String typeName, String operator, MyGenerator generator) -> scope( let("con1", generator.next()), let("con2", generator.next()), """ @@ -116,7 +116,7 @@ public static String generate(CompileFramework comp) { )); // Template for the Class. - var classTemplate = Template.make("types", (List types) -> body( + var classTemplate = Template.make("types", (List types) -> scope( let("classpath", comp.getEscapedClassPathOfCompiledClasses()), """ package p.xyz; diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java index c21d2492fc768..6a0a2d3786a9c 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java @@ -40,7 +40,7 @@ import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; import static compiler.lib.template_framework.Template.let; import compiler.lib.template_framework.library.Expression; import compiler.lib.template_framework.library.Operations; @@ -78,7 +78,7 @@ public static String generate(CompileFramework comp) { // precision results from some operators. We only compare the results if we know that the // result is deterministically the same. TemplateToken expressionToken = expression.asToken(expression.argumentTypes.stream().map(t -> t.con()).toList()); - return body( + return scope( let("returnType", expression.returnType), """ @Test diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java index a04a5771cb49a..b1f5f74e682cb 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java @@ -41,7 +41,8 @@ import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.transparentScope; import static compiler.lib.template_framework.Template.dataNames; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; @@ -77,7 +78,7 @@ public static String generate() { Map tests = new HashMap<>(); // The boxing tests check if we can autobox with "boxedTypeName". - var boxingTemplate = Template.make("name", "type", (String name, PrimitiveType type) -> body( + var boxingTemplate = Template.make("name", "type", (String name, PrimitiveType type) -> scope( let("CON1", type.con()), let("CON2", type.con()), let("Boxed", type.boxedTypeName()), @@ -99,7 +100,7 @@ public static String generate() { } // Integral and Float types have a size. Also test if "isFloating" is correct. - var integralFloatTemplate = Template.make("name", "type", (String name, PrimitiveType type) -> body( + var integralFloatTemplate = Template.make("name", "type", (String name, PrimitiveType type) -> scope( let("size", type.byteSize()), let("isFloating", type.isFloating()), """ @@ -129,27 +130,31 @@ public static String generate() { // Finally, test the type by creating some DataNames (variables), and sampling // from them. There should be no cross-over between the types. - var variableTemplate = Template.make("type", (PrimitiveType type) -> body( + // IMPORTANT: since we are adding the DataName via an inserted Template, we + // must chose a "transparentScope", so that the DataName escapes. If we + // instead chose "scope", the test would fail, because it later + // finds no DataNames when we sample. + var variableTemplate = Template.make("type", (PrimitiveType type) -> transparentScope( let("CON", type.con()), - addDataName($("var"), type, MUTABLE), + addDataName($("var"), type, MUTABLE), // escapes the Template """ #type $var = #CON; """ )); - var sampleTemplate = Template.make("type", (PrimitiveType type) -> body( - let("var", dataNames(MUTABLE).exactOf(type).sample().name()), + var sampleTemplate = Template.make("type", (PrimitiveType type) -> scope( let("CON", type.con()), + dataNames(MUTABLE).exactOf(type).sampleAndLetAs("var"), """ #var = #CON; """ )); - var namesTemplate = Template.make(() -> body( + var namesTemplate = Template.make(() -> scope( """ public static void test_names() { """, - Hooks.METHOD_HOOK.anchor( + Hooks.METHOD_HOOK.anchor(scope( Collections.nCopies(10, CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(type -> Hooks.METHOD_HOOK.insert(variableTemplate.asToken(type)) @@ -161,7 +166,7 @@ public static void test_names() { Collections.nCopies(10, CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(sampleTemplate::asToken).toList() ) - ), + )), """ } """ @@ -172,7 +177,7 @@ public static void test_names() { // Test runtime random value generation with LibraryRNG // Runtime random number generation of a given primitive type can be very helpful // when writing tests that require random inputs. - var libraryRNGWithTypeTemplate = Template.make("type", (PrimitiveType type) -> body( + var libraryRNGWithTypeTemplate = Template.make("type", (PrimitiveType type) -> scope( """ { // Fill an array with 1_000 random values. Every type has at least 2 values, @@ -196,7 +201,7 @@ public static void test_names() { """ )); - var libraryRNGTemplate = Template.make(() -> body( + var libraryRNGTemplate = Template.make(() -> scope( // Make sure we instantiate the LibraryRNG class. PrimitiveType.generateLibraryRNG(), // Now we can use it inside the test. @@ -213,7 +218,7 @@ public static void test_LibraryRNG() { // Finally, put all the tests together in a class, and invoke all // tests from the main method. - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ package p.xyz; diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestSimple.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestSimple.java index e06671ca95188..c8afb34e42355 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestSimple.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestSimple.java @@ -34,7 +34,7 @@ import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; public class TestSimple { @@ -61,7 +61,7 @@ public static void main(String[] args) { // Generate a source Java file as String public static String generate() { // Create a Template with two arguments. - var template = Template.make("arg1", "arg2", (Integer arg1, String arg2) -> body( + var template = Template.make("arg1", "arg2", (Integer arg1, String arg2) -> scope( """ package p.xyz; public class InnerTest { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java index faa05b29d827f..ed542180bad3f 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java @@ -43,7 +43,9 @@ import compiler.lib.template_framework.TemplateBinding; import compiler.lib.template_framework.DataName; import compiler.lib.template_framework.StructuralName; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.transparentScope; +import static compiler.lib.template_framework.Template.hashtagScope; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.fuel; @@ -68,13 +70,14 @@ public static void main(String[] args) { comp.addJavaSourceCode("p.xyz.InnerTest2", generateWithTemplateArguments()); comp.addJavaSourceCode("p.xyz.InnerTest3", generateWithHashtagAndDollarReplacements()); comp.addJavaSourceCode("p.xyz.InnerTest3b", generateWithHashtagAndDollarReplacements2()); + comp.addJavaSourceCode("p.xyz.InnerTest3c", generateWithHashtagAndDollarReplacements3()); comp.addJavaSourceCode("p.xyz.InnerTest4", generateWithCustomHooks()); comp.addJavaSourceCode("p.xyz.InnerTest5", generateWithLibraryHooks()); comp.addJavaSourceCode("p.xyz.InnerTest6", generateWithRecursionAndBindingsAndFuel()); comp.addJavaSourceCode("p.xyz.InnerTest7", generateWithDataNamesSimple()); comp.addJavaSourceCode("p.xyz.InnerTest8", generateWithDataNamesForFieldsAndVariables()); - comp.addJavaSourceCode("p.xyz.InnerTest9a", generateWithDataNamesAndScopes1()); - comp.addJavaSourceCode("p.xyz.InnerTest9b", generateWithDataNamesAndScopes2()); + comp.addJavaSourceCode("p.xyz.InnerTest9a", generateWithScopes1()); + comp.addJavaSourceCode("p.xyz.InnerTest9b", generateWithScopes2()); comp.addJavaSourceCode("p.xyz.InnerTest10", generateWithDataNamesForFuzzing()); comp.addJavaSourceCode("p.xyz.InnerTest11", generateWithStructuralNamesForMethods()); @@ -91,6 +94,7 @@ public static void main(String[] args) { comp.invoke("p.xyz.InnerTest2", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest3", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest3b", "main", new Object[] {}); + comp.invoke("p.xyz.InnerTest3c", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest4", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest5", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest6", "main", new Object[] {}); @@ -105,9 +109,9 @@ public static void main(String[] args) { // This example shows the use of various Tokens. public static String generateWithListOfTokens() { // A Template is essentially a function / lambda that produces a - // token body, which is a list of Tokens that are concatenated. - var templateClass = Template.make(() -> body( - // The "body" method is filled by a sequence of "Tokens". + // scope, which contains a list of Tokens that are concatenated. + var templateClass = Template.make(() -> scope( + // The "scope" arguments are a sequence of "Tokens". // These can be Strings and multi-line Strings, but also // boxed primitives. """ @@ -141,14 +145,14 @@ public static void main() { // This example shows the use of Templates, with and without arguments. public static String generateWithTemplateArguments() { // A Template with no arguments. - var templateHello = Template.make(() -> body( + var templateHello = Template.make(() -> scope( """ System.out.println("Hello"); """ )); // A Template with a single Integer argument. - var templateCompare = Template.make("arg", (Integer arg) -> body( + var templateCompare = Template.make("arg", (Integer arg) -> scope( "System.out.println(", arg, ");\n", // capture arg via lambda argument "System.out.println(#arg);\n", // capture arg via hashtag replacement "System.out.println(#{arg});\n", // capture arg via hashtag replacement with brackets @@ -156,7 +160,7 @@ public static String generateWithTemplateArguments() { // argument values into Strings. However, since these are not (yet) // available, the Template Framework provides two alternative ways of // formatting Strings: - // 1) By appending to the comma-separated list of Tokens passed to body(). + // 1) By appending to the comma-separated list of Tokens passed to scope(). // Appending as a Token works whenever one has a reference to the Object // in Java code. But often, this is rather cumbersome and looks awkward, // given all the additional quotes and commands required. Hence, it @@ -180,7 +184,7 @@ public static String generateWithTemplateArguments() { // A Template that creates the body of the Class and main method, and then // uses the two Templates above inside it. - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; @@ -204,8 +208,16 @@ public static void main() { // Note: hashtag replacements are a workaround for the missing string templates. // If we had string templates, we could just capture the typed lambda // arguments, and use them directly in the String via string templating. + // + // Important: hashtag replacements are always constrained to a single template + // and are not available in any nested templates. Hashtag replacements + // are only there to facilitate string templating within the limited + // scope of a template. You may consider it like a "local variable" + // for code generation purposes only. + // If you need to pass some value to a nested Template, consider using + // a Template argument, and capturing that Template argument. public static String generateWithHashtagAndDollarReplacements() { - var template1 = Template.make("x", (Integer x) -> body( + var template1 = Template.make("x", (Integer x) -> scope( // We have the "#x" hashtag replacement from the argument capture above. // Additionally, we can define "#con" as a hashtag replacement from let: let("con", 3 * x), @@ -219,29 +231,27 @@ public static String generateWithHashtagAndDollarReplacements() { """ )); - var template2 = Template.make("x", (Integer x) -> + var template2 = Template.make("x", (Integer x) -> scope( // Sometimes it can be helpful to not just create a hashtag replacement // with let, but also to capture the variable to use it as lambda parameter. - let("y", 11 * x, y -> - body( - """ - System.out.println("T2: #x, #y"); - """, - template1.asToken(y) - ) - ) - ); + let("y", 11 * x, y -> scope( + """ + System.out.println("T2: #x, #y"); + """, + template1.asToken(y) + )) + )); // This template generates an int variable and assigns it a value. // Together with template4, we see that each template has a unique renaming // for a $-name replacement. - var template3 = Template.make("name", "value", (String name, Integer value) -> body( + var template3 = Template.make("name", "value", (String name, Integer value) -> scope( """ int #name = #value; // Note: $var is not #name """ )); - var template4 = Template.make(() -> body( + var template4 = Template.make(() -> scope( """ // We will define the variable $var: """, @@ -252,7 +262,7 @@ public static String generateWithHashtagAndDollarReplacements() { """ )); - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( // The Template Framework API only guarantees that every Template use // has a unique ID. When using the Templates, all we need is that // variables from different Template uses do not conflict. But it can @@ -300,7 +310,7 @@ public static void main() { // "INT_CON" and "LONG_CON". public static String generateWithHashtagAndDollarReplacements2() { // Let us define some final static variables of a specific type. - var template1 = Template.make("type", (String type) -> body( + var template1 = Template.make("type", (String type) -> scope( // The type (e.g. "int") is lower case, let us create the upper case "INT_CON" from it. let("TYPE", type.toUpperCase()), """ @@ -309,7 +319,7 @@ public static String generateWithHashtagAndDollarReplacements2() { )); // Let's write a simple class to demonstrate that this works, i.e. produces compilable code. - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; @@ -331,50 +341,221 @@ public static void main() { return templateClass.render(); } + // We already have used "scope" multiple times, but not explained it yet. + // So far, we have seen "scope" mostly in the context of Template scopes, but they + // can be used in many contexts as we will see below. They can also be used on + // their own and in the use of "let", as we will show right now. + // + // Scopes are even more relevant for DataNames and Structural names. + // See: generateWithDataNamesForFieldsAndVariables + // See: generateWithScopes1 + // See: generateWithScopes2 + public static String generateWithHashtagAndDollarReplacements3() { + + var template1 = Template.make(() -> scope( + // We can use scopes to limit the liveness of hashtag replacements. + scope( + let("x", 3), // does not escape + """ + static int v1_3 = #x; + """ + ), + scope( + let("x", 5), // does not escape + """ + static int v1_5 = #x; + """ + ), + // Using "scope" does not just limit the liveness / availability + // of hashtag replacements, but also of DataNames, StructuralNames, + // and setFuelCost. We can use "hashtagScope" to only limit hashtag + // replacements. + hashtagScope( + let("x", 7), // does not escape + """ + static int v1_7 = #x; + """ + ), + // Using "transparentScope" means the scope is transparent, and the hashtag + // replacements escape the scope. + transparentScope( + let("x", 11), // escapes the "transparentScope". + """ + static int v1_11a = #x; + """ + ), + // The hashtag replacement from the "transparentScope" escaped, and is + // still available. + """ + static int v1_11b = #x; + """ + )); + + var template2 = Template.make("x", (Integer x) -> scope( + // We can map a list of values to a list of scopes. Using a scope that is + // non-transparent for hashtag replacements means that we can reuse the same + // hashtag key when looping / streaming over multiple values. + List.of(3, 5, 7).stream().map(y -> scope( + let("y", y), // does not escape -> allows reuse of hashtag key "y". + """ + static int v2_#{x}_#{y} = #x * #y; + """ + )).toList() + )); + + var template3 = Template.make("x", (Integer x) -> scope( + // When using a "let" that captures the value in a lambda argument, we have + // to choose what kind of scope we generate. In most cases "scope" or + // "hashtagScope" are the best, because they limit the hashtag replacement + // of "y" to the same scope as the lambda argument. + let("y", x * 11, y -> scope( + """ + static int v3a_#{x} = #y; + """ + )), + // But in rare cases, we may want "y" and some nested "z" to escape. + let("y", x * 11, y -> transparentScope( + let("z", y * 2), + """ + static int v3b_#{x} = #y - #z; + """ + )), + // Because of the "transparentScope", "y" and "z" have escaped. + """ + static int v3c_#{x} = #y - #z; + """, + // Side note: We can simulate a "let" without lambda with a "let" that has a lambda. + // That is not very useful, but a similar trick can be used for other queries, that + // only provide a lambda version, and where we only want to use the hashtag replacement. + // + // Below we see the standard use of "let", where we add a hashtag replacement for "a" + // for the rest of the enclosing scope. We then also use a lambda version of "let" + // with a transparent scope, which means that "b" escapes that scope and is also + // available in the enclosing scope. In the implementation of the framework, we + // actually use a "transparentScope", so the standard "let" is really just syntactic + // sugar for the lambda "let" with "transparentScope". + let("a", -x), + let("b", -x, b -> transparentScope()), + """ + static int v3d_#{x} = #a + #b; + """ + )); + + // Let's write a simple class to demonstrate that this works, i.e. produces compilable code. + var templateClass = Template.make(() -> scope( + """ + package p.xyz; + + public class InnerTest3c { + """, + template1.asToken(), + template2.asToken(1), + template2.asToken(2), + template3.asToken(2), + """ + public static void main() { + if (v1_3 != 3 || + v1_5 != 5 || + v1_7 != 7 || + v1_11a != 11 || + v1_11b != 11 || + v2_1_3 != 3 || + v2_1_5 != 5 || + v2_1_7 != 7 || + v2_2_3 != 6 || + v2_2_5 != 10 || + v2_2_7 != 14 || + v3a_2 != 22 || + v3b_2 != -22 || + v3c_2 != -22 || + v3d_2 != -4) { + throw new RuntimeException("Wrong result!"); + } + } + } + """ + )); + + // Render templateClass to String. + return templateClass.render(); + } + // In this example, we look at the use of Hooks. They allow us to reach back, to outer // scopes. For example, we can reach out from inside a method body to a hook anchored at // the top of the class, and insert a field. + // + // When we insert to a hook, we have 3 relevant scopes: + // - Anchor scope: the scope defined at "hook.anchor(scope(...))" + // - Insertion scope: the scope that is inserted, see "hook.insert(scope(...))" + // - Caller scope: the scope we insert from. + // + // The choice of transparency of an insertion scope (the scope that is inserted) is quite + // important. A common use case is to insert a DataName. + // See: generateWithDataNamesForFieldsAndVariables + // See: generateWithScopes1 + // See: generateWithScopes2 public static String generateWithCustomHooks() { // We can define a custom hook. // Note: generally we prefer using the pre-defined CLASS_HOOK and METHOD_HOOK from the library, // whenever possible. See also the example after this one. var myHook = new Hook("MyHook"); - var template1 = Template.make("name", "value", (String name, Integer value) -> body( + var template1 = Template.make("name", "value", (String name, Integer value) -> scope( """ public static int #name = #value; """ )); - var template2 = Template.make("x", (Integer x) -> body( + var template2 = Template.make("x", (Integer x) -> scope( + """ + // Let us go back to where we anchored the hook with anchor() (see 'templateClass' below) and define a field + // named $field1 there. + """, + myHook.insert(scope( // <- insertion scope + """ + public static int $field1 = #x; + """ + // Note that we were able to use the dollar replacement "$field1" and the hashtag + // replacement "#x" inside the scope that is inserted to myHook. + )), """ - // Let us go back to where we anchored the hook with anchor() and define a field named $field there. - // Note that in the Java code we have not defined anchor() on the hook, yet. But since it's a lambda - // expression, it is not evaluated, yet! Eventually, anchor() will be evaluated before insert() in - // this example. + // We can do that by inserting a scope like above, or by inserting a template, like below. + // + // Which method is used is up to the user. General guidance is if the same code may also + // be inserted elsewhere, one should lean towards inserting templates. But in many cases + // it is nice to see the inserted code directly, and to be able to use hashtag replacements + // from the outer scope directly, without having to route them via template arguments, + // as we have to do below. """, - myHook.insert(template1.asToken($("field"), x)), + // <- caller scope + myHook.insert(template1.asToken($("field2"), x)), """ - System.out.println("$field: " + $field); - if ($field != #x) { throw new RuntimeException("Wrong value!"); } + System.out.println("$field1: " + $field1); + System.out.println("$field2: " + $field2); + if ($field1 != #x) { throw new RuntimeException("Wrong value 1!"); } + if ($field2 != #x) { throw new RuntimeException("Wrong value 2!"); } """ )); - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; public class InnerTest4 { """, // We anchor a Hook outside the main method, but inside the Class. - // Anchoring a Hook creates a scope, spanning the braces of the - // "anchor" call. Any Hook.insert that happens inside this scope - // goes to the top of that scope. - myHook.anchor( + // Anchoring a Hook requires the definition of an inner scope, + // aka the "anchor scope", spanning the braces of the "anchor" call. + // Any Hook.insert that happens inside this scope goes to the top of + // that scope. + myHook.anchor(scope( // <- anchor scope // Any Hook.insert goes here. // - // <-------- field_X = 5 ------------------+ - // <-------- field_Y = 7 -------------+ | + // <-------- field1_X = 5 -----------------+ + // field2_X = 5 | + // | + // <-------- field1_Y = 7 ------------+ | + // field2_Y = 7 | | // | | """ public static void main() { @@ -384,7 +565,7 @@ public static void main() { """ } """ - ), // The Hook scope ends here. + )), // The Hook scope ends here. """ } """ @@ -408,46 +589,54 @@ public static void main() { // there is a class scope inside another class scope. Similarly, we can nest lambda bodies // inside method bodies, so also METHOD_HOOK can be used in such a "re-entrant" way. public static String generateWithLibraryHooks() { - var templateStaticField = Template.make("name", "value", (String name, Integer value) -> body( - """ - static { System.out.println("Defining static field #name"); } - public static int #name = #value; - """ - )); - - var templateLocalVariable = Template.make("name", "value", (String name, Integer value) -> body( - """ - System.out.println("Defining local variable #name"); - int #name = #value; - """ - )); - var templateMethodBody = Template.make(() -> body( + var templateMethodBody = Template.make(() -> scope( """ // Let's define a local variable $var and a static field $field. - """, - Hooks.CLASS_HOOK.insert(templateStaticField.asToken($("field"), 5)), - Hooks.METHOD_HOOK.insert(templateLocalVariable.asToken($("var"), 11)), - """ + // Since we are inserting them at the anchor before the code below, + // they will already be available: System.out.println("$field: " + $field); System.out.println("$var: " + $var); + """, + Hooks.CLASS_HOOK.insert(scope( + """ + static { System.out.println("Defining static field $field"); } + public static int $field = 5; + """ + )), + Hooks.METHOD_HOOK.insert(scope( + """ + System.out.println("Defining local variable $var"); + int $var = 11; + """ + )), + """ if ($field * $var != 55) { throw new RuntimeException("Wrong value!"); } """ + // Note: we have used "scope" for the "insert" scope. This is fine here as + // we are only working with code and hashtags, but not with DataNames. If + // we were to also "addDataName" inside the insert scope, we would have to + // make sure that the scope is transparent for DataNames, so that they can + // escape to the anchor scope, and can be available to the caller of the + // insertion. One might want to use "transparentScope" for the insertion scope. + // See: generateWithDataNamesForFieldsAndVariables. + // See: generateWithScopes1 + // See: generateWithScopes2 )); - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; public class InnerTest5 { """, // Class Hook for fields. - Hooks.CLASS_HOOK.anchor( + Hooks.CLASS_HOOK.anchor(scope( """ public static void main() { """, // Method Hook for local variables, and earlier computations. - Hooks.METHOD_HOOK.anchor( + Hooks.METHOD_HOOK.anchor(scope( """ // This is the beginning of the "main" method body. System.out.println("Welcome to main!"); @@ -457,7 +646,7 @@ public static void main() { System.out.println("Going to call other..."); other(); """ - ), + )), """ } @@ -465,7 +654,7 @@ private static void other() { """, // Have a separate method hook for other, so that it can insert // its own local variables. - Hooks.METHOD_HOOK.anchor( + Hooks.METHOD_HOOK.anchor(scope( """ System.out.println("Welcome to other!"); """, @@ -473,11 +662,11 @@ private static void other() { """ System.out.println("Done with other."); """ - ), + )), """ } """ - ), + )), """ } """ @@ -493,7 +682,7 @@ private static void other() { public static String generateWithRecursionAndBindingsAndFuel() { // Binding allows the use of template1 inside of template1, via the binding indirection. var binding1 = new TemplateBinding>(); - var template1 = Template.make("depth", (Integer depth) -> body( + var template1 = Template.make("depth", (Integer depth) -> scope( let("fuel", fuel()), """ System.out.println("At depth #depth with fuel #fuel."); @@ -514,7 +703,7 @@ public static String generateWithRecursionAndBindingsAndFuel() { )); binding1.bind(template1); - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; @@ -561,6 +750,12 @@ public static void main() { // // To get started, we show an example where all DataNames have the same type, and where // all Names are mutable. For simplicity, our type represents the primitive int type. + // + // Note: the template library contains a lot of types that model the Java types, + // such as primitive types ({@code PrimitiveType}). The following examples + // give insight into how those types work. If you are just interested in + // how to use the predefined types, then you can find other examples in + // {@code examples/TestPrimitiveTypes.java}. private record MySimpleInt() implements DataName.Type { // The type is only subtype of itself. This is relevant when sampling or weighing // DataNames, because we do not just sample from the given type, but also its subtypes. @@ -577,31 +772,25 @@ public boolean isSubtypeOf(DataName.Type other) { private static final MySimpleInt mySimpleInt = new MySimpleInt(); // In this example, we generate 3 fields, and add their names to the - // current scope. In a nested Template, we can then sample one of these - // DataNames, which gives us one of the fields. We increment that randomly - // chosen field. At the end, we print all three fields. + // current scope. We can then sample some of these DataNames, which + // gives us one of those fields each time. We increment those randomly + // chosen fields. At the end, we print all three fields. public static String generateWithDataNamesSimple() { - var templateMain = Template.make(() -> body( - // Sample a random DataName, i.e. field, and assign its name to - // the hashtag replacement "#f". - // We are picking a mutable DataName, because we are not just - // reading but also writing to the field. - let("f", dataNames(MUTABLE).exactOf(mySimpleInt).sample().name()), - """ - // Let us now sample a random field #f, and increment it. - #f += 42; - """ - )); - - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( // Let us define the names for the three fields. - // We can then sample from these names in a nested Template. // We make all DataNames mutable, and with the same weight of 1, // so that they have equal probability of being sampled. // Note: the default weight is 1, so we can also omit the weight. + // + // Also note that DataNames are only available once they are defined: + // + // Nothing defined, yet: dataNames() = {} addDataName($("f1"), mySimpleInt, MUTABLE, 1), + // Only now dataNames() contains f1: dataNames() = {f1} addDataName($("f2"), mySimpleInt, MUTABLE, 1), + // dataNames() = {f1, f2} addDataName($("f3"), mySimpleInt, MUTABLE), // omit weight, default is 1. + // dataNames() = {f1, f2, f3} """ package p.xyz; @@ -612,18 +801,35 @@ public class InnerTest7 { public static int $f3 = 0; public static void main() { - // Let us now call the nested template that samples - // a random field and increments it. + // Let us now sample a random field and assign its name to + // the hashtag replacement "a". """, - templateMain.asToken(), + dataNames(MUTABLE).exactOf(mySimpleInt).sampleAndLetAs("a"), + """ + // We can now access the field, and increment it. + #a += 42; + // If we are also interested in the type of the field, we can do: + """, + dataNames(MUTABLE).exactOf(mySimpleInt).sampleAndLetAs("b", "bType"), + """ + #b += 7; + // In some cases, we may want to capture the DataName directly, which + // requires capturing the value in a lambda that creates an inner scope: + """, + dataNames(MUTABLE).exactOf(mySimpleInt).sample((DataName dn) -> scope( + let("c", dn.name()), + """ + #c += 12; + """ + )), """ // Now, we can print all three fields, and see which - // one was incremented. + // ones were incremented. System.out.println("f1: " + $f1); System.out.println("f2: " + $f2); System.out.println("f3: " + $f3); - // We have two zeros, and one 42. - if ($f1 + $f2 + $f3 != 42) { throw new RuntimeException("wrong result!"); } + // Make sure they add up to the correct sum. + if ($f1 + $f2 + $f3 != 42 + 7 + 12) { throw new RuntimeException("wrong result!"); } } } """ @@ -662,8 +868,15 @@ public boolean isSubtypeOf(DataName.Type other) { public static String generateWithDataNamesForFieldsAndVariables() { // Define a static field. - var templateStaticField = Template.make("type", (DataName.Type type) -> body( - addDataName($("field"), type, MUTABLE), + // Note: it is very important that we use a "transparentScope" for the template here, + // so that the DataName can escape to outer scopes, so that it is available to + // everything that follows the DataName definition in the outer scope. + // (We could also use "hashtagScope", since those are also transparent for + // names. But it is not great style, because template boundaries are + // non-transparent for hashtags and setFuelCost anyway. So we might as + // well just use "transparentScope".) + var templateStaticField = Template.make("type", (DataName.Type type) -> transparentScope( + addDataName($("field"), type, MUTABLE), // escapes template because of "transparentScope" // Note: since we have overridden MyPrimitive::toString, we can use // the type directly as "#type" in the template, which then // gets hashtag replaced with "int" or "long". @@ -673,8 +886,10 @@ public static String generateWithDataNamesForFieldsAndVariables() { )); // Define a local variable. - var templateLocalVariable = Template.make("type", (DataName.Type type) -> body( - addDataName($("var"), type, MUTABLE), + // Note: it is very important that we use a "transparentScope" for the template here, + // so that the DataName can escape to outer scopes. + var templateLocalVariable = Template.make("type", (DataName.Type type) -> transparentScope( + addDataName($("var"), type, MUTABLE), // escapes template because of "transparentScope" """ #type $var = 0; """ @@ -682,8 +897,8 @@ public static String generateWithDataNamesForFieldsAndVariables() { // Sample a random field or variable, from those that are available at // the current scope. - var templateSample = Template.make("type", (DataName.Type type) -> body( - let("name", dataNames(MUTABLE).exactOf(type).sample().name()), + var templateSample = Template.make("type", (DataName.Type type) -> scope( + dataNames(MUTABLE).exactOf(type).sampleAndLetAs("name"), // Note: we could also sample from MUTABLE_OR_IMMUTABLE, we will // cover the concept of mutability in an example further down. """ @@ -692,18 +907,36 @@ public static String generateWithDataNamesForFieldsAndVariables() { )); // Check how many fields and variables are available at the current scope. - var templateStatus = Template.make(() -> body( - let("ints", dataNames(MUTABLE).exactOf(myInt).count()), - let("longs", dataNames(MUTABLE).exactOf(myLong).count()), - // Note: we could also count the MUTABLE_OR_IMMUTABLE, we will - // cover the concept of mutability in an example further down. + var templateStatus = Template.make(() -> scope( + dataNames(MUTABLE).exactOf(myInt).count(ints -> scope( + dataNames(MUTABLE).exactOf(myLong).count(longs -> scope( + // We have now captured the values as Java variables, and can + // use them inside the scope in some "let" definitions. + let("ints", ints), + let("longs", longs), + // Note: we could also count the MUTABLE_OR_IMMUTABLE, we will + // cover the concept of mutability in an example further down. + """ + System.out.println("Status: #ints ints, #longs longs."); + """ + )) + )), + // In a real code generation case, we would most likely want to + // have the count as a Java variable so that one can take conditional + // action based on the value. For that we have to capture the count + // with a lambda and inner scope as above. If we only need to have + // the count as a hashtag replacement, we can also use the following + // trick: + dataNames(MUTABLE).exactOf(myInt).count(c -> transparentScope(let("ints", c))), + dataNames(MUTABLE).exactOf(myLong).count(c -> transparentScope(let("longs", c))), + // Because of the "transparentScope", the hashtag replacements escape. """ System.out.println("Status: #ints ints, #longs longs."); """ )); // Definition of the main method body. - var templateMain = Template.make(() -> body( + var templateMain = Template.make(() -> scope( """ System.out.println("Starting inside main..."); """, @@ -736,7 +969,7 @@ public static String generateWithDataNamesForFieldsAndVariables() { // Definition of another method's body. It is in the same class // as the main method, so it has access to the same static fields. - var templateOther = Template.make(() -> body( + var templateOther = Template.make(() -> scope( """ System.out.println("Starting inside other..."); """, @@ -755,19 +988,19 @@ public static String generateWithDataNamesForFieldsAndVariables() { )); // Finally, we put it all together in a class. - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; public class InnerTest8 { """, // Class Hook for fields. - Hooks.CLASS_HOOK.anchor( + Hooks.CLASS_HOOK.anchor(scope( """ public static void main() { """, // Method Hook for local variables. - Hooks.METHOD_HOOK.anchor( + Hooks.METHOD_HOOK.anchor(scope( """ // This is the beginning of the "main" method body. System.out.println("Welcome to main!"); @@ -777,7 +1010,7 @@ public static void main() { System.out.println("Going to call other..."); other(); """ - ), + )), """ } @@ -785,7 +1018,7 @@ private static void other() { """, // Have a separate method hook for other, where it could insert // its own local variables (but happens not to). - Hooks.METHOD_HOOK.anchor( + Hooks.METHOD_HOOK.anchor(scope( """ System.out.println("Welcome to other!"); """, @@ -793,11 +1026,11 @@ private static void other() { """ System.out.println("Done with other."); """ - ), + )), """ } """ - ), + )), """ } """ @@ -807,83 +1040,119 @@ private static void other() { return templateClass.render(); } - // Let us have a closer look at how DataNames interact with scopes created by - // Templates and Hooks. Additionally, we see how the execution order of the - // lambdas and token evaluation affects the availability of DataNames. - // - // We inject the results directly into verification inside the code, so it - // is relatively simple to see what the expected results are. - // - // For simplicity, we define a simple "list" function. It collects all - // field and variable names, and immediately returns the comma separated - // list of the names. We can use that to visualize the available names - // at any point. - public static String listNames() { - return "{" + String.join(", ", dataNames(MUTABLE).exactOf(myInt).toList() - .stream().map(DataName::name).toList()) + "}"; - } - - // Even simpler: count the available variables and return the count immediately. - public static int countNames() { - return dataNames(MUTABLE).exactOf(myInt).count(); - } - - // Having defined these helper methods, let us start with the first example. - // You should start reading this example bottom-up, starting at - // templateClass, then going to templateMain and last to templateInner. - public static String generateWithDataNamesAndScopes1() { - - var templateInner = Template.make(() -> body( - // We just got called from the templateMain. All tokens from there - // are already evaluated, so "v1" is now available: - let("l1", listNames()), - """ - if (!"{v1}".equals("#l1")) { throw new RuntimeException("l1 should have been '{v1}' but was '#l1'"); } + public static String generateWithScopes1() { + + // For the examples below, we need a convenient way of asserting the state + // of the available DataNames. + var templateVerify = Template.make("count", "hasAny", "toList", (Integer count, Boolean hasAny, String toList) -> scope( + dataNames(MUTABLE).exactOf(myInt).count(c -> transparentScope(let("count2", c))), + dataNames(MUTABLE).exactOf(myInt).hasAny(h -> transparentScope(let("hasAny2", h))), + dataNames(MUTABLE).exactOf(myInt).toList(list -> transparentScope( + let("toList2", String.join(", ", list.stream().map(DataName::name).toList())) + )), + """ + if (#count != #count2 || + #hasAny != #hasAny2 || + !"#toList".equals("#toList2")) { + throw new RuntimeException("verify failed"); + } """ )); - var templateMain = Template.make(() -> body( - // So far, no names were defined. We expect "c1" to be zero. - let("c1", countNames()), - """ - if (#c1 != 0) { throw new RuntimeException("c1 was not zero but #c1"); } - """, - // We now add a local variable "v1" to the scope of this templateMain. - // This only generates a token, and does not immediately add the name. - // The name is only added once we evaluate the tokens, and arrive at - // this particular token. + var templateMain = Template.make(() -> scope( + "// Start with nothing:\n", + templateVerify.asToken(0, false, ""), + "// Add v1:\n", addDataName("v1", myInt, MUTABLE), - // We count again with "c2". The variable "v1" is at this point still - // in token form, hence it is not yet made available while executing - // the template lambda of templateMain. - let("c2", countNames()), - """ - if (#c2 != 0) { throw new RuntimeException("c2 was not zero but #c2"); } + "int v1 = 1;\n", + "// Check that it is visible:\n", + templateVerify.asToken(1, true, "v1"), + "// Add v2:\n", + addDataName("v2", myInt, MUTABLE), + "int v2 = 2;\n", + "// Check that both are visible:\n", + templateVerify.asToken(2, true, "v1, v2"), + + "// Create a local scope:\n", + "{\n", scope( // for consistency, we model the code and template scope together. + "// Add v3:\n", + addDataName("v3", myInt, MUTABLE), + "int v3 = 3;\n", + "// Check that all are visible:\n", + templateVerify.asToken(3, true, "v1, v2, v3") + ), "}\n", + "// But after the scope, v3 is no longer available:\n", + templateVerify.asToken(2, true, "v1, v2"), + + "// Now let's create a list of variables.\n", + List.of(4, 5, 6).stream().map(i -> hashtagScope( + // The hashtagScope allows hashtag replacements to be local, + // and DataNames to escape, so we can use them afterwards. + let("i", i), + addDataName("v" + i, myInt, MUTABLE), + "int v#i = #i;\n" + )).toList(), + templateVerify.asToken(5, true, "v1, v2, v4, v5, v6"), + + "// Let's multiply all variables by a factor of 2, using forEach:\n", + dataNames(MUTABLE).exactOf(myInt).forEach(dn -> scope( + let("v", dn.name()), + "#v *= 2;\n" + )), + "// We can also capture the name (v) and type of the DataName:\n", + dataNames(MUTABLE).exactOf(myInt).forEach("v", "type", dn -> scope( + "#v *= 2;\n" + )), + "// Yet another option is using toList, but here that is more cumbersome:\n", + dataNames(MUTABLE).exactOf(myInt).toList(list -> scope( + list.stream().map(dn -> scope( + let("v", dn.name()), + "#v *= 2;\n" + )).toList() + )), + + """ + // We verify the result again. + """, + templateVerify.asToken(5, true, "v1, v2, v4, v5, v6"), + """ + if (v1 != 1 * 8 || + v2 != 2 * 8 || + v4 != 4 * 8 || + v5 != 5 * 8 || + v6 != 6 * 8) { + throw new RuntimeException("wrong value!"); + } """, - // But now we call an inner Template. This is added as a TemplateToken. - // This means it is not evaluated immediately, but only once we evaluate - // the tokens. By that time, all tokens from above are already evaluated - // and we see that "v1" is available. - templateInner.asToken() + + "// Let us copy each variable:\n", + dataNames(MUTABLE).exactOf(myInt).forEach("v", "type", dn -> hashtagScope( + // Note that we need a hashtagScope here, so that we can reuse "v" and + // "type" as hashtag replacements in each iteration, but still let the + // copied DataNames escape. + addDataName(dn.name() + "_copy", myInt, MUTABLE), + "#type #{v}_copy = #v;\n" + )), + templateVerify.asToken(10, true, "v1, v2, v4, v5, v6, v1_copy, v2_copy, v4_copy, v5_copy, v6_copy") )); - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; public class InnerTest9a { """, - Hooks.CLASS_HOOK.anchor( + Hooks.CLASS_HOOK.anchor(scope( """ public static void main() { """, - Hooks.METHOD_HOOK.anchor( + Hooks.METHOD_HOOK.anchor(scope( templateMain.asToken() - ), + )), """ } """ - ), + )), """ } """ @@ -893,111 +1162,129 @@ public static void main() { return templateClass.render(); } - // Now that we understand this simple example, we go to a more complicated one - // where we use Hook.insert. Just as above, you should read this example - // bottom-up, starting at templateClass. - public static String generateWithDataNamesAndScopes2() { + public static String generateWithScopes2() { - var templateFields = Template.make(() -> body( - // We were just called from templateMain. But the code is not - // generated into the main scope, rather into the class scope - // out in templateClass. - // Let us now add a field "f1". - addDataName("f1", myInt, MUTABLE), - // And let's also generate the code for it. - """ - public static int f1 = 42; - """, - // But why is this DataName now available inside the scope of - // templateInner? Does that not mean that "f1" escapes this - // templateFields here? Yes it does! - // For normal template nesting, the names do not escape the - // scope of the nested template. But this here is no normal - // template nesting, rather it is an insertion into a Hook, - // and we treat those differently. We make the scope of the - // inserted templateFields transparent, so that any added - // DataNames are added to the scope of the Hook we just - // inserted into, i.e. the CLASS_HOOK. This is very important, - // if we did not make that scope transparent, we could not - // add any DataNames to the class scope anymore, and we could - // not add any fields that would be available in the class - // scope. - Hooks.METHOD_HOOK.anchor( - // We now create a separate scope. This one is not the - // template scope from above, and it is not transparent. - // Hence, "f2" will not be available outside of this - // scope. - addDataName("f2", myInt, MUTABLE), - // And let's also generate the code for it. - """ - public static int f2 = 666; - """ - // Similarly, if we called any nested Template here, - // and added DataNames inside, this would happen inside - // nested scopes that are not transparent. If one wanted - // to add names to the CLASS_HOOK from there, one would - // have to do another Hook.insert, and make sure that - // the names are added from the outermost scope of that - // inserted Template, because only that outermost scope - // is transparent to the CLASS_HOOK. - ) - )); + // In this section, we will look at some subtle facts about the behavior of + // transparent scopes around hook insertion. This is intended for expert users + // so feel free to skip it until you extensively use hook insertion. + // More info can also be found in the Javadocs of the Hook class. - var templateInner = Template.make(() -> body( - // We just got called from the templateMain. All tokens from there - // are already evaluated, so there should be some fields available. - // We can see field "f1". - let("l1", listNames()), + // Helper method to check that the expected DataNames are available. + var templateVerify = Template.make("toList", (String toList) -> scope( + dataNames(MUTABLE).exactOf(myInt).toList(list -> transparentScope( + let("toList2", String.join(", ", list.stream().map(DataName::name).toList())) + )), """ - if (!"{f1}".equals("#l1")) { throw new RuntimeException("l1 should have been '{f1}' but was '#l1'"); } + if (!"#toList".equals("#toList2")) { + throw new RuntimeException("verify failed: '#toList' vs '#toList2'."); + } """ - // Now go and have a look at templateFields, to understand how that - // field was added, and why not any others. )); - var templateMain = Template.make(() -> body( - // So far, no names were defined. We expect "c1" to be zero. - let("c1", countNames()), - """ - if (#c1 != 0) { throw new RuntimeException("c1 was not zero but #c1"); } - """, - // We would now like to add some fields to the class scope, out in the - // templateClass. This creates a token, which is only evaluated after - // the completion of the templateMain lambda. Before you go and look - // at templateFields, just assume that it does add some fields, and - // continue reading in templateMain. - Hooks.CLASS_HOOK.insert(templateFields.asToken()), - // We count again with "c2". The fields we wanted to add above are not - // yet available, because the token is not yet evaluated. Hence, we - // still only count zero names. - let("c2", countNames()), - """ - if (#c2 != 0) { throw new RuntimeException("c2 was not zero but #c2"); } - """, - // Now we call an inner Template. This also creates a token, and so it - // is not evaluated immediately. And by the time this token is evaluated - // the tokens from above are already evaluated, and so the fields should - // be available. Go have a look at templateInner now. - templateInner.asToken() + var myHook = new Hook("MyHook"); + + var templateMain = Template.make(() -> scope( + // Start with nothing: + templateVerify.asToken(""), + addDataName("v1", myInt, MUTABLE), + templateVerify.asToken("v1"), + // Non-transparent hook anchor: + myHook.anchor(scope( + templateVerify.asToken("v1"), + addDataName("v2", myInt, MUTABLE), + templateVerify.asToken("v1, v2"), + // Insert a non-transparent scope: nothing escapes. + myHook.insert(scope( + // Note that at the anchor insertion point, v2 is not yet + // available, because it is added after the anchoring. + templateVerify.asToken("v1"), + let("x3", 42), + addDataName("v3", myInt, MUTABLE), + templateVerify.asToken("v1, v3") + )), + // Note: x3 and v3 do not escape. + let("x3", 7), // we can define it again. + templateVerify.asToken("v1, v2"), + // While not letting hashtags escape may be helpful, it is probably + // not very helpful if the DataNames don't escape. For example, if + // we are inserting some variable at an outer scope, we would like + // it to be available for the rest of the scope. + // That's where a transparent scope can be helpful. + myHook.insert(transparentScope( + // At the anchoring, still only v1 is available. + templateVerify.asToken("v1"), + let("x4", 42), // escapes to caller scope + addDataName("v4", myInt, MUTABLE), // escapes to anchor scope + templateVerify.asToken("v1, v4") + )), + // x4 escapes to the caller out here, and not to the anchor scope. + "// x4: #x4\n", + // And v4 escapes to the anchor scope, which is available from here too. + // Interesting detail: the ordering in the list indicates that v1 + // is from the outermost scope of the template, v4 is located at the + // anchor scope, and v2 is located inside the anchor scope, and + // thus comes last. + templateVerify.asToken("v1, v4, v2"), + // In most practical cases we probably don't want to let the hashtag + // escape, because they just represent something local. So we can + // use a hashtagScope, so that DataNames escape, but not hashtags. + myHook.insert(hashtagScope( + // Note: both v1 and v4 are now available at the anchoring, since + // v1 was inserted outside the anchoring scope, and v4 was just + // inserted to the anchoring scope. + templateVerify.asToken("v1, v4"), + let("x5", 42), // local, does not escape. + addDataName("v5", myInt, MUTABLE), // escapes to anchor scope + templateVerify.asToken("v1, v4, v5") + )), + let("x5", 7), // we can define it again. + templateVerify.asToken("v1, v4, v5, v2") + )), + // We left the non-transparent anchoring scope which does not let anything escape + templateVerify.asToken("v1"), + + // Let us now do something that probably should never be done. But still + // we want to demonstrate it for educational purposes: transparent anchoring + // scopes. + myHook.anchor(transparentScope( + templateVerify.asToken("v1"), + // For one, this means that DataName escape the scope directly. + addDataName("v6", myInt, MUTABLE), + templateVerify.asToken("v1, v6"), + // But also if we insert to the anchoring scope, DataNames don't just + // escape from the anchoring scope, but further out to the enclosing + // scope. + myHook.insert(transparentScope( + templateVerify.asToken("v1, v6"), + addDataName("v7", myInt, MUTABLE), + templateVerify.asToken("v1, v6, v7") + )), + templateVerify.asToken("v1, v6, v7"), + let("x6", 42) // escapes the anchor scope + )), + // We left the transparent anchoring scope which lets the DataNames and + // hashtags escape. + "// x6: #x6\n", + templateVerify.asToken("v1, v6, v7") )); - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; public class InnerTest9b { """, - Hooks.CLASS_HOOK.anchor( + Hooks.CLASS_HOOK.anchor(scope( """ public static void main() { """, - Hooks.METHOD_HOOK.anchor( + Hooks.METHOD_HOOK.anchor(scope( templateMain.asToken() - ), + )), """ } """ - ), + )), """ } """ @@ -1006,8 +1293,6 @@ public static void main() { // Render templateClass to String. return templateClass.render(); } - - // There are two more concepts to understand more deeply with DataNames. // // One is the use of mutable and immutable DataNames. @@ -1045,38 +1330,40 @@ public boolean isSubtypeOf(DataName.Type other) { private static final List myClassList = List.of(myClassA, myClassA1, myClassA2, myClassA11, myClassB); public static String generateWithDataNamesForFuzzing() { - var templateStaticField = Template.make("type", "mutable", (DataName.Type type, Boolean mutable) -> body( - addDataName($("field"), type, mutable ? MUTABLE : IMMUTABLE), + // This template is used to insert a DataName (field) into an outer scope, hence we must use + // "transparentScope" instead of "scope". + var templateStaticField = Template.make("type", "mutable", (DataName.Type type, Boolean mutable) -> transparentScope( + addDataName($("field"), type, mutable ? MUTABLE : IMMUTABLE), // Escapes the template. let("isFinal", mutable ? "" : "final"), """ public static #isFinal #type $field = new #type(); """ )); - var templateLoad = Template.make("type", (DataName.Type type) -> body( + var templateLoad = Template.make("type", (DataName.Type type) -> scope( // We only load from the field, so we do not need a mutable one, // we can load from final and non-final fields. // We want to find any field from which we can read the value and store // it in our variable v of our given type. Hence, we can take a field // of the given type or any subtype thereof. - let("field", dataNames(MUTABLE_OR_IMMUTABLE).subtypeOf(type).sample().name()), + dataNames(MUTABLE_OR_IMMUTABLE).subtypeOf(type).sampleAndLetAs("field"), """ #type $v = #field; System.out.println("#field: " + $v); """ )); - var templateStore = Template.make("type", (DataName.Type type) -> body( + var templateStore = Template.make("type", (DataName.Type type) -> scope( // We are storing to a field, so it better be non-final, i.e. mutable. // We want to store a new instance of our given type to a field. This // field must be of the given type or any supertype. - let("field", dataNames(MUTABLE).supertypeOf(type).sample().name()), + dataNames(MUTABLE).supertypeOf(type).sampleAndLetAs("field"), """ #field = new #type(); """ )); - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; @@ -1094,7 +1381,7 @@ public static class MyClassB {} // addDataName is restricted to the scope of the templateStaticField. But // with the insertion to CLASS_HOOK, the addDataName goes through the scope // of the templateStaticField out to the scope of the CLASS_HOOK. - Hooks.CLASS_HOOK.anchor( + Hooks.CLASS_HOOK.anchor(scope( myClassList.stream().map(c -> (Object)Hooks.CLASS_HOOK.insert(templateStaticField.asToken(c, true)) ).toList(), @@ -1118,7 +1405,7 @@ public static void main() { """ } """ - ), + )), """ } """ @@ -1126,7 +1413,6 @@ public static void main() { // Render templateClass to String. return templateClass.render(); - } // "DataNames" are useful for modeling fields and variables. They hold data, @@ -1165,9 +1451,9 @@ public boolean isSubtypeOf(StructuralName.Type other) { public static String generateWithStructuralNamesForMethods() { // Define a method, which takes two ints, returns the result of op. - var templateMethod = Template.make("op", (String op) -> body( + var templateMethod = Template.make("op", (String op) -> transparentScope( // Register the method name, so we can later sample. - addStructuralName($("methodName"), myMethodType), + addStructuralName($("methodName"), myMethodType), // escapes the template because of "transparentScope" """ public static int $methodName(int a, int b) { return a #op b; @@ -1175,16 +1461,16 @@ public static String generateWithStructuralNamesForMethods() { """ )); - var templateSample = Template.make(() -> body( + var templateSample = Template.make(() -> scope( // Sample a random method, and retrieve its name. - let("methodName", structuralNames().exactOf(myMethodType).sample().name()), + structuralNames().exactOf(myMethodType).sampleAndLetAs("methodName"), """ System.out.println("Calling #methodName with inputs 7 and 11"); System.out.println(" result: " + #methodName(7, 11)); """ )); - var templateClass = Template.make(() -> body( + var templateClass = Template.make(() -> scope( """ package p.xyz; @@ -1192,7 +1478,7 @@ public class InnerTest11 { // Let us define some methods that we can sample from later. """, // We must anchor a CLASS_HOOK here, and insert the method definitions to that hook. - Hooks.CLASS_HOOK.anchor( + Hooks.CLASS_HOOK.anchor(scope( // If we directly nest the templateMethod, then the addStructuralName goes to the nested // scope, and is not available at the class scope, i.e. it is not visible // for sampleStructuralName outside of the templateMethod. @@ -1218,7 +1504,7 @@ public static void main() { } } """ - ) + )) )); // Render templateClass to String. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestWithTestFrameworkClass.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestWithTestFrameworkClass.java index 813f2976ef251..01b49db2c0137 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestWithTestFrameworkClass.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestWithTestFrameworkClass.java @@ -43,7 +43,7 @@ import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; import static compiler.lib.template_framework.Template.let; import compiler.lib.template_framework.library.Hooks; @@ -82,7 +82,7 @@ public static void main(String[] args) { // Generate a source Java file as String public static String generate(CompileFramework comp) { // A simple template that adds a comment. - var commentTemplate = Template.make(() -> body( + var commentTemplate = Template.make(() -> scope( """ // Comment inserted from test method to class hook. """ @@ -103,7 +103,7 @@ public static String generate(CompileFramework comp) { // - The test method makes use of hashtag replacements (#con2 and #op). // - The Check method verifies the results of the test method with the // GOLD value. - var testTemplate = Template.make("op", (String op) -> body( + var testTemplate = Template.make("op", (String op) -> scope( let("size", Generators.G.safeRestrict(Generators.G.ints(), 10_000, 20_000).next()), let("con1", Generators.G.ints().next()), let("con2", Generators.G.safeRestrict(Generators.G.ints(), 1, Integer.MAX_VALUE).next()), diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java index 2dac740dd936a..b34538c39c165 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java @@ -38,7 +38,7 @@ import compiler.lib.template_framework.DataName; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; import compiler.lib.template_framework.library.CodeGenerationDataNameType; import compiler.lib.template_framework.library.Expression; @@ -93,7 +93,7 @@ public static void testAsToken() { Expression e3 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeB, ",", myTypeA1, "]"); Expression e4 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeB, ",", myTypeA1, ",", myTypeA, "]"); - var template = Template.make(() -> body( + var template = Template.make(() -> scope( "xx", e1.toString(), "yy\n", "xx", e2.toString(), "yy\n", "xx", e3.toString(), "yy\n", @@ -141,7 +141,7 @@ public static void testNest() { Expression e3e1 = e3.nest(0, e1); Expression e4e5 = e4.nest(1, e5); - var template = Template.make(() -> body( + var template = Template.make(() -> scope( "xx", e1e1.toString(), "yy\n", "xx", e2e1.toString(), "yy\n", "xx", e3e1.toString(), "yy\n", @@ -184,7 +184,7 @@ public static void testNestRandomly() { // Alternating pattern Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5); - var template = Template.make(() -> body( + var template = Template.make(() -> scope( "xx", e1e2.toString(), "yy\n", "xx", e1ex.toString(), "yy\n", "xx", e1e4.toString(), "yy\n", diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestFormat.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestFormat.java index fe267a3ff638b..577542e085bac 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestFormat.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestFormat.java @@ -39,7 +39,7 @@ import compiler.lib.generators.*; import compiler.lib.verify.*; import compiler.lib.template_framework.Template; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; import static compiler.lib.template_framework.Template.let; public class TestFormat { @@ -84,7 +84,7 @@ public static void main(String[] args) { private static String generate(List list) { // Generate 2 "get" methods, one that formats via "let" (hashtag), the other via direct token. - var template1 = Template.make("info", (FormatInfo info) -> body( + var template1 = Template.make("info", (FormatInfo info) -> scope( let("id", info.id()), let("type", info.type()), let("value", info.value()), @@ -95,7 +95,7 @@ private static String generate(List list) { )); // For each FormatInfo in list, generate the "get" methods inside InnerTest class. - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( """ package p.xyz; public class InnerTest { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 35d020b608022..9be74d232a76e 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -44,7 +44,11 @@ import compiler.lib.template_framework.Hook; import compiler.lib.template_framework.TemplateBinding; import compiler.lib.template_framework.RendererException; -import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.transparentScope; +import static compiler.lib.template_framework.Template.nameScope; +import static compiler.lib.template_framework.Template.hashtagScope; +import static compiler.lib.template_framework.Template.setFuelCostScope; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.fuel; @@ -121,41 +125,56 @@ public static void main(String[] args) { // The following tests all pass, i.e. have no errors during rendering. testSingleLine(); testMultiLine(); - testBodyTokens(); + testBasicTokens(); testWithOneArgument(); testWithTwoArguments(); testWithThreeArguments(); - testNested(); - testHookSimple(); + testNestedTemplates(); + testHookSimple1(); + testHookSimple2(); + testHookSimple3(); testHookIsAnchored(); testHookNested(); testHookWithNestedTemplates(); testHookRecursion(); testDollar(); - testLet(); + testLet1(); + testLet2(); testDollarAndHashtagBrackets(); testSelector(); testRecursion(); testFuel(); testFuelCustom(); + testFuelAndScopes(); + testDataNames0a(); + testDataNames0b(); + testDataNames0c(); + testDataNames0d(); testDataNames1(); testDataNames2(); testDataNames3(); testDataNames4(); testDataNames5(); + testDataNames6(); + testStructuralNames0(); testStructuralNames1(); testStructuralNames2(); + testStructuralNames3(); + testStructuralNames4(); + testStructuralNames5(); + testStructuralNames6(); testListArgument(); + testNestedScopes1(); + testNestedScopes2(); + testTemplateScopes(); + testHookAndScopes1(); + testHookAndScopes2(); + testHookAndScopes3(); // The following tests should all fail, with an expected exception and message. expectRendererException(() -> testFailingNestedRendering(), "Nested render not allowed."); expectRendererException(() -> $("name"), "A Template method such as"); - expectRendererException(() -> let("x","y"), "A Template method such as"); expectRendererException(() -> fuel(), "A Template method such as"); - expectRendererException(() -> setFuelCost(1.0f), "A Template method such as"); - expectRendererException(() -> dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).count(), "A Template method such as"); - expectRendererException(() -> dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).sample(), "A Template method such as"); - expectRendererException(() -> (new Hook("abc")).isAnchored(), "A Template method such as"); expectRendererException(() -> testFailingDollarName1(), "Is not a valid '$' name: ''."); expectRendererException(() -> testFailingDollarName2(), "Is not a valid '$' name: '#abc'."); expectRendererException(() -> testFailingDollarName3(), "Is not a valid '$' name: 'abc#'."); @@ -178,20 +197,31 @@ public static void main(String[] args) { expectRendererException(() -> testFailingDollarHashtagName3(), "Is not a valid '#' replacement pattern: '#' in '#$name'."); expectRendererException(() -> testFailingDollarHashtagName4(), "Is not a valid '$' replacement pattern: '$' in '$#name'."); expectRendererException(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); - expectRendererException(() -> testFailingSample1(), "No variable: MUTABLE, subtypeOf(int), supertypeOf(int)."); + expectRendererException(() -> testFailingSample1a(), "No Name found for DataName.FilterdSet(MUTABLE, subtypeOf(int), supertypeOf(int))"); + expectRendererException(() -> testFailingSample1b(), "No Name found for StructuralName.FilteredSet( subtypeOf(StructuralA) supertypeOf(StructuralA))"); expectRendererException(() -> testFailingHashtag1(), "Duplicate hashtag replacement for #a"); expectRendererException(() -> testFailingHashtag2(), "Duplicate hashtag replacement for #a"); expectRendererException(() -> testFailingHashtag3(), "Duplicate hashtag replacement for #a"); expectRendererException(() -> testFailingHashtag4(), "Missing hashtag replacement for #a"); + expectRendererException(() -> testFailingHashtag5(), "Missing hashtag replacement for #a"); expectRendererException(() -> testFailingBinding1(), "Duplicate 'bind' not allowed."); expectRendererException(() -> testFailingBinding2(), "Cannot 'get' before 'bind'."); - expectIllegalArgumentException(() -> body(null), "Unexpected tokens: null"); - expectIllegalArgumentException(() -> body("x", null), "Unexpected token: null"); - expectIllegalArgumentException(() -> body(new Hook("Hook1")), "Unexpected token:"); + expectIllegalArgumentException(() -> scope(null), "Unexpected tokens: null"); + expectIllegalArgumentException(() -> scope("x", null), "Unexpected token: null"); + expectIllegalArgumentException(() -> scope(new Hook("Hook1")), "Unexpected token:"); + expectIllegalArgumentException(() -> transparentScope(null), "Unexpected tokens: null"); + expectIllegalArgumentException(() -> transparentScope("x", null), "Unexpected token: null"); + expectIllegalArgumentException(() -> transparentScope(new Hook("Hook1")), "Unexpected token:"); + expectIllegalArgumentException(() -> nameScope(null), "Unexpected tokens: null"); + expectIllegalArgumentException(() -> nameScope("x", null), "Unexpected token: null"); + expectIllegalArgumentException(() -> nameScope(new Hook("Hook1")), "Unexpected token:"); + expectIllegalArgumentException(() -> hashtagScope(null), "Unexpected tokens: null"); + expectIllegalArgumentException(() -> hashtagScope("x", null), "Unexpected token: null"); + expectIllegalArgumentException(() -> hashtagScope(new Hook("Hook1")), "Unexpected token:"); + expectIllegalArgumentException(() -> setFuelCostScope(null), "Unexpected tokens: null"); + expectIllegalArgumentException(() -> setFuelCostScope("x", null), "Unexpected token: null"); + expectIllegalArgumentException(() -> setFuelCostScope(new Hook("Hook1")), "Unexpected token:"); Hook hook1 = new Hook("Hook1"); - expectIllegalArgumentException(() -> hook1.anchor(null), "Unexpected tokens: null"); - expectIllegalArgumentException(() -> hook1.anchor("x", null), "Unexpected token: null"); - expectIllegalArgumentException(() -> hook1.anchor(hook1), "Unexpected token:"); expectIllegalArgumentException(() -> testFailingAddDataName1(), "Unexpected mutability: MUTABLE_OR_IMMUTABLE"); expectIllegalArgumentException(() -> testFailingAddDataName2(), "Unexpected weight: "); expectIllegalArgumentException(() -> testFailingAddDataName3(), "Unexpected weight: "); @@ -199,7 +229,8 @@ public static void main(String[] args) { expectIllegalArgumentException(() -> testFailingAddStructuralName1(), "Unexpected weight: "); expectIllegalArgumentException(() -> testFailingAddStructuralName2(), "Unexpected weight: "); expectIllegalArgumentException(() -> testFailingAddStructuralName3(), "Unexpected weight: "); - expectUnsupportedOperationException(() -> testFailingSample2(), "Must first call 'subtypeOf', 'supertypeOf', or 'exactOf'."); + expectUnsupportedOperationException(() -> testFailingSample2a(), "Must first call 'subtypeOf', 'supertypeOf', or 'exactOf'."); + expectUnsupportedOperationException(() -> testFailingSample2b(), "Must first call 'subtypeOf', 'supertypeOf', or 'exactOf'."); expectRendererException(() -> testFailingAddNameDuplication1(), "Duplicate name:"); expectRendererException(() -> testFailingAddNameDuplication2(), "Duplicate name:"); expectRendererException(() -> testFailingAddNameDuplication3(), "Duplicate name:"); @@ -208,16 +239,23 @@ public static void main(String[] args) { expectRendererException(() -> testFailingAddNameDuplication6(), "Duplicate name:"); expectRendererException(() -> testFailingAddNameDuplication7(), "Duplicate name:"); expectRendererException(() -> testFailingAddNameDuplication8(), "Duplicate name:"); + expectRendererException(() -> testFailingScope1(), "Duplicate hashtag replacement for #x. previous: x1, new: x2"); + expectRendererException(() -> testFailingScope2(), "Duplicate hashtag replacement for #x. previous: x1, new: x2"); + expectRendererException(() -> testFailingScope3(), "Duplicate hashtag replacement for #x. previous: a, new: b"); + expectRendererException(() -> testFailingScope4(), "Duplicate hashtag replacement for #x. previous: a, new: b"); + expectRendererException(() -> testFailingScope5(), "Duplicate name:"); + expectRendererException(() -> testFailingScope6(), "Duplicate name:"); + expectRendererException(() -> testFailingScope7(), "Duplicate name:"); } public static void testSingleLine() { - var template = Template.make(() -> body("Hello World!")); + var template = Template.make(() -> scope("Hello World!")); String code = template.render(); checkEQ(code, "Hello World!"); } public static void testMultiLine() { - var template = Template.make(() -> body( + var template = Template.make(() -> scope( """ Code on more than a single line @@ -232,10 +270,10 @@ public static void testMultiLine() { checkEQ(code, expected); } - public static void testBodyTokens() { - // We can fill the body with Objects of different types, and they get concatenated. - // Lists get flattened into the body. - var template = Template.make(() -> body( + public static void testBasicTokens() { + // We can fill the scope with Objects of different types, and they get concatenated. + // Lists get flattened into the scope. + var template = Template.make(() -> scope( "start ", Integer.valueOf(1), 1, Long.valueOf(2), 2L, @@ -250,31 +288,31 @@ public static void testBodyTokens() { public static void testWithOneArgument() { // Capture String argument via String name. - var template1 = Template.make("a", (String a) -> body("start #a end")); + var template1 = Template.make("a", (String a) -> scope("start #a end")); checkEQ(template1.render("x"), "start x end"); checkEQ(template1.render("a"), "start a end"); checkEQ(template1.render("" ), "start end"); // Capture String argument via typed lambda argument. - var template2 = Template.make("a", (String a) -> body("start ", a, " end")); + var template2 = Template.make("a", (String a) -> scope("start ", a, " end")); checkEQ(template2.render("x"), "start x end"); checkEQ(template2.render("a"), "start a end"); checkEQ(template2.render("" ), "start end"); // Capture Integer argument via String name. - var template3 = Template.make("a", (Integer a) -> body("start #a end")); + var template3 = Template.make("a", (Integer a) -> scope("start #a end")); checkEQ(template3.render(0 ), "start 0 end"); checkEQ(template3.render(22 ), "start 22 end"); checkEQ(template3.render(444), "start 444 end"); // Capture Integer argument via templated lambda argument. - var template4 = Template.make("a", (Integer a) -> body("start ", a, " end")); + var template4 = Template.make("a", (Integer a) -> scope("start ", a, " end")); checkEQ(template4.render(0 ), "start 0 end"); checkEQ(template4.render(22 ), "start 22 end"); checkEQ(template4.render(444), "start 444 end"); // Test Strings with backslashes: - var template5 = Template.make("a", (String a) -> body("start #a " + a + " end")); + var template5 = Template.make("a", (String a) -> scope("start #a " + a + " end")); checkEQ(template5.render("/"), "start / / end"); checkEQ(template5.render("\\"), "start \\ \\ end"); checkEQ(template5.render("\\\\"), "start \\\\ \\\\ end"); @@ -282,25 +320,25 @@ public static void testWithOneArgument() { public static void testWithTwoArguments() { // Capture 2 String arguments via String names. - var template1 = Template.make("a1", "a2", (String a1, String a2) -> body("start #a1 #a2 end")); + var template1 = Template.make("a1", "a2", (String a1, String a2) -> scope("start #a1 #a2 end")); checkEQ(template1.render("x", "y"), "start x y end"); checkEQ(template1.render("a", "b"), "start a b end"); checkEQ(template1.render("", "" ), "start end"); // Capture 2 String arguments via typed lambda arguments. - var template2 = Template.make("a1", "a2", (String a1, String a2) -> body("start ", a1, " ", a2, " end")); + var template2 = Template.make("a1", "a2", (String a1, String a2) -> scope("start ", a1, " ", a2, " end")); checkEQ(template2.render("x", "y"), "start x y end"); checkEQ(template2.render("a", "b"), "start a b end"); checkEQ(template2.render("", "" ), "start end"); // Capture 2 Integer arguments via String names. - var template3 = Template.make("a1", "a2", (Integer a1, Integer a2) -> body("start #a1 #a2 end")); + var template3 = Template.make("a1", "a2", (Integer a1, Integer a2) -> scope("start #a1 #a2 end")); checkEQ(template3.render(0, 1 ), "start 0 1 end"); checkEQ(template3.render(22, 33 ), "start 22 33 end"); checkEQ(template3.render(444, 555), "start 444 555 end"); // Capture 2 Integer arguments via templated lambda arguments. - var template4 = Template.make("a1", "a2", (Integer a1, Integer a2) -> body("start ", a1, " ", a2, " end")); + var template4 = Template.make("a1", "a2", (Integer a1, Integer a2) -> scope("start ", a1, " ", a2, " end")); checkEQ(template4.render(0, 1 ), "start 0 1 end"); checkEQ(template4.render(22, 33 ), "start 22 33 end"); checkEQ(template4.render(444, 555), "start 444 555 end"); @@ -308,46 +346,46 @@ public static void testWithTwoArguments() { public static void testWithThreeArguments() { // Capture 3 String arguments via String names. - var template1 = Template.make("a1", "a2", "a3", (String a1, String a2, String a3) -> body("start #a1 #a2 #a3 end")); + var template1 = Template.make("a1", "a2", "a3", (String a1, String a2, String a3) -> scope("start #a1 #a2 #a3 end")); checkEQ(template1.render("x", "y", "z"), "start x y z end"); checkEQ(template1.render("a", "b", "c"), "start a b c end"); checkEQ(template1.render("", "", "" ), "start end"); // Capture 3 String arguments via typed lambda arguments. - var template2 = Template.make("a1", "a2", "a3", (String a1, String a2, String a3) -> body("start ", a1, " ", a2, " ", a3, " end")); + var template2 = Template.make("a1", "a2", "a3", (String a1, String a2, String a3) -> scope("start ", a1, " ", a2, " ", a3, " end")); checkEQ(template1.render("x", "y", "z"), "start x y z end"); checkEQ(template1.render("a", "b", "c"), "start a b c end"); checkEQ(template1.render("", "", "" ), "start end"); // Capture 3 Integer arguments via String names. - var template3 = Template.make("a1", "a2", "a3", (Integer a1, Integer a2, Integer a3) -> body("start #a1 #a2 #a3 end")); + var template3 = Template.make("a1", "a2", "a3", (Integer a1, Integer a2, Integer a3) -> scope("start #a1 #a2 #a3 end")); checkEQ(template3.render(0, 1 , 2 ), "start 0 1 2 end"); checkEQ(template3.render(22, 33 , 44 ), "start 22 33 44 end"); checkEQ(template3.render(444, 555, 666), "start 444 555 666 end"); // Capture 2 Integer arguments via templated lambda arguments. - var template4 = Template.make("a1", "a2", "a3", (Integer a1, Integer a2, Integer a3) -> body("start ", a1, " ", a2, " ", a3, " end")); + var template4 = Template.make("a1", "a2", "a3", (Integer a1, Integer a2, Integer a3) -> scope("start ", a1, " ", a2, " ", a3, " end")); checkEQ(template3.render(0, 1 , 2 ), "start 0 1 2 end"); checkEQ(template3.render(22, 33 , 44 ), "start 22 33 44 end"); checkEQ(template3.render(444, 555, 666), "start 444 555 666 end"); } - public static void testNested() { - var template1 = Template.make(() -> body("proton")); + public static void testNestedTemplates() { + var template1 = Template.make(() -> scope("proton")); - var template2 = Template.make("a1", "a2", (String a1, String a2) -> body( + var template2 = Template.make("a1", "a2", (String a1, String a2) -> scope( "electron #a1\n", "neutron #a2\n" )); - var template3 = Template.make("a1", "a2", (String a1, String a2) -> body( + var template3 = Template.make("a1", "a2", (String a1, String a2) -> scope( "Universe ", template1.asToken(), " {\n", template2.asToken("up", "down"), template2.asToken(a1, a2), "}\n" )); - var template4 = Template.make(() -> body( + var template4 = Template.make(() -> scope( template3.asToken("low", "high"), "{\n", template3.asToken("42", "24"), @@ -374,19 +412,45 @@ public static void testNested() { checkEQ(code, expected); } - public static void testHookSimple() { + public static void testHookSimple1() { var hook1 = new Hook("Hook1"); - var template1 = Template.make(() -> body("Hello\n")); + var template1 = Template.make(() -> scope("Hello\n")); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "{\n", - hook1.anchor( + hook1.anchor(scope( "World\n", // Note: "Hello" from the template below will be inserted // above "World" above. hook1.insert(template1.asToken()) - ), + )), + "}" + )); + + String code = template2.render(); + String expected = + """ + { + Hello + World + }"""; + checkEQ(code, expected); + } + + public static void testHookSimple2() { + var hook1 = new Hook("Hook1"); + + var template2 = Template.make(() -> scope( + "{\n", + hook1.anchor(scope( + "World\n", + // Note: "Hello" from the scope below will be inserted + // above "World" above. + hook1.insert(scope( + "Hello\n" + )) + )), "}" )); @@ -400,21 +464,59 @@ public static void testHookSimple() { checkEQ(code, expected); } + public static void testHookSimple3() { + var hook1 = new Hook("Hook1"); + + // Ensure that insert inside insert really goes first. + var template = Template.make(() -> scope( + "{\n", + hook1.anchor(scope( + "Outer Insert\n" + )), + ">Anchor\n" + )), + "}" + )); + + String code = template.render(); + String expected = + """ + { + Inner Insert + Outer Insert + Anchor + }"""; + checkEQ(code, expected); + } + public static void testHookIsAnchored() { var hook1 = new Hook("Hook1"); - var template0 = Template.make(() -> body("isAnchored: ", hook1.isAnchored(), "\n")); + var template0 = Template.make(() -> scope("t0 isAnchored: ", hook1.isAnchored(a -> scope(a)), "\n")); - var template1 = Template.make(() -> body("Hello\n", template0.asToken())); + var template1 = Template.make(() -> scope("Hello\n", template0.asToken())); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "{\n", + "t2 isAnchored: ", hook1.isAnchored(a -> scope(a)), "\n", template0.asToken(), - hook1.anchor( + hook1.anchor(scope( "World\n", + "t2 isAnchored: ", hook1.isAnchored(a -> scope(a)), "\n", template0.asToken(), - hook1.insert(template1.asToken()) - ), + hook1.insert(template1.asToken()), + hook1.insert(scope("Beautiful\n", template0.asToken())), + "t2 isAnchored: ", hook1.isAnchored(a -> scope(a)), "\n" + )), + "t2 isAnchored: ", hook1.isAnchored(a -> scope(a)), "\n", template0.asToken(), "}" )); @@ -423,12 +525,18 @@ public static void testHookIsAnchored() { String expected = """ { - isAnchored: false + t2 isAnchored: false + t0 isAnchored: false Hello - isAnchored: true + t0 isAnchored: true + Beautiful + t0 isAnchored: true World - isAnchored: true - isAnchored: false + t2 isAnchored: true + t0 isAnchored: true + t2 isAnchored: true + t2 isAnchored: false + t0 isAnchored: false }"""; checkEQ(code, expected); } @@ -436,36 +544,41 @@ public static void testHookIsAnchored() { public static void testHookNested() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("a", (String a) -> body("x #a x\n")); + var template1 = Template.make("a", (String a) -> scope("x #a x\n")); // Test nested use of hooks in the same template. - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "{\n", - hook1.anchor(), // empty + hook1.anchor(scope()), // empty "zero\n", - hook1.anchor( + hook1.anchor(scope( template1.asToken("one"), template1.asToken("two"), hook1.insert(template1.asToken("intoHook1a")), hook1.insert(template1.asToken("intoHook1b")), + hook1.insert(scope("y 1 y\n")), + hook1.insert(scope("y 2 y\n")), template1.asToken("three"), - hook1.anchor( + hook1.anchor(scope( template1.asToken("four"), hook1.insert(template1.asToken("intoHook1c")), + hook1.insert(scope("y 3 y\n")), template1.asToken("five") - ), + )), template1.asToken("six"), - hook1.anchor(), // empty + hook1.anchor(scope()), // empty template1.asToken("seven"), hook1.insert(template1.asToken("intoHook1d")), + hook1.insert(scope("y 4 y\n")), template1.asToken("eight"), - hook1.anchor( + hook1.anchor(scope( template1.asToken("nine"), hook1.insert(template1.asToken("intoHook1e")), + hook1.insert(scope("y 5 y\n")), template1.asToken("ten") - ), + )), template1.asToken("eleven") - ), + )), "}" )); @@ -476,17 +589,22 @@ public static void testHookNested() { zero x intoHook1a x x intoHook1b x + y 1 y + y 2 y x intoHook1d x + y 4 y x one x x two x x three x x intoHook1c x + y 3 y x four x x five x x six x x seven x x eight x x intoHook1e x + y 5 y x nine x x ten x x eleven x @@ -498,30 +616,30 @@ public static void testHookWithNestedTemplates() { var hook1 = new Hook("Hook1"); var hook2 = new Hook("Hook2"); - var template1 = Template.make("a", (String a) -> body("x #a x\n")); + var template1 = Template.make("a", (String a) -> scope("x #a x\n")); - var template2 = Template.make("b", (String b) -> body( + var template2 = Template.make("b", (String b) -> scope( "{\n", template1.asToken(b + "A"), hook1.insert(template1.asToken(b + "B")), hook2.insert(template1.asToken(b + "C")), template1.asToken(b + "D"), - hook1.anchor( + hook1.anchor(scope( template1.asToken(b + "E"), hook1.insert(template1.asToken(b + "F")), hook2.insert(template1.asToken(b + "G")), template1.asToken(b + "H"), - hook2.anchor( + hook2.anchor(scope( template1.asToken(b + "I"), hook1.insert(template1.asToken(b + "J")), hook2.insert(template1.asToken(b + "K")), template1.asToken(b + "L") - ), + )), template1.asToken(b + "M"), hook1.insert(template1.asToken(b + "N")), hook2.insert(template1.asToken(b + "O")), template1.asToken(b + "O") - ), + )), template1.asToken(b + "P"), hook1.insert(template1.asToken(b + "Q")), hook2.insert(template1.asToken(b + "R")), @@ -530,18 +648,18 @@ public static void testHookWithNestedTemplates() { )); // Test use of hooks across templates. - var template3 = Template.make(() -> body( + var template3 = Template.make(() -> scope( "{\n", "base-A\n", - hook1.anchor( + hook1.anchor(scope( "base-B\n", - hook2.anchor( + hook2.anchor(scope( "base-C\n", template2.asToken("sub-"), "base-D\n" - ), + )), "base-E\n" - ), + )), "base-F\n", "}\n" )); @@ -586,32 +704,32 @@ public static void testHookWithNestedTemplates() { public static void testHookRecursion() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("a", (String a) -> body("x #a x\n")); + var template1 = Template.make("a", (String a) -> scope("x #a x\n")); - var template2 = Template.make("b", (String b) -> body( + var template2 = Template.make("b", (String b) -> scope( "<\n", template1.asToken(b + "A"), hook1.insert(template1.asToken(b + "B")), // sub-B is rendered before template2. template1.asToken(b + "C"), "inner-hook-start\n", - hook1.anchor( + hook1.anchor(scope( "inner-hook-end\n", template1.asToken(b + "E"), hook1.insert(template1.asToken(b + "E")), template1.asToken(b + "F") - ), + )), ">\n" )); // Test use of hooks across templates. - var template3 = Template.make(() -> body( + var template3 = Template.make(() -> scope( "{\n", "hook-start\n", - hook1.anchor( + hook1.anchor(scope( "hook-end\n", hook1.insert(template2.asToken("sub-")), "base-C\n" - ), + )), "base-D\n", "}\n" )); @@ -642,16 +760,16 @@ public static void testHookRecursion() { public static void testDollar() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("a", (String a) -> body("x $name #a x\n")); + var template1 = Template.make("a", (String a) -> scope("x $name #a x\n")); - var template2 = Template.make("a", (String a) -> body( + var template2 = Template.make("a", (String a) -> scope( "{\n", "y $name #a y\n", template1.asToken($("name")), "}\n" )); - var template3 = Template.make(() -> body( + var template3 = Template.make(() -> scope( "{\n", "$name\n", "$name", "\n", @@ -660,11 +778,11 @@ public static void testDollar() { template1.asToken("name"), // does not capture -> literal "$name" template1.asToken("$name"), // does not capture -> literal "$name" template1.asToken($("name")), // capture replacement name "name_1" - hook1.anchor( + hook1.anchor(scope( "$name\n" - ), + )), "break\n", - hook1.anchor( + hook1.anchor(scope( "one\n", hook1.insert(template1.asToken($("name"))), "two\n", @@ -672,7 +790,7 @@ public static void testDollar() { "three\n", hook1.insert(template2.asToken($("name"))), "four\n" - ), + )), "}\n" )); @@ -704,10 +822,10 @@ public static void testDollar() { checkEQ(code, expected); } - public static void testLet() { + public static void testLet1() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("a", (String a) -> body( + var template1 = Template.make("a", (String a) -> scope( "{\n", "y #a y\n", let("b", "<" + a + ">"), @@ -715,25 +833,25 @@ public static void testLet() { "}\n" )); - var template2 = Template.make("a", (Integer a) -> let("b", a * 10, b -> - body( + var template2 = Template.make("a", (Integer a) -> scope( + let("b", a * 10, b -> scope( let("c", b * 3), "abc = #a #b #c\n" - ) + )) )); - var template3 = Template.make(() -> body( + var template3 = Template.make(() -> scope( "{\n", let("x", "abc"), template1.asToken("alpha"), "break\n", "x1 = #x\n", - hook1.anchor( + hook1.anchor(transparentScope( // transparentScope allows hashtags to escape "x2 = #x\n", // leaks inside template1.asToken("beta"), let("y", "one"), "y1 = #y\n" - ), + )), "break\n", "y2 = #y\n", // leaks outside "break\n", @@ -766,8 +884,30 @@ public static void testLet() { checkEQ(code, expected); } + public static void testLet2() { + var template = Template.make(() -> scope( + "outer {\n", + let("x", "x1", x -> scope( + "x: #x ", x, ".\n" + )), + let("x", "x2"), // definition above is limited to its scope + "x: #x\n", + "} outer\n" + )); + + String code = template.render(); + String expected = + """ + outer { + x: x1 x1. + x: x2 + } outer + """; + checkEQ(code, expected); + } + public static void testDollarAndHashtagBrackets() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( let("xyz", "abc"), let("xyz_", "def"), let("xyz_klm", "ghi"), @@ -792,19 +932,19 @@ public static void testDollarAndHashtagBrackets() { } public static void testSelector() { - var template1 = Template.make("a", (String a) -> body( + var template1 = Template.make("a", (String a) -> scope( "<\n", "x #a x\n", ">\n" )); - var template2 = Template.make("a", (String a) -> body( + var template2 = Template.make("a", (String a) -> scope( "<\n", "y #a y\n", ">\n" )); - var template3 = Template.make("a", (Integer a) -> body( + var template3 = Template.make("a", (Integer a) -> scope( "[\n", "z #a z\n", // Select which template should be used: @@ -813,7 +953,7 @@ public static void testSelector() { "]\n" )); - var template4 = Template.make(() -> body( + var template4 = Template.make(() -> scope( "{\n", template3.asToken(-1), "break\n", @@ -865,7 +1005,7 @@ public static void testRecursion() { // Binding allows use of template1 inside template1, via the Binding indirection. var binding1 = new TemplateBinding>(); - var template1 = Template.make("i", (Integer i) -> body( + var template1 = Template.make("i", (Integer i) -> scope( "[ #i\n", // We cannot yet use the template1 directly, as it is being defined. // So we use binding1 instead. @@ -874,7 +1014,7 @@ public static void testRecursion() { )); binding1.bind(template1); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "{\n", // Now, we can use template1 normally, as it is already defined. template1.asToken(3), @@ -902,7 +1042,7 @@ public static void testRecursion() { } public static void testFuel() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( let("f", fuel()), "<#f>\n" @@ -910,7 +1050,7 @@ public static void testFuel() { // Binding allows use of template2 inside template2, via the Binding indirection. var binding2 = new TemplateBinding>(); - var template2 = Template.make("i", (Integer i) -> body( + var template2 = Template.make("i", (Integer i) -> scope( let("f", fuel()), "[ #i #f\n", @@ -920,7 +1060,7 @@ public static void testFuel() { )); binding2.bind(template2); - var template3 = Template.make(() -> body( + var template3 = Template.make(() -> scope( "{\n", template2.asToken(3), "}\n" @@ -948,7 +1088,7 @@ public static void testFuel() { } public static void testFuelCustom() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( setFuelCost(2.0f), let("f", fuel()), @@ -957,7 +1097,7 @@ public static void testFuelCustom() { // Binding allows use of template2 inside template2, via the Binding indirection. var binding2 = new TemplateBinding>(); - var template2 = Template.make("i", (Integer i) -> body( + var template2 = Template.make("i", (Integer i) -> scope( setFuelCost(3.0f), let("f", fuel()), @@ -968,7 +1108,7 @@ public static void testFuelCustom() { )); binding2.bind(template2); - var template3 = Template.make(() -> body( + var template3 = Template.make(() -> scope( setFuelCost(5.0f), let("f", fuel()), @@ -1002,46 +1142,277 @@ public static void testFuelCustom() { checkEQ(code, expected); } + public static void testFuelAndScopes() { + var readFuelTemplate = Template.make(() -> scope( + let("f", fuel()), + "<#f>\n" + )); + + var template = Template.make(() -> scope( + let("f", fuel()), + "{#f}\n", + readFuelTemplate.asToken(), + + "scope:\n", + setFuelCost(1.0f), + scope( + readFuelTemplate.asToken(), + setFuelCost(2.0f), + readFuelTemplate.asToken() + ), + readFuelTemplate.asToken(), + + "transparentScope:\n", + setFuelCost(4.0f), + transparentScope( + readFuelTemplate.asToken(), + setFuelCost(8.0f), + readFuelTemplate.asToken() + ), + readFuelTemplate.asToken(), + + "nameScope:\n", + setFuelCost(16.0f), + nameScope( + readFuelTemplate.asToken(), + setFuelCost(32.0f), + readFuelTemplate.asToken() + ), + readFuelTemplate.asToken(), + + "hashtagScope:\n", + setFuelCost(64.0f), + hashtagScope( + readFuelTemplate.asToken(), + setFuelCost(128.0f), + readFuelTemplate.asToken() + ), + readFuelTemplate.asToken(), + + "setFuelCostScope:\n", + setFuelCost(256.0f), + setFuelCostScope( + readFuelTemplate.asToken(), + setFuelCost(512.0f), + readFuelTemplate.asToken() + ), + readFuelTemplate.asToken() + )); + + String code = template.render(1000.0f); + String expected = + """ + {1000.0f} + <990.0f> + scope: + <999.0f> + <998.0f> + <999.0f> + transparentScope: + <996.0f> + <992.0f> + <992.0f> + nameScope: + <984.0f> + <968.0f> + <968.0f> + hashtagScope: + <936.0f> + <872.0f> + <872.0f> + setFuelCostScope: + <744.0f> + <488.0f> + <744.0f> + """; + checkEQ(code, expected); + } + + public static void testDataNames0a() { + var template = Template.make(() -> scope( + // When a DataName is added, it is immediately available afterwards. + // This may seem trivial, but it requires that either both "add" and + // "sample" happen in lambda execution, or in token evaluation. + // Otherwise, one can float above the other, and lead to unintuitive + // behavior. + addDataName("x", myInt, MUTABLE), + dataNames(MUTABLE).exactOf(myInt).sampleAndLetAs("v"), + "sample: #v." + )); + + String code = template.render(); + checkEQ(code, "sample: x."); + } + + public static void testDataNames0b() { + // Test that the scope keeps local DataNames only for the scope, but that + // we can see DataNames of outer scopes. + var template = Template.make(() -> scope( + // Outer scope DataName: + addDataName("outerInt", myInt, MUTABLE), + dataNames(MUTABLE).exactOf(myInt).sample((DataName dn) -> scope( + let("name1", dn.name()), + "sample: #name1.\n", + // We can also see the outer DataName: + dataNames(MUTABLE).exactOf(myInt).sampleAndLetAs("name2"), + "sample: #name2.\n", + // Local DataName: + addDataName("innerLong", myLong, MUTABLE), + dataNames(MUTABLE).exactOf(myLong).sampleAndLetAs("name3"), + "sample: #name3.\n" + )), + // We can still see the outer scope DataName: + dataNames(MUTABLE).exactOf(myInt).sampleAndLetAs("name4"), + "sample: #name4.\n", + // But we cannot see the DataNames that are local to the inner scope. + // So here, we will always see "outerLong", and never "innerLong". + addDataName("outerLong", myLong, MUTABLE), + dataNames(MUTABLE).exactOf(myLong).sampleAndLetAs("name5"), + "sample: #name5.\n" + )); + + String code = template.render(); + String expected = + """ + sample: outerInt. + sample: outerInt. + sample: innerLong. + sample: outerInt. + sample: outerLong. + """; + checkEQ(code, expected); + } + + public static void testDataNames0c() { + // Test that hashtag replacements that are local to inner scopes are + // only visible to inner scopes, but dollar replacements are the same + // for the whole Template. + var template = Template.make(() -> scope( + let("global", "GLOBAL"), + "g: #global. $a\n", + // Create a dummy DataName so we don't get an exception from sample. + addDataName("x", myInt, MUTABLE), + dataNames(MUTABLE).exactOf(myInt).sample((DataName dn) -> scope( + "g: #global. $b\n", + let("local", "LOCAL1"), + "l: #local. $c\n" + )), + "g: #global. $d\n", + // Open the scope again just to see if we can create the local again there. + dataNames(MUTABLE).exactOf(myInt).sample((DataName dn) -> scope( + "g: #global. $e\n", + let("local", "LOCAL2"), + "l: #local. $f\n" + )), + // We can now use the "local" hashtag replacement again, since it + // was previously only defined in an inner scope. + let("local", "LOCAL3"), + "g: #global. $g\n", + "l: #local. $h\n" + )); + + String code = template.render(); + String expected = + """ + g: GLOBAL. a_1 + g: GLOBAL. b_1 + l: LOCAL1. c_1 + g: GLOBAL. d_1 + g: GLOBAL. e_1 + l: LOCAL2. f_1 + g: GLOBAL. g_1 + l: LOCAL3. h_1 + """; + checkEQ(code, expected); + } + + public static void testDataNames0d() { + var template = Template.make(() -> scope( + addDataName("x", myInt, MUTABLE), + addDataName("y", myInt, MUTABLE), + addDataName("z", myInt, MUTABLE), + addDataName("a", myLong, MUTABLE), + addDataName("b", myLong, MUTABLE), + addDataName("c", myLong, MUTABLE), + dataNames(MUTABLE).exactOf(myInt).forEach((DataName dn) -> scope( + let("name", dn.name()), + let("type", dn.type()), + "listI: #name #type.\n" + )), + dataNames(MUTABLE).exactOf(myLong).forEach((DataName dn) -> scope( + let("name", dn.name()), + let("type", dn.type()), + "listL: #name #type.\n" + )) + )); + + String code = template.render(); + String expected = + """ + listI: x int. + listI: y int. + listI: z int. + listL: a long. + listL: b long. + listL: c long. + """; + checkEQ(code, expected); + } public static void testDataNames1() { var hook1 = new Hook("Hook1"); - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "[", - dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).hasAny(), + dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).hasAny(h -> scope(h)), ", ", - dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).count(), + dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).count(c -> scope(c)), ", names: {", - String.join(", ", dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).toList().stream().map(DataName::name).toList()), + dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).toList(list -> scope( + String.join(", ", list.stream().map(DataName::name).toList()) + )), "}]\n" )); - var template2 = Template.make("name", "type", (String name, DataName.Type type) -> body( - addDataName(name, type, MUTABLE), + // Note: the scope of the template must be transparentScope, so that the addDataName can escape. + var template2 = Template.make("name", "type", (String name, DataName.Type type) -> transparentScope( + addDataName(name, type, MUTABLE), // escapes "define #type #name\n", template1.asToken() )); - var template3 = Template.make(() -> body( + var template3 = Template.make(() -> scope( "<\n", hook1.insert(template2.asToken($("name"), myInt)), "$name = 5\n", ">\n" )); - var template4 = Template.make(() -> body( + var template4 = Template.make(() -> scope( "{\n", template1.asToken(), - hook1.anchor( + hook1.anchor(scope( template1.asToken(), "something\n", - template3.asToken(), + template3.asToken(), // name_4 is inserted to hook1 "more\n", template1.asToken(), "more\n", - template2.asToken($("name"), myInt), + template2.asToken($("name"), myInt), // name_1 escapes "more\n", + template1.asToken(), + "extra\n", + hook1.insert(scope( + addDataName($("extra1"), myInt, MUTABLE), // does not escape + "$extra1 = 666\n" + )), + hook1.insert(transparentScope( + addDataName($("extra2"), myInt, MUTABLE), // escapes + "$extra2 = 42\n" + )), template1.asToken() - ), + )), + // But no names escape to down here, because the anchor scope is "scope". + "final:\n", template1.asToken(), "}\n" )); @@ -1053,6 +1424,8 @@ public static void testDataNames1() { [false, 0, names: {}] define int name_4 [true, 1, names: {name_4}] + extra1_1 = 666 + extra2_1 = 42 [false, 0, names: {}] something < @@ -1064,7 +1437,10 @@ public static void testDataNames1() { define int name_1 [true, 2, names: {name_4, name_1}] more - [true, 1, names: {name_4}] + [true, 2, names: {name_4, name_1}] + extra + [true, 3, names: {name_4, extra2_1, name_1}] + final: [false, 0, names: {}] } """; @@ -1074,17 +1450,19 @@ public static void testDataNames1() { public static void testDataNames2() { var hook1 = new Hook("Hook1"); - var template0 = Template.make("type", "mutability", (DataName.Type type, DataName.Mutability mutability) -> body( + var template0 = Template.make("type", "mutability", (DataName.Type type, DataName.Mutability mutability) -> scope( " #mutability: [", - dataNames(mutability).exactOf(myInt).hasAny(), + dataNames(mutability).exactOf(myInt).hasAny(h -> scope(h)), ", ", - dataNames(mutability).exactOf(myInt).count(), + dataNames(mutability).exactOf(myInt).count(c -> scope(c)), ", names: {", - String.join(", ", dataNames(mutability).exactOf(myInt).toList().stream().map(DataName::name).toList()), + dataNames(mutability).exactOf(myInt).toList(list -> scope( + String.join(", ", list.stream().map(DataName::name).toList()) + )), "}]\n" )); - var template1 = Template.make("type", (DataName.Type type) -> body( + var template1 = Template.make("type", (DataName.Type type) -> scope( "[#type:\n", template0.asToken(type, MUTABLE), template0.asToken(type, IMMUTABLE), @@ -1092,51 +1470,51 @@ public static void testDataNames2() { "]\n" )); - var template2 = Template.make("name", "type", (String name, DataName.Type type) -> body( - addDataName(name, type, MUTABLE), + var template2 = Template.make("name", "type", (String name, DataName.Type type) -> transparentScope( + addDataName(name, type, MUTABLE), // escapes "define mutable #type #name\n", template1.asToken(type) )); - var template3 = Template.make("name", "type", (String name, DataName.Type type) -> body( - addDataName(name, type, IMMUTABLE), + var template3 = Template.make("name", "type", (String name, DataName.Type type) -> transparentScope( + addDataName(name, type, IMMUTABLE), // escapes "define immutable #type #name\n", template1.asToken(type) )); - var template4 = Template.make("type", (DataName.Type type) -> body( + var template4 = Template.make("type", (DataName.Type type) -> scope( "{ $store\n", hook1.insert(template2.asToken($("name"), type)), "$name = 5\n", "} $store\n" )); - var template5 = Template.make("type", (DataName.Type type) -> body( + var template5 = Template.make("type", (DataName.Type type) -> scope( "{ $load\n", hook1.insert(template3.asToken($("name"), type)), "blackhole($name)\n", "} $load\n" )); - var template6 = Template.make("type", (DataName.Type type) -> body( - let("v", dataNames(MUTABLE).exactOf(type).sample().name()), + var template6 = Template.make("type", (DataName.Type type) -> scope( + dataNames(MUTABLE).exactOf(type).sampleAndLetAs("v"), "{ $sample\n", "#v = 7\n", "} $sample\n" )); - var template7 = Template.make("type", (DataName.Type type) -> body( - let("v", dataNames(MUTABLE_OR_IMMUTABLE).exactOf(type).sample().name()), + var template7 = Template.make("type", (DataName.Type type) -> scope( + dataNames(MUTABLE_OR_IMMUTABLE).exactOf(type).sampleAndLetAs("v"), "{ $sample\n", "blackhole(#v)\n", "} $sample\n" )); - var template8 = Template.make(() -> body( + var template8 = Template.make(() -> scope( "class $X {\n", template1.asToken(myInt), - hook1.anchor( - "begin $body\n", + hook1.anchor(scope( + "begin $scope\n", template1.asToken(myInt), "start with immutable\n", template5.asToken(myInt), @@ -1148,7 +1526,7 @@ public static void testDataNames2() { "then store to it\n", template6.asToken(myInt), template1.asToken(myInt) - ), + )), template1.asToken(myInt), "}\n" )); @@ -1174,7 +1552,7 @@ class X_1 { IMMUTABLE: [true, 1, names: {name_10}] MUTABLE_OR_IMMUTABLE: [true, 2, names: {name_10, name_21}] ] - begin body_1 + begin scope_1 [int: MUTABLE: [false, 0, names: {}] IMMUTABLE: [false, 0, names: {}] @@ -1219,17 +1597,19 @@ class X_1 { public static void testDataNames3() { var hook1 = new Hook("Hook1"); - var template0 = Template.make("type", "mutability", (DataName.Type type, DataName.Mutability mutability) -> body( + var template0 = Template.make("type", "mutability", (DataName.Type type, DataName.Mutability mutability) -> scope( " #mutability: [", - dataNames(mutability).exactOf(myInt).hasAny(), + dataNames(mutability).exactOf(myInt).hasAny(h -> scope(h)), ", ", - dataNames(mutability).exactOf(myInt).count(), + dataNames(mutability).exactOf(myInt).count(c -> scope(c)), ", names: {", - String.join(", ", dataNames(mutability).exactOf(myInt).toList().stream().map(DataName::name).toList()), + dataNames(mutability).exactOf(myInt).toList(list -> scope( + String.join(", ", list.stream().map(DataName::name).toList()) + )), "}]\n" )); - var template1 = Template.make("type", (DataName.Type type) -> body( + var template1 = Template.make("type", (DataName.Type type) -> scope( "[#type:\n", template0.asToken(type, MUTABLE), template0.asToken(type, IMMUTABLE), @@ -1237,11 +1617,11 @@ public static void testDataNames3() { "]\n" )); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "class $Y {\n", template1.asToken(myInt), - hook1.anchor( - "begin $body\n", + hook1.anchor(scope( + "begin $scope\n", template1.asToken(myInt), "define mutable $v1\n", addDataName($("v1"), myInt, MUTABLE), @@ -1249,7 +1629,7 @@ public static void testDataNames3() { "define immutable $v2\n", addDataName($("v2"), myInt, IMMUTABLE), template1.asToken(myInt) - ), + )), template1.asToken(myInt), "}\n" )); @@ -1263,7 +1643,7 @@ class Y_1 { IMMUTABLE: [false, 0, names: {}] MUTABLE_OR_IMMUTABLE: [false, 0, names: {}] ] - begin body_1 + begin scope_1 [int: MUTABLE: [false, 0, names: {}] IMMUTABLE: [false, 0, names: {}] @@ -1294,47 +1674,62 @@ class Y_1 { public static void testDataNames4() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("type", (DataName.Type type) -> body( + var template1 = Template.make("type", (DataName.Type type) -> scope( "[#type:\n", " exact: ", - dataNames(MUTABLE).exactOf(type).hasAny(), + dataNames(MUTABLE).exactOf(type).hasAny(h -> scope(h)), ", ", - dataNames(MUTABLE).exactOf(type).count(), + dataNames(MUTABLE).exactOf(type).count(c -> scope(c)), ", {", - String.join(", ", dataNames(MUTABLE).exactOf(type).toList().stream().map(DataName::name).toList()), + dataNames(MUTABLE).exactOf(type).toList(list -> scope( + String.join(", ", list.stream().map(DataName::name).toList()) + )), "}\n", " subtype: ", - dataNames(MUTABLE).subtypeOf(type).hasAny(), + dataNames(MUTABLE).subtypeOf(type).hasAny(h -> scope(h)), ", ", - dataNames(MUTABLE).subtypeOf(type).count(), + dataNames(MUTABLE).subtypeOf(type).count(c -> scope(c)), ", {", - String.join(", ", dataNames(MUTABLE).subtypeOf(type).toList().stream().map(DataName::name).toList()), + dataNames(MUTABLE).subtypeOf(type).toList(list -> scope( + String.join(", ", list.stream().map(DataName::name).toList()) + )), + "}\n", " supertype: ", - dataNames(MUTABLE).supertypeOf(type).hasAny(), + dataNames(MUTABLE).supertypeOf(type).hasAny(h -> scope(h)), ", ", - dataNames(MUTABLE).supertypeOf(type).count(), + dataNames(MUTABLE).supertypeOf(type).count(c -> scope(c)), ", {", - String.join(", ", dataNames(MUTABLE).supertypeOf(type).toList().stream().map(DataName::name).toList()), + dataNames(MUTABLE).supertypeOf(type).toList(list -> scope( + String.join(", ", list.stream().map(DataName::name).toList()) + )), "}\n", "]\n" )); List types = List.of(myClassA, myClassA1, myClassA2, myClassA11, myClassB); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "DataNames:\n", types.stream().map(t -> template1.asToken(t)).toList() )); - var template3 = Template.make("type", (DataName.Type type) -> body( - let("name", dataNames(MUTABLE).subtypeOf(type).sample()), - "Sample #type: #name\n" + var template3 = Template.make("type", (DataName.Type type) -> scope( + dataNames(MUTABLE).subtypeOf(type).sampleAndLetAs("name1"), + "Sample #type: #name1\n", + dataNames(MUTABLE).subtypeOf(type).sampleAndLetAs("name2", "type2"), + "Sample #type: #name2 #type2\n", + dataNames(MUTABLE).subtypeOf(type).sample((DataName dn) -> scope( + let("name3", dn.name()), + let("type3", dn.type()), + let("dn", dn), // format the whole DataName with toString + "Sample #type: #name3 #type3 #dn\n" + )) )); - var template4 = Template.make(() -> body( + var template4 = Template.make(() -> scope( "class $W {\n", template2.asToken(), - hook1.anchor( + hook1.anchor(scope( "Create name for myClassA11, should be visible for the super classes\n", addDataName($("v1"), myClassA11, MUTABLE), template3.asToken(myClassA11), @@ -1345,7 +1740,7 @@ public static void testDataNames4() { template3.asToken(myClassA11), template3.asToken(myClassA1), template2.asToken() - ), + )), template2.asToken(), "}\n" )); @@ -1381,12 +1776,22 @@ class W_1 { supertype: false, 0, {} ] Create name for myClassA11, should be visible for the super classes - Sample myClassA11: DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] - Sample myClassA1: DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] - Sample myClassA: DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] + Sample myClassA11: v1_1 + Sample myClassA11: v1_1 myClassA11 + Sample myClassA11: v1_1 myClassA11 DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] + Sample myClassA1: v1_1 + Sample myClassA1: v1_1 myClassA11 + Sample myClassA1: v1_1 myClassA11 DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] + Sample myClassA: v1_1 + Sample myClassA: v1_1 myClassA11 + Sample myClassA: v1_1 myClassA11 DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] Create name for myClassA, should never be visible for the sub classes - Sample myClassA11: DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] - Sample myClassA1: DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] + Sample myClassA11: v1_1 + Sample myClassA11: v1_1 myClassA11 + Sample myClassA11: v1_1 myClassA11 DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] + Sample myClassA1: v1_1 + Sample myClassA1: v1_1 myClassA11 + Sample myClassA1: v1_1 myClassA11 DataName[name=v1_1, type=myClassA11, mutable=true, weight=1] DataNames: [myClassA: exact: true, 1, {v2_1} @@ -1449,49 +1854,61 @@ public static void testDataNames5() { var hook1 = new Hook("Hook1"); var hook2 = new Hook("Hook2"); - // It is safe in separate Hook scopes. - var template1 = Template.make(() -> body( - hook1.anchor( + // It is safe in separate scopes. + var template1 = Template.make(() -> scope( + scope( + addDataName("name1", myInt, MUTABLE) + ), + scope( + addDataName("name1", myInt, MUTABLE) + ), + nameScope( + addDataName("name1", myInt, MUTABLE) + ), + nameScope( addDataName("name1", myInt, MUTABLE) ), - hook1.anchor( + hook1.anchor(scope( addDataName("name1", myInt, MUTABLE) - ) + )), + hook1.anchor(scope( + addDataName("name1", myInt, MUTABLE) + )) )); // It is safe in separate Template scopes. - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( addDataName("name2", myInt, MUTABLE) )); - var template3 = Template.make(() -> body( + var template3 = Template.make(() -> scope( template2.asToken(), template2.asToken() )); - var template4 = Template.make(() -> body( + var template4 = Template.make(() -> scope( // The following is not safe, it would collide // with (1), because it would be inserted to the // hook1.anchor in template5, and hence be available // inside the scope where (1) is available. // See: testFailingAddNameDuplication8 // addDataName("name", myInt, MUTABLE), - hook2.anchor( + hook2.anchor(scope( // (2) This one is added second. Since it is // inside the hook2.anchor, it does not go // out to the hook1.anchor, and is not // available inside the scope of (1). addDataName("name3", myInt, MUTABLE) - ) + )) )); - var template5 = Template.make(() -> body( - hook1.anchor( + var template5 = Template.make(() -> scope( + hook1.anchor(scope( // (1) this is the first one we add. addDataName("name3", myInt, MUTABLE) - ) + )) )); // Put it all together into a single test. - var template6 = Template.make(() -> body( + var template6 = Template.make(() -> scope( template1.asToken(), template3.asToken(), template5.asToken() @@ -1502,31 +1919,128 @@ public static void testDataNames5() { checkEQ(code, expected); } + public static void testDataNames6() { + var template = Template.make(() -> scope( + addDataName("x", myInt, IMMUTABLE), + "int x = 5;\n", + // A DataName can be captured, and used to define a new one with the same type. + // It is important that the new DataName can escape the hashtagScope, so we have + // access to it later. + // Using "scope", it does not escape. + dataNames(IMMUTABLE).exactOf(myInt).sample(dn -> scope( + addDataName("a", dn.type(), MUTABLE), + let("v1", "a"), + "int #v1 = x + 1;\n" + )), + // Using "transparentScope", it is available. + dataNames(IMMUTABLE).exactOf(myInt).sample(dn -> transparentScope( + addDataName("b", dn.type(), MUTABLE), + let("v2", "b"), + "int #v2 = x + 2;\n" + )), + // Using "nameScope", it does not escape. + dataNames(IMMUTABLE).exactOf(myInt).sample(dn -> nameScope( + addDataName("c", dn.type(), MUTABLE), + let("v3", "c"), + "int #v3 = x + 3;\n" + )), + // Using "hashtagScope", it is available. + dataNames(IMMUTABLE).exactOf(myInt).sample(dn -> hashtagScope( + addDataName("d", dn.type(), MUTABLE), + let("v4", "d"), + "int #v4 = x + 4;\n" + )), + // Using "setFuelCostScope", it is available. + dataNames(IMMUTABLE).exactOf(myInt).sample(dn -> setFuelCostScope( + addDataName("e", dn.type(), MUTABLE), + let("v5", "e"), + "int #v5 = x + 5;\n" + )), + dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).forEach("name", "type", dn -> scope( + "available1: #name #type.\n" + )), + dataNames(MUTABLE_OR_IMMUTABLE).exactOf(myInt).forEach("name", "type", dn -> hashtagScope( + "available2: #name #type.\n" + )), + // Check that hashtags escape correctly too. + "hashtag v2: #v2.\n", + "hashtag v3: #v3.\n", + "hashtag v5: #v5.\n", + let("v1", "aaa"), + let("v4", "ddd") + )); + + String code = template.render(); + String expected = + """ + int x = 5; + int a = x + 1; + int b = x + 2; + int c = x + 3; + int d = x + 4; + int e = x + 5; + available1: x int. + available1: b int. + available1: d int. + available1: e int. + available2: x int. + available2: b int. + available2: d int. + available2: e int. + hashtag v2: b. + hashtag v3: c. + hashtag v5: e. + """; + checkEQ(code, expected); + } + + public static void testStructuralNames0() { + var template = Template.make(() -> scope( + // When a StructuralName is added, it is immediately available afterwards. + // This may seem trivial, but it requires that either both "add" and + // "sample" happen in lambda execution, or in token evaluation. + // Otherwise, one can float above the other, and lead to unintuitive + // behavior. + addStructuralName("x", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).sampleAndLetAs("v"), + "sample: #v." + )); + + String code = template.render(); + checkEQ(code, "sample: x."); + } + public static void testStructuralNames1() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("type", (StructuralName.Type type) -> body( + var template1 = Template.make("type", (StructuralName.Type type) -> scope( "[#type:\n", " exact: ", - structuralNames().exactOf(type).hasAny(), + structuralNames().exactOf(type).hasAny(h -> scope(h)), ", ", - structuralNames().exactOf(type).count(), + structuralNames().exactOf(type).count(c -> scope(c)), ", {", - String.join(", ", structuralNames().exactOf(type).toList().stream().map(StructuralName::name).toList()), + structuralNames().exactOf(type).toList(list -> scope( + String.join(", ", list.stream().map(StructuralName::name).toList()) + )), "}\n", " subtype: ", - structuralNames().subtypeOf(type).hasAny(), + structuralNames().subtypeOf(type).hasAny(h -> scope(h)), ", ", - structuralNames().subtypeOf(type).count(), + structuralNames().subtypeOf(type).count(c -> scope(c)), ", {", - String.join(", ", structuralNames().subtypeOf(type).toList().stream().map(StructuralName::name).toList()), + structuralNames().subtypeOf(type).toList(list -> scope( + String.join(", ", list.stream().map(StructuralName::name).toList()) + )), "}\n", " supertype: ", - structuralNames().supertypeOf(type).hasAny(), + structuralNames().supertypeOf(type).hasAny(h -> scope(h)), ", ", - structuralNames().supertypeOf(type).count(), + structuralNames().supertypeOf(type).count(c -> scope(c)), ", {", - String.join(", ", structuralNames().supertypeOf(type).toList().stream().map(StructuralName::name).toList()), + structuralNames().supertypeOf(type).toList(list -> scope( + String.join(", ", list.stream().map(StructuralName::name).toList()) + )), "}\n", "]\n" )); @@ -1536,20 +2050,28 @@ public static void testStructuralNames1() { myStructuralTypeA2, myStructuralTypeA11, myStructuralTypeB); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "StructuralNames:\n", types.stream().map(t -> template1.asToken(t)).toList() )); - var template3 = Template.make("type", (StructuralName.Type type) -> body( - let("name", structuralNames().subtypeOf(type).sample()), - "Sample #type: #name\n" + var template3 = Template.make("type", (StructuralName.Type type) -> scope( + structuralNames().subtypeOf(type).sampleAndLetAs("name1"), + "Sample #type: #name1\n", + structuralNames().subtypeOf(type).sampleAndLetAs("name2", "type2"), + "Sample #type: #name2 #type2\n", + structuralNames().subtypeOf(type).sample((StructuralName sn) -> scope( + let("name3", sn.name()), + let("type3", sn.type()), + let("sn", sn), // format the whole StructuralName with toString + "Sample #type: #name3 #type3 #sn\n" + )) )); - var template4 = Template.make(() -> body( + var template4 = Template.make(() -> scope( "class $Q {\n", template2.asToken(), - hook1.anchor( + hook1.anchor(scope( "Create name for myStructuralTypeA11, should be visible for the supertypes\n", addStructuralName($("v1"), myStructuralTypeA11), template3.asToken(myStructuralTypeA11), @@ -1560,7 +2082,7 @@ public static void testStructuralNames1() { template3.asToken(myStructuralTypeA11), template3.asToken(myStructuralTypeA1), template2.asToken() - ), + )), template2.asToken(), "}\n" )); @@ -1596,12 +2118,22 @@ class Q_1 { supertype: false, 0, {} ] Create name for myStructuralTypeA11, should be visible for the supertypes - Sample StructuralA11: StructuralName[name=v1_1, type=StructuralA11, weight=1] - Sample StructuralA1: StructuralName[name=v1_1, type=StructuralA11, weight=1] - Sample StructuralA: StructuralName[name=v1_1, type=StructuralA11, weight=1] + Sample StructuralA11: v1_1 + Sample StructuralA11: v1_1 StructuralA11 + Sample StructuralA11: v1_1 StructuralA11 StructuralName[name=v1_1, type=StructuralA11, weight=1] + Sample StructuralA1: v1_1 + Sample StructuralA1: v1_1 StructuralA11 + Sample StructuralA1: v1_1 StructuralA11 StructuralName[name=v1_1, type=StructuralA11, weight=1] + Sample StructuralA: v1_1 + Sample StructuralA: v1_1 StructuralA11 + Sample StructuralA: v1_1 StructuralA11 StructuralName[name=v1_1, type=StructuralA11, weight=1] Create name for myStructuralTypeA, should never be visible for the subtypes - Sample StructuralA11: StructuralName[name=v1_1, type=StructuralA11, weight=1] - Sample StructuralA1: StructuralName[name=v1_1, type=StructuralA11, weight=1] + Sample StructuralA11: v1_1 + Sample StructuralA11: v1_1 StructuralA11 + Sample StructuralA11: v1_1 StructuralA11 StructuralName[name=v1_1, type=StructuralA11, weight=1] + Sample StructuralA1: v1_1 + Sample StructuralA1: v1_1 StructuralA11 + Sample StructuralA1: v1_1 StructuralA11 StructuralName[name=v1_1, type=StructuralA11, weight=1] StructuralNames: [StructuralA: exact: true, 1, {v2_1} @@ -1662,41 +2194,43 @@ class Q_1 { public static void testStructuralNames2() { var hook1 = new Hook("Hook1"); - var template1 = Template.make("type", (StructuralName.Type type) -> body( + var template1 = Template.make("type", (StructuralName.Type type) -> scope( "[#type: ", - structuralNames().exactOf(type).hasAny(), + structuralNames().exactOf(type).hasAny(h -> scope(h)), ", ", - structuralNames().exactOf(type).count(), + structuralNames().exactOf(type).count(c -> scope(c)), ", names: {", - String.join(", ", structuralNames().exactOf(type).toList().stream().map(StructuralName::name).toList()), + structuralNames().exactOf(type).toList(list -> scope( + String.join(", ", list.stream().map(StructuralName::name).toList()) + )), "}]\n" )); - var template2 = Template.make("name", "type", (String name, StructuralName.Type type) -> body( - addStructuralName(name, type), + var template2 = Template.make("name", "type", (String name, StructuralName.Type type) -> transparentScope( + addStructuralName(name, type), // escapes "define #type #name\n" )); - var template3 = Template.make("type", (StructuralName.Type type) -> body( + var template3 = Template.make("type", (StructuralName.Type type) -> scope( "{ $access\n", hook1.insert(template2.asToken($("name"), type)), "$name = 5\n", "} $access\n" )); - var template4 = Template.make("type", (StructuralName.Type type) -> body( - let("v", structuralNames().exactOf(type).sample().name()), + var template4 = Template.make("type", (StructuralName.Type type) -> scope( + structuralNames().exactOf(type).sampleAndLetAs("v"), "{ $sample\n", "blackhole(#v)\n", "} $sample\n" )); - var template8 = Template.make(() -> body( + var template8 = Template.make(() -> scope( "class $X {\n", template1.asToken(myStructuralTypeA), template1.asToken(myStructuralTypeB), - hook1.anchor( - "begin $body\n", + hook1.anchor(scope( + "begin $scope\n", template1.asToken(myStructuralTypeA), template1.asToken(myStructuralTypeB), "start with A\n", @@ -1711,7 +2245,7 @@ public static void testStructuralNames2() { template4.asToken(myStructuralTypeB), template1.asToken(myStructuralTypeA), template1.asToken(myStructuralTypeB) - ), + )), template1.asToken(myStructuralTypeA), template1.asToken(myStructuralTypeB), "}\n" @@ -1725,7 +2259,7 @@ class X_1 { [StructuralB: false, 0, names: {}] define StructuralA name_6 define StructuralB name_11 - begin body_1 + begin scope_1 [StructuralA: false, 0, names: {}] [StructuralB: false, 0, names: {}] start with A @@ -1755,16 +2289,269 @@ class X_1 { checkEQ(code, expected); } + public static void testStructuralNames3() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> scope( + let("name1", sn.name()), + "sn1: #name1.\n", + addStructuralName("scope_garbage1", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> nameScope( + // We cannot use "let" here (at least not easily), otherwise we get + // a duplicate hashtag replacement. It would probably be better style + // to use a "let", but we are just checking that "nameScope" works + // for reuse of names. + "sn2: ", sn.name(), ".\n", + // But for testing, we still do a "let", just with different key. + // (This is probably bad practice, we just do this for testing) + let("name2_" + sn.name(), sn.name()), + addStructuralName("scope_garbage2", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> transparentScope( + // Same issue with hashtags as with "nameScope". + "sn3: ", sn.name(), ".\n", + let("name3_" + sn.name(), sn.name()), + // Using the same name for each would lead to duplicates, + // so we have to modify the name here. + addStructuralName("x_" + sn.name(), myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> hashtagScope( + let("name4", sn.name()), + "sn4: #name4.\n", + // Same issue with duplicate names as with "transparentScope". + addStructuralName("y_" + sn.name(), myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> setFuelCostScope( + // Same issue with hashtags as with "nameScope". + "sn5: ", sn.name(), ".\n", + let("name5_" + sn.name(), sn.name()), + // Same issue with duplicate names as with "transparentScope". + addStructuralName("z_" + sn.name(), myStructuralTypeA) + )), + "sn2: #name2_a #name2_b.\n", // hashtags escaped + "sn3: #name3_a #name3_b.\n", // hashtags escaped + "sn5: #name5_a #name5_b #name5_x_a #name5_x_b.\n", // hashtags escaped + let("name1", "shouldBeOK1"), // hashtag did not escape + let("name4", "shouldBeOk4") // hashtag did not escape + )); + + String code = template.render(); + String expected = + """ + sn1: a. + sn1: b. + sn2: a. + sn2: b. + sn3: a. + sn3: b. + sn4: a. + sn4: b. + sn4: x_a. + sn4: x_b. + sn5: a. + sn5: b. + sn5: x_a. + sn5: x_b. + sn5: y_a. + sn5: y_b. + sn5: y_x_a. + sn5: y_x_b. + sn2: a b. + sn3: a b. + sn5: a b x_a x_b. + """; + checkEQ(code, expected); + } + + public static void testStructuralNames4() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).toList(list -> scope( + let("name1", list.size()), + "list1: #name1.\n", + addStructuralName("scope_garbage1", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).toList(list -> nameScope( + let("name2", list.size()), + "list2: #name2.\n", + addStructuralName("scope_garbage2", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).toList(list -> transparentScope( + let("name3", list.size()), + "list3: #name3.\n", + addStructuralName("x", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).toList(list -> hashtagScope( + let("name4", list.size()), + "list4: #name4.\n", + addStructuralName("y", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).toList(list -> setFuelCostScope( + let("name5", list.size()), + "list5: #name5.\n", + addStructuralName("z", myStructuralTypeA) + )), + "list2: #name2.\n", // hashtag escaped + "list3: #name3.\n", // hashtag escaped + "list5: #name5.\n", // hashtag escaped + let("name1", "shouldBeOk4"), // hashtag did not escape + let("name4", "shouldBeOk4"), // hashtag did not escape + structuralNames().exactOf(myStructuralTypeA).forEach("name", "type", sn -> scope( + "available: #name #type.\n" + )) + )); + + String code = template.render(); + String expected = + """ + list1: 2. + list2: 2. + list3: 2. + list4: 3. + list5: 4. + list2: 2. + list3: 2. + list5: 4. + available: a StructuralA. + available: b StructuralA. + available: x StructuralA. + available: y StructuralA. + available: z StructuralA. + """; + checkEQ(code, expected); + } + + public static void testStructuralNames5() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).count(c -> scope( + let("name1", c), + "list1: #name1.\n", + addStructuralName("scope_garbage1", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).count(c -> nameScope( + let("name2", c), + "list2: #name2.\n", + addStructuralName("scope_garbage2", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).count(c -> transparentScope( + let("name3", c), + "list3: #name3.\n", + addStructuralName("x", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).count(c -> hashtagScope( + let("name4", c), + "list4: #name4.\n", + addStructuralName("y", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).count(c -> setFuelCostScope( + let("name5", c), + "list5: #name5.\n", + addStructuralName("z", myStructuralTypeA) + )), + "list2: #name2.\n", // hashtag escaped + "list3: #name3.\n", // hashtag escaped + "list5: #name5.\n", // hashtag escaped + let("name1", "shouldBeOk4"), // hashtag did not escape + let("name4", "shouldBeOk4"), // hashtag did not escape + structuralNames().exactOf(myStructuralTypeA).forEach("name", "type", sn -> scope( + "available: #name #type.\n" + )) + )); + + String code = template.render(); + String expected = + """ + list1: 2. + list2: 2. + list3: 2. + list4: 3. + list5: 4. + list2: 2. + list3: 2. + list5: 4. + available: a StructuralA. + available: b StructuralA. + available: x StructuralA. + available: y StructuralA. + available: z StructuralA. + """; + checkEQ(code, expected); + } + + public static void testStructuralNames6() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).hasAny(h -> scope( + let("name1", h), + "list1: #name1.\n", + addStructuralName("scope_garbage1", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).hasAny(h -> nameScope( + let("name2", h), + "list2: #name2.\n", + addStructuralName("scope_garbage2", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).hasAny(h -> transparentScope( + let("name3", h), + "list3: #name3.\n", + addStructuralName("x", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).hasAny(h -> hashtagScope( + let("name4", h), + "list4: #name4.\n", + addStructuralName("y", myStructuralTypeA) + )), + structuralNames().exactOf(myStructuralTypeA).hasAny(h -> setFuelCostScope( + let("name5", h), + "list5: #name5.\n", + addStructuralName("z", myStructuralTypeA) + )), + "list2: #name2.\n", // hashtag escaped + "list3: #name3.\n", // hashtag escaped + "list5: #name5.\n", // hashtag escaped + let("name1", "shouldBeOk4"), // hashtag did not escape + let("name4", "shouldBeOk4"), // hashtag did not escape + structuralNames().exactOf(myStructuralTypeA).forEach("name", "type", sn -> scope( + "available: #name #type.\n" + )) + )); + + String code = template.render(); + String expected = + """ + list1: true. + list2: true. + list3: true. + list4: true. + list5: true. + list2: true. + list3: true. + list5: true. + available: a StructuralA. + available: b StructuralA. + available: x StructuralA. + available: y StructuralA. + available: z StructuralA. + """; + checkEQ(code, expected); + } + record MyItem(DataName.Type type, String op) {} public static void testListArgument() { - var template1 = Template.make("item", (MyItem item) -> body( + var template1 = Template.make("item", (MyItem item) -> scope( let("type", item.type()), let("op", item.op()), "#type apply #op\n" )); - var template2 = Template.make("list", (List list) -> body( + var template2 = Template.make("list", (List list) -> scope( "class $Z {\n", // Use template1 for every item in the list. list.stream().map(item -> template1.asToken(item)).toList(), @@ -1797,12 +2584,746 @@ class Z_1 { checkEQ(code, expected); } + public static void testNestedScopes1() { + var listDataNames = Template.make(() -> scope( + "dataNames: {", + dataNames(MUTABLE).exactOf(myInt).forEach("name", "type", (DataName dn) -> scope( + "#name #type; " + )), + "}\n" + )); + + var template = Template.make("x", (String x) -> scope( + "$start\n", + addDataName("vx", myInt, MUTABLE), + "x: #x.\n", + listDataNames.asToken(), + // A "transparentScope" nesting essencially does nothing but create + // a list of tokens. It passes through names and hashtags. + "open transparentScope:\n", + transparentScope( + "$transparentScope\n", + let("y", "YYY"), + addDataName("vy", myInt, MUTABLE), + "x: #x.\n", + "y: #y.\n", + listDataNames.asToken() + ), + "close transparentScope.\n", + "x: #x.\n", + "y: #y.\n", + listDataNames.asToken(), + // A "hashtagScope" nesting makes hashtags local, but names + // escape the nesting. + "open hashtagScope:\n", + hashtagScope( + "$hashtagScope\n", + let("z", "ZZZ1"), + "z: #z.\n", + addDataName("vz", myInt, MUTABLE), + listDataNames.asToken() + ), + "close hashtagScope.\n", + let("z", "ZZZ2"), // we can define it again outside. + "z: #z.\n", + listDataNames.asToken(), + // We can also use hashtagScopes for loops. + List.of("a", "b", "c").stream().map(str -> hashtagScope( + "$hashtagScope\n", + let("str", str), // the hashtag is local to every element + "str: #str.\n", + addDataName("v_" + str, myInt, MUTABLE), + listDataNames.asToken() + )).toList(), + "finish str list.\n", + listDataNames.asToken(), + // A "nameScope" nesting makes names local, but hashtags + // escape the nesting. + "open nameScope:\n", + nameScope( + "$nameScope\n", + let("p", "PPP"), + "p: #p.\n", + addDataName("vp", myInt, MUTABLE), + listDataNames.asToken() + ), + "close hashtagScope.\n", + "p: #p.\n", + listDataNames.asToken(), + // A "scope" nesting makes names and hashtags local + "open scope:\n", + scope( + "$scope\n", + let("q", "QQQ1"), + "q: #q.\n", + addDataName("vq", myInt, MUTABLE), + listDataNames.asToken() + ), + "close scope.\n", + let("q", "QQQ2"), + "q: #q.\n", + listDataNames.asToken(), + // A "setFuelCostScope" nesting behaves the same as "transparentScope", as we are not using fuel here. + "open setFuelCostScope:\n", + setFuelCostScope( + "$setFuelCostScope\n", + let("r", "RRR"), + "r: #r.\n", + addDataName("vr", myInt, MUTABLE), + listDataNames.asToken() + ), + "close setFuelCostScope.\n", + "r: #r.\n", + listDataNames.asToken() + + )); + + String code = template.render("XXX"); + String expected = + """ + start_1 + x: XXX. + dataNames: {vx int; } + open transparentScope: + transparentScope_1 + x: XXX. + y: YYY. + dataNames: {vx int; vy int; } + close transparentScope. + x: XXX. + y: YYY. + dataNames: {vx int; vy int; } + open hashtagScope: + hashtagScope_1 + z: ZZZ1. + dataNames: {vx int; vy int; vz int; } + close hashtagScope. + z: ZZZ2. + dataNames: {vx int; vy int; vz int; } + hashtagScope_1 + str: a. + dataNames: {vx int; vy int; vz int; v_a int; } + hashtagScope_1 + str: b. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; } + hashtagScope_1 + str: c. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; v_c int; } + finish str list. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; v_c int; } + open nameScope: + nameScope_1 + p: PPP. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; v_c int; vp int; } + close hashtagScope. + p: PPP. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; v_c int; } + open scope: + scope_1 + q: QQQ1. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; v_c int; vq int; } + close scope. + q: QQQ2. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; v_c int; } + open setFuelCostScope: + setFuelCostScope_1 + r: RRR. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; v_c int; vr int; } + close setFuelCostScope. + r: RRR. + dataNames: {vx int; vy int; vz int; v_a int; v_b int; v_c int; vr int; } + """; + checkEQ(code, expected); + } + + public static void testNestedScopes2() { + var listDataNames = Template.make(() -> scope( + "dataNames: {", + dataNames(MUTABLE).exactOf(myInt).forEach("name", "type", (DataName dn) -> scope( + "#name #type; " + )), + "}\n" + )); + + var template = Template.make(() -> scope( + // Define some global variables. + List.of("a", "b", "c").stream().map(str -> hashtagScope( + let("var", "g_" + str), + addDataName("g_" + str, myInt, MUTABLE), + "def global #var.\n" + )).toList(), + listDataNames.asToken(), + scope( + "open scope:\n", + // Define some variables. + List.of("i", "j", "k").stream().map(str -> hashtagScope( + let("var", "v_" + str), + addDataName("v_" + str, myInt, MUTABLE), + "def #var.\n" + )).toList(), + listDataNames.asToken(), + scope( + "open inner scope:\n", + addDataName("v_local", myInt, MUTABLE), + "def v_local.\n", + listDataNames.asToken(), + "close inner scope.\n" + ), + listDataNames.asToken(), + "close scope.\n" + ), + listDataNames.asToken() + )); + + String code = template.render(); + String expected = + """ + def global g_a. + def global g_b. + def global g_c. + dataNames: {g_a int; g_b int; g_c int; } + open scope: + def v_i. + def v_j. + def v_k. + dataNames: {g_a int; g_b int; g_c int; v_i int; v_j int; v_k int; } + open inner scope: + def v_local. + dataNames: {g_a int; g_b int; g_c int; v_i int; v_j int; v_k int; v_local int; } + close inner scope. + dataNames: {g_a int; g_b int; g_c int; v_i int; v_j int; v_k int; } + close scope. + dataNames: {g_a int; g_b int; g_c int; } + """; + checkEQ(code, expected); + } + + public static void testTemplateScopes() { + var statusTemplate = Template.make(() -> scope( + "{", + structuralNames().exactOf(myStructuralTypeA).toList(list -> scope( + String.join(", ", list.stream().map(StructuralName::name).toList()) + )), + "}\n", + let("fuel", fuel()), + "fuel: #fuel\n" + )); + + var scopeTemplate = Template.make(() -> scope( + "scope:\n", + let("local", "inner scope"), + addStructuralName("x", myStructuralTypeA), + statusTemplate.asToken(), + setFuelCost(50) + )); + + var transparentScopeTemplate = Template.make(() -> transparentScope( + "transparentScope:\n", + let("local", "inner flag"), + addStructuralName("y", myStructuralTypeA), // should escape + statusTemplate.asToken(), + setFuelCost(50) + )); + + var template = Template.make(() -> scope( + setFuelCost(1), + let("local", "root"), + addStructuralName("a", myStructuralTypeA), + statusTemplate.asToken(), + scopeTemplate.asToken(), + statusTemplate.asToken(), + transparentScopeTemplate.asToken(), + statusTemplate.asToken() + )); + + String code = template.render(); + String expected = + """ + {a} + fuel: 99.0f + scope: + {a, x} + fuel: 89.0f + {a} + fuel: 99.0f + transparentScope: + {a, y} + fuel: 89.0f + {a, y} + fuel: 99.0f + """; + checkEQ(code, expected); + } + + public static void testHookAndScopes1() { + Hook hook1 = new Hook("Hook1"); + + var listNamesTemplate = Template.make(() -> scope( + "{", + structuralNames().exactOf(myStructuralTypeA).toList(list -> scope( + String.join(", ", list.stream().map(StructuralName::name).toList()) + )), + "}\n" + )); + + var insertScopeTemplate = Template.make("name", (String name) -> scope( + let("local", "insert scope garbage"), + addStructuralName(name, myStructuralTypeA), + "inserted scope: #name\n", + listNamesTemplate.asToken() + )); + + var insertTransparentScopeTemplate = Template.make("name", (String name) -> transparentScope( + let("local", "insert transparentScope garbage"), + addStructuralName(name, myStructuralTypeA), + "inserted transparentScope: #name\n", + listNamesTemplate.asToken() + )); + + var probeTemplate = Template.make(() -> scope( + "inserted probe:\n", + listNamesTemplate.asToken() + )); + + var template = Template.make(() -> scope( + "scope:\n", + hook1.anchor(scope( + let("local", "scope garbage"), + addStructuralName("x1a", myStructuralTypeA), + "scope before insert scope:\n", + listNamesTemplate.asToken(), + hook1.insert(insertScopeTemplate.asToken("x1b")), + "scope after insert scope:\n", + listNamesTemplate.asToken(), + "scope before insert transparentScope:\n", + listNamesTemplate.asToken(), + hook1.insert(insertTransparentScopeTemplate.asToken("x1c")), + "scope after insert transparentScope:\n", + listNamesTemplate.asToken(), + "scope insert probe.\n", + hook1.insert(probeTemplate.asToken()) + )), + "after scope:\n", + listNamesTemplate.asToken(), + + "transparentScope:\n", + hook1.anchor(transparentScope( + let("transparentScope2", "abc"), + addStructuralName("x2a", myStructuralTypeA), + "transparentScope before insert scope:\n", + listNamesTemplate.asToken(), + hook1.insert(insertScopeTemplate.asToken("x2b")), + "transparentScope after insert scope:\n", + listNamesTemplate.asToken(), + "transparentScope before insert transparentScope:\n", + listNamesTemplate.asToken(), + hook1.insert(insertTransparentScopeTemplate.asToken("x2c")), + "transparentScope after insert transparentScope:\n", + listNamesTemplate.asToken(), + "transparentScope insert probe.\n", + hook1.insert(probeTemplate.asToken()) + )), + "after transparentScope:\n", + listNamesTemplate.asToken(), + "transparentScope2: #transparentScope2\n", + + "hashtagScope:\n", + hook1.anchor(hashtagScope( + let("local", "hashtagScope garbage"), + addStructuralName("x3a", myStructuralTypeA), + "hashtagScope before insert scope:\n", + listNamesTemplate.asToken(), + hook1.insert(insertScopeTemplate.asToken("x3b")), + "hashtagScope after insert scope:\n", + listNamesTemplate.asToken(), + "hashtagScope before insert transparentScope:\n", + listNamesTemplate.asToken(), + hook1.insert(insertTransparentScopeTemplate.asToken("x3c")), + "hashtagScope after insert transparentScope:\n", + listNamesTemplate.asToken(), + "hashtagScope insert probe.\n", + hook1.insert(probeTemplate.asToken()) + )), + "after hashtagScope:\n", + listNamesTemplate.asToken(), + + "nameScope:\n", + hook1.anchor(nameScope( + let("transparentScope4", "abcde"), + addStructuralName("x4a", myStructuralTypeA), + "nameScope before insert scope:\n", + listNamesTemplate.asToken(), + hook1.insert(insertScopeTemplate.asToken("x4b")), + "nameScope after insert scope:\n", + listNamesTemplate.asToken(), + "nameScope before insert transparentScope:\n", + listNamesTemplate.asToken(), + hook1.insert(insertTransparentScopeTemplate.asToken("x4c")), + "nameScope after insert transparentScope:\n", + listNamesTemplate.asToken(), + "nameScope insert probe.\n", + hook1.insert(probeTemplate.asToken()) + )), + "after nameScope:\n", + listNamesTemplate.asToken(), + "transparentScope4: #transparentScope4\n", + + let("local", "outer garbage") + )); + + String code = template.render(); + String expected = + """ + scope: + inserted scope: x1b + {x1b} + inserted transparentScope: x1c + {x1c} + inserted probe: + {x1c} + scope before insert scope: + {x1a} + scope after insert scope: + {x1a} + scope before insert transparentScope: + {x1a} + scope after insert transparentScope: + {x1c, x1a} + scope insert probe. + after scope: + {} + transparentScope: + inserted scope: x2b + {x2a, x2b} + inserted transparentScope: x2c + {x2a, x2c} + inserted probe: + {x2a, x2c} + transparentScope before insert scope: + {x2a} + transparentScope after insert scope: + {x2a} + transparentScope before insert transparentScope: + {x2a} + transparentScope after insert transparentScope: + {x2a, x2c} + transparentScope insert probe. + after transparentScope: + {x2a, x2c} + transparentScope2: abc + hashtagScope: + inserted scope: x3b + {x2a, x2c, x3a, x3b} + inserted transparentScope: x3c + {x2a, x2c, x3a, x3c} + inserted probe: + {x2a, x2c, x3a, x3c} + hashtagScope before insert scope: + {x2a, x2c, x3a} + hashtagScope after insert scope: + {x2a, x2c, x3a} + hashtagScope before insert transparentScope: + {x2a, x2c, x3a} + hashtagScope after insert transparentScope: + {x2a, x2c, x3a, x3c} + hashtagScope insert probe. + after hashtagScope: + {x2a, x2c, x3a, x3c} + nameScope: + inserted scope: x4b + {x2a, x2c, x3a, x3c, x4b} + inserted transparentScope: x4c + {x2a, x2c, x3a, x3c, x4c} + inserted probe: + {x2a, x2c, x3a, x3c, x4c} + nameScope before insert scope: + {x2a, x2c, x3a, x3c, x4a} + nameScope after insert scope: + {x2a, x2c, x3a, x3c, x4a} + nameScope before insert transparentScope: + {x2a, x2c, x3a, x3c, x4a} + nameScope after insert transparentScope: + {x2a, x2c, x3a, x3c, x4c, x4a} + nameScope insert probe. + after nameScope: + {x2a, x2c, x3a, x3c} + transparentScope4: abcde + """; + checkEQ(code, expected); + } + + public static void testHookAndScopes2() { + Hook hook1 = new Hook("Hook1"); + + var listNamesTemplate = Template.make(() -> scope( + "{", + structuralNames().exactOf(myStructuralTypeA).toList(list -> scope( + String.join(", ", list.stream().map(StructuralName::name).toList()) + )), + "}\n" + )); + + var template = Template.make(() -> scope( + "scope:\n", + hook1.anchor(scope( + let("local0", "scope garbage"), + let("local1", "LOCAL1"), + addStructuralName("x1a", myStructuralTypeA), + + "scope before insert scope:\n", + listNamesTemplate.asToken(), + hook1.insert(scope( + let("local2", "insert scope garbage"), + let("name", "x1b"), + addStructuralName("x1b", myStructuralTypeA), // does NOT escape to anchor scope + "inserted scope: #name\n", + "local1: #local1\n", + listNamesTemplate.asToken() + )), + "scope after insert scope:\n", + listNamesTemplate.asToken(), + + "scope before insert transparentScope:\n", + listNamesTemplate.asToken(), + hook1.insert(transparentScope( + let("nameTransparentScope", "x1c"), // escapes to caller + addStructuralName("x1c", myStructuralTypeA), // escapes to anchor scope + "inserted transparentScope: #nameTransparentScope\n", + "local1: #local1\n", + listNamesTemplate.asToken() + )), + "scope after insert transparentScope:\n", + "nameTransparentScope: #nameTransparentScope\n", + listNamesTemplate.asToken(), + + "scope before insert nameScope:\n", + listNamesTemplate.asToken(), + hook1.insert(nameScope( + let("nameNameScope", "x1d"), // escapes to caller + addStructuralName("x1d", myStructuralTypeA), // does NOT escape to anchor scope + "inserted nameScope: #nameNameScope\n", + "local1: #local1\n", + listNamesTemplate.asToken() + )), + "scope after insert nameScope:\n", + "nameNameScope: #nameNameScope\n", + listNamesTemplate.asToken(), + + "scope before insert hashtagScope:\n", + listNamesTemplate.asToken(), + hook1.insert(hashtagScope( + let("local2", "insert hashtagScope garbage"), + let("name", "x1e"), // escapes to caller + addStructuralName("x1e", myStructuralTypeA), // escapes to anchor scope + "inserted hashtagScope: #name\n", + "local1: #local1\n", + listNamesTemplate.asToken() + )), + "scope after insert hashtagScope:\n", + listNamesTemplate.asToken(), + + "scope insert probe.\n", + hook1.insert(scope( + "inserted probe:\n", + listNamesTemplate.asToken() + )) + )), + "after scope:\n", + listNamesTemplate.asToken(), + + let("name", "name garbage"), + let("local0", "outer garbage 0"), + let("local1", "outer garbage 1"), + let("local2", "outer garbage 2"), + let("nameTransparentScope", "outer garbage nameTransparentScope"), + let("nameNameScope", "outer garbage nameNameScope") + )); + + String code = template.render(); + String expected = + """ + scope: + inserted scope: x1b + local1: LOCAL1 + {x1b} + inserted transparentScope: x1c + local1: LOCAL1 + {x1c} + inserted nameScope: x1d + local1: LOCAL1 + {x1c, x1d} + inserted hashtagScope: x1e + local1: LOCAL1 + {x1c, x1e} + inserted probe: + {x1c, x1e} + scope before insert scope: + {x1a} + scope after insert scope: + {x1a} + scope before insert transparentScope: + {x1a} + scope after insert transparentScope: + nameTransparentScope: x1c + {x1c, x1a} + scope before insert nameScope: + {x1c, x1a} + scope after insert nameScope: + nameNameScope: x1d + {x1c, x1a} + scope before insert hashtagScope: + {x1c, x1a} + scope after insert hashtagScope: + {x1c, x1e, x1a} + scope insert probe. + after scope: + {} + """; + checkEQ(code, expected); + } + + // Analogue to testHookAndScopes2, but with "transparentScope" instead of "scope". + public static void testHookAndScopes3() { + Hook hook1 = new Hook("Hook1"); + + var listNamesTemplate = Template.make(() -> scope( + "{", + structuralNames().exactOf(myStructuralTypeA).toList(list -> scope( + String.join(", ", list.stream().map(StructuralName::name).toList()) + )), + "}\n" + )); + + var template = Template.make(() -> scope( + "transparentScope:\n", + hook1.anchor(transparentScope( + let("global0", "transparentScope garbage"), + let("global1", "GLOBAL1"), + addStructuralName("x1a", myStructuralTypeA), + + "transparentScope before insert scope:\n", + listNamesTemplate.asToken(), + hook1.insert(scope( + let("local2", "insert scope garbage"), + let("name", "x1b"), + addStructuralName("x1b", myStructuralTypeA), // does NOT escape to anchor scope + "inserted scope: #name\n", + "global1: #global1\n", + listNamesTemplate.asToken() + )), + "transparentScope after insert scope:\n", + listNamesTemplate.asToken(), + + "transparentScope before insert transparentScope:\n", + listNamesTemplate.asToken(), + hook1.insert(transparentScope( + let("nameTransparentScope", "x1c"), // escapes to caller + addStructuralName("x1c", myStructuralTypeA), // escapes to anchor scope + "inserted transparentScope: #nameTransparentScope\n", + "global1: #global1\n", + listNamesTemplate.asToken() + )), + "transparentScope after insert transparentScope:\n", + "nameTransparentScope: #nameTransparentScope\n", + listNamesTemplate.asToken(), + + "transparentScope before insert nameScope:\n", + listNamesTemplate.asToken(), + hook1.insert(nameScope( + let("nameNameScope", "x1d"), // escapes to caller + addStructuralName("x1d", myStructuralTypeA), // does NOT escape to anchor scope + "inserted nameScope: #nameNameScope\n", + "global1: #global1\n", + listNamesTemplate.asToken() + )), + "transparentScope after insert nameScope:\n", + "nameNameScope: #nameNameScope\n", + listNamesTemplate.asToken(), + + "transparentScope before insert hashtagScope:\n", + listNamesTemplate.asToken(), + hook1.insert(hashtagScope( + let("local2", "insert hashtagScope garbage"), + let("name", "x1e"), // escapes to caller + addStructuralName("x1e", myStructuralTypeA), // escapes to anchor scope + "inserted hashtagScope: #name\n", + "global1: #global1\n", + listNamesTemplate.asToken() + )), + "transparentScope after insert hashtagScope:\n", + listNamesTemplate.asToken(), + + "transparentScope insert probe.\n", + hook1.insert(scope( + "inserted probe:\n", + listNamesTemplate.asToken() + )) + )), + "after transparentScope:\n", + listNamesTemplate.asToken(), + """ + global0: #global0 + global1: #global1 + nameTransparentScope: #nameTransparentScope + nameNameScope: #nameNameScope + """, + let("name", "name garbage"), + let("local2", "outer garbage 2") + )); + + String code = template.render(); + String expected = + """ + transparentScope: + inserted scope: x1b + global1: GLOBAL1 + {x1a, x1b} + inserted transparentScope: x1c + global1: GLOBAL1 + {x1a, x1c} + inserted nameScope: x1d + global1: GLOBAL1 + {x1a, x1c, x1d} + inserted hashtagScope: x1e + global1: GLOBAL1 + {x1a, x1c, x1e} + inserted probe: + {x1a, x1c, x1e} + transparentScope before insert scope: + {x1a} + transparentScope after insert scope: + {x1a} + transparentScope before insert transparentScope: + {x1a} + transparentScope after insert transparentScope: + nameTransparentScope: x1c + {x1a, x1c} + transparentScope before insert nameScope: + {x1a, x1c} + transparentScope after insert nameScope: + nameNameScope: x1d + {x1a, x1c} + transparentScope before insert hashtagScope: + {x1a, x1c} + transparentScope after insert hashtagScope: + {x1a, x1c, x1e} + transparentScope insert probe. + after transparentScope: + {x1a, x1c, x1e} + global0: transparentScope garbage + global1: GLOBAL1 + nameTransparentScope: x1c + nameNameScope: x1d + """; + checkEQ(code, expected); + } + public static void testFailingNestedRendering() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "alpha\n" )); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "beta\n", // Nested "render" call not allowed! template1.render(), @@ -1813,63 +3334,63 @@ public static void testFailingNestedRendering() { } public static void testFailingDollarName1() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( let("x", $("")) // empty string not allowed )); String code = template1.render(); } public static void testFailingDollarName2() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( let("x", $("#abc")) // "#" character not allowed )); String code = template1.render(); } public static void testFailingDollarName3() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( let("x", $("abc#")) // "#" character not allowed )); String code = template1.render(); } public static void testFailingDollarName4() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( let("x", $(null)) // Null input to dollar )); String code = template1.render(); } public static void testFailingDollarName5() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "$" // empty dollar name )); String code = template1.render(); } public static void testFailingDollarName6() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "asdf$" // empty dollar name )); String code = template1.render(); } public static void testFailingDollarName7() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "asdf$1" // Bad pattern after dollar )); String code = template1.render(); } public static void testFailingDollarName8() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "abc$$abc" // empty dollar name )); String code = template1.render(); } public static void testFailingLetName1() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( let(null, $("abc")) // Null input for hashtag name )); String code = template1.render(); @@ -1877,20 +3398,20 @@ public static void testFailingLetName1() { public static void testFailingHashtagName1() { // Empty Template argument - var template1 = Template.make("", (String x) -> body( + var template1 = Template.make("", (String x) -> scope( )); String code = template1.render("abc"); } public static void testFailingHashtagName2() { // "#" character not allowed in template argument - var template1 = Template.make("abc#abc", (String x) -> body( + var template1 = Template.make("abc#abc", (String x) -> scope( )); String code = template1.render("abc"); } public static void testFailingHashtagName3() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // Empty let hashtag name not allowed let("", "abc") )); @@ -1898,7 +3419,7 @@ public static void testFailingHashtagName3() { } public static void testFailingHashtagName4() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // "#" character not allowed in let hashtag name let("xyz#xyz", "abc") )); @@ -1906,56 +3427,56 @@ public static void testFailingHashtagName4() { } public static void testFailingHashtagName5() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "#" // empty hashtag name )); String code = template1.render(); } public static void testFailingHashtagName6() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "asdf#" // empty hashtag name )); String code = template1.render(); } public static void testFailingHashtagName7() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "asdf#1" // Bad pattern after hashtag )); String code = template1.render(); } public static void testFailingHashtagName8() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "abc##abc" // empty hashtag name )); String code = template1.render(); } public static void testFailingDollarHashtagName1() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "#$" // empty hashtag name )); String code = template1.render(); } public static void testFailingDollarHashtagName2() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "$#" // empty dollar name )); String code = template1.render(); } public static void testFailingDollarHashtagName3() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "#$name" // empty hashtag name )); String code = template1.render(); } public static void testFailingDollarHashtagName4() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "$#name" // empty dollar name )); String code = template1.render(); @@ -1964,11 +3485,11 @@ public static void testFailingDollarHashtagName4() { public static void testFailingHook() { var hook1 = new Hook("Hook1"); - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "alpha\n" )); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( "beta\n", // Use hook without hook1.anchor hook1.insert(template1.asToken()), @@ -1978,20 +3499,40 @@ public static void testFailingHook() { String code = template2.render(); } - public static void testFailingSample1() { - var template1 = Template.make(() -> body( - // No variable added yet. - let("v", dataNames(MUTABLE).exactOf(myInt).sample().name()), + public static void testFailingSample1a() { + var template1 = Template.make(() -> scope( + // No DataName added yet. + dataNames(MUTABLE).exactOf(myInt).sampleAndLetAs("v"), "v is #v\n" )); String code = template1.render(); } - public static void testFailingSample2() { - var template1 = Template.make(() -> body( + public static void testFailingSample1b() { + var template1 = Template.make(() -> scope( + // No StructuralName added yet. + structuralNames().exactOf(myStructuralTypeA).sampleAndLetAs("v"), + "v is #v\n" + )); + + String code = template1.render(); + } + + public static void testFailingSample2a() { + var template1 = Template.make(() -> scope( // no type restriction - let("v", dataNames(MUTABLE).sample().name()), + dataNames(MUTABLE).sampleAndLetAs("v"), + "v is #v\n" + )); + + String code = template1.render(); + } + + public static void testFailingSample2b() { + var template1 = Template.make(() -> scope( + // no type restriction + structuralNames().sampleAndLetAs("v"), "v is #v\n" )); @@ -2000,7 +3541,7 @@ public static void testFailingSample2() { public static void testFailingHashtag1() { // Duplicate hashtag definition from arguments. - var template1 = Template.make("a", "a", (String _, String _) -> body( + var template1 = Template.make("a", "a", (String _, String _) -> scope( "nothing\n" )); @@ -2008,7 +3549,7 @@ public static void testFailingHashtag1() { } public static void testFailingHashtag2() { - var template1 = Template.make("a", (String _) -> body( + var template1 = Template.make("a", (String _) -> scope( // Duplicate hashtag name let("a", "x"), "nothing\n" @@ -2018,7 +3559,7 @@ public static void testFailingHashtag2() { } public static void testFailingHashtag3() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( let("a", "x"), // Duplicate hashtag name let("a", "y"), @@ -2029,7 +3570,7 @@ public static void testFailingHashtag3() { } public static void testFailingHashtag4() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // Missing hashtag name definition "#a\n" )); @@ -2037,9 +3578,20 @@ public static void testFailingHashtag4() { String code = template1.render(); } + public static void testFailingHashtag5() { + var template1 = Template.make(() -> scope( + "use before definition: #a\n", + // let is a token, and is only evaluated after + // the string above, and so the string above fails. + let("a", "x") + )); + + String code = template1.render(); + } + public static void testFailingBinding1() { var binding = new TemplateBinding(); - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "nothing\n" )); binding.bind(template1); @@ -2049,7 +3601,7 @@ public static void testFailingBinding1() { public static void testFailingBinding2() { var binding = new TemplateBinding(); - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( "nothing\n", // binding was never bound. binding.get() @@ -2059,7 +3611,7 @@ public static void testFailingBinding2() { } public static void testFailingAddDataName1() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // Must pick either MUTABLE or IMMUTABLE. addDataName("name", myInt, MUTABLE_OR_IMMUTABLE) )); @@ -2067,7 +3619,7 @@ public static void testFailingAddDataName1() { } public static void testFailingAddDataName2() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // weight out of bounds [0..1000] addDataName("name", myInt, MUTABLE, 0) )); @@ -2075,7 +3627,7 @@ public static void testFailingAddDataName2() { } public static void testFailingAddDataName3() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // weight out of bounds [0..1000] addDataName("name", myInt, MUTABLE, -1) )); @@ -2083,7 +3635,7 @@ public static void testFailingAddDataName3() { } public static void testFailingAddDataName4() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // weight out of bounds [0..1000] addDataName("name", myInt, MUTABLE, 1001) )); @@ -2091,7 +3643,7 @@ public static void testFailingAddDataName4() { } public static void testFailingAddStructuralName1() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // weight out of bounds [0..1000] addStructuralName("name", myStructuralTypeA, 0) )); @@ -2099,7 +3651,7 @@ public static void testFailingAddStructuralName1() { } public static void testFailingAddStructuralName2() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // weight out of bounds [0..1000] addStructuralName("name", myStructuralTypeA, -1) )); @@ -2107,7 +3659,7 @@ public static void testFailingAddStructuralName2() { } public static void testFailingAddStructuralName3() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( // weight out of bounds [0..1000] addStructuralName("name", myStructuralTypeA, 1001) )); @@ -2116,7 +3668,7 @@ public static void testFailingAddStructuralName3() { // Duplicate name in the same scope, name identical -> expect RendererException. public static void testFailingAddNameDuplication1() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( addDataName("name", myInt, MUTABLE), addDataName("name", myInt, MUTABLE) )); @@ -2125,7 +3677,7 @@ public static void testFailingAddNameDuplication1() { // Duplicate name in the same scope, names have different mutability -> expect RendererException. public static void testFailingAddNameDuplication2() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( addDataName("name", myInt, MUTABLE), addDataName("name", myInt, IMMUTABLE) )); @@ -2134,7 +3686,7 @@ public static void testFailingAddNameDuplication2() { // Duplicate name in the same scope, names have different type -> expect RendererException. public static void testFailingAddNameDuplication3() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( addDataName("name", myInt, MUTABLE), addDataName("name", myLong, MUTABLE) )); @@ -2143,7 +3695,7 @@ public static void testFailingAddNameDuplication3() { // Duplicate name in the same scope, name identical -> expect RendererException. public static void testFailingAddNameDuplication4() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( addStructuralName("name", myStructuralTypeA), addStructuralName("name", myStructuralTypeA) )); @@ -2152,7 +3704,7 @@ public static void testFailingAddNameDuplication4() { // Duplicate name in the same scope, names have different type -> expect RendererException. public static void testFailingAddNameDuplication5() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( addStructuralName("name", myStructuralTypeA), addStructuralName("name", myStructuralTypeB) )); @@ -2161,10 +3713,10 @@ public static void testFailingAddNameDuplication5() { // Duplicate name in inner Template, name identical -> expect RendererException. public static void testFailingAddNameDuplication6() { - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( addDataName("name", myInt, MUTABLE) )); - var template2 = Template.make(() -> body( + var template2 = Template.make(() -> scope( addDataName("name", myInt, MUTABLE), template1.asToken() )); @@ -2175,11 +3727,11 @@ public static void testFailingAddNameDuplication6() { public static void testFailingAddNameDuplication7() { var hook1 = new Hook("Hook1"); - var template1 = Template.make(() -> body( + var template1 = Template.make(() -> scope( addDataName("name", myInt, MUTABLE), - hook1.anchor( + hook1.anchor(scope( addDataName("name", myInt, MUTABLE) - ) + )) )); String code = template1.render(); } @@ -2188,19 +3740,94 @@ public static void testFailingAddNameDuplication7() { public static void testFailingAddNameDuplication8() { var hook1 = new Hook("Hook1"); - var template1 = Template.make(() -> body( - addDataName("name", myInt, MUTABLE) + var template1 = Template.make(() -> transparentScope( + addDataName("name", myInt, MUTABLE) // escapes )); - var template2 = Template.make(() -> body( - hook1.anchor( + var template2 = Template.make(() -> scope( + hook1.anchor(scope( addDataName("name", myInt, MUTABLE), hook1.insert(template1.asToken()) - ) + )) )); String code = template2.render(); } + public static void testFailingScope1() { + var template = Template.make(() -> scope( + transparentScope( + let("x", "x1") // escapes + ), + let("x", "x2") // second definition + )); + String code = template.render(); + } + + public static void testFailingScope2() { + var template = Template.make(() -> scope( + nameScope( + let("x", "x1") // escapes + ), + let("x", "x2") // second definition + )); + String code = template.render(); + } + + public static void testFailingScope3() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> transparentScope( + let("x", sn.name()) // leads to duplicate hashtag + )) + )); + String code = template.render(); + } + + public static void testFailingScope4() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> nameScope( + let("x", sn.name()) // leads to duplicate hashtag + )) + )); + String code = template.render(); + } + + public static void testFailingScope5() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> transparentScope( + addStructuralName("x", myStructuralTypeA) // leads to duplicate name + )) + )); + String code = template.render(); + } + + public static void testFailingScope6() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> hashtagScope( + addStructuralName("x", myStructuralTypeA) // leads to duplicate name + )) + )); + String code = template.render(); + } + + public static void testFailingScope7() { + var template = Template.make(() -> scope( + addStructuralName("a", myStructuralTypeA), + addStructuralName("b", myStructuralTypeA), + structuralNames().exactOf(myStructuralTypeA).forEach(sn -> setFuelCostScope( + addStructuralName("x", myStructuralTypeA) // leads to duplicate name + )) + )); + String code = template.render(); + } + public static void expectRendererException(FailingTest test, String errorPrefix) { try { test.run(); From ad38a1253ae3ff92f7e0cf0fbc4d4726957b1443 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 20 Nov 2025 10:19:57 +0000 Subject: [PATCH 43/44] 8371557: java/net/httpclient/http3/H3RequestRejectedTest.java: javax.net.ssl.SSLHandshakeException: local endpoint (wildcard) and remote endpoint (loopback) ports conflict Reviewed-by: jpai --- .../jdk/java/net/httpclient/http3/H3RequestRejectedTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/net/httpclient/http3/H3RequestRejectedTest.java b/test/jdk/java/net/httpclient/http3/H3RequestRejectedTest.java index 361a78fdd4757..1731ddae833e7 100644 --- a/test/jdk/java/net/httpclient/http3/H3RequestRejectedTest.java +++ b/test/jdk/java/net/httpclient/http3/H3RequestRejectedTest.java @@ -50,6 +50,7 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpRequest.BodyPublishers; import static java.nio.charset.StandardCharsets.US_ASCII; +import static jdk.httpclient.test.lib.common.HttpServerAdapters.createClientBuilderForH3; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -100,7 +101,7 @@ static void afterAll() throws Exception { */ @Test void testAlwaysRejected() throws Exception { - try (final HttpClient client = HttpClient.newBuilder() + try (final HttpClient client = createClientBuilderForH3() .sslContext(sslContext).proxy(NO_PROXY).version(HTTP_3) .build()) { @@ -134,7 +135,7 @@ void testAlwaysRejected() throws Exception { */ @Test void testRejectedRequest() throws Exception { - try (final HttpClient client = HttpClient.newBuilder().sslContext(sslContext) + try (final HttpClient client = createClientBuilderForH3().sslContext(sslContext) .proxy(NO_PROXY).version(HTTP_3) .build()) { From 177e7ee31e9193e45f2fa210ce1a4953e0cf8dc9 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 20 Nov 2025 10:22:22 +0000 Subject: [PATCH 44/44] 8372198: Avoid closing PlainHttpConnection while holding a lock --- .../jdk/internal/net/http/ConnectionPool.java | 36 +++++++++++-------- .../jdk/internal/net/http/HttpClientImpl.java | 8 +++++ .../net/http/PlainHttpConnection.java | 20 +++++------ 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java b/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java index 1d8cb013295cc..e1725aa92d592 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java @@ -205,32 +205,38 @@ void returnToPool(HttpConnection conn, Deadline now, long keepAlive) { // it's possible that cleanup may have been called. HttpConnection toClose = null; + boolean stopping = false; stateLock.lock(); try { if (cleanup.isDone()) { return; - } else if (stopped) { - conn.close(); - return; - } - if (MAX_POOL_SIZE > 0 && expiryList.size() >= MAX_POOL_SIZE) { - toClose = expiryList.removeOldest(); - if (toClose != null) removeFromPool(toClose); - } - if (conn instanceof PlainHttpConnection) { - putConnection(conn, plainPool); + } else if (stopping = stopped) { + toClose = conn; } else { - assert conn.isSecure(); - putConnection(conn, sslPool); + if (MAX_POOL_SIZE > 0 && expiryList.size() >= MAX_POOL_SIZE) { + toClose = expiryList.removeOldest(); + if (toClose != null) removeFromPool(toClose); + } + if (conn instanceof PlainHttpConnection) { + putConnection(conn, plainPool); + } else { + assert conn.isSecure(); + putConnection(conn, sslPool); + } + expiryList.add(conn, now, keepAlive); } - expiryList.add(conn, now, keepAlive); } finally { stateLock.unlock(); } if (toClose != null) { if (debug.on()) { - debug.log("Maximum pool size reached: removing oldest connection %s", - toClose.dbgString()); + if (stopping) { + debug.log("Stopping: close connection %s", + toClose.dbgString()); + } else { + debug.log("Maximum pool size reached: removing oldest connection %s", + toClose.dbgString()); + } } close(toClose); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java index af9fd3b96ba51..1b9a79b8803a7 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java @@ -1333,6 +1333,13 @@ String getName() { // Only called by the selector manager thread private void shutdown() { + // first stop the client to avoid seeing exceptions + // about "selector manager closed" + Log.logTrace("{0}: stopping", owner.dbgTag); + try { + owner.stop(); + } catch (Throwable ignored) { + } try { lock.lock(); try { @@ -1345,6 +1352,7 @@ private void shutdown() { } } catch (IOException ignored) { } finally { + // cleanup anything that might have been left behind owner.stop(); } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java index e705aae72a14e..45d242df6717b 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java @@ -311,20 +311,20 @@ void close(Throwable cause) { var connectTimerEvent = this.connectTimerEvent; if (connectTimerEvent != null) client().cancelTimer(connectTimerEvent); - if (Log.channel()) { - Log.logChannel("Closing channel: " + chan); - } - try { - tube.signalClosed(errorRef.get()); - chan.close(); - } finally { - client().connectionClosed(this); - } + } finally { + stateLock.unlock(); + } + if (Log.channel()) { + Log.logChannel("Closing channel: " + chan); + } + try { + tube.signalClosed(errorRef.get()); + chan.close(); } catch (IOException e) { debug.log("Closing resulted in " + e); Log.logTrace("Closing resulted in " + e); } finally { - stateLock.unlock(); + client().connectionClosed(this); } }