Dependent Types for JavaScript Ravi Chugh Ranjit Jhala University of California, San Diego David...

Post on 14-Dec-2015

218 views 0 download

Transcript of Dependent Types for JavaScript Ravi Chugh Ranjit Jhala University of California, San Diego David...

DependentTypes for JavaScript

Ravi Chugh Ranjit JhalaUniversity of California, San Diego

David HermanMozilla Research

DependentTypes for JavaScript

Why?

Pervasive

Used at Massive Scale

3

Why Add Types?

var x = {};

x.f;

x.f.f;

produces undefined rather than error…

… but this raises TypeError

4

Why Add Types?

There Are Run-time Errorsreading from undefined, applying non-function values, …

Worse: Browsers Hide Them!

var x = {};

x.f;

x.f.f;

5

Why Add Types?

Prevent Errors

Prevent the Unexpected

6

Okay, But Who Cares?

ProgrammersJSLint, Closure Compiler, TypeScript, Dart

Browser VendorsCompete based on performance, security, reliability

Standards CommitteesActively evolving JavaScript and Web standards

DependentTypes for JavaScript

Why?

Isn’t the Language Terrible?

8

JavaScript

implicit global object

scope manipulation

var lifting

‘,,,’ == new Array(4)

9

JavaScript

implicit global object

scope manipulation

var lifting

‘,,,’ == new Array(4)

objects prototypes

lambdastype-tests

10

JavaScript

“The Good Parts”

implicit global object

scope manipulation

var lifting

‘,,,’ == new Array(4)

objects prototypes

lambdastype-tests

11

JavaScript

“The Good Parts”

PriorType

Systems

Key Idea:Use Logic!

Our Approach

12

JavaScript

“The Good Parts”

Dependent JavaScriptDJS

13

Outline

Motivation

Our Approach: Logic!

Evaluation

14

typeof true // “boolean”

typeof 0.1 // “number”typeof 0 // “number”

typeof {} // “object”typeof [] // “object”typeof null // “object”

15

typeof returns run-time “tags”

Tags are very coarse-grained types

“undefined”

“boolean”

“string”

“number”

“object”

“function”

16

Refinement Types{x|p}

“set of values x s.t. formula p is true”

{n|tag(n) = “number” }Num

{v|tag(v) = “number” ∨ tag(v) = “boolean” }NumOrBool

{i|tag(i) = “number” integer(i) }Int

{x|true }Any

17

{n|tag(n) = “number” }Num

{v|tag(v) = “number” ∨ tag(v) = “boolean” }NumOrBool

{i|tag(i) = “number” integer(i) }Int

{x|true }Any

Refinement Types

Syntactic Sugarfor Common Types

Refinement Types

18

3 :: {n|n = 3}

{n|n > 0} 3 ::{n|tag(n) = “number” integer(n)} 3 ::{n|tag(n) = “number”} 3 ::

Refinement Types

19

{n|n = 3}

{n|n > 0} {n|tag(n) = “number” integer(n)} {n|tag(n) = “number” }

<:<:<:<:

Subtyping is Implication

Refinement Types

20

{n|n = 3}

{n|n > 0} {n|tag(n) = “number” integer(n)} {n|tag(n) = “number” }

Subtyping is Implication

21

ArraysPrototypesMutable ObjectsDuck TypingTag-Tests

22

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

!true

true

// false

23

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

0 - 2 // -2

2

24

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

0 - [] // 0WAT?!

[]

25

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

} Use types to prevent implicit coercion

(-) :: (Num,Num) Num

26

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

Function type annotation inside

comments

//: negate :: (x:Any) Any

27

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

//: negate :: (x:Any) Any

so negationis well-typed

x is boolean...

DJS is Path Sensitive

28

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

//: negate :: (x:Any) Any

x is arbitrarynon-boolean value…so DJS signals error!

DJS is Path Sensitive

29

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

//: negate :: (x:NumOrBool) Any

30

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

//: negate :: (x:NumOrBool) Any

this time,x is a number…✓so subtractionis well-typed

31

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

//: negate :: (x:NumOrBool) Any✓

but returntype is imprecise

32

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

//: negate :: (x:NumOrBool) NumOrBool

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

33

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

/*: negate :: (x:NumOrBool) {v|tag(v) = tag(x)} */

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

output type depends on input value

34

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

/*: negate :: (x:NumOrBool) {v|tag(v) = tag(x)} */

Programmer choosesdegree of precision

in specification

35

Duck Typing ArraysPrototypesMutable ObjectsTag-Tests

if (duck.quack)

return “Duck says ” + duck.quack();

else

return “This duck can’t quack!”;

What is “Duck Typing”?

36

Duck Typing ArraysPrototypesMutable ObjectsTag-Tests

if (duck.quack)

return “Duck says ” + duck.quack();

else

return “This duck can’t quack!”;

What is “Duck Typing”?

(+) :: (Num,Num) Num

(+) :: (Str,Str) Str

37

Duck Typing ArraysPrototypesMutable ObjectsTag-Tests

if (duck.quack)

return “Duck says ” + duck.quack();

else

return “This duck can’t quack!”;

What is “Duck Typing”?

Can dynamically testthe presence of a method

but not its type

38

Duck Typing ArraysPrototypesMutable ObjectsTag-Tests

if (duck.quack)

return “Duck says ” + duck.quack();

else

return “This duck can’t quack!”;

{d|tag(d) = “Dict”∧

{v|has(d,“quack”)

{v| sel(d,“quack”) :: UnitStr }

Operators from McCarthy theory of arrays

39

Duck Typing ArraysPrototypesMutable ObjectsTag-Tests

if (duck.quack)

return “Duck says ” + duck.quack();

else

return “This duck can’t quack!”;

{d|tag(d) = “Dict”∧

{v|has(d,“quack”)

{v| sel(d,“quack”) :: Unit Str }

Call produces Str, so concat well-typed

40

Mutable Objects ArraysPrototypesTag-Tests Duck Typing

var x = {};

x.f = 7;

x.f + 2;

x0:Empty

x1:{d|d = upd(x0,“f”,7)}

DJS is Flow Sensitive

McCarthy operatorDJS verifies that x.f is definitely a number

41

Mutable Objects ArraysPrototypesTag-Tests Duck Typing

var x = {};

x.f = 7;

x.f + 2;

x0:Empty

x1:{d|d = upd(x0,“f”,7)}

DJS is Flow Sensitive

Strong updates to singleton objects

Weak updates to collections of objects

42

ArraysPrototypesTag-Tests Duck Typing Mutable Objects

43

ArraysPrototypesTag-Tests Duck Typing Mutable Objects

Typical“Dynamic”Features

44

ArraysPrototypesTag-Tests Duck Typing Mutable Objects

Typical“Dynamic”Features

JavaScript

tl;dr

Harder, butDJS handles them

45

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

child

parent

...

grandpa

null

Upon construction, each object links to a

prototype object

46

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

If child contains k, then Read k from child

Else if parent contains k, then Read k from parent

Else if grandpa contains k, then Read k from grandpa

Else if …

Else Return undefined

child

parent

...

grandpa

null

var k = “first”; child[k];Semantics of Key Lookup

...

47

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

child

parent

grandpa

null

H(Rest of Heap)

var k = “first”; child[k];Semantics of Key Lookup

If child contains k, then Read k from child

Else if parent contains k, then Read k from parent

Else if grandpa contains k, then Read k from grandpa

Else if …

Else Return undefined

{v|if has(child,k) then

{v|ifv=sel(child,k)

...

48

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

child

parent

grandpa

null

H(Rest of Heap)

var k = “first”; child[k];Semantics of Key Lookup

If child contains k, then Read k from child

Else if parent contains k, then Read k from parent

Else if grandpa contains k, then Read k from grandpa

Else if …

Else Return undefined

{v|if has(child,k) then

{v|ifv=sel(child,k)

{v|else if has(parent,k) then

{v|ifv=sel(parent,k)

If child contains k, then Read k from child

Else if parent contains k, then Read k from parent

Else if grandpa contains k, then Read k from grandpa

Else if …

Else Return undefined

...

49

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

{v|if has(child,k) then

{v|ifv=sel(child,k)

{v|else if has(parent,k) then

{v|ifv=sel(parent,k)

child

parent

grandpa ???

null

H(Rest of Heap)

var k = “first”; child[k];Semantics of Key Lookup

...

50

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

{v|if has(child,k) then

{v|ifv=sel(child,k)

{v|else if has(parent,k) then

{v|ifv=sel(parent,k)

{v|else

{v|ifv=HeapSel(H,grandpa,k)) }

child

parent

grandpa ???

null

H(Rest of Heap)

var k = “first”; child[k];Semantics of Key Lookup

Abstract predicateto summarize theunknown portion

of the prototype chain

...

51

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

{v|if has(child,k) then

{v|ifv=sel(child,k)

{v|else if has(parent,k) then

{v|ifv=sel(parent,k)

{v|else

{v|ifv=HeapSel(H,grandpa,k)) }

{ “first” : “John” }child

parent{ “first” : “Ida”, “last” : “McCarthy” }

grandpa ???

null

H(Rest of Heap)

<:

{v|v=“John”}

var k = “first”; child[k];

...

52

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

{v|if has(child,k) then

{v|ifv=sel(child,k)

{v|else if has(parent,k) then

{v|ifv=sel(parent,k)

{v|else

{v|ifv=HeapSel(H,grandpa,k)) }

{ “first” : “John” }child

parent{ “first” : “Ida”, “last” : “McCarthy” }

grandpa ???

null

H(Rest of Heap)

var k = “last”; child[k];

<:

{v|v=“McCarthy”}

53

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

Key Idea:

Reduce prototypesemantics to decidable

theory of arrays

Prototype Chain Unrolling

54

ArraysTag-Tests PrototypesMutable ObjectsDuck Typing

var nums = [0,1,2]while (…) { nums[nums.length] = 17}

A finite tuple…

… extended to unbounded collection

55

ArraysTag-Tests PrototypesMutable ObjectsDuck Typing

var nums = [0,1,2]while (…) { nums[nums.length] = 17}

delete nums[1]

for (i = 0; i < nums.length; i++) sum += nums[i]

A “hole” in the array

Missing element within “length”

56

ArraysTag-Tests PrototypesMutable ObjectsDuck Typing

Track types, “packedness,” and lengthof arrays where possible

{a |a :: Arr(T)

{a | packed(a)

{a | len(a) = 10}

X T T T T… X ……

T? T? T? T? T?… T? ……

T? { x | T(x) x = undefined }

-1 0 1 2 len(a)

X { x | x = undefined }

57

ArraysTag-Tests PrototypesMutable ObjectsDuck Typing

{a |a :: Arr(Any)

{a | packed(a) len(a) = 2

{a | Int(sel(a,0))

{a | Str(sel(a,1))}

Encode tuples as arrays

var tup = [17, “cacti”]

58

ArraysTag-Tests PrototypesMutable ObjectsDuck Typing

var tup = [17, “cacti”]

tup[tup.length] = true

{a |a :: Arr(Any)

{a | packed(a) len(a) = 3

{a | …}

59

ArraysTag-Tests PrototypesMutable ObjectsDuck Typing

DJS handles other quirks:

Special length property

Array.prototypeNon-integer keys

60

Tag-Tests PrototypesMutable ObjectsDuck Typing Arrays

61

What About eval?

Arbitrary code loading

eval(“…”);

Old Types

62

What About eval?…

eval(“…”);

//: #assume

Old Types

New TypesNew Types

“Contract Checking” at Run-timeaka “Gradual Typing”

63

Recap of DJS Techniques

Logic!

Path and Flow Sensitivity

Prototype Unrolling

Syntactic Sugar

64

Outline

Motivation

Our Approach: Logic!

Evaluation

65

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

66

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

67

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

Four inheritance patterns fromCrockford’s JS: The Good Parts

68

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

Array-manipulating examplesfrom SunSpider

69

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

typeOf() function to replace typeoffrom Closure Compiler

70

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

Well-typed programsdon’t have run-time errors

DesugaredProgram

DJSProgram

DesugarerBased on Guha et al.

[ECOOP 2010]

Implementation

JavaScript λ-Calculus + References + Prototypes

DesugaredProgram

Z3 SMTSolver

TypeChecker

DJSProgram

DesugarerBased on Guha et al.

[ECOOP 2010]

ImplementationProgrammer Chooses

Warnings or Errors

Local Type Inference

Subtyping w/o Z3 If Possible

Annotation Burden

+= ~300 LOC to start+= ~100 LOC annotations

=+ ~400 LOC total

(Improved since paper)

33% Annotation Overhead

Common Cases Simplified viaSyntactic Sugar and Local Type Inference

Performance(Improved since paper)

Total benchmark suite:

~10 seconds~1100 Z3 queries

11/13 benchmarks in 0-1 s

Common Cases Fast viaSyntactic Reasoning When Possible

75

Future Work

Syntax, Inference, Performance

Larger Examples

Type Checker in JS; Run in Browser

IDE Support for Refactoring, etc.

76

JavaScript

“The Good Parts”

Types via Logic!DJS

Thanks!

77

ravichugh.com/djs

D::PS: I’m on the Job Market

78

Extra Slides

Coq

refinement types

dependent types

Expressivity

“Usability”

JS

+ nested refinements

System D[POPL ’12]

DJS

DependentJavaScript

[this paper]

syntactic types

occurrence types

∨, ∧

F≤

System D [POPL 2012]+ Types for JS Primitives+ Strong Updates+ Quirky JS Arrays+ Prototype Inheritance

Dependent JavaScript (DJS)

81

/*: x:NumOrBool {ite Num(x) Num(v) Bool(v)} */

function negate(x) { x = (typeof x == “number”) ? 0 – x : !x return x}

/*: x:Any {v iff falsy(x)} */

function negate(x) { x = (typeof x == “number”) ? 0 – x : !x return x}

Function Types and Objects{p} {v|p}

82

ObjHas(d,k,H,d’) has(d,k) HeapHas(H,d’,k)

/*: x:Ref / [x |-> d:Dict |> x.pro] {v iff ObjHas(d,”f”,curHeap,x.pro)} / sameHeap */

function hasF(x) { return “f” in x}

x:T1/H1 T2/H2

Function Types and Objects

output typeinput heap

input type output heap

83

ObjSel(d,k,H,d’) ite has(d,k) sel(d,k) HeapSel(H,d’,k)

x:T1/H1 T2/H2

Function Types and Objects

output typeinput heap

input type output heap

/*: x:Ref / [x |-> d:Dict |> x.pro] {v = ObjSel(d,”f”,curHeap,x.pro)} / sameHeap */

function readF(x) { return x.f}

84

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

0 - “2”// -2

“2”

Whoa, but perhaps okay…

85

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

0 - undefined// NaN

undefinedError would be nicer,

but okay…

86

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

0 - {}// NaN

{}

Seems about right…

87

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

0 - [] // 0

[]

WAT?!

negate has good intentionsBut too many corner cases in JS semantics!

88

Duck Typing ArraysPrototypesMutable ObjectsTag-Tests

if (duck.quack)

return “Duck says ” + duck.quack();

else

return “This duck can’t quack!”;

{d|tag(d) = “Dict”∧

{v|has(d,“quack”)

{v| sel(d,“quack”) :: Unit Str }

Function types nested inside refinements

[POPL 2012]

89

ArraysPrototypesTag-Tests Duck Typing

var x = {};

x.f;

x.f.f; Programmer configures DJS to report either

warnings or errors for:

1) Possible unbound keys

Mutable Objects

90

ArraysPrototypesTag-Tests Duck Typing

var x = {};

x.f;

x.f.f; Programmer configures DJS to report either

warnings or errors for:

1) Possible unbound keys

2) Possible run-time errors

Mutable Objects