nashorn: javascript that doesn’t suck tomer gabel, wix buildstuff, november 2014
TRANSCRIPT
Nashorn: JavaScript that doesn’t suck
Tomer Gabel, WixBuildStuff, 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 server-side growth
• Reference use-case for JSR-292– 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 glanceimport 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) //== 100square(3.14) //== 9.8596square("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
• It’s not about invocation
• … but about runtime linkage
java.lang.invoke
• MethodHandle– An immutable
function pointer– Composable:• Guards• Exception handlers• Argument mutation
– Typesafe andJIT-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
• 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
Supported module highlights
QUESTIONS?
WE’RE HIRING!
Contact me:• http://www.tomergabel.com• [email protected]• @tomerg