Dependent Types for JavaScript Ravi Chugh Ranjit Jhala University of California, San Diego David...
-
Upload
dylan-harper -
Category
Documents
-
view
218 -
download
0
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