【问题标题】:JAVA/JAVAC: label & break exact semanticJAVA/JAVAC:标记和打破精确语义
【发布时间】:2022-12-09 12:50:02
【问题描述】:

让我们考虑一些关于 Java 中的标签和中断语句的示例,并尝试通过 OpenJDK (v18) javac 对其进行编译。主要目标是获得标签和分支(并继续)的准确语义。

版本 1。

public class L {
    public static void main( String[] args) {
        System.out.println( "Start\n");
Label1:
Label2:
        break Label1;
        //break Label2;
        System.out.println( "Finish\n");
    }
}

编译和反汇编。

$ javac L.java && echo $?
0
$ javap -c L
    ...
    Code:
       0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #13                 // String Start\n
       5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: ldc           #21                 // String Finish\n
      13: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      16: return
}

字节码中没有任何 goto 指令。 (可能是字节码生成过程中优化的结果。这个例子的 AST 有一个 JCLabeledStatement 和一个 JCBreak 树节点。)以两种方式改变例子。

版本 2。

        //break Label1;
        break Label2;

它仍然像 Version1。

版本 3。

        break Label1;
        break Label2;
...
$ javac L.java
L.java:8: error: undefined label: Label2
        break Label2;
        ^
1 error

Version3 不正确吗? javac 编译 Version1 和 Version2 的时候是 feature 吗?或者示例都不正确,但 OpenJDK 的 javac 不会根据 JAVA 语言标准捕获语法错误。

可能的解释。

Label1:
Label2:
    break Label1;

方法

Label1: {
    Label2: {
        break Label1;
    }
}

和 Version3 意味着

Label1: {
    Label2: {
        break Label1;
    }
}
break Label2;

当然 break Label2 尝试在其范围之外使用 Label2。 (许多关于 break 的教程都集中在 for/while 上。)

【问题讨论】:

  • 你读过the language spec了吗?
  • 感谢您的链接。现在看来我明白了我最初在语法含义上的错误。

标签: java


【解决方案1】:

让我们看看语句是如何被解析的。标记语句的 syntax 是:

LabeledStatement:
    Identifier : Statement
LabeledStatementNoShortIf:
    Identifier : StatementNoShortIf

带标签的语句有一个标签并包含内部语句。

所以在前两种情况下,

Label1:
Label2:
break Label2;

被解析为单个标记语句,标签 Label1 作为标签,另一个标记语句作为其包含的语句。包含的标记语句以 Label2 作为标签,break 语句作为它的包含声明。

AST 看起来有点像这样:

LabeledStatement
    Identifier ('Label1')
    ':'
    LabeledStatement
        Identifier ('Label2')
        ':'
        BreakStatement

在第三种情况下,

Label1:
Label2:
break Label1;
break Label2;

被解析为两个语句。第一条语句结构同上,第二条语句为break语句。

规范还说:

带标签语句的标签范围是直接包含的语句。

这就解释了为什么第三条语句不能编译。 Label2标签的范围不包括第三种情况下的第二条语句(break Label2;)。它只包括紧跟在标签后面的语句。在前两种情况下,标签都在范围内,因为您在包含的语句中使用它们。

break 语句的语义指定为here

带有标签 Identifier 的 break 语句试图将控制权转移到与其标签具有相同标识符的封闭标签语句(§14.7);这个封闭语句称为中断目标,然后立即正常完成。在这种情况下,中断目标不必是 switchwhiledofor 语句。

在前两种情况下,break 语句转移控制本身,并立即使自身正常完成。这基本上是一个空操作,因此不会产生任何字节码。

【讨论】:

    猜你喜欢
    • 2015-07-30
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2014-09-25
    • 2018-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多