81
Implementing a JavaScript Engine Krystal Mok (@rednaxelafx ) 2013-11-10

Implementing a JavaScript Engine

Embed Size (px)

DESCRIPTION

Presentation done at JingJS, Nov 10, 2013.

Citation preview

Page 1: Implementing a JavaScript Engine

Implementing aJavaScript Engine

Krystal Mok (@rednaxelafx)2013-11-10

Page 2: Implementing a JavaScript Engine

Implementing a(Modern, High Performance?)

JavaScript Engine

Page 3: Implementing a JavaScript Engine

About Me

• Programming language and virtual machine enthusiast

• Worked on the HotSpot JVM at Taobao and Oracle

• Also worked on a JavaScript engine project• Twitter / Sina Weibo: @rednaxelafx• Blog: English / Chinese

Page 4: Implementing a JavaScript Engine

Agenda

• Know the Heritage• JavaScript Engine Overview• Implementation Strategies and Tradeoffs• A bit about Nashorn

Page 5: Implementing a JavaScript Engine

KNOW THE HERITAGEThe roots of JavaScript language and modern JavaScript engines

Page 6: Implementing a JavaScript Engine

Heritage of the Language

Self

Scheme

Java

JavaScript

function closure

prototype-based OO

C-like syntax,built-in objects

Page 7: Implementing a JavaScript Engine

Language Comparison

Self• Prototype-based OO• Multiple Prototype• Dynamically Typed• Dynamically Extend Objects• Mirror-based Reflection• Block (closure)• Support Non-local Return• (pass a error handler to

methods that might one)

JavaScript (ECMAScript 5)• Prototype-based OO• Single Prototype• Dynamically Typed• Dynamically Extend Objects• Reflection• First-class Function (closure)• (no non-local return)• Exception Handling

Page 8: Implementing a JavaScript Engine

Heritage of the Languagefunction MyPoint(x, y) { this.x = x; this.y = y;}

MyPoint.prototype.distance = function (p) { var xd = this.x - p.x, yd = this.y - p.y; return Math.sqrt(xd*xd + yd*yd);}

var p = new Point(2013, 11);

Page 9: Implementing a JavaScript Engine

Heritage of the Languagetraits myPoint = (| parent* = traits clonable. initX: newX Y: newY = (x: newX. y: newY) distance: p = (| xd. yd | xd: x - p x. yd: y - p y. (xd squared + yd squared) squareRooted ).|).

myPoint = (| parent* = traits myPoint. x <- 0. y <- 0|).

p: myPoint copy initX: 2013 Y: 11

Page 10: Implementing a JavaScript Engine

Heritage of the Language

on Self 4.4 / Mac OS X 10.7.5

Page 11: Implementing a JavaScript Engine

Heritage of the VM

Self VM(Self)

Strongtalk VM(Smalltalk)

HotSpot VM(Java)

V8(JavaScript)

CLDC-HI(Java)

Page 12: Implementing a JavaScript Engine

What’s in common?

• Lars Bak!

Page 13: Implementing a JavaScript Engine

VM Comparison

Self VM (3rd Generation)• Fast Object w/Hidden Class• Tiered Compilation

– OSR and deoptimization– support for full-speed debugging

• Type Feedback– Polymorphic Inline Caching

• Type Inference• Method/Block Inlining• Method Customization• Generational Scavenging

– Scavenging + Mark-Compact

V8 (with Crankshaft)• Fast Object w/Hidden Class• Tiered Compilation

– OSR and deoptimization– support for full-speed debugging

• Type Feedback– Polymorphic Inline Caching

• Type Inference• Function Inlining• Generational Scavenging

– Scavenging + Mark-Sweep/Mark-Compact

Page 14: Implementing a JavaScript Engine

To Implement a High Performance JavaScript Engine

• Learn from Self VM as a basis!

Page 15: Implementing a JavaScript Engine

Themes

• Pay-as-you-go / Lazy• Take advantage of runtime information– Type feedback

• Take advantage of actual code stability– Try to behave as static as possible

Page 16: Implementing a JavaScript Engine

JAVASCRIPT ENGINE OVERVIEWOutline of the main components

Page 17: Implementing a JavaScript Engine

Components of a JavaScript Engine

• Parser• Runtime• Execution Engine• Garbage Collector (GC)• Foreign Function Interface (FFI)• Debugger and Diagnostics

Page 18: Implementing a JavaScript Engine

Components of a JavaScript Engine

Memory (Runtime Data Areas)

Source Code

FFI

host / external library

GCJavaScriptObjects

CallStack

Parser ExecutionEngineAST

Page 19: Implementing a JavaScript Engine

Components of a JavaScript Engine

• Parser• Runtime• Execution Engine• Garbage Collector (GC)• Foreign Function Interface (FFI)• Debugger and Diagnostics

Page 20: Implementing a JavaScript Engine

Parser

• Parse source code into internal representation• Usually generates AST

var z = x + y

VarDecl: z

BinaryArith: +

x y

Page 21: Implementing a JavaScript Engine

Components of a JavaScript Engine

• Parser• Runtime• Execution Engine• Garbage Collector (GC)• Foreign Function Interface (FFI)• Debugger and Diagnostics

Page 22: Implementing a JavaScript Engine

Runtime

• Value Representation• Object Model• Built-in Objects• Misc.

__proto__

x 2013

y 11

Object

__proto__

prototype

__proto__ null

constructor

… …

Function

__proto__

prototype

__proto__

constructor

… …

Page 23: Implementing a JavaScript Engine

Components of a JavaScript Engine

• Parser• Runtime• Execution Engine• Garbage Collector (GC)• Foreign Function Interface (FFI)• Debugger and Diagnostics

Page 24: Implementing a JavaScript Engine

Execution Engine

• Execute JavaScript Code

VarDecl: z

BinaryArith: +

x y

addl %rcx, %rax

Page 25: Implementing a JavaScript Engine

Components of a JavaScript Engine

• Parser• Runtime• Execution Engine• Garbage Collector (GC)• Foreign Function Interface (FFI)• Debugger and Diagnostics

Page 26: Implementing a JavaScript Engine

Garbage Collector

• Collect memory from unused objects

Page 27: Implementing a JavaScript Engine

Components of a JavaScript Engine

• Parser• Runtime• Execution Engine• Garbage Collector (GC)• Foreign Function Interface (FFI)• Debugger and Diagnostics

Page 28: Implementing a JavaScript Engine

Foreign Function Interface

• Handle interaction between JavaScript and “the outside world”

• JavaScript call out to native function• Native function call into JavaScript function, or

access JavaScript object

Page 29: Implementing a JavaScript Engine

Components of a JavaScript Engine

• Parser• Runtime• Execution Engine• Garbage Collector (GC)• Foreign Function Interface (FFI)• Debugger and Diagnostics

Page 30: Implementing a JavaScript Engine

Debugger and Diagnostics

Page 31: Implementing a JavaScript Engine

IMPLEMENTATION STRATEGIES AND TRADEOFFS

Page 32: Implementing a JavaScript Engine

Parser

• LR• LL• Recursive Descent• Operator Precedence• Lazy Parsing / Deferred Parsing

Page 33: Implementing a JavaScript Engine

Value Representation

• Pointers, and all values allocated on heap• Discriminated Union• Tagged Value / Tagged Pointer

Page 34: Implementing a JavaScript Engine

Value Representation

• Pointers, and all values allocated on heap• Discriminated Union• Tagged Value / Tagged Pointer

Tag_Int

2013

typedef Object* JSValue;

Page 35: Implementing a JavaScript Engine

Value Representation

• Pointers, and all values allocated on heap• Discriminated Union• Tagged Value / Tagged Pointer

Tag_Int

2013

class JSValue { ObjectType ot; union { double n; bool b; Object* o; // … } u;}

Page 36: Implementing a JavaScript Engine

Tagged

• Tagged Pointer– Non-zero tag on pointer– Favor small integer arithmetics

• Tagged Value– Non-zero tag on non-pointer– Favor pointer access

• NaN-boxing– use special NaN value as box

small integer 00

pointer 01

Page 37: Implementing a JavaScript Engine

Tagged

• Tagged Pointer– Non-zero tag on pointer– Favor small integer arithmetics

• Tagged Value– Non-zero tag on non-pointer– Favor pointer access

• NaN-boxing– use special NaN value as box

small integer 01

pointer 00

Page 38: Implementing a JavaScript Engine

Tagged

• Tagged Pointer– Non-zero tag on pointer– Favor small integer arithmetics

• Tagged Value– Non-zero tag on non-pointer– Favor pointer access

• NaN-boxing– use special QNaN value as box

00000000 pointer

xxxxxxxx double

11111111 00000000 integer

Page 39: Implementing a JavaScript Engine

Value Representation in Self

Page 40: Implementing a JavaScript Engine

Numeric Tower

• Internal Numeric Tower• Smi -> HeapDouble• int -> long -> double• unboxed number

Page 41: Implementing a JavaScript Engine

Object Model

• Hash based– “Dictionary Mode”

• Hidden Class based– “Fast Object”

Page 42: Implementing a JavaScript Engine

Groovy code: Equivalent Java code:

Object ModelExample: behind Groovy’s “object literal”-ish syntax

obj = [ x: 2013, y: 42];

i = obj.x;

obj = new LinkedHashMap(2);obj.put("x", 2013);obj.put("y", 42);

i = obj.get("x");

Page 43: Implementing a JavaScript Engine

keySet null

values null

table

size 2

threshold 1

loadFactor 0.75

modCount 2

entrySet null

header

accessOrder false

2013

42

0

1

key “y”

value

next null

hash 126

before x

after header

key null

value null

next null

hash -1

before

after

key “x”

value

next null

hash 127

before header

after y

Page 44: Implementing a JavaScript Engine

map

__proto__

context …

flags 0

spill null

arrayData

L0 x

L1 y

L2 (unused)

L3 (unused)

Key Getter Setter“x” x getter x setter“y” y getter y setter

map

__proto__

2013

42

EMPTY_ARRAY

Nashorn Object Model

Page 45: Implementing a JavaScript Engine

map

__proto__

context …

flags 0

spill null

arrayData

L0 x

L1 y

L2 (unused)

L3 (unused)

Key Getter Setter“x” x getter x setter“y” y getter y setter

map

__proto__

2013

42

EMPTY_ARRAY

Let’s ignore some fields

for now

Page 46: Implementing a JavaScript Engine

map

L0 x

L1 y

Key Getter Setter“x” x getter x setter“y” y getter y setter

2013

42

… and we’ll get this

Page 47: Implementing a JavaScript Engine

metadata

x

y

Key Offset“x” +12“y” +16

2013

42

looks just like a Java

object

class Point { Object x; Object y;}

… with boxed fields

Page 48: Implementing a JavaScript Engine

metadata

x 2013

y 42

Key Offset“x” +12“y” +16

would be even better

if …

class Point { int x; int y;}

but Nashorn doesn’t go this far yet

Page 49: Implementing a JavaScript Engine

map

__proto__

context …

flags 0

spill

arrayData

L0 x

L1 y

L2 z

L3 a

Key Getter Setter“x” x getter x setter“y” y getter y setter“z” z getter z setter“a” a getter a setter“b” b getter b setter

map

__proto__

1

2

0 6

1 7

b

5

3

4

Page 50: Implementing a JavaScript Engine

Inline Cache

• Facilitated by use of hidden class• Improve property access efficiency• Collect type information for type feedback– later fed to JIT compilers for better optimization

• Works with both interpreted and compiled code

Page 51: Implementing a JavaScript Engine

String

• Flat string• Rope / ConsString / ConcatString• Substring / Span• Symbol / Atom• External String

Page 52: Implementing a JavaScript Engine

RegExp

• NFA• Optimize to DFA where profitable• Interpreted• JIT Compiled

Page 53: Implementing a JavaScript Engine

Call Stack

• Native or separate?• Native– fast– easier transition between execution modes– harder to implement

• Separate (aka “stack-less”)– easy to implement– slow– overhead when transitioning between exec modes

Page 54: Implementing a JavaScript Engine

Execution Engine

• Interpreter• Compiler– Ahead-of-Time Compiler– Just-in-Time Compiler– Dynamic / Adaptive Compiler

• Mixed-mode• Tiered

Page 55: Implementing a JavaScript Engine

Execution Engine in Self

Page 56: Implementing a JavaScript Engine

Interpreter

• Line Interpreter• AST Interpreter• Stack-based Bytecode Interpreter• Register-based Bytecode Interpreter

Page 57: Implementing a JavaScript Engine

Interpreter

• Written in– C/C++– Assembler– others?

Page 58: Implementing a JavaScript Engine

Compiler Concurrency

• Foreground/Blocking Compilation• Background Compilation• Parallel Compilation

Page 59: Implementing a JavaScript Engine

Baseline Compiler

• Fast compilation, little optimization• Should generate type-stable code

Page 60: Implementing a JavaScript Engine

Optimizing Compiler

• Type Feedback• Type Inference• Function Inlining

Page 61: Implementing a JavaScript Engine

On-stack Replacement

Page 62: Implementing a JavaScript Engine

Garbage Collection

• Reference Counting?– not really used by any mainstream impl

• Tracing GC– mark-sweep– mark-compact– copying

Page 63: Implementing a JavaScript Engine

GC Advances

• Generational GC• Incremental GC• Concurrent GC• Parallel GC

Page 64: Implementing a JavaScript Engine

GC Concurrency

Application Thread

JavaScript

GCmark sweep

Mark-Sweep

Page 65: Implementing a JavaScript Engine

GC Concurrency

Application Thread

JavaScript

GCmark compact

Mark-Compact

Page 66: Implementing a JavaScript Engine

GC Concurrency

Application Thread

JavaScript

GCscavenge

Scavenging

Page 67: Implementing a JavaScript Engine

GC Concurrency

Application Thread

JavaScript

GCincremental mark sweep

Incremental Mark

Page 68: Implementing a JavaScript Engine

GC Concurrency

Application Thread

JavaScript

GCmark lazy sweep

Lazy Sweep

Page 69: Implementing a JavaScript Engine

GC Concurrency

Application Thread

JavaScript

GCincremental mark lazy sweep

Incremental Mark + Lazy Sweep

Page 70: Implementing a JavaScript Engine

GC Concurrency

Application Thread

JavaScript

GCincremental markand scavenge

lazy sweep

Generational:Scavenging + (Incremental Mark + Lazy Sweep)

Page 71: Implementing a JavaScript Engine

GC Concurrency

GC Thread

Application Thread

JavaScript

GC

initial markconcurrent sweepconcurrent mark

remark reset

(Mostly) Concurrent Mark-Sweep

Page 72: Implementing a JavaScript Engine

A BIT ABOUT NASHORNA new high performance JavaScript on top of the JVM

Page 73: Implementing a JavaScript Engine

What is Nashorn?

• Oracle’s ECMAScript 5.1 implementation, on the JVM

• Clean code base, 100% Java– started from scratch; no code from Rhino

• An OpenJDK project• GPLv2 licensed

Overview

Page 74: Implementing a JavaScript Engine

What is Nashorn?Origins of the “Nashorn” name: the Rhino book

Page 75: Implementing a JavaScript Engine

What is Nashorn?Origins of the “Nashorn” name: Mozilla Rhino

Page 76: Implementing a JavaScript Engine

What is Nashorn?Origins of the “Nashorn” name: the unofficial Nashorn logo

Page 77: Implementing a JavaScript Engine

What is Nashorn?Origins of the “Nashorn” name: my impression

Page 78: Implementing a JavaScript Engine

Dynamic Languages on the JVMCan easily get to a sports-car-ish level

Page 79: Implementing a JavaScript Engine

Dynamic Languages on the JVMTakes some effort to get to a decent sports car level

Page 80: Implementing a JavaScript Engine

Dynamic Languages on the JVMHard to achieve extremely good performance

Page 81: Implementing a JavaScript Engine

Nashorn Execution Model

Lexical Analysis

Syntax Analysis

Constant Folding

Control-flow Lowering

Type Annotating

Range Analysis (*)

Code Splitting

Type Hardening

Bytecode Generation

JavaScript Source Code

AST

Java Bytecode

Parser (Compiler Frontend)

Compiler Backend

* Not complete yet