Transcript
Page 1: Dependent Types for JavaScript

DependentTypes for JavaScript

Ravi Chugh Ranjit JhalaUniversity of California, San Diego

David HermanMozilla Research

Page 2: Dependent Types for JavaScript

DependentTypes for JavaScript

Why?

Pervasive

Used at Massive Scale

Page 3: Dependent Types for JavaScript

3

Why Add Types?

var x = {};

x.f;

x.f.f;

produces undefined rather than error…

… but this raises TypeError

Page 4: Dependent Types for JavaScript

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;

Page 5: Dependent Types for JavaScript

5

Why Add Types?

Prevent Errors

Prevent the Unexpected

Page 6: Dependent Types for JavaScript

6

Okay, But Who Cares?

ProgrammersJSLint, Closure Compiler, TypeScript, Dart

Browser VendorsCompete based on performance, security, reliability

Standards CommitteesActively evolving JavaScript and Web standards

Page 7: Dependent Types for JavaScript

DependentTypes for JavaScript

Why?

Isn’t the Language Terrible?

Page 8: Dependent Types for JavaScript

8

JavaScript

implicit global object

scope manipulation

var lifting

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

Page 9: Dependent Types for JavaScript

9

JavaScript

implicit global object

scope manipulation

var lifting

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

objects prototypes

lambdastype-tests

Page 10: Dependent Types for JavaScript

10

JavaScript

“The Good Parts”

implicit global object

scope manipulation

var lifting

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

objects prototypes

lambdastype-tests

Page 11: Dependent Types for JavaScript

11

JavaScript

“The Good Parts”

PriorType

Systems

Key Idea:Use Logic!

Our Approach

Page 12: Dependent Types for JavaScript

12

JavaScript

“The Good Parts”

Dependent JavaScriptDJS

Page 13: Dependent Types for JavaScript

13

Outline

Motivation

Our Approach: Logic!

Evaluation

Page 14: Dependent Types for JavaScript

14

typeof true // “boolean”

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

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

Page 15: Dependent Types for JavaScript

15

typeof returns run-time “tags”

Tags are very coarse-grained types

“undefined”

“boolean”

“string”

“number”

“object”

“function”

Page 16: Dependent Types for JavaScript

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

Page 17: Dependent Types for JavaScript

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

Page 18: Dependent Types for JavaScript

Refinement Types

18

3 :: {n|n = 3}

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

Page 19: Dependent Types for JavaScript

Refinement Types

19

{n|n = 3}

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

<:<:<:<:

Subtyping is Implication

Page 20: Dependent Types for JavaScript

Refinement Types

20

{n|n = 3}

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

Subtyping is Implication

Page 21: Dependent Types for JavaScript

21

ArraysPrototypesMutable ObjectsDuck TypingTag-Tests

Page 22: Dependent Types for JavaScript

22

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

!true

true

// false

Page 23: Dependent Types for JavaScript

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

Page 24: Dependent Types for JavaScript

24

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

negate( )

0 - [] // 0WAT?!

[]

Page 25: Dependent Types for JavaScript

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

Page 26: Dependent Types for JavaScript

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

Page 27: Dependent Types for JavaScript

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

Page 28: Dependent Types for JavaScript

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

Page 29: Dependent Types for JavaScript

29

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

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

Page 30: Dependent Types for JavaScript

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

Page 31: Dependent Types for JavaScript

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

Page 32: Dependent Types for JavaScript

32

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

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

var negate = function(x) {

if (typeof x == “boolean”)

return !x;

else

return 0 - x;

}

Page 33: Dependent Types for JavaScript

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

Page 34: Dependent Types for JavaScript

34

Tag-Tests ArraysPrototypesMutable ObjectsDuck Typing

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

Programmer choosesdegree of precision

in specification

Page 35: Dependent Types for JavaScript

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”?

Page 36: Dependent Types for JavaScript

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

Page 37: Dependent Types for JavaScript

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

Page 38: Dependent Types for JavaScript

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

Page 39: Dependent Types for JavaScript

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

Page 40: Dependent Types for JavaScript

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

Page 41: Dependent Types for JavaScript

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

Page 42: Dependent Types for JavaScript

42

ArraysPrototypesTag-Tests Duck Typing Mutable Objects

Page 43: Dependent Types for JavaScript

43

ArraysPrototypesTag-Tests Duck Typing Mutable Objects

Typical“Dynamic”Features

Page 44: Dependent Types for JavaScript

44

ArraysPrototypesTag-Tests Duck Typing Mutable Objects

Typical“Dynamic”Features

JavaScript

tl;dr

Harder, butDJS handles them

Page 45: Dependent Types for JavaScript

45

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

child

parent

...

grandpa

null

Upon construction, each object links to a

prototype object

Page 46: Dependent Types for JavaScript

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

Page 47: Dependent Types for JavaScript

...

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)

Page 48: Dependent Types for JavaScript

...

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)

Page 49: Dependent Types for JavaScript

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

Page 50: Dependent Types for JavaScript

...

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

Page 51: Dependent Types for JavaScript

...

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];

Page 52: Dependent Types for JavaScript

...

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”}

Page 53: Dependent Types for JavaScript

53

Prototypes ArraysTag-Tests Mutable ObjectsDuck Typing

Key Idea:

Reduce prototypesemantics to decidable

theory of arrays

Prototype Chain Unrolling

Page 54: Dependent Types for JavaScript

54

ArraysTag-Tests PrototypesMutable ObjectsDuck Typing

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

A finite tuple…

… extended to unbounded collection

Page 55: Dependent Types for JavaScript

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”

Page 56: Dependent Types for JavaScript

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 }

Page 57: Dependent Types for JavaScript

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”]

Page 58: Dependent Types for JavaScript

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 | …}

Page 59: Dependent Types for JavaScript

59

ArraysTag-Tests PrototypesMutable ObjectsDuck Typing

DJS handles other quirks:

Special length property

Array.prototypeNon-integer keys

Page 60: Dependent Types for JavaScript

60

Tag-Tests PrototypesMutable ObjectsDuck Typing Arrays

Page 61: Dependent Types for JavaScript

61

What About eval?

Arbitrary code loading

eval(“…”);

Old Types

Page 62: Dependent Types for JavaScript

62

What About eval?…

eval(“…”);

//: #assume

Old Types

New TypesNew Types

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

Page 63: Dependent Types for JavaScript

63

Recap of DJS Techniques

Logic!

Path and Flow Sensitivity

Prototype Unrolling

Syntactic Sugar

Page 64: Dependent Types for JavaScript

64

Outline

Motivation

Our Approach: Logic!

Evaluation

Page 65: Dependent Types for JavaScript

65

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

Page 66: Dependent Types for JavaScript

66

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

Page 67: Dependent Types for JavaScript

67

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

Four inheritance patterns fromCrockford’s JS: The Good Parts

Page 68: Dependent Types for JavaScript

68

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

Array-manipulating examplesfrom SunSpider

Page 69: Dependent Types for JavaScript

69

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

typeOf() function to replace typeoffrom Closure Compiler

Page 70: Dependent Types for JavaScript

70

“The Good Parts”

objects prototypes

lambdastype-tests

arrays

13 Benchmarks

Well-typed programsdon’t have run-time errors

Page 71: Dependent Types for JavaScript

DesugaredProgram

DJSProgram

DesugarerBased on Guha et al.

[ECOOP 2010]

Implementation

JavaScript λ-Calculus + References + Prototypes

Page 72: Dependent Types for JavaScript

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

Page 73: Dependent Types for JavaScript

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

Page 74: Dependent Types for JavaScript

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

Page 75: Dependent Types for JavaScript

75

Future Work

Syntax, Inference, Performance

Larger Examples

Type Checker in JS; Run in Browser

IDE Support for Refactoring, etc.

Page 76: Dependent Types for JavaScript

76

JavaScript

“The Good Parts”

Types via Logic!DJS

Page 77: Dependent Types for JavaScript

Thanks!

77

ravichugh.com/djs

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

Page 78: Dependent Types for JavaScript

78

Extra Slides

Page 79: Dependent Types for JavaScript

Coq

refinement types

dependent types

Expressivity

“Usability”

JS

+ nested refinements

System D[POPL ’12]

DJS

DependentJavaScript

[this paper]

syntactic types

occurrence types

∨, ∧

F≤

Page 80: Dependent Types for JavaScript

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

Dependent JavaScript (DJS)

Page 81: Dependent Types for JavaScript

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}

Page 82: Dependent Types for JavaScript

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

Page 83: Dependent Types for JavaScript

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}

Page 84: Dependent Types for JavaScript

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…

Page 85: Dependent Types for JavaScript

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…

Page 86: Dependent Types for JavaScript

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…

Page 87: Dependent Types for JavaScript

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!

Page 88: Dependent Types for JavaScript

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]

Page 89: Dependent Types for JavaScript

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

Page 90: Dependent Types for JavaScript

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


Top Related