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

StackOverflowError when subcommand is subclass #787

Closed
remkop opened this issue Aug 17, 2019 · 1 comment
Closed

StackOverflowError when subcommand is subclass #787

remkop opened this issue Aug 17, 2019 · 1 comment

Comments

@remkop
Copy link
Owner

remkop commented Aug 17, 2019

As reported by @petermr on the picocli mailing list, when a subcommand subclasses its parent command, a StackOverflowError is thrown during initialization.

To reproduce:

@Command(name = "parent", subcommands = Sub.class)
static class Parent { }

@Command(name = "sub")
static class Sub extends Parent {}

@Test
public void testSimple() {
    new CommandLine(new Parent());
}

Example stack trace (differs per invocation):

java.lang.StackOverflowError
	at java.util.regex.Pattern.atom(Pattern.java:2200)
	at java.util.regex.Pattern.sequence(Pattern.java:2081)
	at java.util.regex.Pattern.expr(Pattern.java:1998)
	at java.util.regex.Pattern.group0(Pattern.java:2907)
	at java.util.regex.Pattern.sequence(Pattern.java:2053)
	at java.util.regex.Pattern.expr(Pattern.java:1998)
	at java.util.regex.Pattern.group0(Pattern.java:2907)
	at java.util.regex.Pattern.sequence(Pattern.java:2053)
	at java.util.regex.Pattern.expr(Pattern.java:1998)
	at java.util.regex.Pattern.compile(Pattern.java:1698)
	at java.util.regex.Pattern.<init>(Pattern.java:1351)
	at java.util.regex.Pattern.compile(Pattern.java:1028)
	at picocli.CommandLine$RegexTransformer$Builder.addPattern(CommandLine.java:4628)
	at picocli.CommandLine$RegexTransformer.createDefault(CommandLine.java:4555)
	at picocli.CommandLine$Model$CommandSpec.<init>(CommandLine.java:5082)
	at picocli.CommandLine$Model$CommandSpec.wrapWithoutInspection(CommandLine.java:5101)
	at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:9527)
	at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:5116)
	at picocli.CommandLine.<init>(CommandLine.java:223)
	at picocli.CommandLine.toCommandLine(CommandLine.java:3212)
	at picocli.CommandLine.access$12900(CommandLine.java:145)
	at picocli.CommandLine$Model$CommandReflection.initSubcommands(CommandLine.java:9603)
	at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:9539)
	at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:5116)
	at picocli.CommandLine.<init>(CommandLine.java:223)
	at picocli.CommandLine.toCommandLine(CommandLine.java:3212)
	at picocli.CommandLine.access$12900(CommandLine.java:145)
	at picocli.CommandLine$Model$CommandReflection.initSubcommands(CommandLine.java:9603)
	at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:9539)
...
	at picocli.CommandLine.toCommandLine(CommandLine.java:3212)
	at picocli.CommandLine.access$12900(CommandLine.java:145)
	at picocli.CommandLine$Model$CommandReflection.initSubcommands(CommandLine.java:9603)
	at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:9539)
	at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:5116)
	at picocli.CommandLine.<init>(CommandLine.java:223)
@remkop remkop added this to the 4.0.3 milestone Aug 17, 2019
@remkop remkop changed the title StackOverflowError when subclassing subcommands StackOverflowError when subcommand is subclass Aug 18, 2019
remkop added a commit that referenced this issue Aug 18, 2019
…` when subcommand is subclass of itself
@remkop
Copy link
Owner Author

remkop commented Aug 18, 2019

I believe this is simply a consequence of how reuse works in picocli:
When a command B subclasses another command A, it will get all attributes (and subcommands) of A.
If one of these subcommands is actually command B, then B becomes a subcommand of itself. This is not supported.

I improved things by throwing a more friendly InitializationException when such an eternal loop is detected.

Example 1:

    @Command(name = "self-ref", subcommands = Simplest.class)
    class Simplest {}

This gives the following InitializationException: "self-ref (Simplest) cannot be a subcommand of itself"

Example 2:

    @Command(name = "parent", subcommands = Sub.class)
    class Parent { }

    @Command(name = "sub")
    class Sub extends Parent {}

This gives the following InitializationException: "parent (Parent) has a subcommand (Sub) that is a subclass of itself".

To avoid this, add another level that does not have subclasses, and put the shared functionality there:

    //@Command // optionally define some shared command annotation attributes
    class SharedFunctionality {}

    @Command(name = "parent", subcommands = Sub.class)
    class Parent extends SharedFunctionality { }

    @Command(name = "sub")
    class Sub extends SharedFunctionality {}

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

1 participant