gpt4 book ai didi

java - 与JSE JavaDoc相比,可以存在哪些类层次结构差异?

转载 作者:行者123 更新时间:2023-11-30 02:13:54 25 4
gpt4 key购买 nike

我目前正在Maven后期编译任务中生成一些ASM代码。在Java 6中,引入了StackMapTable来表示堆栈上的数据类型,这在更高版本中是必需的。所以我会自动确定可以在堆栈上的最具体的类。
现在我遇到了一个问题,在我的VM ThaiBuddhistDate和HijrahDate中继承了ChronoLocalDateImpl,因此它将在StackMapTable中创建此类型,这显然会在其他VM(甚至是版本)中崩溃。所以我想,也许我应该将计算更改为最小强制性的,这可能会(从理论上)引起类和接口的类似问题。
现在,我正在尝试为我的问题找到解决方案,因此我必须找出可能会出现的差异。

附加类只能在继承层次结构中的任何地方出现吗?
假设JavaDoc具有如下继承层次结构:

对象-Foo-酒吧-FooBar

到处都可以在继承结构中添加其他类吗?

对象-巴兹-富-酒吧-富巴尔

对象-Foo-Baz-酒吧-FooBar

物件-Foo-酒吧-Baz-FooBar

类似于接口:
接口是否还可以继承文档中未定义的其他接口,或者“仅”类可以具有其他独立的接口或基于定义的接口或什至没有接口的接口?

最佳答案

看来您正在使用COMPUTE_FRAMES选项,这将导致ASM库通过getCommonSuperClass合并可能的代码路径中遇到的类型,这与旧的验证程序所做的类似,并且在某种程度上扭曲了堆栈映射表的概念。

正如您已经提到的,ASM的getCommonSuperClass实现可能返回实际上不可访问的类型(例如JRE内部基类),并且会忽略接口关系。更大的问题是,您无法使用此方法的其他实现来解决此问题,因为传递给此方法的信息不足以确定正确的类型。

正确的类型是随后将需要的类型,当然,它也应该与通过所有可能的代码路径提供的类型兼容,验证者将/应该检查该类型。如果您的代码生成器以生成有效代码的方式进行设计,则指定后续所需的类型应足以创建有效的堆栈映射表条目,但是传递给getCommonSuperClass的传入类型不足以告诉您将要执行的操作。所需的类型。

为了说明问题,请考虑以下示例类

class Example {
public static CharSequence problematicMethod() {
return Math.random()>0.5? new StringBuilder("x"): new StringBuffer("y");
}
}


以及以下代码分析已编译的(例如,由 javac)类以及在被告知从头开始重新计算堆栈映射帧时,默认情况下将生成的ASM:

static void printFrame(int nLocal, Object[] local, int nStack, Object[] stack) {
StringBuilder sb = decode(new StringBuilder().append("Locals: "), local, nLocal);
System.out.println(decode(sb.append(", Stack: "), stack, nStack));
}
private static StringBuilder decode(StringBuilder sb, Object[] array, int num) {
if(num==0) return sb.append("[]");
sb.append('[');
for(int ix = 0; ix<num; ix++) {
Object o = array[ix];
if(o==Opcodes.UNINITIALIZED_THIS) sb.append("this <uninit>");
else if(o==Opcodes.INTEGER) sb.append("int");
else if(o==Opcodes.FLOAT) sb.append("float");
else if(o==Opcodes.DOUBLE) sb.append("double");
else if(o==Opcodes.LONG) sb.append("long");
else if(o==Opcodes.NULL) sb.append("null");
else if(o==Opcodes.TOP) sb.append("-");
else sb.append(Type.getObjectType(o.toString()).getClassName());
sb.append(",");
}
sb.setCharAt(sb.length()-1, ']');
return sb;
}
public static void main(String[] args) throws IOException {
final MethodVisitor printFramesMV = new MethodVisitor(Opcodes.ASM5) {
@Override public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
printFrame(nLocal, local, nStack, stack);
}
};
final ClassVisitor printFrames = new ClassVisitor(Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return name.equals("problematicMethod")? printFramesMV: null;
}
};
ClassReader cr = new ClassReader(Example.class.getName());
System.out.println("##original");
cr.accept(printFrames, ClassReader.EXPAND_FRAMES);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cr.accept(cw, ClassReader.SKIP_FRAMES);
System.out.println("##from ASM");
new ClassReader(cw.toByteArray()).accept(printFrames, ClassReader.EXPAND_FRAMES);
}


这将打印

##original
Locals: [], Stack: []
Locals: [], Stack: [java.lang.CharSequence]
##from ASM
Locals: [], Stack: []
Locals: [], Stack: [java.lang.AbstractStringBuilder]


这显示出与您在问题中解释的相同问题,ASM将生成一个引用特定于实现的类的框架。 javac生成的代码涉及必需的类型,该类型与方法的返回类型兼容。您可以在 StringBuilder中研究 StringBuffergetCommonSuperClass并发现它们都实现了 CharSequence,但这不足以理解 CharSequence在此处是正确的类型,因为我们可以将示例简单地更改为

class Example {
public static Appendable problematicMethod() {
return Math.random()>0.5? new StringBuilder("x"): new StringBuffer("y");
}
}


并得到

##original
Locals: [], Stack: []
Locals: [], Stack: [java.lang.Appendable]
##from ASM
Locals: [], Stack: []
Locals: [], Stack: [java.lang.AbstractStringBuilder]


由于传入的类同时实现两个接口,因此仅通过查看传入的 CharSequenceAppendable类型就无法确定 StringBuilderStringBuffer是正确的合并类型。

要进一步评估此问题,请查看

class Example {
public static Comparable problematicMethod() {
return Math.random()>0.5? BigInteger.valueOf(123): Double.valueOf(1.23);
}
}


产生

##original
Locals: [], Stack: []
Locals: [], Stack: [java.lang.Comparable]
##from ASM
Locals: [], Stack: []
Locals: [], Stack: [java.lang.Number]


在这里,ASM的结果是 public类型,但是此通用基类未实现必需的 Comparable,因此该代码实际上已损坏。



对于使用ASM的 COMPUTE_FRAMES选项的所有代码生成器来说,这是很幸运的,HotSpot的验证程序对接口类型具有很大的容忍度,换句话说,它根本不验证分配的正确性(包括方法的接收者)调用)时,两种类型中的至少一种是接口。

如果您想生成在验证者严格执行其任务的情况下仍能生存的代码,即使是对于接口,您也不应使用该选项,而无需使用 COMPUTE_FRAMES选项并发出正确的 visitFrame调用来开始生成堆栈映射框架(或插入适当的节点(如果您使用的是Tree API)。

似乎普遍担心这样做,但这并不复杂。如前所述,它基本上意味着说明您的代码生成器已经知道的内容。实际上,这并不是要查找常见的类型,而是要指定以后要使用的类型,如果代码生成器是正确的,那么就已经确定了,但是,否则,ASM的计算也无法修复该代码。

待在您的特定示例中,在处理 ThaiBuddhistDateHijrahDate时,您已经知道在分支合并点之后(我想)您将它们作为 ChronoLocalDate处理,而ASM最终在实现特定的非< cc>类型,但是如果该类型不存在,则ASM仅使用 public,因为它不考虑接口。如果ASM考虑了接口,则必须在 java.lang.ObjectChronoLocalDate之间进行选择,两者都不比另一个更具体。这种设计根本无法解决。

为了进一步说明,“合并传入的类型”和“将使用什么”之间的结果有何不同,请看一下

class Example {
public static void problematicMethod() {
if(Math.random()>0.5) {
java.awt.ScrollPane b = new java.awt.ScrollPane();
}
else {
javax.swing.JTabbedPane t = new javax.swing.JTabbedPane();
}
}
}


##original
Locals: [], Stack: []
Locals: [], Stack: []
##from ASM
Locals: [], Stack: []
Locals: [java.awt.Container], Stack: []


在这里,ASM浪费了资源,无法在一个深层的类层次结构树中找到通用的基类,而仅仅声明“删除变量”就足够了……

关于java - 与JSE JavaDoc相比,可以存在哪些类层次结构差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49222338/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com