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

在最近的一篇文章中,我了解了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在运行时将其传递给该字符串,作为交换,它将向一个接受对象并返回一个对象的方法返回一个句柄。 只要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 Engine中编译Lambda表达式 。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue 组件开发

作者QQ&#xff1a;1095737364 QQ群&#xff1a;123300273 欢迎加入&#xff01;1.新建路由:router-->index.js,修改成下面的代码 import Vue from vueimport Router from vue-routerimport index from /components/index/indexVue.use(Router)export default new Ro…

sql中有一些保留字,当你的字段名是它的保留字时,这个时候sql语句的字段不加``就会报错...

sql中有一些保留字&#xff0c;当你的字段名是它的保留字时&#xff0c;这个时候sql语句的字段不加就会报错转载于:https://www.cnblogs.com/w123w/p/10673692.html

C++语言实现-邻接表

图的邻接表实现 邻接表是图的一种链式存储结构。主要是应对于邻接矩阵在顶点多边少的时候&#xff0c;浪费空间的问题。它的方法就是声明两个结构。如下图所示&#xff1a; 先来看看伪代码&#xff1a; typedef char Vertextype; //表结点结构 struct ArcNode { int adjvex; …

为了适应云数据库mySQL产品_为了适应不同的应用场景,云数据库mysql版提供的产品系列包括哪些...

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航&#xff0c;为用户…

TestNG:在一个测试类中使用@DataProvider依次运行测试

许多Java开发人员和自动化测试工程师在他们的工作中都使用TestNG作为测试框架。 我也不例外。 这是一个显而易见的选择&#xff0c;因为TestNG提供了非常强大的工具集&#xff0c;使处理各种测试变得更加容易。 为了证明这一点&#xff0c;我将在本文中向您展示如何解决一项不平…

mysql 数据库读取_教你如何从 MySQL 数据库读取数据

PHP MySQL 读取数据从 MySQL 数据库读取数据SELECT 语句用于从数据表中读取数据:SELECT column_name(s) FROM table_name我们可以使用 * 号来读取所有数据表中的字段&#xff1a;SELECT * FROM table_name如需学习更多关于 SQL 的知识&#xff0c;请访问我们的 SQL 教程。使用 …

GXC 钱包部署

参考: [ 官方 wiki ] 基于 Ubuntu 的 GXC 部署 基础环境 OS: Ubuntugxc: 官方 [ release 最新版本 ]下载 release 包(ubuntu) cd /usr/src wget https://github.com/gxchain/gxb-core/releases/download/v1.0.181106b/gxb_ubuntu_1.0.1801106.tar.gz 拷贝可执行命令到系统 /usr…

js-js的全局变量和局部变量

*** 全局变量&#xff1a;在script标签里面定义一个变量&#xff0c;这个变量在页面中js部分都可以使用   - 在方法外部使用&#xff0c;在方法内部使用&#xff0c;在另外一个script标签中使用 *** 局部变量&#xff1a;在方法内部定义一个变量&#xff0c;只能在方法内部调…

使用Lucene的新FreeTextSuggester查找长尾建议

Lucene的“ 建议”模块提供了许多有趣的自动建议实现&#xff0c;以便在用户将每个字符输入搜索框时为他们提供实时搜索建议。 例如&#xff0c; WFSTCompletionLookup将所有建议及其权重编译到一个紧凑的有限状态传感器中 &#xff0c;从而可以对基本建议进行快速前缀查找。 …

Java属性中指定Json的属性名称(序列化和反序列化)

序列化对象&#xff0c;只需要使用注解"JsonProperty(value "pwd")" import com.fasterxml.jackson.annotation.JsonProperty;public class User{JsonProperty(value "pwd")private String password; } 比如上面例子&#xff0c;在作为请求接…

网站表单输入框去除浏览器默认样式

网页不可避免的使用到表单&#xff0c;提交一些内容到后端&#xff0c;进行前后端交互。可是由于浏览器总是有各自的默认样式&#xff0c;所以需要对其进行清除。总的来说有以下几种&#xff1a; 1、input输入框获取焦点的时候&#xff0c;默认带蓝色边框&#xff0c;如果设计要…

mysql 查看锁表日志_MYSQL 表锁情况查看

查看锁表情况mysql> show status like ‘Table%’;—————————-——–| Variable_name | Value |—————————-——–| Table_locks_immediate | 795505 || Table_locks_waited | 0 || Table_open_cache_hits | 0 || Table_open_cache_misses | 0 || Table_ope…

js-原始类型和声明变量

** Java的基本数据类型&#xff1a;byte、short、int、long、float、double、char、boolean ** 定义变量 都是用关键字 var(ES6中可以使用const和let来定义变量&#xff0c;后面会有ES6的语法介绍) ** js的原始类型&#xff08;5个&#xff09; - string&#xff1a;字符串  …

Java,Scala,Guava和Trove集合-它们可以容纳多少数据?

关于我们的数据结构&#xff0c;令人着迷的事情之一是&#xff0c;即使我们对它们非常熟悉&#xff0c;我们仍然很难说出像HashMap这样基本的东西在1GB的内存中可以容纳多少个项目。 我们可能会在学校&#xff0c;高级开发人员那里学到这一点&#xff0c;或者由于数据结构选择不…

spoj1026 favorite dice

1 #include <bits/stdc.h>2 using namespace std;3 int n,t;4 const int N 1200;5 double dp[N];6 /*7 甩一个n面的骰子&#xff0c;问每一面都被甩到的需要甩的次数期望是多少。8 dp[i]&#xff1a;已经甩到i个面了&#xff0c;要达到n个面还需要次数的期望9 显然dp[…

switch 失效

switch 开关失效无法切换&#xff0c;可以关闭&#xff0c;无法开启。 发现问题点 require-table.js 中toggle value的数据类型不是 number 导致 &#xff08;value ? no : yes )判断总为no&#xff1b; 前面将value 强制转换为number类型即可 转载于:https://www.cnblogs.c…

纯php socket mysql_PHP 连接 unix_socket MySQL

当MySQL使用Unix Socket启动时&#xff0c;直接使用localhost会发生了一个数据库错误&#xff0c;发生无法连接数据库错误。 Warning: mysql_connect() [function.mysql-connect]: [2002] 这时应当修改hostname&#xff0c;例如在CI 配置数据库 (database.php) 从&#xff1a; …

html-其他常见标签的使用

b&#xff1a;加粗 s&#xff1a;删除线 u&#xff1a;下划线 i&#xff1a;斜体 per&#xff1a;原样输出 sup:上标 sub:下标 p&#xff1a;段落标签 比br多一行 &#xff08;CSS&#xff09; div&#xff1a;自动换行 span&#xff1a;在一行显示 完整代码&#xff1a; <h…

在OpenShift上将JMS与JBoss A-MQ结合使用。 从远程客户端和加密中学到的经验教训。...

OpenShift是“红帽开发的开放式混合云应用程序平台”。 它具有不同的风格&#xff0c;对于大多数您想做的事情&#xff0c;最有趣的部分是公共云应用程序开发和托管平台“ OpenShift Online ”。 您可以轻松地尝试一下&#xff0c;因为在云中使用OpenShift Online是免费的&…

[Swift]LeetCode281. 之字形迭代器 $ Zigzag Iterator

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号&#xff1a;山青咏芝&#xff08;shanqingyongzhi&#xff09;➤博客园地址&#xff1a;山青咏芝&#xff08;https://www.cnblogs.com/strengthen/&#xff09;➤GitHub地址&a…