我有一个Javac插件(com.sun.source.util.Plugin
的实现),我希望它可以改变解析器的行为。
初始化插件后,我可以扩展com.sun.tools.javac.parser.ParserFactory
并将其注册到com.sun.tools.javac.util.Context
中,但是存在一个问题:
插件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开发人员使用?
答案 0 :(得分:0)
发现了最不容易破解的方法,因为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);
}