目标是以编程方式使用javac(没有其他编译器)来编译扩展另一个类的Java类,该类不作为java源或字节码存在,并且不能作为这些提供。 Java类作为源文件存在,或者作为字符串存在于内存中(我知道如何从字符串编译)。
我认为我需要挂钩查找预编译类。但到目前为止,我无法找到这一部分。因此,如果我知道javac究竟是如何查找预编译的类,我如何能够挂钩并提供我自己的表示,那么这个问题就会得到解答。
或允许我动态提供所需依赖关系的任何其他方式......
修改 由于提到Groovy并且目的受到质疑,让我举一个例子...... 想象一下你在Groovy中有一个名为G的类,有一个类J的字段,它是一个Java类,J扩展了G.我不能编译没有J的G而且我不能编译没有G的J.但是我有G的AST和if我可以用javac连接AST,我将能够编译J然后在Groovy编译器G中 - 或者反过来。目前,这是通过生成存根来绕过的,但我正在寻找更好的解决方案。
编辑2: 使它绝对清楚。这个问题的最终目标是让groovy编译器和javac编译器以某种方式相互通信,它们可以告诉对方是否有某个类,然后让其他编译器知道某个类。让我再说一遍,由于未解析的类,字节码中的存根无法工作。在源头他们的工作,当依赖进口解决至少相似。但是由于Groovy编译器的性质,它实际上类似于javac所处理的东西,我们必须在很早的阶段生成这些源存根,对于大多数可以在groovy中应用的ast变换来说太早了。这是一个问题
答案 0 :(得分:3)
如果第一个评论是正确的,并且你正在尝试编译当你没有B的源代码或字节码时扩展B的A类,那么我认为答案是“你不能”。如果你考虑编译子类意味着什么,你会发现编译器需要来自超类的细节 - 有什么方法,有什么抽象方法,什么放在跳转表中,什么保护变量和方法可能Java仍然是一种强类型语言(至少在撰写本文时)。
答案 1 :(得分:0)
我可以建议以下内容。你确实不需要提供真实的"基类。你必须提供基类
因此,您可以生成满足所有这些要求的类,在类路径中使用此基类编译子类,然后删除此假基类。
如何创建这样的课程?您可以使用ASM生成源代码并对其进行编译或生成字节代码。
问题是如何获得类的所有元素:必需的构造函数和超类方法。我不知道Groovy,但我希望你可以使用反射从Groovy类中获取所有这些信息。
修改
其他解决方案是使用接口并解耦Groovy和Java代码。只需在Java中定义将从Groovy引用的接口。此接口不具有任何其他依赖项,可以先编译。然后编译Groovy代码。然后编译扩展Groovy类的java类。这有什么问题?
答案 2 :(得分:0)
看起来我现在可以自己回答这个问题了。通过直接使用com.sun.tools.javac.main.JavaCompiler,我可以提供一个Context,我在其中设置了一个自定义的ClassReader。然后可以覆盖loadClass方法,以提供我自己的ClassSymbol。我使用此代码提供ClassSymbol
的类型 // ClassSymbol cs is from super.loadClass
// Context context is same as used for creating the compiler
Type.ClassType newClassType = new Type.ClassType(Type.noType, List.<Type>nil(), cs);
cs.type = newClassType;
newClassType.tsym = cs;
Names names = Names.instance(context);
Symtab symtab = Symtab.instance(context);
newClassType.supertype_field = symtab.objectType;
cs.kind = Kinds.TYP;
Scope members = new Scope(cs);
cs.members_field = members;
Type.MethodType mt = new Type.MethodType(List.<Type>nil(), symtab.voidType, List.<Type>nil(), symtab.methodClass);
Symbol.MethodSymbol constructor = new Symbol.MethodSymbol(Flags.PUBLIC, names.init, mt, cs);
members.enter(constructor);
代码将为缺少的类型创建一个幻像类型(扩展Object),并提供不带参数和主体的构造函数。不确定这是不是最好的方法,但让javac运行
已经足够了