dependent types for javascript ravi chugh ranjit jhala university of california, san diego david...
Post on 14-Dec-2015
221 Views
Preview:
TRANSCRIPT
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
top related