nashorn预编译

在最近的一篇文章中,我了解了Java 8和Scala如何实现Lambda表达式。 众所周知,Java 8不仅引入了对Javac编译器的改进,而且还引入了全新的解决方案-Nashorn。

这个新引擎旨在替代Java现有JavaScript解释器Rhino。 这为我们带来了JVM的前列,当谈到在速度与世界的V8引擎执行JavaScript,在那里(我希望我们会最终得到过去那种汽车地毯的事情 :))所以,我认为这将是一个很好的时机,也可以通过深入了解Nashorn,看看它如何编译Lambda表达式(尤其是与Java和Scala相比)。

我们将要看的lambda表达式类似于我们用Java和Scala测试过的表达式。

这是代码:

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");String js;js = "var map = Array.prototype.map \n";
js += "var names = [\"john\", \"jerry\", \"bob\"]\n";
js += "var a = map.call(names, function(name) { return name.length() })\n";
js += "print(a)";engine.eval(js);

您似乎觉得足够纯真。 但是,请稍等...

进入字节码

我们的第一个挑战是获取JVM看到的实际字节码。 与Java和Scala的编译器具有持久性(即将.class / jar文件生成到磁盘)不同,Nashorn会编译内存中的所有内容,并将字节码直接传递给JVM。 幸运的是,我们已经有了Java代理来进行救援。 我编写了一个简单的Java代理来捕获并保留生成的字节码。 从那里开始,有一个简单的javap可以打印代码。

如果您还记得的话,我很高兴看到新的Java 8编译器如何使用Java 7中引入的invokeDynamic指令链接到Lambda函数代码。 好吧,与Nashorn一起,他们真的参加了比赛。 现在一切都完全基于它。 看看下面。

读取字节码

invokeDynamic 。 就像我们都在同一页面上一样,Java 7中添加了invokeDynamic指令,以允许人们编写自己的动态语言来在运行时决定如何链接代码。

对于Java和Scala之类的静态语言,编译器在编译时决定将调用哪种方法(在JVM运行时的帮助下,用于多态)。 运行时链接是通过标准ClassLoader完成的,以查找类。 甚至方法重载解析之类的事情都在编译时完成。

动态与静态链接 。 不幸的是,对于本质上更具动态性的语言(并且JS是一个很好的例子),静态解析可能是不可能的。 当我们在Java中说obj.foo()时,obj类具有foo()方法,否则就没有。 在像JS这样的语言中,它将取决于运行时obj引用的实际对象,这是静态编译器的噩梦。 在这种情况下,编译时的链接方法行不通。 但是invokeDynamic确实可以。

InvokeDynamic允许在运行时将链接推迟回该语言的编写者,因此他们可以根据自己的语言语义,指导JVM确定要调用哪种方法。 这是双赢的局面。 JVM获得了一种链接,优化和执行的实际方法,并且语言制造商可以控制其解析度。 动态链接是我们在Takipi中必须努力支持的工作 。

Nashorn如何链接

Nashorn确实有效地利用了这一点。 让我们看一下示例,以了解其工作原理。 这是Lambda代码中的第一条invokeDynamic指令,用于检索JS Array类的值–

invokedynamic 0 "dyn:getProp|getElem|getMethod:prototype":(Ljava/lang/Object;)Ljava/lang/Object;

Nashorn要求JVM在运行时将其传递给该字符串,作为交换,它将返回一个方法的句柄,该方法接受一个Object并返回一个。 只要JVM获取此类方法的句柄,它就可以链接。

.class文件的特殊部分中指定了负责返回此句柄的方法(也称为引导程序方法),该文件中包含可用的引导程序方法列表。 您看到的0值是该方法在该表中的索引,JVM将调用该索引来获取将链接到的方法句柄。

在我看来,Nashorn的人们做了一件很酷的事情,他们没有编写自己的库来解析和链接代码, 而是继续集成了dynalink这个开源项目,该项目旨在帮助动态语言基于统一平台链接代码。 这就是为什么您在每个字符串的开头看到“ dyn:”前缀的原因。

实际流量

现在我们已经掌握了Nashorn所使用的方法,现在让我们看一下实际流程。 为了简洁起见,我删除了一些说明。 完整的代码可以在这里找到。

1.第一组指令将数组映射函数加载到脚本中。

//load JS array
invokedynamic 0 "dyn:getProp|getElem|getMethod:Array":(Ljava/lang/Object;)Ljava/lang/Object;//load its prototype element
invokedynamic 0 "dyn:getProp|getElem|getMethod:prototype":(Ljava/lang/Object;)Ljava/lang/Object;//load the map method
invokedynamic 0 "dyn:getProp|getElem|getMethod:map":(Ljava/lang/Object;)Ljava/lang/Object;//set it to the map local
invokedynamic 0 #0:"dyn:setProp|setElem:map":(Ljava/lang/Object;Ljava/lang/Object;)V

2.接下来,我们分配名称数组

//allocate the names array as a JS object
invokestatic jdk/nashorn/internal/objects/Global.allocate:([Ljava/lang/Object;)Ljdk/nashorn/internal/objects/NativeArray;//places it into names
invokedynamic 0 #0:"dyn:setProp|setElem:names":(Ljava/lang/Object;Ljava/lang/Object;)Vinvokedynamic 0 #0:"dyn:getProp|getElem|getMethod:names":(Ljava/lang/Object;)Ljava/lang/Object;

3.查找并加载Lambda函数

//load the constants field for this script compiled and filled at runtime by Nashorn
getstatic constants//refer to the 2nd entry, where Nashorn will place a handle to the lambda code
iconst_2//get it from the constants array
aaload//ensure it’s a JS function object
checkcast class jdk/nashorn/internal/runtime/RecompilableScriptFunctionData

4.使用名称和Lambda调用map,然后将结果放入

//call the map function, passing it names and the Lambda function from the stackinvokedynamic 0 #1:"dyn:call":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljdk/nashorn/internal/runtime/ScriptFunction ;)Ljava/lang/Object;//put the result in a
invokedynamic 0 #0:"dyn:setProp|setElem:a":(Ljava/lang/Object;Ljava/lang/Object;)V

5.找到打印功能并在打印机上调用

//load the print function
invokedynamic 0 #0:"dyn:getMethod|getProp|getElem:print":(Ljava/lang/Object;)Ljava/lang/Object;//load a
invokedynamic 0 #0:"dyn:getProp|getElem|getMethod:a":(Ljava/lang/Object;)Ljava/lang/Object;// call print on it
invokedynamic 0 #2:"dyn:call":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

lambda函数本身被编译并作为私有函数放在与脚本相同的类中。 这与Java 8 lambda非常相似。 代码本身很简单。 我们加载字符串,找到其长度函数并调用它。

//Load the name argument (var #1)
aload_1//find its length() function
invokedynamic 0 "dyn:getMethod|getProp|getElem:length":(Ljava/lang/Object;)Ljava/lang/Object;//call length
invokedynamic 0 "dyn:call":(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;//return the result
areturn

奖金回合–最终字节码

到目前为止,我们一直在处理的代码实际上并不是JVM在运行时将执行的代码。 请记住,每个invokeDynamic指令都将解析为物理字节码方法,然后JVM将其编译为机器代码并执行。

为了查看JVM运行的实际字节码,我使用了一个简单的技巧。 我在类中用一个简单的Java方法调用包装了对length()的调用。 这使我可以放置一个断点,并查看JVM执行以进入Lambda的最终调用堆栈。

这是代码–

js += "var a = map.call(names, function(name) {
return Java.type("LambdaTest”).wrap(name.length())
})";

包好了–

public static int wrap(String s)
{
return s.length();
}

现在让我们玩一个游戏。 该堆栈中有几帧? 想一想。 如果您猜到少于<100 –您欠我一杯啤酒。 完整的调用堆栈可以在这里找到。

之所以如此,也是非常有趣的原因,这是关于即将发布的全新帖子的一个故事。

参考: Java 8:在Takipi博客上由我们的JCG合作伙伴 Iris Shoor 在New Nashorn JS引擎中编译Lambda表达式 。

翻译自: https://www.javacodegeeks.com/2014/02/java-8-compiling-lambda-expressions-in-the-new-nashorn-js-engine.html

nashorn预编译

nashorn预编译_Java 8:在新的Nashorn JS引擎中编译Lambda表达式相关推荐

  1. Java 8:在新的Nashorn JS引擎中编译Lambda表达式

    在最近的一篇文章中,我了解了Java 8和Scala如何实现Lambda表达式. 众所周知,Java 8不仅引入了对Javac编译器的改进,而且还引入了全新的解决方案-Nashorn. 这个新引擎旨在 ...

  2. Java新特性:Java8函数式接口与Lambda表达式(一)

    摘要 何为函数式接口? 什么是lambda表达式,lambda表达式的本质: 函数式接口与lambda表达式的联系:lambda是实现函数式接口的一个快捷方式,可以作为函数式接口的一个实例: 常用Ja ...

  3. lambda java 表达式_Java中的Lambda表达式

    介绍 Lambda函数是 Java 8 附带的一项功能,它是该语言向 函数式编程 迈出的第一步,这是实现各种兼容 范例的 有用功能的普遍趋势. 引入lambda函数的动机主要是为了减少传递给类实例以模 ...

  4. java只能在运行和编译吗_如何在另一个Java程序中编译和运行Java程序?

    斯蒂芬大帝 我修改了代码以包括一些检查:public class Laj {  private static void printLines(String name, InputStream ins) ...

  5. java enum判断_Java Enum枚举 遍历判断 四种方式(包括 Lambda 表达式过滤)

    packagecom.miracle.luna.lambda;importjava.util.Arrays;/*** @Author Miracle Luna * @Date 2019/6/9 23: ...

  6. java8 入门脚本之家_Java 8中的Lambda表达式

    我想使用lambda表达式而不是for循环生成数字列表. 所以让我们想要生成一个100以下的所有三角形数字的列表.三角数字是以下公式的数字:(n * n n)/ 2 这样做最好的方法是什么? 目前我有 ...

  7. java 表达式写法_java内置核心4大函数式接口写法和lambda表达式

    java.util.function , Java 内置核心四大函数式接口标准写发和lambda表达式 消费型接口,有一个输入参数,没有返回值 public static void main(Stri ...

  8. java单个变量的表达式_java中使用Lambda表达式的5种语法

    1,标准写法 思考下述情况: String[] arr = {"program", "creek", "is", "a" ...

  9. λ表达式_Java 8新特性:学习如何使用Lambda表达式,一看必懂

    我将分为两篇系列文章来描述了使用Java 8的新特性 - lambda表达式. 目录 介绍 我们为什么需要lambdas? Lambdas的语法 功能接口 方法参考 构造函数参考 可变范围 默认方法 ...

最新文章

  1. 驭势科技引入国家队战略注资,完成超10亿元人民币融资
  2. 它又来了!C**HashMap是如何保证线程安全的?会用不就完了?
  3. Datawhale两岁啦!
  4. linux脚本重启进程,linux下通过脚本实现自动重启程序
  5. [Android学习笔记]使用ListView
  6. NoSQL入门第一天——NoSQL入门与基本概述
  7. 第五十期:工作强度超996,失业半年即出局,硅谷为何如此“嗜血”?
  8. android 開發人員模式,Android
  9. 朴素Bayse新闻分类实践
  10. mall-applet小程序项目是一套电商系统
  11. 单主机Docker容器VLAN划分
  12. 时间戳转datetime格式
  13. pytorch torch.nn.Module.register_buffer
  14. Informix日志报错:Could not do a physical-order read to fetch netxt row
  15. jQuery 的 ajax
  16. 数据库DBeaverEE 22.0.2
  17. 安徽师范大学数学计算机徐德琴,Snake模型在指纹图像分割中的应用
  18. android_studio运行,Android Studio怎么运行程序?
  19. java 项目中遇到的问题 和解决方案_java开发常见的问题及解决办法 - java开发中遇到的难点有哪些_java开发常见的问题及解决办法...
  20. 1.认识华为数据通信

热门文章

  1. 洛谷P1801 黑匣子 双堆套路的使用
  2. Nacos(八)之Docker
  3. JavaFX UI控件教程(八)之Choice Box
  4. 深入Synchronized和java.util.concurrent.locks.Lock的区别详解
  5. Oracle入门(五C)之68个系统变量的key和默认value
  6. Java架构师必看的10本书
  7. 关于人脸和指纹识别共同交流方案
  8. 张老师讲Python~
  9. SpringMVC的视图解析器
  10. 使用Eclipse构建Maven项目 (step-by-step)