当前位置: 首页 > 面试题库 >

使用Eclipse编译器进行编译时,LocalVariableTypeTable中出现奇怪的“!*”条目

段成益
2023-03-14
问题内容

让我们使用Eclipse Mars.2软件包中的ECJ编译器编译以下代码:

import java.util.stream.*;

public class Test {
    String test(Stream<?> s) {
        return s.collect(Collector.of(() -> "", (a, t) -> {}, (a1, a2) -> a1));
    }
}

编译命令如下:

$ java -jar org.eclipse.jdt.core_3.11.2.v20160128-0629.jar -8 -g Test.java

成功编译后,让我们使用来检查生成的类文件javap -v -p Test.class。最有趣的是为(a, t) -> {}lambda
生成的合成方法:

  private static void lambda$1(java.lang.String, java.lang.Object);
    descriptor: (Ljava/lang/String;Ljava/lang/Object;)V
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=0, locals=2, args_size=2
         0: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0     a   Ljava/lang/String;
            0       1     1     t   Ljava/lang/Object;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       1     1     t   !*

看到这个!*条目我感到很惊讶LocalVariableTypeTable。JVM规范涵盖了
LocalVariableTypeTable属性,并说:

constant_pool索引处的条目必须包含表示字段签名的CONSTANT_Utf8_info结构(第4.4.7节),该签名对源程序中的局部变量的类型进行编码(第4.7.9.1节)。

第4.7.9.1节为字段签名定义了一种语法,如果我理解正确,它不会涵盖任何与相似的内容!*

还应注意,javac编译器和较早的ECJ
3.10.x版本都不会生成此条LocalVariableTypeTable目。是否存在!*一些非标准的Eclipse扩展,或者我在JVM规范中缺少某些内容?这是否意味着ECJ不符合JVM规范?!*实际含义是什么,LocalVariableTypeTable属性中可能还会出现其他类似的字符串吗?


问题答案:

!ecj使用该令牌在通用签名中对捕获类型进行编码。因此!*表示捕获了无限制的通配符。

在内部,ecj使用的两种风格CaptureBinding,一种实现JLS
18.4
称为“新鲜类型变量”,另一种实现捕获la
JLS
5.1.10(使用与“自由类型变量”相同的术语)。两者都使用产生签名!。仔细观察,在此示例中,我们有一个“旧式”捕获:t具有type
capture#1-of ?,捕获<T>in Stream<T>

问题是:JVMS
4.7.9.1。似乎没有为这种新鲜的类型变量定义编码(其他属性在源代码中没有对应关系,因此也没有名称)。

我无法为lambda javac发出任何信号LocalVariableTypeTable,因此它们可能只是避免回答这个问题。

鉴于两个编译器都同意推断t捕获,为什么一个编译器生成LVTT,而另一个则不生成?JVMS
4.7.14拥有这个

这种差异仅对类型使用类型变量或参数化类型的变量有意义。

根据JLS,捕获是新鲜的类型变量,因此LVTT条目很重要,并且在JVMS中没有为这种类型指定格式是遗漏的。

后果

上面仅描述和解释了现状,表明没有任何规范告诉编译器的行为与当前状态有所不同。显然,这不是完全理想的情况。

  1. 可能有人希望与Oracle联系,并提到Java 8引入了JVMS部分未涵盖的情况。一旦局部变量也受类型推断的影响,这种情况可能变得更加相关。
  2. 希望看到当前情况的负面影响的任何人都可以通过494198(ecj)报名参加,否则优先级较低。

更新:
同时,有人报告了一个示例,其中需要常规的
Signature
属性(不能机会性地省略)来编码无法根据JVMS进行编码的类型。在这种情况下,javac也会创建未指定的字节码。根据后续文章, 任何变量 都不应该
具有这样的类型,但我认为讨论尚未结束(顺便说一句,JLS尚未确保这一目标)。

更新2: 在收到规范作者的建议后,我看到了最终解决方案的三个部分:

(1)任何字节码属性中的每个类型签名都 必须 遵守JVMS 4.7.9.1中的语法。ecj !和javac 都不<captured wildcard>合法。

(2)编译器 在不存在合法编码的 情况下 近似类型签名,例如,使用擦除而不是捕获。对于LVTT条目,这种近似应视为合法。

(3)JLS 必须 确保只有使用JVMS 4.7.9.1可编码的类型才会出现在必须生成Signature属性的位置。

对于ecj的将来版本,已解决(1)和(2)。我不能谈论javac和JLS何时进行相应修复的时间表。



 类似资料:
  • 由于IntelliJ,我发现需要将以下选项传递给:

  • 我试图使用Boost的program_options库来构建一个简单的命令行应用程序库,但它失败了,出现了一个非常奇怪的错误。以下是所有有问题的代码(是的,非常简单): 发生的编译错误是: 错误C2679:二进制' 错误C2228:左边的'。“失败”必须具有类/struct/union c:\work\digitalbp\projects\releasedev\nsdk\u 4\u 0\dev\s

  • 问题内容: 我有一个在j2me项目和android项目中引用的java项目。在这个项目中,我想使用条件编译。 就像是… 我一直在阅读有关此内容的信息,但没有发现任何有用的信息。 问题答案: 您可以使用Antenna(有一个Eclipse插件,您可以将其与Ant构建系统一起使用)。我正在按照您描述的方式在我的项目中使用它,并且效果很好:) 编辑:这是与@ WhiteFang34解决方案相关的示例,是

  • 问题内容: 在Eclipse中运行Ant构建时,出现以下错误消息,并且Ant构建失败。 错误消息: 运行javac.exe编译器时出错 停止构建的行: 有人遇到过类似的问题吗?还是有人知道这是怎么回事? 系统信息:Eclipse Helio,JDK 1.5 问题答案: 我有同样的问题,问题是,在Eclipse中,java.home是指JRE而不是JDK。我进行了更改,构建成功。您可以执行以下操作将

  • 当我从Eclipse构建到maven构建时,我发现了项目中的许多问题。我使用2.5.1编译器插件。 JDK是开放的-JDK-7 我在一个新项目中隔离了这个问题,并对其进行了深入研究。问题是: 这无法使用javaC进行编译(但在Eclipse中工作),并说明以下错误: [错误]无法执行目标组织。阿帕奇。专家插件:maven编译器插件:2.5.1:项目测试时编译(默认编译):编译失败 [错误]/hom