[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.
This commit is contained in:
committed by
Space Team
parent
d7f166eda3
commit
6e50bbee3b
@@ -58,9 +58,12 @@ internal abstract class AbstractCharClass : SpecialToken() {
|
|||||||
internal var alt: Boolean = false
|
internal var alt: Boolean = false
|
||||||
internal var altSurrogates: 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?
|
internal val lowHighSurrogates = BitSet(SURROGATE_CARDINALITY) // Bit set for surrogates?
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Indicates if this class may contain supplementary Unicode codepoints.
|
* 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.
|
* 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<AbstractCharClass?>(null)
|
private val surrogates_ = AtomicReference<AbstractCharClass?>(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 {
|
fun classWithSurrogates(): AbstractCharClass {
|
||||||
surrogates_.value?.let {
|
surrogates_.value?.let {
|
||||||
return it
|
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())
|
surrogates_.compareAndSet(null, result.freeze())
|
||||||
return surrogates_.value!!
|
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
|
// We cannot cache this class as we've done with surrogates above because
|
||||||
// here is a circular reference between it and AbstractCharClass.
|
// here is a circular reference between it and AbstractCharClass.
|
||||||
fun classWithoutSurrogates(): AbstractCharClass {
|
fun classWithoutSurrogates(): AbstractCharClass {
|
||||||
@@ -129,8 +153,9 @@ internal abstract class AbstractCharClass : SpecialToken() {
|
|||||||
return this@AbstractCharClass.contains(ch) && !containslHS
|
return this@AbstractCharClass.contains(ch) && !containslHS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.setNegative(isNegative())
|
result.alt = this.alt
|
||||||
result.mayContainSupplCodepoints = mayContainSupplCodepoints
|
result.altSurrogates = this.altSurrogates
|
||||||
|
result.mayContainSupplCodepoints = this.mayContainSupplCodepoints
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,9 +170,10 @@ internal abstract class AbstractCharClass : SpecialToken() {
|
|||||||
if (alt xor value) {
|
if (alt xor value) {
|
||||||
alt = !alt
|
alt = !alt
|
||||||
altSurrogates = !altSurrogates
|
altSurrogates = !altSurrogates
|
||||||
}
|
|
||||||
if (!mayContainSupplCodepoints) {
|
if (!mayContainSupplCodepoints) {
|
||||||
mayContainSupplCodepoints = true
|
mayContainSupplCodepoints = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user