多态类型中原始类型的类型信息
Type information for primitive types in polymorphic types
给定以下对象:
object Foo {
val bar: List[Int] = List(1, 2, 3)
}
当我们将此文件编译为 JVM 字节码时,由于类型擦除以及 Java 不支持原始类型作为泛型类型的参数这一事实,这被翻译成 List<Object>
.
我们可以通过使用 javap -l
编译和检查 .class
来看到这一点:
public static com.yuvalitzchakov.github.Foo$ MODULE$;
descriptor: Lcom/yuvalitzchakov/github/Foo$;
flags: ACC_PUBLIC, ACC_STATIC
public scala.collection.immutable.List<java.lang.Object> bar();
descriptor: ()Lscala/collection/immutable/List;
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #19 // Field bar:Lscala/collection/immutable/List;
4: areturn
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/yuvalitzchakov/github/Foo$;
Signature: #17 // ()Lscala/collection/immutable/List<Ljava/lang/Object;>;
但是,如果我们将其编译成 JAR 文件,然后将其作为不同 Scala 项目中的依赖项并尝试将 Foo.bar
设置为不同的值,Scala 编译器会将此类型推断为 List[Int]
,不是 List[Object]
:
浏览完 .class
文件后,我找不到有关类型参数的信息,而这将使 Scala 编译器成功地将其推断为 List[Int]
。
此元数据存储在哪里,以便我们可以将此类型称为实际 List[Int]
而不是 List[Object]
?
JVM class 文件格式允许编译器将自定义属性放入 class 文件中,请参阅 Section 4.7.1 of the Java Virtual Machine Specification。除其他事项外,Scala 编译器将有关名称的 Scala 签名的信息放入它生成的 class 文件中,以便在以后的编译器 运行 上,它可以再次读取此信息。 Java 虚拟机需要忽略它们不理解的属性,因此这在 运行 时没有影响。
我没有找到注释的二进制格式规范,但如果您想深入了解实现,我发现:
- https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
中类型的序列化
- 类型的反序列化
https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
对于 Scala 3.0,甚至计划使用新的 "tasty" 格式在 class 文件中存储完整的抽象语法树,包括类型检查器生成的信息。美味代表 "typed abstract syntax trees"。基本思想是在类型检查阶段之后序列化抽象语法树并将其放入 class 文件中。以后的编译器 运行s 可以加载依赖项的完整抽象语法。这不仅可以进行类型检查,还可以进行跨模块内联和其他全局优化。
Tasty 计划成为 Scala 抽象语法树的通用交换格式,也用于编译器和集成开发环境之间的通信以及元编程。
如果您想深入了解实现,也许 https://github.com/lampepfl/dotty/tree/master/compiler/src/dotty/tools/dotc/core/tasty 中的文件是一个好的开始。
给定以下对象:
object Foo {
val bar: List[Int] = List(1, 2, 3)
}
当我们将此文件编译为 JVM 字节码时,由于类型擦除以及 Java 不支持原始类型作为泛型类型的参数这一事实,这被翻译成 List<Object>
.
我们可以通过使用 javap -l
编译和检查 .class
来看到这一点:
public static com.yuvalitzchakov.github.Foo$ MODULE$;
descriptor: Lcom/yuvalitzchakov/github/Foo$;
flags: ACC_PUBLIC, ACC_STATIC
public scala.collection.immutable.List<java.lang.Object> bar();
descriptor: ()Lscala/collection/immutable/List;
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #19 // Field bar:Lscala/collection/immutable/List;
4: areturn
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/yuvalitzchakov/github/Foo$;
Signature: #17 // ()Lscala/collection/immutable/List<Ljava/lang/Object;>;
但是,如果我们将其编译成 JAR 文件,然后将其作为不同 Scala 项目中的依赖项并尝试将 Foo.bar
设置为不同的值,Scala 编译器会将此类型推断为 List[Int]
,不是 List[Object]
:
浏览完 .class
文件后,我找不到有关类型参数的信息,而这将使 Scala 编译器成功地将其推断为 List[Int]
。
此元数据存储在哪里,以便我们可以将此类型称为实际 List[Int]
而不是 List[Object]
?
JVM class 文件格式允许编译器将自定义属性放入 class 文件中,请参阅 Section 4.7.1 of the Java Virtual Machine Specification。除其他事项外,Scala 编译器将有关名称的 Scala 签名的信息放入它生成的 class 文件中,以便在以后的编译器 运行 上,它可以再次读取此信息。 Java 虚拟机需要忽略它们不理解的属性,因此这在 运行 时没有影响。
我没有找到注释的二进制格式规范,但如果您想深入了解实现,我发现:
- https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala 中类型的序列化
- 类型的反序列化 https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
对于 Scala 3.0,甚至计划使用新的 "tasty" 格式在 class 文件中存储完整的抽象语法树,包括类型检查器生成的信息。美味代表 "typed abstract syntax trees"。基本思想是在类型检查阶段之后序列化抽象语法树并将其放入 class 文件中。以后的编译器 运行s 可以加载依赖项的完整抽象语法。这不仅可以进行类型检查,还可以进行跨模块内联和其他全局优化。
Tasty 计划成为 Scala 抽象语法树的通用交换格式,也用于编译器和集成开发环境之间的通信以及元编程。
如果您想深入了解实现,也许 https://github.com/lampepfl/dotty/tree/master/compiler/src/dotty/tools/dotc/core/tasty 中的文件是一个好的开始。