Skip to content

Running annotation-based picocli applications without runtime reflection #539

@remkop

Description

@remkop

The annotation processor implemented in #500 can create a CommandSpec model at compile time.
One use case for an annotation processor like this is to generate source code at compile time that would allow picocli-based applications to run without runtime reflection.

This ticket is to explore some ideas for achieving that.

One idea is to generate code (actually modify an existing annotated class) to implement an interface, where a method in this interface generates a CommandSpec model. Picocli needs to be modified to not perform reflection when the user object implements this interface, and instead call the interface method to obtain the CommandSpec.

Example input:

@picocli.codegen.GenerateModel
class App {
    @Option(names = "-x")
    private int x;
    
    public static void main(String[] args) {
        CommandLine cmd = new CommandLine(new App());
        cmd.parseArgs(args);
    }
}

Example output of annotation processor:

// after annotation processing:
class App implements picocli.CommandLine.Model.ICommandSpecFactory {
    @Option(names = "-x")
    private int x;
    
    public static void main(String[] args) {
        CommandLine cmd = new CommandLine(new AppV1());
        cmd.parseArgs(args);
    }
    
    // ICommandSpecFactory implementation (generated)
    public CommandSpec getCommandSpec() {
        CommandSpec result = CommandSpec.wrapWithoutInspection(this);
        result.addOption(OptionSpec.builder("-x").type(int.class)
            .getter(new IGetter() {
                public Object get() {
                    return App.this.x;
                }
            })
            .setter(new ISetter() {
                public Object set(Object newValue) {
                    Object old = App.this.x;
                    App.this.x = (Integer) newValue;
                    return old;
                }
            })
            .build());
            
        // other options
        return result;
    }
}

Lombok does something similar: it inserts accessor code into an existing class for fields annotated with @Getter and @Setter.
The way Lombok does this is by using internal APIs from Javac and the Eclipse compiler.

Metadata

Metadata

Assignees

No one assigned

    Labels

    theme: codegenAn issue or change related to the picocli-codegen module

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions