扩展 Javac 解析器行为
Extend Javac parser behavior
我有一个 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 开发人员使用?
找到了最简单的方法,因为 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);
}
我有一个 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 开发人员使用?
找到了最简单的方法,因为 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);
}