Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ParseContext#parse() throws StackOverflowError on tiny stack Android devices #1525

Closed
2 tasks done
gfx opened this issue Dec 18, 2016 · 10 comments
Closed
2 tasks done

Comments

@gfx
Copy link
Contributor

gfx commented Dec 18, 2016

Before submitting an issue to ANTLR, please check off these boxes:


This issue is alike to #744, but with some extra information.

I have found this issue on my Android library project Android-Orma, which parses SQLite DDL with an ANTLR4-based SQL parser.

How can I avoid this issue?

new Thread(group, runnable, name, stackSize) does not affect this issue. In fact, stackSize seems to be ignored on Android (at least in older versions like Android 4.x).

Environment

Stacktrace

Copied from maskarade/Android-Orma#352 (comment)

org.antlr.v4.runtime.misc.ParseCancellationException: SQL is too complex to parse: CREATE TABLE foo (title TEXT DEFAULT (''))
at com.github.gfx.android.orma.migration.sqliteparser.SQLiteParserUtils.parseIntoCreateTableStatement(SQLiteParserUtils.java:64)
at com.github.gfx.android.orma.migration.test.sqliteparser_test.SQLiteParserUtilsTest.testComplexTable(SQLiteParserUtilsTest.java:161)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1584)
Caused by: java.lang.StackOverflowError
at java.util.Arrays.equals(Arrays.java:1582)
at org.antlr.v4.runtime.atn.ArrayPredictionContext.equals(ArrayPredictionContext.java:78)
at java.util.LinkedHashMap.get(LinkedHashMap.java:259)
at org.antlr.v4.runtime.misc.DoubleKeyMap.get(DoubleKeyMap.java:38)
at org.antlr.v4.runtime.atn.PredictionContext.mergeArrays(PredictionContext.java:363)
at org.antlr.v4.runtime.atn.PredictionContext.merge(PredictionContext.java:174)
at org.antlr.v4.runtime.atn.ATNConfigSet.add(ATNConfigSet.java:155)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1529)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closure_(ParserATNSimulator.java:1583)
at org.antlr.v4.runtime.atn.ParserATNSimulator.closureCheckingStopState(ParserATNSimulator.java:1513)
(snip)
@ericvergnaud
Copy link
Contributor

ericvergnaud commented Dec 18, 2016 via email

@mike-lischke
Copy link
Member

@ericvergnaud is this exploration something that can publicly be seen? I've been thinking for a while about changing the highly recursive ATN simulator closure code to a loop based one, as it can exhaust the stack even on a a full desktop machine with increased stack size (e.g. for complex expression rules with left recursion).

@ericvergnaud
Copy link
Contributor

ericvergnaud commented Dec 20, 2016 via email

@mike-lischke
Copy link
Member

Hmm, I believe you mean the iterative walker, which meanwhile made it into the runtime.

@ericvergnaud
Copy link
Contributor

ericvergnaud commented Dec 20, 2016 via email

@parrt
Copy link
Member

parrt commented Dec 20, 2016

Yeah, not sure we can do anything about wanting to parse a complex language on a tiny device.

@gfx
Copy link
Contributor Author

gfx commented Dec 24, 2016

Hmm, if CREATE TABLE foo (title TEXT DEFAULT ('')) is too complex to parse, I think no language can be processed with ANTLR4.

Anyway, now I got it. Thanks.

@parrt
Copy link
Member

parrt commented Dec 24, 2016

@gfx the recursive overflow is actually a function of the deeply nested SQL grammar not the input. A few tokens could easily push recursion into the hundreds of invocations. :)

@mike-lischke
Copy link
Member

@parrt Whenever I've seen a stack overflow due to deep recursion it was during ATN execution, never in the parser itself (have frequently seen stack depths of 1000+). The closure code does a roundtrip per ATN state (not per rule as the parser), so it is much more sensitive to the parsed language. By converting this to a loop we will probably get a much better behavior.

@parrt
Copy link
Member

parrt commented Dec 25, 2016

Yes, but the ATN is a version of the grammar 1-to-1. Analysis during parsing must traverse the ATN (grammar) to answer prediction questions. The ALL(*) is extremely complex particularly when you consider optimizations. Converting such a recursion process to a loop would be nigh on impossible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants