Nashorn BuildStuff 2014

Download Report

Transcript Nashorn BuildStuff 2014

Nashorn: JavaScript that
doesn’t suck
Tomer Gabel, Wix
BuildStuff, November 2014
Agenda
• History
• Features
• Behind the scenes
• Performance
• Juggling Act
History
• Existing Rhino engine:
– Slow
– Non-compliant
– Did I mention slow?
• JavaScript since 1998:
– Adoption went through
the roof
– Technology advances
(V8, SpiderMonkey…)
Nashorn in a nutshell
• Project Nashorn
– Started in July 2011
– Integrates with JSR-223
– Reference use-case
for JSR-292
• Released with Java 8 in
March 2014
Why bother?
Why you care
• Extensibility and scripting
– Macro systems (a la VBA)
– Event hooks
• Server-side JavaScript
– More on this later
• End-user programmability
– Rule engines
– Reporting engines/ETL
– BPL and workflow
• … and more
Why Oracle cares
• Atwood’s law
• Node.js
– A real threat to Java’s serverside growth
• Reference use-case for JSR292
– Drive innovation
– Drive optimization
– Reference implementation
Features
• Self-contained
• Small: 1.5MB JAR
• Implements ECMAScript 5.1
– 100% compliant!
• Runtime-compiled to bytecode
(no interpreted mode as with Rhino)
• Fast!
JSR-223 at a glance
import javax.script.*;
ScriptEngineManager manager =
new ScriptEngineManager();
ScriptEngine nashorn =
manager.getEngineByName("nashorn");
nashorn.eval(
"print(\"hello world!\");");
Integrating JSR-223
• Injecting state
nashorn.put("name", "Schnitzel McFry");
nashorn.eval(
"print(\"hello \" + name + \"!\");");
Integrating JSR-223
• Invoking JavaScript from Java
nashorn.eval(
"function hello(name) {
" +
" print(\"hello \" + name + \"!\");" +
"}
" );
Invocable context = (Invocable) nashorn;
context.invokeFunction("hello", "world");
Nashorn Extensions
• Java object
– Java.type
– Java.extend
– Java.from
– Java.to
– Java.super
• A bunch more
Integrating JSR-223
• Invoking Java from JavaScript
nashorn.eval(
"var HashMap = Java.type(\"java.util.HashMap\");"
"var map = new HashMap();
"
"map.put(\"name\", \"Schnitzel\");
"
"map.put(\"surname\", \"McFry\");
"
HashMap<String, String> map =
(HashMap<String, String>) nashorn.get("map");
+
+
+
);
Integrating JSR-223
• Implementing a Java interface
nashorn.eval(
"var runnable = {
" +
" \"run\": function() { print(\"hello world\"); }" +
"}
" );
Invocable invocable = (Invocable) nashorn;
Runnable runnable = invocable.getInterface(
nashorn.get("runnable"), Runnable.class);
Integrating JSR-223
• Single Abstract Method (SAM) promotion:
nashorn.eval(
"var Thread = Java.type(\"java.lang.Thread\");"
"var thread = new Thread(function() {
"
" print(\"hello world\");
"
"} );
"
Thread thread = (Thread) nashorn.get("thread");
thread.start();
thread.join();
+
+
+
);
BEHIND THE SCENES
The challenge
• JavaScript is dynamic
– Things can change at runtime
– Optimization is all about assumptions
The challenge
• JavaScript is dynamic
– Things can change at runtime
– Optimization is all about assumptions
• For example, what is the type of:
var x = 500000;
The challenge
• JavaScript is dynamic
– Things can change at runtime
– Optimization is all about assumptions
• For example, what is the type of:
var x = 500000;
x *= 500000;
And now?
The challenge
• JavaScript is dynamic
– Things can change at runtime
– Optimization is all about assumptions
• For example, what is the type of:
var x = 500000;
x *= 500000;
And now?
• How would you implement this?
The challenge
• Naïve multiplication operator:
– Receive LHS and RHS as java.lang.Objects
– Unbox
– Test for potential over/underflow
– Multiply via appropriate codepath
– Box and return
• You can specialize via static analysis
• … but on-the-fly optimization is better
The challenge
• That’s just one
example.
– Primitive widening
– Dynamic dispatch
– Type coercions
– Prototype mutation
• All of these per call
site!
function square(x) {
return x * x;
}
square(10)
//== 100
square(3.14) //== 9.8596
square("wat") //== NaN
The challenge
• Runtime code manipulation is not
the JVM’s strong suite
–Can only load entire classes
–High overhead
–Hard PermGen space limit
–Class unloading issues
Enter JSR 292
• Based on an experimental project, the
Da Vinci Machine
• Adds two new concepts to the JVM:
– invokedynamic bytecode instruction
– MethodHandles
• The first bytecode extension since 1999!
invokedynamic
• The name is
misleading
invokedynamic
Invokes
Compile-time
Runtime
• It’s not about
invocation
Bootstrap
Method
Returns
• … but about
runtime linkage
Call Site
Target
MethodHandle
java.lang.invoke
• MethodHandle
– An immutable
function pointer
– Composable:
• Guards
• Exception handlers
• Argument mutation
– Typesafe and
JIT-optimizable
• CallSite
– Binds an
invokedynamic
“instance” to a target
MethodHandle
– Optionally mutable
Why is this cool?
• Generated code is linked at runtime
• Code is hotswappable
– Guards for branching (over/underflow)
– SwitchPoints for hotswap (promotions)
• Hotswapping is lightweight
– No class generation or loading
• Resulting codepath can be JITted
Don’t forget JEP 122
• The PermGen space is no more
– Now called “Metaspace”
– Resides in native heap
– Block allocator and classloader isolation
– Dynamic size (with bounds)
• Maximum size (hard)
• Low/high watermark for GC
• This applies to all GCs (not just G1)
SO HOW DOES IT PERFORM?
Pretty damn fast!
• 2-10 times faster than Rhino
• ~60% percent of V8
• Not much research out there
Avatar.js
Supported module highlights
• Implements the
Node model and API
on the JVM
• Supports most
modules
– Some natively
– Some via JNI
• … and it does work!
•
•
•
•
•
•
•
•
•
•
mocha
coffee-script
uglify-js
underscore
request
async
jade
express
mongodb
Redis
Most popular per nodejsmodules.org
QUESTIONS?
Contact me:
• http://www.tomergabel.com
• [email protected]
• @tomerg
WE’RE HIRING!