日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

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

發(fā)布時間:2023/12/3 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 8:在新的Nashorn JS引擎中编译Lambda表达式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在最近的一篇文章中,我了解了Java 8和Scala如何實現(xiàn)Lambda表達式。 眾所周知,Java 8不僅引入了對Javac編譯器的改進,而且還引入了全新的解決方案-Nashorn。

這個新引擎旨在替代Java現(xiàn)有JavaScript解釋器Rhino。 這為我們帶來了JVM的前列,當談到在速度與世界的V8引擎執(zhí)行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);

您似乎天真無邪。 但是,請稍等...

進入字節(jié)碼

我們的第一個挑戰(zhàn)是獲取JVM看到的實際字節(jié)碼。 與Java和Scala的編譯器具有持久性(即將.class / jar文件生成到磁盤)不同,Nashorn會編譯內存中的所有內容,并將字節(jié)碼直接傳遞給JVM。 幸運的是,我們已經有了Java代理來進行救援。 我編寫了一個簡單的Java代理來捕獲并保留生成的字節(jié)碼。 從那里開始,有一個簡單的javap可以打印代碼。

如果您還記得的話,我很高興看到新的Java 8編譯器如何使用Java 7中引入的invokeDynamic指令鏈接到Lambda函數(shù)代碼。 好吧,與Nashorn一起,他們真的參加了比賽。 現(xiàn)在,一切都完全基于它。 看看下面。

讀取字節(jié)碼

invokeDynamic 。 就像我們都在同一頁面上一樣,Java 7中添加了invokeDynamic指令,以允許人們編寫自己的動態(tài)語言來在運行時決定如何鏈接代碼。

對于Java和Scala之類的靜態(tài)語言,編譯器在編譯時決定要調用哪種方法(在JVM運行時的幫助下,用于多態(tài))。 運行時鏈接是通過標準ClassLoader完成的,以查找類。 甚至方法重載解析之類的事情都在編譯時完成。

動態(tài)與靜態(tài)鏈接 。 不幸的是,對于本質上更具動態(tài)性的語言(并且JS是一個很好的例子),靜態(tài)解析可能是不可能的。 當我們在Java中說obj.foo()時,obj類要么具有foo()方法,要么沒有。 在像JS這樣的語言中,它將依賴于運行時obj引用的實際對象,這是靜態(tài)編譯器的噩夢。 在這種情況下,編譯時的鏈接方法行不通。 但是invokeDynamic確實可以。

InvokeDynamic允許在運行時將鏈接推遲回該語言的編寫者,因此他們可以根據自己的語言語義來指導JVM確定要調用哪種方法。 這是雙贏的局面。 JVM獲得了一種鏈接,優(yōu)化和執(zhí)行的實際方法,并且語言制造商可以控制其解析度。 動態(tài)鏈接是我們在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的人們做了一件很酷的事情,他們沒有編寫自己的庫來解析和鏈接代碼, 而是繼續(xù)集成了dynalink這個開源項目,該項目旨在幫助動態(tài)語言基于統(tǒng)一平臺鏈接代碼。 這就是為什么您在每個字符串的開頭看到“ dyn:”前綴的原因。

實際流量

現(xiàn)在我們已經掌握了Nashorn所使用的方法,現(xiàn)在讓我們看一下實際流程。 為了簡潔起見,我刪除了一些說明。 完整的代碼可以在這里找到。

1.第一組指令將數(shù)組映射函數(shù)加載到腳本中。

//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.接下來,我們分配名稱數(shù)組

//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函數(shù)

//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函數(shù)本身被編譯并作為私有函數(shù)放在與腳本相同的類中。 這與Java 8 lambda非常相似。 代碼本身很簡單。 我們加載字符串,找到其長度函數(shù)并調用它。

//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

獎金回合–最終的字節(jié)碼

到目前為止,我們一直在處理的代碼實際上并不是JVM在運行時將執(zhí)行的代碼。 請記住,每個invokeDynamic指令都將解析為物理字節(jié)碼方法,然后JVM將其編譯為機器代碼并執(zhí)行。

為了查看JVM運行的實際字節(jié)碼,我使用了一個簡單的技巧。 我在類中使用簡單的Java方法調用包裝了對length()的調用。 這使我可以放置一個斷點,并查看JVM執(zhí)行以進入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(); }

現(xiàn)在玩游戲。 該堆棧中有幾幀? 想一想。 如果您猜不到<100 –您欠我一杯啤酒。 完整的調用堆??梢栽谶@里找到。

之所以如此,也是非常有趣的原因,這是關于即將發(fā)布的全新帖子的故事。

參考: 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

總結

以上是生活随笔為你收集整理的Java 8:在新的Nashorn JS引擎中编译Lambda表达式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。