nashorn: javascript that doesn't suck - tomer gabel, wix

30
Nashorn: JavaScript that doesn’t suck Tomer Gabel, Wix CodeMotion Tel-Aviv, November 2014

Upload: codemotion-tel-aviv

Post on 16-Jul-2015

297 views

Category:

Technology


1 download

TRANSCRIPT

Nashorn: JavaScript that doesn’t suck

Tomer Gabel, Wix

CodeMotion Tel-Aviv, 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 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

• It’s not about

invocation

• … but about

runtime linkage

invokedynamic

Bootstrap

MethodTarget

Call Site

Compile-time

Runtime

Invokes

Returns MethodHandle

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

• RedisMost popular per nodejsmodules.org

Supported module highlights

QUESTIONS?

WE’RE HIRING!

Contact me:

• http://www.tomergabel.com

[email protected]

• @tomerg