java - 谁调用 describeConstable(),何时调用?
问题描述
Constable
我在 JDK 15 中偶然发现了类似的东西。我基本明白。
把所有的编译原理都玩了一遍,稍微了解了一下,我发现我还有一个问题:谁调用了 aConstable
的describeConstable()
方法,什么时候调用的? Brian 的介绍似乎暗示它可以在编译时以某种方式访问。由于对这些事情很天真,我希望它会出现在使用页面下jdk.compiler
或其他地方。相反,唯一的消费似乎是在jdk.incubator.foreign
包装中。(显然,我知道它可能会被使用页面未公开的某些私人机器使用;因此我的问题。)
我将 a 放置Thread.dumpStack()
在describeConstable()
一个哑类的实现中,该类实现Constable
并返回Optional.ofNullable(null)
只是为了看看会发生什么……在编译或运行时什么都没有发生。
(我确实知道,在 JDK 12 之前,如果您想编写动态常量,则必须使用 ASM 或 ByteBuddy 或类似的东西。但在我天真的眼中,它看起来像是Constable
允许您的用户类“插入”Java编译器并让它为你做不断的写作。我也知道里面的类和方法java.lang.constant
主要是为编译器编写者准备的,但Constable
在我看来有点例外。最后,我显然明白我可以调用任何时候都可以使用这种方法,但这显然不是它的目的。)
编辑:(非常)感谢下面一些非常有帮助和耐心的回答和评论,我想我开始明白了(我不是编译器,这点应该很明显)。虽然我知道一旦一个实例X implements Constable
存在,那么ContantDesc
它返回的它describeConstable()
必须由其他常量描述符(本身)制成,虽然我知道可以在编译时调用“常量工厂”(例如ClassDesc#of()
等等),显然必须只接受其他常量作为它们可能需要的任何参数,我仍然不清楚在编译过程中如何首先实例化任意 X implements Constable
变量......它正在被编译(!)这样describeConstable()
可以在编译时调用它。
请记住,这个问题的答案可能是我对一般编译器的基本了解,或者他们在静态分析期间遇到的各种问题。我只看到一个实例方法 ( describeConstable()
),它需要在对象 ( X implements Constable
) 的实例上调用,并且为了拥有一个对象的实例,必须调用它的构造函数。我不清楚 Java 编译器如何知道如何X implements Constable
使用其任意的、可能的多参数构造函数构造 my ,以便它可以调用describeConstable()
它。
解决方案
我会说到目前为止我所理解和知道的。这确实是一个有趣的功能。
谁调用了警员的 describeConstable()
javac
将要。
什么时候?
当它第一次被调用/需要时。
更详细的解释。你知道 lambda 是如何编译的吗?如果没有,这里有一个非常简短的介绍(稍后会有很大帮助):
Runnable r = () -> {System.out.println("easy, peasy");};
r.run();
如果您查看字节码,将会有一个invokedynamic
调用:
invokedynamic #7, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
反过来,这将调用“引导”方法:
BootstrapMethods:
0: #39 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
引导方法的名称是:
LambdaMetafactory::metafactory
.作为输入,它需要一个
Lookup
(由 JVM 提供)除其他外,
javac
提供了一个MethodType
(它描述了方法的返回类型和方法参数类型,在这种情况下它是run
fromRunnable
)它将返回 a
CallSite
(在这种情况下它实际上是 aConstantCallSite
)。
因此,用相当简单的话(而且很可能有点错误),invokedynamic
将调用绑定到 a ConstantCallSite
,它在内部将调用委托给您提供Runnable
的run
方法的实现(在内部它委托给“去糖”私有定义 lambda 的方法)。这种情况只发生一次,在第一次调用时,所有后续调用都不会经历这种痛苦。我在其他答案中以某种方式提供了更多详细信息,例如此处。
相同的机制将用于动态常量(但它必须使用ldc
而不是invokedynamic
)。“机器”已经在 jdk-11 中提供。注意类的名称:ConstantBootstraps
,我们知道为什么是“bootstrap”并且我们知道为什么是“Constant”。如果你看一下这些参数,它肯定会开始变得有意义,因为它真的很像invokedynamic
for lambdas。
现在您知道为什么Constable/ConstantDesc
需要:以便引导方法调用正确的实现。在上面的例子中,javac
“知道”(推断/推断/等等) lambda 实际上是一个Runnable
. 在“恒定动态”的情况下,该信息将被类实现的事实所暗示Constable
。这将成为如何建立常数的“秘诀”;至少在我的理解中。
请注意,其他人已经在JVM
Scala 的lazy
. 但是他们只是在窗帘后面实施了双重检查锁定volatile
,有时您需要为阅读JVM
付费…… 到什么程度以及具体如何尚不清楚;因为这还没有实现javac
,至少在主流 jdk 中是这样。可能是这样的:
// made-up syntax
__@lazy__
private static final MyObject obj = null;
这最终将委托给Constable::describeConstable
或可能是:
__@lazy(provider="myProvider")__
private static final MyObject obj = null;
private MyObject myProvider(){....}
但我敢打赌,比我聪明得多的人会想出我在这里没有提到的如何使用它的想法。当这种情况发生时(我知道它会发生),我需要更新这篇文章。
推荐阅读
- typescript - 如何在 Aurelia 中使用授权自定义属性禁用自定义组件
- angular - angular5的地图框
- c# - 允许用户将多个项目添加到 ObservableCollection c#
- java - 如何使用单个用于具有相同 dtasnapshot 对象的每个循环获取 2 个节点的组合数据
- kentico - Kentico 11 - 托管环境中的电子邮件营销 CMS 显示问题
- javascript - 在 node.js 中重复 cryptojs aes 加密的替代方法
- javascript - 在 Javascript 中获取属性、名称和值
- templates - Visual Studio 2017 ASP NET Core 2 脚手架模板
- php - 第一个循环打破了第二个循环
- ios - 如何在 iOS Swift 中与 UIActivityViewController 共享的 WhatsApp 中设置不同的参数?