Merge pull request #2076 from ewanmellor/swift-retain-cycles

Fix retain cycles in the Swift runtime
This commit is contained in:
Terence Parr 2017-10-27 08:28:14 -07:00 committed by GitHub
commit bce47ca744
14 changed files with 86 additions and 56 deletions

View File

@ -7,12 +7,6 @@
public class CommonToken: WritableToken {
///
/// An empty _org.antlr.v4.runtime.misc.Pair_ which is used as the default value of
/// _#source_ for tokens that do not have a source.
///
internal static let EMPTY_SOURCE: (TokenSource?, CharStream?) = (nil, nil)
///
/// This is the backing field for _#getType_ and _#setType_.
///
@ -47,7 +41,7 @@ public class CommonToken: WritableToken {
/// _org.antlr.v4.runtime.misc.Pair_ containing these values.
///
internal var source: (TokenSource?, CharStream?)
internal let source: TokenSourceAndStream
///
/// This is the backing field for _#getText_ when the token text is
@ -85,16 +79,16 @@ public class CommonToken: WritableToken {
public init(_ type: Int) {
self.type = type
self.source = CommonToken.EMPTY_SOURCE
self.source = TokenSourceAndStream.EMPTY
}
public init(_ source: (TokenSource?, CharStream?), _ type: Int, _ channel: Int, _ start: Int, _ stop: Int) {
public init(_ source: TokenSourceAndStream, _ type: Int, _ channel: Int, _ start: Int, _ stop: Int) {
self.source = source
self.type = type
self.channel = channel
self.start = start
self.stop = stop
if let tsource = source.0 {
if let tsource = source.tokenSource {
self.line = tsource.getLine()
self.charPositionInLine = tsource.getCharPositionInLine()
}
@ -111,20 +105,12 @@ public class CommonToken: WritableToken {
self.type = type
self.channel = CommonToken.DEFAULT_CHANNEL
self.text = text
self.source = CommonToken.EMPTY_SOURCE
self.source = TokenSourceAndStream.EMPTY
}
///
/// Constructs a new _org.antlr.v4.runtime.CommonToken_ as a copy of another _org.antlr.v4.runtime.Token_.
///
///
/// If `oldToken` is also a _org.antlr.v4.runtime.CommonToken_ instance, the newly
/// constructed token will share a reference to the _#text_ field and
/// the _org.antlr.v4.runtime.misc.Pair_ stored in _#source_. Otherwise, _#text_ will
/// be assigned the result of calling _#getText_, and _#source_
/// will be constructed from the result of _org.antlr.v4.runtime.Token#getTokenSource_ and
/// _org.antlr.v4.runtime.Token#getInputStream_.
///
///
/// - parameter oldToken: The token to copy.
///
public init(_ oldToken: Token) {
@ -135,14 +121,8 @@ public class CommonToken: WritableToken {
channel = oldToken.getChannel()
start = oldToken.getStartIndex()
stop = oldToken.getStopIndex()
if oldToken is CommonToken {
text = (oldToken as! CommonToken).text
source = (oldToken as! CommonToken).source
} else {
text = oldToken.getText()
source = (oldToken.getTokenSource(), oldToken.getInputStream())
}
text = oldToken.getText()
source = oldToken.getTokenSourceAndStream()
}
@ -252,12 +232,16 @@ public class CommonToken: WritableToken {
public func getTokenSource() -> TokenSource? {
return source.0
return source.tokenSource
}
public func getInputStream() -> CharStream? {
return source.1
return source.stream
}
public func getTokenSourceAndStream() -> TokenSourceAndStream {
return source
}
public var description: String {

View File

@ -65,7 +65,7 @@ public class CommonTokenFactory: TokenFactory {
}
public func create(_ source: (TokenSource?, CharStream?), _ type: Int, _ text: String?,
public func create(_ source: TokenSourceAndStream, _ type: Int, _ text: String?,
_ channel: Int, _ start: Int, _ stop: Int,
_ line: Int, _ charPositionInLine: Int) -> Token {
let t = CommonToken(source, type, channel, start, stop)
@ -74,7 +74,7 @@ public class CommonTokenFactory: TokenFactory {
if let text = text {
t.setText(text)
}
else if let cStream = source.1, copyText {
else if let cStream = source.stream, copyText {
t.setText(try! cStream.getText(Interval.of(start, stop)))
}

View File

@ -540,10 +540,12 @@ public class DefaultErrorStrategy: ANTLRErrorStrategy {
current = lookback!
}
let token = recognizer.getTokenFactory().create((current.getTokenSource(), current.getTokenSource()!.getInputStream()), expectedTokenType, tokenText,
CommonToken.DEFAULT_CHANNEL,
-1, -1,
current.getLine(), current.getCharPositionInLine())
let token = recognizer.getTokenFactory().create(
current.getTokenSourceAndStream(),
expectedTokenType, tokenText,
CommonToken.DEFAULT_CHANNEL,
-1, -1,
current.getLine(), current.getCharPositionInLine())
return token
}

View File

@ -26,7 +26,7 @@ open class Lexer: Recognizer<LexerATNSimulator>, TokenSource {
public static let MAX_CHAR_VALUE = Character.MAX_VALUE;
public var _input: CharStream?
internal var _tokenFactorySourcePair: (TokenSource?, CharStream?)
internal var _tokenFactorySourcePair: TokenSourceAndStream
///
/// How to create token objects
@ -87,13 +87,17 @@ open class Lexer: Recognizer<LexerATNSimulator>, TokenSource {
public var _text: String?
public override init() {
self._tokenFactorySourcePair = TokenSourceAndStream()
super.init()
self._tokenFactorySourcePair.tokenSource = self
}
public init(_ input: CharStream) {
super.init()
self._input = input
self._tokenFactorySourcePair = (self, input)
self._tokenFactorySourcePair = TokenSourceAndStream()
super.init()
self._tokenFactorySourcePair.tokenSource = self
self._tokenFactorySourcePair.stream = input
}
open func reset() throws {
@ -234,10 +238,10 @@ open class Lexer: Recognizer<LexerATNSimulator>, TokenSource {
open override func setInputStream(_ input: IntStream) throws {
self._input = nil
self._tokenFactorySourcePair = (self, _input!)
self._tokenFactorySourcePair = makeTokenSourceAndStream()
try reset()
self._input = input as? CharStream
self._tokenFactorySourcePair = (self, _input!)
self._tokenFactorySourcePair = makeTokenSourceAndStream()
}
@ -449,4 +453,8 @@ open class Lexer: Recognizer<LexerATNSimulator>, TokenSource {
// TODO: Do we lose character or line position information?
try _input!.consume()
}
internal func makeTokenSourceAndStream() -> TokenSourceAndStream {
return TokenSourceAndStream(self, _input)
}
}

View File

@ -45,9 +45,7 @@ public class LexerInterpreter: Lexer {
for i in 0..<_decisionToDFALength {
_decisionToDFA[i] = DFA(atn.getDecisionState(i)!, i)
}
super.init()
self._input = input
self._tokenFactorySourcePair = (self, input)
super.init(input)
self._interp = LexerATNSimulator(self, atn, _decisionToDFA, _sharedContextCache)
if atn.grammarType != ATNType.lexer {

View File

@ -112,7 +112,8 @@ public class ListTokenSource: TokenSource {
}
let stop = max(-1, start - 1)
eofToken = _factory.create((self, getInputStream()!), CommonToken.EOF, "EOF", CommonToken.DEFAULT_CHANNEL, start, stop, getLine(), getCharPositionInLine())
let source = TokenSourceAndStream(self, getInputStream())
eofToken = _factory.create(source, CommonToken.EOF, "EOF", CommonToken.DEFAULT_CHANNEL, start, stop, getLine(), getCharPositionInLine())
}
return eofToken!

View File

@ -17,9 +17,9 @@ public class RecognitionException {
///
private final var recognizer: RecognizerProtocol?
private final let ctx: RuleContext?
private final weak var ctx: RuleContext?
private final let input: IntStream
private final var input: IntStream?
///
/// The current _org.antlr.v4.runtime.Token_ when an error occurred. Since not all streams
@ -101,10 +101,13 @@ public class RecognitionException {
/// where this exception was thrown, or `null` if the stream is not
/// available.
///
public func getInputStream() -> IntStream {
public func getInputStream() -> IntStream? {
return input
}
public func clearInputStream() {
input = nil
}
public func getOffendingToken() -> Token {
return offendingToken
@ -125,4 +128,8 @@ public class RecognitionException {
public func getRecognizer() -> RecognizerProtocol? {
return recognizer
}
public func clearRecognizer() {
self.recognizer = nil
}
}

View File

@ -59,7 +59,7 @@ open class RuleContext: RuleNode {
public static let EMPTY = ParserRuleContext()
/// What context invoked this rule?
public var parent: RuleContext?
public weak var parent: RuleContext?
/// What state invoked the rule associated with this context?
/// The "return address" is the followState of invokingState

View File

@ -98,5 +98,7 @@ public protocol Token: class, CustomStringConvertible {
///
func getInputStream() -> CharStream?
func getTokenSourceAndStream() -> TokenSourceAndStream
var visited: Bool { get set }
}

View File

@ -15,10 +15,33 @@ public protocol TokenFactory {
/// error handling strategy. If text!=null, than the start and stop positions
/// are wiped to -1 in the text override is set in the CommonToken.
///
func create(_ source: (TokenSource?, CharStream?), _ type: Int, _ text: String?,
func create(_ source: TokenSourceAndStream, _ type: Int, _ text: String?,
_ channel: Int, _ start: Int, _ stop: Int,
_ line: Int, _ charPositionInLine: Int) -> Token
/// Generically useful
func create(_ type: Int, _ text: String) -> Token
}
/**
Holds the references to the TokenSource and CharStream used to create a Token.
These are together to reduce memory footprint by having one instance of
TokenSourceAndStream shared across many tokens. The references here are weak
to avoid retain cycles.
*/
public class TokenSourceAndStream {
///
/// An empty TokenSourceAndStream which is used as the default value of
/// _#source_ for tokens that do not have a source.
///
public static let EMPTY = TokenSourceAndStream()
public weak var tokenSource: TokenSource?
public weak var stream: CharStream?
public init(_ tokenSource: TokenSource? = nil, _ stream: CharStream? = nil) {
self.tokenSource = tokenSource
self.stream = stream
}
}

View File

@ -50,7 +50,7 @@ open class LexerATNSimulator: ATNSimulator {
}
internal let recog: Lexer?
internal weak var recog: Lexer?
///
/// The current token's starting index into the character stream.

View File

@ -250,7 +250,8 @@ open class ParserATNSimulator: ATNSimulator {
}
return false
}()
internal final var parser: Parser
internal final unowned let parser: Parser
public final var decisionToDFA: [DFA]

View File

@ -6,7 +6,7 @@
public class TerminalNodeImpl: TerminalNode {
public var symbol: Token
public var parent: ParseTree?
public weak var parent: ParseTree?
public init(_ symbol: Token) {
self.symbol = symbol

View File

@ -149,14 +149,18 @@ public class RuleTagToken: Token, CustomStringConvertible {
return nil
}
///
///
/// The implementation for _org.antlr.v4.runtime.tree.pattern.RuleTagToken_ always returns `null`.
///
public func getInputStream() -> CharStream? {
return nil
}
///
public func getTokenSourceAndStream() -> TokenSourceAndStream {
return TokenSourceAndStream.EMPTY
}
///
/// The implementation for _org.antlr.v4.runtime.tree.pattern.RuleTagToken_ returns a string of the form
/// `ruleName:bypassTokenType`.
///