From 6e50bbee3b2f8af5ec9f6a282d7730d95917dce3 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 16 Nov 2022 00:00:14 +0200 Subject: [PATCH] [K/N] Set `mayContainSupplCodepoints` flag only when `alt` is updated Motivation: Calling AbstractCharClass.setNegative always leads to mayContainSupplCodepoints being `true`, even when `alt` is already equal to the argument. Given that CharClass always calls setNegative with its constructor parameter - `negative`, mayContainSupplCodepoints always gets set. i.e. `[a]` will have mayContainSupplCodepoints set. mayContainSupplCodepoints affects what node (RangeSet or SupplementaryRangeSet) wraps this char class. See Pattern.processRangeSet. RangeSet is better optimized and avoids recursion when it is quantified. Modification: Set `mayContainSupplCodepoints` flag only when `alt` is updated. When `alt` is updated, this char class gets inverted, hence now may contain supplementary code points. Otherwise, content of this char class does not change, i.e. no new supplementary code points is added. Result: Fixes KT-46211. --- .../kotlin/text/regex/AbstractCharClass.kt | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/libraries/stdlib/native-wasm/src/kotlin/text/regex/AbstractCharClass.kt b/libraries/stdlib/native-wasm/src/kotlin/text/regex/AbstractCharClass.kt index bd1a8b5ea78..7a43eb51a9d 100644 --- a/libraries/stdlib/native-wasm/src/kotlin/text/regex/AbstractCharClass.kt +++ b/libraries/stdlib/native-wasm/src/kotlin/text/regex/AbstractCharClass.kt @@ -58,9 +58,12 @@ internal abstract class AbstractCharClass : SpecialToken() { internal var alt: Boolean = false internal var altSurrogates: Boolean = false + /** + * For each unpaired surrogate char indicates whether it is contained in this char class. + */ internal val lowHighSurrogates = BitSet(SURROGATE_CARDINALITY) // Bit set for surrogates? - /* + /** * Indicates if this class may contain supplementary Unicode codepoints. * If this flag is specified it doesn't mean that this class contains supplementary characters but may contain. */ @@ -92,6 +95,20 @@ internal abstract class AbstractCharClass : SpecialToken() { private val surrogates_ = AtomicReference(null) + /** + * Returns a char class that contains only unpaired surrogate chars from this char class. + * + * Consider the following char class: `[a\uD801\uDC00\uD800]`. + * This function returns a char class that contains only `\uD800`: `[\uD800]`. + * [classWithoutSurrogates] returns a char class that does not contain `\uD800`: `[a\uD801\uDC00]`. + * + * The returned char class is used to create [SurrogateRangeSet] node + * that matches any unpaired surrogate from this char class. [SurrogateRangeSet] + * doesn't match a surrogate that is paired with the char before or after it. + * The result of [classWithoutSurrogates] is used to create [SupplementaryRangeSet] + * or [RangeSet] depending on [mayContainSupplCodepoints]. + * The two nodes are then combined in [CompositeRangeSet] node to fully represent this char class. + */ fun classWithSurrogates(): AbstractCharClass { surrogates_.value?.let { return it @@ -108,12 +125,19 @@ internal abstract class AbstractCharClass : SpecialToken() { } } } - result.setNegative(this.altSurrogates) + result.alt = this.alt + result.altSurrogates = this.altSurrogates + result.mayContainSupplCodepoints = this.mayContainSupplCodepoints surrogates_.compareAndSet(null, result.freeze()) return surrogates_.value!! } + /** + * Returns a char class that contains all chars from this char class excluding the unpaired surrogate chars. + * + * See [classWithSurrogates] for details. + */ // We cannot cache this class as we've done with surrogates above because // here is a circular reference between it and AbstractCharClass. fun classWithoutSurrogates(): AbstractCharClass { @@ -129,8 +153,9 @@ internal abstract class AbstractCharClass : SpecialToken() { return this@AbstractCharClass.contains(ch) && !containslHS } } - result.setNegative(isNegative()) - result.mayContainSupplCodepoints = mayContainSupplCodepoints + result.alt = this.alt + result.altSurrogates = this.altSurrogates + result.mayContainSupplCodepoints = this.mayContainSupplCodepoints return result } @@ -145,9 +170,10 @@ internal abstract class AbstractCharClass : SpecialToken() { if (alt xor value) { alt = !alt altSurrogates = !altSurrogates - } - if (!mayContainSupplCodepoints) { - mayContainSupplCodepoints = true + + if (!mayContainSupplCodepoints) { + mayContainSupplCodepoints = true + } } return this }