hcss13-djs

advertisement
Dependent
Types for JavaScript
Ravi Chugh
Ranjit Jhala
Dave Herman
Pat Rondon
Panos Vekris
UCSD
UCSD
Mozilla
Google
UCSD
“Dynamic” Features
Facilitate Rapid Innovation
Dependent
Types for JavaScript
1. Better Development Tools
2. Better Reliability
3. Better Performance
var person = {
name : { first : “John”,
last : “McCarthy” }};
person.nom;
produces undefined
rather than error…
person.nom.first;
… but this raises TypeError
3
var person = {
name : { first : “John”,
last : “McCarthy” }};
if (unlikely()) {
person.nom;
some errors hard to
catch with testing
person.nom.first;
}
4
Dependent
Types for JavaScript
Will Never Replace Need for
Testing and Dynamic Checking
But Want Static Checking When Possible
JavaScript
scope
manipulation
implicit
global
object
var
lifting
‘,,,’ == new Array(4)
6
scope
manipulation
JavaScript
“The Good Parts”
implicit
global
object
arrays
objects
prototypes
type-tests
lambdas
var
lifting
eval()
‘,,,’ == new Array(4)
7
JavaScript
“The Good Parts”
Dependent JavaScript
Use Logic, but
Avoid Quantifiers!
8
“Usability”
Shriram
@2:30pm
Me
@now
TypedJS
Dependent
JavaScript (DJS)
Nik
@9:00am
[POPL ’12, OOPSLA ’12]
F* + Dijkstra
Expressiveness
9
DJS = Refinement Types
+ Several New
Quantifier-Free
Mechanisms
Me
@now
Dependent
JavaScript (DJS)
[POPL ’12, OOPSLA ’12]
10
typeof true // “boolean”
typeof 0.1 // “number”
typeof 0 // “number”
typeof {} // “object”
typeof [] // “object”
typeof null // “object”
11
typeof returns run-time “tags”
Tags are very coarse-grained types
“undefined”
“boolean”
“string”
“number”
“object”
“function”
12
Refinement Types
{x|p}
“set of values x s.t. formula p is true”
Num 
NumOrBool 
{ n | tag(n) = “number” }
{ v | tag(v) = “number”  tag(v) = “boolean” }
Int  { i | tag(i) = “number”  integer(i) }
Any  { x | true }
13
Refinement Types
Syntactic Sugar
for Common Types
Num 
NumOrBool 
{ n | tag(n) = “number” }
{ v | tag(v) = “number”  tag(v) = “boolean” }
Int  { i | tag(i) = “number”  integer(i) }
Any  { x | true }
14
Refinement Types
3 ::
3 ::
3 ::
3 ::
{n|n=3}
{n|n>0}
{ n | tag(n) = “number”  integer(n) }
{ n | tag(n) = “number” }
15
Refinement Types
Subtyping is Implication
<:
<:
<:
<:
{n|n=3}
{n|n>0}
{ n | tag(n) = “number”  integer(n) }
{ n | tag(n) = “number” }
16
Refinement Types
Subtyping is Implication




{n|n=3}
{n|n>0}
{ n | tag(n) = “number”  integer(n) }
{ n | tag(n) = “number” }
17
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
18
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
!true
// false
else
return 0 - x;
}
negate(
)true
19
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
else
return 0 - x; 0 - 2
// -2
}
negate(
)2
20
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
else
return 0 - x; 0 - []
// 0
?!?
}
negate(
) []
21
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
else
return 0 - x;
}
Use types to prevent implicit
coercion
(-) :: (Num,Num)  Num
22
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
//: negate :: (x:Any)  Any
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
else
return 0 - x;
}
Function type
annotation inside
comments
23
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
//: negate :: (x:Any)  Any
var negate = function(x) {
if (typeof x == “boolean”)
return !x;

else
return 0 - x;
}
x is boolean...
so negation
is well-typed
DJS is Path Sensitive
24
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
✗
//: negate :: (x:Any)  Any
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
else
return 0 - x;
✗
x is arbitrary
non-boolean value…
so DJS signals error!
}
DJS is Path Sensitive
25
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
//: negate :: (x:NumOrBool)  Any
var negate = function(x) {
if (typeof x == “boolean”)
return !x;

else
return 0 - x;
}
26
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
//: negate :: (x:NumOrBool)  Any
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
else
return 0 - x;
}

this time,
x is a number…
so subtraction
is well-typed
27
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays

//: negate :: (x:NumOrBool)  Any
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
else
return 0 - x;
}
but return
type is imprecise
28
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays

//: negate :: (x:NumOrBool)  NumOrBool
var negate = function(x) {
if (typeof x == “boolean”)
return !x;
else
return 0 - x;
}
29
Tag-Tests
Duck Typing
Mutable Objects
/*: negate :: (x:NumOrBool)
 { y | tag(y) = tag(x) } */
var negate = function(x) {
Prototypes
Arrays

if (typeof x == “boolean”)
return !x;
else
return 0 - x;
}
output type
depends on
input value
30
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
What is “Duck Typing”?
if (duck.quack)
return “Duck says ” + duck.quack();
else
return “This duck can’t quack!”;
31
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
What is “Duck Typing”?
(+) :: (Num,Num)  Num
(+) :: (Str,Str)  Str
if (duck.quack)
return “Duck says ” + duck.quack();
else
return “This duck can’t quack!”;
32
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
What is “Duck Typing”?
Can dynamically test
the presence of a method
but not its type
if (duck.quack)
return “Duck says ” + duck.quack();
else
return “This duck can’t quack!”;
33
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
{ d | tag(d) = “object” ∧
{ v | has(d,“quack”) 
{ v | sel(d,“quack”) :: Unit  Str }
Operators from McCarthy theory of arrays
if (duck.quack)
return “Duck says ” + duck.quack();
else
return “This duck can’t quack!”;
34
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
{ d | tag(d) = “object” ∧
{ v | has(d,“quack”) 
{ v | sel(d,“quack”) :: Unit  Str }
Call produces Str, so concat well-typed

return “Duck says ” + duck.quack();
if (duck.quack)
else
return “This duck can’t quack!”;
35
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
DJS is Flow Sensitive
var x = {};
x.f = 7;
x.f + 2;
DJS verifies that x.f is
definitely a number
x0:Empty
x1:{d|d = upd(x0,“f”,7)}
McCarthy operator
36
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
DJS is Flow Sensitive
var x = {};
x.f = 7;
x.f + 2;
x0:Empty
x1:{d|d = upd(x0,“f”,7)}
Strong updates to singleton objects
Weak updates to collections of objects
37
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
38
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
Typical
“Dynamic”
Features
39
Tag-Tests
Duck Typing
Typical
“Dynamic”
Features
Mutable Objects
Prototypes
Arrays
JavaScript
40
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
Upon construction,
each object links to a
prototype object
child
parent
grandpa
...
null
41
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
SemanticsvarofkKey
Lookup
= “first”;
child[k];
If child contains k, then
Read k from child
child
Else if parent contains k, then
Read k from parent
parent
Else if grandpa contains k, then
Read k from grandpa
grandpa
...
null
Else if …
Else
Return undefined
42
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
SemanticsvarofkKey
Lookup
= “first”;
child[k];
If child contains
{ v | if has(child,k)
then k, then
Read k from child
{ v | ifv = sel(child,k)
child
Else if parent contains k, then
Read k from parent
parent
Else if grandpa contains k, then
Read k from grandpa
grandpa
H
...
(Rest of Heap)
null
Else if …
Else
Return undefined
43
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
SemanticsvarofkKey
Lookup
= “first”;
child[k];
If child contains
{ v | if has(child,k)
then k, then
Read k from child
{ v | ifv = sel(child,k)
child
{ v | elseElse
if has(parent,k)
thenk, then
if parent contains
Read k from parent
{ v | ifv = sel(parent,k)
parent
Else if grandpa contains k, then
Read k from grandpa
grandpa
H
...
(Rest of Heap)
null
Else if …
Else
Return undefined
44
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
SemanticsvarofkKey
Lookup
= “first”;
child[k];
If child contains
{ v | if has(child,k)
then k, then
Read k from child
{ v | ifv = sel(child,k)
child
{ v | elseElse
if has(parent,k)
thenk, then
if parent contains
Read k from parent
{ v | ifv = sel(parent,k)
parent
grandpa
H
???
...
(Rest of Heap)
null
Else if grandpa contains k, then
Read k from grandpa
Else if …
Else
Return undefined
45
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
SemanticsvarofkKey
Lookup
= “first”;
child[k];
{ v | if has(child,k) then
child
{ v | ifv = sel(child,k)
{ v | else if has(parent,k) then
parent
{ v | ifv = sel(parent,k)
{ v | else
grandpa
H
???
...
(Rest of Heap)
null
{ v | ifv = HeapSel(H,grandpa,k)) }
Abstract predicate
to summarize the
unknown portion
of the prototype chain
46
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
var k = “first”; child[k];
child
parent
{ “first” : “John” }
{ “first” : “Ida”
, “last” : “McCarthy” }
{ 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
grandpa
H
???
...
{ v | ifv = HeapSel(H,grandpa,k)) }
<:
{ v | v = “John” }
(Rest of Heap)
null
47
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
var k = “last”; child[k];
child
parent
{ “first” : “John” }
{ “first” : “Ida”
, “last” : “McCarthy” }
{ 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
grandpa
H
???
...
{ v | ifv = HeapSel(H,grandpa,k)) }
<:
{ v | v = “McCarthy” }
(Rest of Heap)
null
48
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
Prototype Chain Unrolling
Key Idea:
Reduce prototype
semantics to decidable
theory of arrays
49
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
A finite tuple…
var nums = [0,1,2];
while (…) {
nums[nums.length] = 17;
}
… extended to
unbounded collection
50
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
var nums = [0,1,2];
while (…) {
nums[nums.length] = 17;
}
delete nums[1];
A “hole” in the array
for (i = 0; i < nums.length; i++) {
sum += nums[i];
}
Missing element within “length”
51
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
Track types, “packedness,” and length
of arrays where possible
-1
0
1
2
len(a)
{ a | a :: Arr(T)
… T? T? T? T? … T? T? …
{ a | packed(a)
…
X
T
T
T
…
T
X
…
{ a | len(a) = 10 }
T?  { x | T(x)  x = undefined }
X  { x | x = undefined }
52
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
Encode tuples as arrays
var tup = [17, “cacti”];
{ a | a :: Arr(Any)
{ a |  packed(a)  len(a) = 2
{ a |  Int(sel(a,0))
{ a |  Str(sel(a,1)) }
53
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
var tup = [17, “cacti”];
tup[tup.length] = true;
{ a | a :: Arr(Any)
{ a |  packed(a)  len(a) = 3
{a| …}
54
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
DJS handles other array quirks:
Special length property
Array.prototype
Non-integer keys
55
Tag-Tests
Duck Typing
Mutable Objects
Prototypes
Arrays
56
What About eval?
…
Old Types
eval(“…”);
…
Arbitrary code loading
57
What About eval?
…
Old Types
eval(“…”);
//: #assume
New Types
New Types
…
Can Integrate DJS with
“Contract Checking” at Run-time
aka “Gradual Typing”
58
“Usability”
DJS = Refinement Types
+ Nested Refinements
+ Flow Sensitive Types
+ Prototype Unrolling
+ Array Encoding
}
QuantifierFree
Mechanisms
Dependent
JavaScript (DJS)
[POPL ’12, OOPSLA ’12]
F* + Dijkstra
Expressiveness
59
Function Subtyping…
{ d | sel(d,“f”) ::
<:
{ d | sel(d,“f”) ::
}
(x:Any)  { y|y = x }
}
(x:Num)  Num
60
Function Subtyping…
{ d | sel(d,“f”) ::
{ d | sel(d,“f”) ::

}
(x:Any)  { y|y = x }
}
(x:Num)  Num
61
Function Subtyping…
{ d | sel(d,“ ”f ::
{ d | sel(d,“”)f ::

}
(x:Any)  { y|y = x }
}
(x:Num)  Num
… With Quantifiers


∀x,y. true ∧ y = f(x)  y = x
∀x,y. Num(x) ∧ y = f(x)  Num(y)
Valid, but First-Order Logic is
Undecidable
62
Function Subtyping…
{ d | sel(d,“ ”f ::
{ d | sel(d,“”)f ::

}
(x:Any)  { y|y = x }
}
(x:Num)  Num
… Without Quantifiers!
Nested Refinements
Treat Function Types as Uninterpreted
Implication = SMT Validity + Syntactic Subtyping
63
Heap Updates…
var x = {};
x.f = 7;
… With Quantifiers
∧ …
h0
h1
h2
Encode Heap w/ McCarthy Operators
∧ sel(h1,x) = empty
∧ ∀y. x ≠ y  sel(h1,y) = sel(h0,y)
∧ sel(h2,x) = upd(sel(h1,x),“f”,7)
∧ ∀y. x ≠ y  sel(h2,y) = sel(h1,y)
64
Heap Updates…
var x = {};
x.f = 7;
… Without Quantifiers!
h0
h1
h2
Flow-Sensitive Types (à la Alias Types)
x:T1/H1  T2/H2
input type
input heap
output heap
output type
65
Prototype Inheritance…
Array Semantics…
… Without Quantifiers!
66
“Usability”
DJS = Refinement Types
+ Nested Refinements
+ Flow Sensitive Types
+ Prototype Unrolling
+ Array Encoding
}
QuantifierFree
Mechanisms
Dependent
JavaScript (DJS)
[POPL ’12, OOPSLA ’12]
F* + Dijkstra
Expressiveness
67
DJS
Program
Desugarer
Based on Guha et al.
[ECOOP ’10]
Desugared
Program
Implementation
JavaScript  λ-Calculus + References +
Prototypes
DJS
Program
Desugarer
Based on Guha et al.
[ECOOP ’10]
Desugared
Program
Implementation
Programmer Chooses
Warnings or Errors
Local Type Inference
Subtyping w/o Z3 If Possible
Type
Checker
Z3 SMT
Solver
Benchmarks
13 Excerpts from:
JavaScript, Good Parts
SunSpider Benchmark Suite
Google Closure Library
LOC
before/after
306
a
408
(+33%)
Chosen to Stretch the Current Limits of
DJS
70
Benchmarks
13 Excerpts from:
JavaScript, Good Parts
SunSpider Benchmark Suite
Google Closure Library
9 Browser Extensions from:
[Guha et al. Oakland ’11]
2 Examples from:
Google Gadgets
TOTALS
LOC
before/after
306
a
321
a
1,003
a
1,630
408
(+33%)
383
(+19%)
1,027
(+2%)
1,818
(+12%)
71
Already Improved by Simple
Type Inference and Syntactic Sugar
Plenty of Room for Improvement
• Iterative Predicate Abstraction
• Bootstrap from Run-Time Traces
TOTALS
1,630
1,818
(+12%)
72
Benchmarks
13 Excerpts from:
JavaScript, Good Parts
SunSpider Benchmark Suite
Google Closure Library
9 Browser Extensions from:
[Guha et al. Oakland ’11]
2 Examples from:
Google Gadgets
TOTALS
LOC
Running
before/after
Time
306
a
321
a
1,003
a
1,630
408
(+33%)
10 sec
383
(+19%)
3 sec
1,027
(+2%)
19 sec
1,818
(+12%)
32 sec
73
Already Improved by Simple
Optimizations
• Avoid SMT Solver When Possible
• Reduce Precision for Common
Patterns
Plenty of Room for Improvement
TOTALS
1,630
1,818
(+12%)
32 sec
74
Dependent
Types for JavaScript
1. Better Development Tools
2. Better Reliability
3. Better Performance
“Usability”
TypeScript
Lightweight (unsound) static checking
tools becoming popular
Opportunity
to improve
IDE tools
TypedJS
Dependent
JavaScript (DJS)
[POPL ’12, OOPSLA ’12]
F* + Dijkstra
Expressiveness
76
Reliability / Security
• Refinement types for security in presence of
untrusted code (e.g. browser extensions)
• Combine with static reasoning for JavaScript
Performance
• JITs use static analysis + profiling to optimize
dynamic features (e.g. dictionaries, bignums)
• Opportunity to enable more optimizations
77
Thanks!
Dependent
Types for JavaScript
}
1. Better Development Tools
2. Better Reliability
3. Better Performance
ravichugh.com/djs
DJS is a Step
Towards
These Goals
Download