Skip to content

Commit 90ef7a5

Browse files
committed
refactor: fix potential deadlocks in Java runtime (antlr4#3752)
See antlr/antlr4#3752 Commit 875911c2b2e6e50317e75ff2b5e80f4014a75954
1 parent 289ad45 commit 90ef7a5

File tree

14 files changed

+84
-71
lines changed

14 files changed

+84
-71
lines changed

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/ParserRuleContext.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package org.antlr.v4.kotlinruntime
88
import org.antlr.v4.kotlinruntime.ast.Position
99
import org.antlr.v4.kotlinruntime.misc.Interval
1010
import org.antlr.v4.kotlinruntime.tree.*
11+
import kotlin.jvm.JvmStatic
1112
import kotlin.reflect.KClass
1213

1314
//
@@ -34,6 +35,11 @@ import kotlin.reflect.KClass
3435
// * satisfy the superclass interface.
3536
// */
3637
open class ParserRuleContext : RuleContext {
38+
companion object {
39+
@JvmStatic
40+
val EMPTY: ParserRuleContext = ParserRuleContext()
41+
}
42+
3743
// override fun setParent(parent: RuleContext) {
3844
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
3945
// }

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/RuleContext.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ import org.antlr.v4.kotlinruntime.tree.Trees
6262
*
6363
* @see ParserRuleContext
6464
*/
65-
66-
val EMPTY_RULECTX = ParserRuleContext()
67-
68-
6965
open class RuleContext : RuleNode {
7066
// override fun setParent(parent: RuleContext) {
7167
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
@@ -212,7 +208,7 @@ open class RuleContext : RuleNode {
212208
}
213209

214210
// recog null unless ParserRuleContext, in which case we use subclass toString(...)
215-
fun toString(recog: Recognizer<*, *>?, stop: RuleContext = EMPTY_RULECTX): String {
211+
fun toString(recog: Recognizer<*, *>?, stop: RuleContext = ParserRuleContext.EMPTY): String {
216212
val ruleNames = recog?.ruleNames
217213
val ruleNamesList = if (ruleNames != null) listOf(*ruleNames) else null
218214
return toString(ruleNamesList, stop)

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfig.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ open class ATNConfig {
8787
constructor(state: ATNState,
8888
alt: Int,
8989
context: PredictionContext?,
90-
semanticContext: SemanticContext = SemanticContext.NONE) {
90+
semanticContext: SemanticContext = SemanticContext.Empty.Instance) {
9191
this.state = state
9292
this.alt = alt
9393
this.context = context
@@ -132,7 +132,7 @@ open class ATNConfig {
132132

133133
return (this.state.stateNumber == other.state.stateNumber
134134
&& this.alt == other.alt
135-
&& (this.context === other.context || (this.context != null && this.context == other.context))
135+
&& this.context == other.context
136136
&& this.semanticContext == other.semanticContext
137137
&& this.isPrecedenceFilterSuppressed == other.isPrecedenceFilterSuppressed)
138138
}
@@ -168,7 +168,7 @@ open class ATNConfig {
168168
buf.append(context!!.toString())
169169
buf.append("]")
170170
}
171-
if (semanticContext != null && semanticContext !== SemanticContext.NONE) {
171+
if (semanticContext != null && semanticContext !== SemanticContext.Empty.Instance) {
172172
buf.append(",")
173173
buf.append(semanticContext)
174174
}

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfigSet.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ open class ATNConfigSet constructor(
8989
get() {
9090
val preds = ArrayList<SemanticContext>()
9191
for (c in configs) {
92-
if (c.semanticContext !== SemanticContext.NONE) {
92+
if (c.semanticContext !== SemanticContext.Empty.Instance) {
9393
preds.add(c.semanticContext!!)
9494
}
9595
}
@@ -178,7 +178,7 @@ open class ATNConfigSet constructor(
178178
config: ATNConfig,
179179
mergeCache: DoubleKeyMap<PredictionContext, PredictionContext, PredictionContext>?): Boolean {
180180
if (isReadonly) throw IllegalStateException("This set is readonly")
181-
if (config.semanticContext !== SemanticContext.NONE) {
181+
if (config.semanticContext !== SemanticContext.Empty.Instance) {
182182
hasSemanticContext = true
183183
}
184184
if (config.outerContextDepth > 0) {

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/EmptyPredictionContext.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@
66

77
package org.antlr.v4.kotlinruntime.atn
88

9+
import kotlin.jvm.JvmStatic
10+
911
class EmptyPredictionContext : SingletonPredictionContext(null, PredictionContext.EMPTY_RETURN_STATE) {
12+
companion object {
13+
/**
14+
* Represents `$` in local context prediction, which means wildcard.
15+
* `*+x = *`.
16+
*/
17+
@JvmStatic
18+
val Instance: EmptyPredictionContext = EmptyPredictionContext()
19+
}
1020

1121
override val isEmpty: Boolean
1222
get() = true

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LL1Analyzer.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class LL1Analyzer(val atn: ATN) {
3434
look[alt] = IntervalSet()
3535
val lookBusy = HashSet<ATNConfig>()
3636
val seeThruPreds = false // fail to get lookahead upon pred
37-
_LOOK(s!!.transition(alt).target!!, null, PredictionContext.EMPTY,
37+
_LOOK(s!!.transition(alt).target!!, null, EmptyPredictionContext.Instance,
3838
look[alt]!!, lookBusy, BitSet(), seeThruPreds, false)
3939
// Wipe out lookahead for this alternative if we found nothing
4040
// or we had a predicate when we !seeThruPreds
@@ -156,7 +156,7 @@ class LL1Analyzer(val atn: ATN) {
156156
return
157157
}
158158

159-
if (ctx !== PredictionContext.EMPTY) {
159+
if (ctx !== EmptyPredictionContext.Instance) {
160160
// run thru all possible stack tops in ctx
161161
val removed = calledRuleStack.get(s.ruleIndex)
162162
try {

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNConfig.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ class LexerATNConfig : ATNConfig {
2323

2424
constructor(state: ATNState,
2525
alt: Int,
26-
context: PredictionContext) : super(state, alt, context, SemanticContext.NONE) {
26+
context: PredictionContext) : super(state, alt, context, SemanticContext.Empty.Instance) {
2727
this.passedThroughNonGreedyDecision = false
2828
this.lexerActionExecutor = null
2929
}
3030

3131
constructor(state: ATNState,
3232
alt: Int,
3333
context: PredictionContext,
34-
lexerActionExecutor: LexerActionExecutor) : super(state, alt, context, SemanticContext.NONE) {
34+
lexerActionExecutor: LexerActionExecutor) : super(state, alt, context, SemanticContext.Empty.Instance) {
3535
this.lexerActionExecutor = lexerActionExecutor
3636
this.passedThroughNonGreedyDecision = false
3737
}

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNSimulator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ class LexerATNSimulator(protected val recog: Lexer?, atn: ATN,
350350

351351
protected fun computeStartState(input: CharStream,
352352
p: ATNState): ATNConfigSet {
353-
val initialContext = PredictionContext.EMPTY
353+
val initialContext = EmptyPredictionContext.Instance
354354
val configs = OrderedATNConfigSet()
355355
for (i in 0 until p.numberOfTransitions) {
356356
val target = p.transition(i).target
@@ -390,7 +390,7 @@ class LexerATNSimulator(protected val recog: Lexer?, atn: ATN,
390390
configs.add(config)
391391
return true
392392
} else {
393-
configs.add(LexerATNConfig(config, config.state, PredictionContext.EMPTY))
393+
configs.add(LexerATNConfig(config, config.state, EmptyPredictionContext.Instance))
394394
currentAltReachedAcceptState = true
395395
}
396396
}

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ParserATNSimulator.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ open class ParserATNSimulator(
350350
}
351351

352352
if (s0 == null) {
353-
if (outerContext == null) outerContext = EMPTY_RULECTX
353+
if (outerContext == null) outerContext = ParserRuleContext.EMPTY
354354
if (debug || debug_list_atn_decisions) {
355355
System.out.println("predictATN decision " + dfa.decision +
356356
" exec LA(1)==" + getLookaheadName(input) +
@@ -359,7 +359,7 @@ open class ParserATNSimulator(
359359

360360
val fullCtx = false
361361
var s0_closure = computeStartState(dfa.atnStartState,
362-
EMPTY_RULECTX,
362+
ParserRuleContext.EMPTY,
363363
fullCtx)
364364

365365
if (dfa.isPrecedenceDfa) {
@@ -1176,8 +1176,8 @@ open class ParserATNSimulator(
11761176
var nPredAlts = 0
11771177
for (i in 1..nalts) {
11781178
if (altToPred!![i] == null) {
1179-
altToPred[i] = SemanticContext.NONE
1180-
} else if (altToPred[i] !== SemanticContext.NONE) {
1179+
altToPred[i] = SemanticContext.Empty.Instance
1180+
} else if (altToPred[i] !== SemanticContext.Empty.Instance) {
11811181
nPredAlts++
11821182
}
11831183
}
@@ -1206,7 +1206,7 @@ open class ParserATNSimulator(
12061206
if (ambigAlts != null && ambigAlts.get(i)) {
12071207
pairs.add(DFAState.PredPrediction(pred!!, i))
12081208
}
1209-
if (pred !== SemanticContext.NONE) containsPredicate = true
1209+
if (pred !== SemanticContext.Empty.Instance) containsPredicate = true
12101210
}
12111211

12121212
return if (!containsPredicate) {
@@ -1308,7 +1308,7 @@ open class ParserATNSimulator(
13081308
val succeeded = ATNConfigSet(configs.fullCtx)
13091309
val failed = ATNConfigSet(configs.fullCtx)
13101310
for (c in configs) {
1311-
if (c.semanticContext !== SemanticContext.NONE) {
1311+
if (c.semanticContext !== SemanticContext.Empty.Instance) {
13121312
val predicateEvaluationResult = evalSemanticContext(c.semanticContext, outerContext, c.alt, configs.fullCtx)
13131313
if (predicateEvaluationResult) {
13141314
succeeded.add(c)
@@ -1333,7 +1333,7 @@ open class ParserATNSimulator(
13331333
complete: Boolean): BitSet {
13341334
val predictions = BitSet()
13351335
for (pair in predPredictions) {
1336-
if (pair!!.pred === SemanticContext.NONE) {
1336+
if (pair!!.pred === SemanticContext.Empty.Instance) {
13371337
predictions.set(pair!!.alt)
13381338
if (!complete) {
13391339
break
@@ -1430,7 +1430,7 @@ open class ParserATNSimulator(
14301430
for (i in 0 until config.context!!.size()) {
14311431
if (config.context!!.getReturnState(i) == PredictionContext.EMPTY_RETURN_STATE) {
14321432
if (fullCtx) {
1433-
configs.add(ATNConfig(config, config.state, PredictionContext.EMPTY), mergeCache)
1433+
configs.add(ATNConfig(config, config.state, EmptyPredictionContext.Instance), mergeCache)
14341434
continue
14351435
} else {
14361436
// we have no context info, just chase follow links (if greedy)

antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContext.kt

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package org.antlr.v4.kotlinruntime.atn
88

99
import com.strumenta.kotlinmultiplatform.IdentityHashMap
1010
import com.strumenta.kotlinmultiplatform.assert
11-
import org.antlr.v4.kotlinruntime.EMPTY_RULECTX
11+
import org.antlr.v4.kotlinruntime.ParserRuleContext
1212
import org.antlr.v4.kotlinruntime.Recognizer
1313
import org.antlr.v4.kotlinruntime.RuleContext
1414
import org.antlr.v4.kotlinruntime.misc.DoubleKeyMap
@@ -38,9 +38,9 @@ abstract class PredictionContext protected constructor(
3838
*/
3939
val cachedHashCode: Int
4040
) {
41-
/** This means only the [.EMPTY] (wildcard? not sure) context is in set. */
41+
/** This means only the [EmptyPredictionContext.Instance] (wildcard? not sure) context is in set. */
4242
open val isEmpty: Boolean
43-
get() = this === EMPTY
43+
get() = this === EmptyPredictionContext.Instance
4444

4545
abstract fun size(): Int
4646

@@ -65,7 +65,7 @@ abstract class PredictionContext protected constructor(
6565
}
6666

6767
fun toStrings(recognizer: Recognizer<*, *>, currentState: Int): Array<String> {
68-
return toStrings(recognizer, EMPTY, currentState)
68+
return toStrings(recognizer, EmptyPredictionContext.Instance, currentState)
6969
}
7070

7171
// FROM SAM
@@ -134,12 +134,6 @@ abstract class PredictionContext protected constructor(
134134
}
135135

136136
companion object {
137-
/**
138-
* Represents `$` in local context prediction, which means wildcard.
139-
* `*+x = *`.
140-
*/
141-
val EMPTY = EmptyPredictionContext()
142-
143137
/**
144138
* Represents `$` in an array in full context mode, when `$`
145139
* doesn't mean wildcard: `$ + x = [$,x]`. Here,
@@ -150,15 +144,15 @@ abstract class PredictionContext protected constructor(
150144
private const val INITIAL_HASH = 1
151145

152146
/** Convert a [RuleContext] tree to a [PredictionContext] graph.
153-
* Return [.EMPTY] if `outerContext` is empty or null.
147+
* Return [EmptyPredictionContext.Instance] if `outerContext` is empty or null.
154148
*/
155149
fun fromRuleContext(atn: ATN, outerContext: RuleContext?): PredictionContext {
156-
val outerContext1 = outerContext ?: EMPTY_RULECTX
150+
val outerContext1 = outerContext ?: ParserRuleContext.EMPTY
157151

158152
// if we are in RuleContext of start rule, s, then PredictionContext
159153
// is EMPTY. Nobody called us. (if we are empty, return empty)
160-
if (outerContext1.readParent() == null || outerContext1 === EMPTY_RULECTX) {
161-
return EMPTY
154+
if (outerContext1.readParent() == null || outerContext1 === ParserRuleContext.EMPTY) {
155+
return EmptyPredictionContext.Instance
162156
}
163157

164158
// If we have a parent, convert it to a PredictionContext graph
@@ -337,8 +331,8 @@ abstract class PredictionContext protected constructor(
337331

338332
/**
339333
* Handle case where at least one of `a` or `b` is
340-
* [.EMPTY]. In the following diagrams, the symbol `$` is used
341-
* to represent [.EMPTY].
334+
* [EmptyPredictionContext.Instance]. In the following diagrams, the symbol `$` is used
335+
* to represent [EmptyPredictionContext.Instance].
342336
*
343337
* <h2>Local-Context Merges</h2>
344338
*
@@ -347,11 +341,11 @@ abstract class PredictionContext protected constructor(
347341
* is true.
348342
*
349343
*
350-
* [.EMPTY] is superset of any graph; return [.EMPTY].<br></br>
344+
* [EmptyPredictionContext.Instance] is superset of any graph; return [EmptyPredictionContext.Instance].<br></br>
351345
* <embed src="images/LocalMerge_EmptyRoot.svg" type="image/svg+xml"></embed>
352346
*
353347
*
354-
* [.EMPTY] and anything is `#EMPTY`, so merged parent is
348+
* [EmptyPredictionContext.Instance] and anything is `#EMPTY`, so merged parent is
355349
* `#EMPTY`; return left graph.<br></br>
356350
* <embed src="images/LocalMerge_EmptyParent.svg" type="image/svg+xml"></embed>
357351
*
@@ -369,7 +363,7 @@ abstract class PredictionContext protected constructor(
369363
* <embed src="images/FullMerge_EmptyRoots.svg" type="image/svg+xml"></embed>
370364
*
371365
*
372-
* Must keep all contexts; [.EMPTY] in array is a special value (and
366+
* Must keep all contexts; [EmptyPredictionContext.Instance] in array is a special value (and
373367
* null parent).<br></br>
374368
* <embed src="images/FullMerge_EmptyRoot.svg" type="image/svg+xml"></embed>
375369
*
@@ -387,17 +381,17 @@ abstract class PredictionContext protected constructor(
387381
rootIsWildcard: Boolean
388382
): PredictionContext? {
389383
if (rootIsWildcard) {
390-
if (a === EMPTY) return EMPTY // * + b = *
391-
if (b === EMPTY) return EMPTY // a + * = *
384+
if (a === EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance // * + b = *
385+
if (b === EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance // a + * = *
392386
} else {
393-
if (a === EMPTY && b === EMPTY) return EMPTY // $ + $ = $
387+
if (a === EmptyPredictionContext.Instance && b === EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance // $ + $ = $
394388

395-
if (a === EMPTY) { // $ + x = [x,$]
389+
if (a === EmptyPredictionContext.Instance) { // $ + x = [x,$]
396390
val payloads = intArrayOf(b.returnState, EMPTY_RETURN_STATE)
397391
val parents = arrayOf(b.parent, null)
398392
return ArrayPredictionContext(parents, payloads)
399393
}
400-
if (b === EMPTY) { // x + $ = [x,$] ($ is always last if present)
394+
if (b === EmptyPredictionContext.Instance) { // x + $ = [x,$] ($ is always last if present)
401395
val payloads = intArrayOf(a.returnState, EMPTY_RETURN_STATE)
402396
val parents = arrayOf(a.parent, null)
403397
return ArrayPredictionContext(parents, payloads)
@@ -599,7 +593,7 @@ abstract class PredictionContext protected constructor(
599593

600594
val updated: PredictionContext
601595
if (parents.size == 0) {
602-
updated = EMPTY
596+
updated = EmptyPredictionContext.Instance
603597
} else if (parents.size == 1) {
604598
updated = SingletonPredictionContext.create(parents[0], context.getReturnState(0))
605599
} else {

0 commit comments

Comments
 (0)