Расширить поведение анализатора Javac - PullRequest
0 голосов
/ 06 декабря 2018

У меня есть плагин Javac (реализация com.sun.source.util.Plugin), и я хочу, чтобы он изменил поведение анализатора.

Я могу расширить com.sun.tools.javac.parser.ParserFactory и зарегистрировать его в своем com.sun.tools.javac.util.Context, когда мой плагинin инициализируется, но есть проблема: плагины инициализируются сразу после инициализации JavacProcessingEnvironment, что создает все необходимое для компиляции, включая ParserFactory, но я могу только добавить свой ParserFactory в контекст, но не перезаписать существующий.

Вот фрагмент кода, который меня интересует (com.sun.tools.javac.api.BasicJavacTask):

public void initPlugins(Set<List<String>> pluginOpts) {
    PlatformDescription platformProvider = context.get(PlatformDescription.class);

    if (platformProvider != null) {
        for (PluginInfo<Plugin> pluginDesc : platformProvider.getPlugins()) {
            //Init platform plugins
        }
    }

    if (pluginOpts.isEmpty())
        return;

    Set<List<String>> pluginsToCall = new LinkedHashSet<>(pluginOpts);
    //JavacProcessingEnvironment is created here
    JavacProcessingEnvironment pEnv = JavacProcessingEnvironment.instance(context);
    //Since here, following will fail:
    //context.put(ParserFactory.parserFactoryKey, new MyParserFactory())
    ServiceLoader<Plugin> sl = pEnv.getServiceLoader(Plugin.class);
    for (Plugin plugin : sl) {
        for (List<String> p : pluginsToCall) {
            if (plugin.getName().equals(p.head)) {
                pluginsToCall.remove(p);
                try {
                    //My plugin is initialized here
                    plugin.init(this, p.tail.toArray(new String[p.tail.size()]));
                } catch (RuntimeException ex) {
                    throw new PropagatedException(ex);
                }
                break;
            }
        }
    }
}

Если бы у меня была возможность добавить свой плагин к platformProvider, он решит мою проблему следующим образом:ранняя инициализация плагина.

Что я делаю не так?Есть ли способ зарегистрировать мою собственную фабрику парсеров?Или, может быть, com.sun.tools.javac.util.Context не предназначен для использования разработчиками плагинов, а только разработчиками JDK?

1 Ответ

0 голосов
/ 07 декабря 2018

Обнаружен наименее хакерский способ, так как кажется, что функциональность com.sun.tools.javac.util.Context предназначена для разработчиков JDK (ага? Я должен был догадаться, когда добавлял 9 --add-exports=jdk.compiler/com.sun.tools.javac.*=mymodule аргументов)

    /*
    When I try to just put my factory here, I am getting
        AssertionError: duplicate context value
    but I can remove base factory by passing null
     */
    context.put(parserFactoryKey, (ParserFactory) null);
    MyParserFactory factory = new MyParserFactory(context);
    context.put(parserFactoryKey, factory);
    /*
    Now context contains my implementation of ParserFactory, but I
    also need to inject it into JavaCompiler which is the only object
    (FOR MY JDK: check your compiler behavior carefully with debugger)
    that have already got base factory with ParserFactory.instance()
    but didn't use it yet.
     */
    try {
        Field f = JavaCompiler.class.getDeclaredField("parserFactory");
        f.setAccessible(true);
        f.set(JavaCompiler.instance(context), factory);
    } catch (NoSuchFieldException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
...