Upload
julius-fields
View
221
Download
0
Embed Size (px)
Citation preview
Nested RefinementTypes for JavaScript
Ravi Chugh
Dissertation Defense − September 3, 2013
Types for JavaScript
“Dynamic” or UntypedFatures Enable
Rapid Prototyping
2
Types for JavaScript
“Dynamic” or UntypedFatures Enable
Rapid Prototyping
http://stackoverflow.com/tags02-03-13
Count Tag411,345 C#361,080 Java337,896 PHP321,757 JavaScript175,765 C++160,768 Python
83,486 C64,345 Ruby
9,827 Haskell1,347 OCaml
3
Types for JavaScript
“Dynamic” or UntypedFatures Enable
Rapid Prototyping
1. Development Tools2. Reliability & Security
3. Performance
4
5
1. Development Tools
optional “type systems”unsound but provide
some IDE support
6
2. Reliability & Securitylightweight static checkers
run-time invariant checkers
secure language subsets
7
3. Performancefast language subset
to facilitate optimizations
Just-In-Time (JIT) enginesperform static/dynamic analysis to predict types
8
Types for JavaScript
Why So Hard?
9
implicit global object
scope manipulationJavaScript
var lifting
‘,,,’ == new Array(4)
“The Good Parts”
objects
reflection
arrays
eval()*
lambdas
JavaScript
implicit global object
scope manipulation
var lifting
‘,,,’ == new Array(4)
10
“The Good Parts”
objects
reflection
arrays
eval()*
lambdas
JavaScript
11
12Expressiveness
Usability Idioms ofDynamic Languages
Types
VerificationHoare Logics
Model CheckingAbstract Interpretation
Theorem Proving…
+ Logic
Thesis Statement:
“Refinement typescan reason about
dynamic languages to verify the absence of
run-time errors …
13Expressiveness
Usability Idioms ofDynamic Languages
Types
VerificationHoare Logics
Model CheckingAbstract Interpretation
Theorem Proving…
+ Logic
Thesis Statement:
… without resortingto undecidable
logics”
14Expressiveness
Usability Idioms ofDynamic Languages
Types
VerificationHoare Logics
Model CheckingAbstract Interpretation
Theorem Proving…
+ LogicDependent
JavaScript (DJS)[POPL 2012, OOPSLA 2012]
15
OutlineMotivation and Thesis
Challenges of Dynamic LanguagesTour of Dependent JavaScript
Technical ContributionsEvaluation
Looking Ahead
“The Good Parts”
objects
reflection
arrays
eval()*
lambdas
JavaScript
16
Core Technical Challenges
reflection
17
objects
Core Technical Challenges
JavaJavaScript
vs.
18
JavaScriptfunction negate(x) {
if (typeof x == “number”) {
return 0 - x;
} else {
return !x;
} }
run-timetype-test
19
JavaScriptfunction negate(x) {
if (typeof x == “number”) {
return 0 - x;
} else {
return !x;
} }
but booleanon else-branch
x should numbericon then-branch…
20
JavaScriptfunction negate(x) {
if (typeof x == “number”) {
return 0 - x;
} else {
return !x;
} }Java
✗21
Javainterface NumOrBool { NumOrBool negate(); }
class Bool implements NumOrBool { boolean data; Bool(boolean b) { this.data = b; }
Bool negate() { return new Bool(!this.data);} }
class Num implements NumOrBool { … }
declared union type
22
JavaScriptfunction negate(x) {
if (typeof x == “number”) {
…
} else {
…
} }Javainterface NumOrBool {
NumOrBool negate(); }
class Bool implements NumOrBool { boolean data; Bool(boolean b) { this.data = b; }
Bool negate() { return new Bool(!this.data);} }
class Num implements NumOrBool { … }
ad-hoc union type
declared union type
23
JavaScript
ML, Haskell a
function negate(x) {
if (typeof x == “number”) {
…
} else {
…
} }
24
datatype NumOrBool = | Num (n:number) | Bool (b:boolean)
let negate x = match x with | Num n Num (0 – n) | Bool b Bool (!b)
declared union type
ad-hoc union type
JavaScriptfunction negate(x) {
if (typeof x == “number”) {
…
} else {
…
} }
Lightweight Reflection
25
ad-hoc union type
Javaclass Person {
String first; String last;
Person (String s1, String s2) { this.first = s1; this.last = s2; }
void greeting(…) { … }}
var john = new Person(“John”, “McCarthy”);
var s = john.first + “…”;
var john = { “first” : “John”, “last” : “McCarthy”, “greeting” : … };
var s = john.first + “…”;
JavaScript
field names are declared
keys are arbitrary computed strings
26
var john = { “first” : “John”, “last” : “McCarthy”, “greeting” : … };
var s = john.first + “…”;
JavaScript
john[“first”]
obj.f means obj[“f”]
Objects are Dictionaries
27
Javaclass Person {
String first; String last;
Person (String s1, String s2) { this.first = s1; this.last = s2; }
void greeting(…) { … }}
var john = new Person(“John”, “McCarthy”);
var s = john.first + “…”;
Javaclass Person {
String first; String last;
Person (String s1, String s2) { this.first = s1; this.last = s2; }
void greeting(…) { … }}
var john = new Person(“John”, “McCarthy”);
var s = john.first + “…”;
var john = {};
john.first = “John”;john.last = “McCarthy”;john.greeting = …;
JavaScript
incrementally extend object
“sealed” after initialization
28
var john = {};
john.first = “John”;john.last = “McCarthy”;john.greeting = …;
// much later in the codejohn.anotherKey = …;
addMoreKeys(john);
if (…) { addEvenMoreKeys(john);}
JavaScript
Objects are Extensible
29
Javaclass Person {
String first; String last;
Person (String s1, String s2) { this.first = s1; this.last = s2; }
void greeting(…) { … }}
var john = new Person(“John”, “McCarthy”);
var s = john.first + “…”;
Java
30
class Person {
String first; String last;
Person (String s1, String s2) { this.first = s1; this.last = s2; }
void greeting(…) { … }}
var john = new Person(“John”, “McCarthy”);
var s = john.first + “…”;
class TuringWinner extends Person {
int year; TuringWinner (…) { … }}
var twJohn = new TuringWinner(…);
twJohn.greeting();
inherit fromsuperclass
Java
31
class Person {
String first; String name;
Person (String s1, String s2) { this.first = s1; this.last = s2; }
void greeting(…) { … }}
var john = new Person(“John”, “McCarthy”);
...... s = john.first + “…”;
class TuringWinner extends Person {
int year; TuringWinner (…) { … }}
var twJohn = new TuringWinner(…);
twJohn.greeting();
inheritance chaindeclared
JavaScriptvar john = { “first” : “John”, “last” : “McCarthy”, “greeting” : … };
var twJohn = { “year” : 1971, “__proto__” : john};
twJohn.greeting();
inheritance chaincomputed
Objects Inherit from Prototypes
var john = { “first” : “John”, “last” : “McCarthy”, “greeting” : … };
var twJohn = { “year” : 1971, “__proto__” : john};
twJohn.greeting();
JavaScript
32
Objects Inherit from Prototypes
Objects are Extensible
Objects are Dictionaries
Lightweight Reflection
Core Technical Challenges
33
Refinement Types
Dependent JavaScript
(DJS)[POPL ’12, OOPSLA ’12]
C#
✓✓✓✓
✓*✓
✗✗
✗✗✗✗
Java
Haskell
Scala
lightweight reflection
objects are dictionaries
objects are extensible
prototype inheritance
34
35
OutlineMotivation and Thesis
Challenges of Dynamic LanguagesTour of Dependent JavaScript
Technical ContributionsEvaluation
Looking Ahead
36
typeof true // “boolean”
typeof 0.1 // “number”typeof 0 // “number”
typeof {} // “object”typeof [] // “object”typeof null // “object”
37
typeof returns run-time “tags”
Tags are very coarse-grained types
“undefined”
“boolean”
“string”
“number”
“object”
“function”
38
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
39
{n|tag(n) = “number” }Num
{v|tag(v) = “number” ∨ tag(v) = “boolean” }NumOrBool
{i|tag(i) = “number” integer(i) }Int
{x|true }Any
Syntactic Sugarfor Common Types
Refinement Types
3 :: {n|n = 3}
{n|n > 0} 3 ::{n|tag(n) = “number” integer(n)} 3 ::{n|tag(n) = “number”} 3 ::
Refinement Types
40
{n|n = 3}
{n|n > 0} {n|tag(n) = “number” integer(n)} {n|tag(n) = “number” }
<:...
Subtyping is Implication
Refinement Types
41
{n|n = 3}
{n|n > 0} {n|tag(n) = “number” integer(n)} {n|tag(n) = “number” }
Subtyping is Implication
Refinement Types
42
Refinement Types
SMT Solveror Theorem
Prover
RefinementType
Checker
p q ?
Yes / No
43
Subtyping is Implication
44
Mutable ObjectsDictionary ObjectsTag-Tests Prototypes
45
Mutable ObjectsDictionary ObjectsTag-Tests Prototypes
function negate(x) {
if (typeof x == “number”) {
return 0 - x;
} else {
return !x;
} }
Type Annotation in Comments
//: negate :: (x:NumOrBool) NumOrBool
{v|tag(v) = “number” ∨ tag(v) = “boolean” }
NumOrBool
Syntactic Sugarfor Common Types
46
Mutable ObjectsDictionary ObjectsTag-Tests Prototypes
function negate(x) {
if (typeof x == “number”) {
return 0 - x;
} else {
return !x;
} }
//: negate :: (x:NumOrBool) NumOrBool
{x|tag(x) = “number” }
“expected args”
{x|tag(x) = “number” }
{x|tag(x) = “number” }
{x|tag(x) = “number” }✓
47
Mutable ObjectsDictionary ObjectsTag-Tests Prototypes
function negate(x) {
if (typeof x == “number”) {
return 0 - x;
} else {
return !x;
} }
//: negate :: (x:NumOrBool) NumOrBool
“expected args”
{x|tag(x) = “boolean” }
✓{x|not(tag(x) = “number”)
{x| ∧ (tag(x) = “number” ∨
{x| tag(x) = “boolean”) }
48
Mutable ObjectsDictionary ObjectsTag-Tests Prototypes
function negate(x) {
if (typeof x == “number”) {
return 0 - x;
} else {
return !x;
} }
//: negate :: (x:NumOrBool) NumOrBool ✓
Refinement Types Enable Path Sensitivity
49
Mutable ObjectsDictionary ObjectsTag-Tests Prototypes
function negate(x) {
if (typeof x == “number”) {
return 0 - x;
} else {
return !x;
} }
Output typedepends on input
value
//: negate :: (x:NumOrBool) { y|tag(y) = tag(x) }
✓
50
if (“quack” in duck)
return “Duck says ” + duck.quack();
else
return “This duck can’t quack!”;
What is “Duck Typing”?
Mutable ObjectsTag-Tests Dictionary Objects Prototypes
51
if (“quack” in duck)
return “Duck says ” + duck.quack();
else
return “This duck can’t quack!”;
What is “Duck Typing”?
(+) :: (Num,Num) Num
(+) :: (Str,Str) Str
Mutable ObjectsTag-Tests Dictionary Objects Prototypes
52
if (“quack” in duck)
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
Mutable ObjectsTag-Tests Dictionary Objects Prototypes
53
if (“quack” in duck)
return “Duck says ” + duck.quack();
else
return “This duck can’t quack!”;
{d|tag(d) = “object”∧
{v|has(d,“quack”)
{v| sel(d,“quack”) :: ()Str }
Operators from McCarthy theory of arrays
Mutable ObjectsTag-Tests Dictionary Objects Prototypes
54
if (“quack” in duck)
return “Duck says ” + duck.quack();
else
return “This duck can’t quack!”;
{d|tag(d) = “object”∧
{v|has(d,“quack”)
{v| sel(d,“quack”) :: () Str }
Call produces Str, so concat well-typed
✓
Mutable ObjectsTag-Tests Dictionary Objects Prototypes
55
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
Dictionary ObjectsTag-Tests Mutable Objects Prototypes
56
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
Dictionary ObjectsTag-Tests Mutable Objects Prototypes
child
parent
...
grandpa
null
Upon construction, each object links to a
prototype object
57
Dictionary ObjectsTag-Tests PrototypesMutable Objects
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
58
Dictionary ObjectsTag-Tests PrototypesMutable Objects
...
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)
59
Dictionary ObjectsTag-Tests PrototypesMutable Objects
...
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)
60
Dictionary ObjectsTag-Tests PrototypesMutable Objects
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)
child
parent
grandpa ???
null
H(Rest of Heap)
var k = “first”; child[k];Semantics of Key Lookup
61
Dictionary ObjectsTag-Tests PrototypesMutable Objects
...
{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
62
Dictionary ObjectsTag-Tests PrototypesMutable Objects
...
{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];
63
Dictionary ObjectsTag-Tests PrototypesMutable Objects
...
{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”}
64
Dictionary ObjectsTag-Tests PrototypesMutable Objects
Key Idea:
Reduce prototypesemantics to decidable
theory of arrays
Prototype Chain Unrolling
65
Dictionary ObjectsTag-Tests PrototypesMutable Objects
“The Good Parts”
reflection
arrays
eval()*
lambdas
JavaScript
66
objects
✓✓
✓
EXTRA SLIDES
67
OutlineMotivation and Thesis
Challenges of Dynamic LanguagesTour of Dependent JavaScript
Technical ContributionsEvaluation
Looking Ahead
68
lambdasdictionariestag tests
mutation
prototypes
Translation à la Guha et al. (ECOOP 2010)
Dependent JavaScript (DJS)
Our Strategy:
Build an expressive type system forcore language
69
lambdasdictionariestag tests
mutation
prototypes
(POPL 2012)
(OOPSLA 2012)
System D
System !D
System DJS (OOPSLA 2012)
Dependent JavaScript (DJS)Dependent JavaScript (DJS)
70
Prior Refinement Types
TypesT ::= {x:B|p }
| x:T1 T2
| [f1:T1; … ]
| Map[A,B]
| Ref(T)
base type (e.g. Int, Bool, Str)
dependent function type
object type (a.k.a. record, struct)
dictionary type (a.k.a. map, hash table)
reference type (a.k.a. pointer)
Predicates refine only base types
71
PriorRefinement
Types
C#System DJS
✓✓✓✓
✓*✓
✗✗
✗✗✗✗
Java
Haskell
Scala
lightweight reflection
objects are dictionaries
objects are extensible
prototype inheritance
72
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
*✓✗✗
objects are dictionaries
objects are extensible
prototype inheritance
✓✓✓
73
Key Challenge:
Function Subtypingas Logical Implication
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
74
42 + negate (17)
{f|f :: }(x:Num) Num “expected function”
{f|f :: } (x:NumOrBool) { y|tag(y) = tag(x) }
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
{f|f :: }
75
(x:NumOrBool) { y|tag(y) = tag(x) }
{f|f :: }(x:Num) Num
How to Prove
?How to Encode Type System Inside Logic?
Prior Refinement SystemsDisallow Function Types Inside Logic!
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
76
T ::= {x|p }
| T1 T2
Types Refinement Logic
Satisfiability Modulo Theories (SMT)
SAT + Decidable Theories
p q✓✗
SMT Solver(e.g. Z3)
[Prior State-of-the-Art]
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
77
T ::= {x|p }
| T1 T2
p ::= p ∧ q | p ∨ q | p
| x = y | x > y | …
| tag(x) = “number” | …
| sel(x,k) = 17 | …
Types Refinement Logic
• Boolean Connectives• Equality• Linear Arithmetic• Uninterpreted Functions• McCarthy Arrays
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
78
T ::= {x|p }
| T1 T2
p ::= p ∧ q | p ∨ q | p
| x = y | x > y | …
| tag(x) = “number” | …
| sel(x,k) = 17 | …
Types Refinement Logic
{v|tag(v) = “number” ∨ tag(v) = “boolean” }NumOrBool
Enables Union Types and Lightweight Reflection
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
79
T ::= {x|p }
| T1 T2
p ::= p ∧ q | p ∨ q | p
| x = y | x > y | …
| tag(x) = “number” | …
| sel(x,k) = 17 | …
Types Refinement Logic
Enables Dictionary Types with Dynamic Keys …{d|tag(sel(d,k)) = “boolean” ∧
{d|tag(sel(d,“f”)) = “function” ∧
{d|sel(d,“f”) ??? }
But DisallowsFunctions inDictionaries!
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
80
p ::= p ∧ q | p ∨ q | p
| x = y | x > y | …
| tag(x) = “number” | …
| sel(x,k) = 17 | …
| sel(x,k) :: T1 T2 | …
Types Refinement Logic
Goal: Refer to Type System Inside LogicGoal: Without Giving up Decidability
T ::= {x|p }
| T1 T2
Solution: Nested Refinements!
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
81
T ::= {x|p } p ::= p ∧ q | p ∨ q | p
| x = y | x > y | …
| tag(x) = “number” | …
| sel(x,k) = 17 | …
| sel(x,k) :: T1 T2 | …
Nested Refinements [POPL ’12]1. Decidable Encoding2. Precise Subtyping3. Type Soundness Proof
Types Refinement LogicJavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
82
{f|f :: } (x:NumOrBool) { y|tag(y) = tag(x) }
{f|f :: }(x:Num) Num
Decidable Encoding
Uninterpreted Predicate in Logic
Distinct UninterpretedConstants in Logic…
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
{f|f :: }
83
(x:NumOrBool) { y|tag(y) = tag(x) }
{f|f :: }(x:Num) Num
Decidable Encoding
Uninterpreted Predicate in Logic
w/ Types Nested Inside
Distinct UninterpretedConstants in Logic…
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
84
Decidable Encoding
{f|f :: } (x:NumOrBool) { y|tag(y) = tag(x) }
{f|f :: }(x:Num) Num
✗SMTSolver
Trivially Decidable, but Imprecise
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
85
p f :: (x:S1) S2
{f|f :: }(x:T1) T2
{f|p }
Precise Subtyping
Step 1:
Step 2:
Find such that(x:S1) S2
SMTSolver✓
T1 S1 ✓✓S2 T2x:T1
Compare and(x:S1) S2 (x:T1) T2
✓
“contra-variance”
“co-variance”
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
86
{f|f :: }(x:Num) Num{f|f = negate }
Precise Subtyping
Step 1:
Step 2:
f = negate
f :: (x:NumOrBool) { y|tag(y) = tag(x) }SMTSolver✓
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
87
{f|f :: }(x:Num) Num{f|f = negate }
Precise Subtyping
Step 1:
Step 2:
f = negate
f :: (x:NumOrBool) { y|tag(y) = tag(x) }
Num
NumOrBool ✓✓{y|tag(y) = tag(x) } Nu
mx:Num
✓SMT
Solver✓
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
88
Precise Subtyping
Step 1:
Step 2:
Decidable Reasoningvia SMT
Precise Function Subtypingvia Syntactic Techniques
+
{f|f :: }{f|f = negate } ✓(x:Num) Num
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
89
Type Soundness Proof
Logic 0
Conventional ProofsRequire Logic to be an Oracle …
… but Nesting Introduces CircularityThat Prevents Typical Inductive Proofs
Type System 0
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
90
Type System ∞
Type Soundness Proof
Logic 0
Logic 1
Logic 2
Type System 1
Type System 2
Type System 0
… …
Proof Technique:Stratify into Infinite Hierarchy
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
91
Key to Encode Dictionary Objects(Even in Statically Typed Languages!)
T ::= {x|p } p ::= p ∧ q | p ∨ q | p
| x = y | x > y | …
| tag(x) = “number” | …
| sel(x,k) = 17 | …
| sel(x,k) :: T1 T2 | …
Nested Refinements [POPL ’12]
Types Refinement LogicJavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
92
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
var x = {};
x.f = 7;
x.f + 2;
let x = new {} in
(*x).f = 7;
(*x).f + 2;
Allocates ptr x :: Ref(Dict)
Heap update doesn’t affect type
So key lookup doesn’t type check!
Key Issue:
“Invariant”Reference Types
93
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
let x = new {} in
(*x).f = 7;
(*x).f + 2;
Lx |-> d0:{d|d =empty}
Lx |-> d1:{d|d = upd(d0,“f”,7)}
Allocates heap location Lx and x :: Ref(Lx)
Type of pointer doesn’t change …
But types of heap locations can!
✓
94
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
x:T1 / h1 T2 / h2
outputtype
inputtype
95
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
x:T1 / h1 T2 / h2
outputtype
inputtype
inputheap
outputheap
96
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
x:T1 / h1 T2 / h2
Our Formulation Extends:
Alias Types (Smith et al., ESOP 2000)syntactic types for higher-order programs
Low-Level Liquid Types (Rondon et al., POPL 2010)refinement types for first-order programs
97
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
let swap (x, y) =
let tmp = *x in
*x = *y;
*y = tmp in …
//: swap :: (x:Ref(Lx), y:Ref(Ly))
//: / H + (Lx |-> a:Any) + (Ly |-> b:Any)
//: Void//: / H + (Lx |-> a’:Any{a’= b})
//: + (Ly |-> b’:Any{b’= a})
98
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
//: getKey :: (x:Ref(Lx), k:Str)
//: / H + (Lx |-> d:Dict > Lx’)
//: { y | if has(d,k)//: then y = sel(d, k)//: else y = HeapHas(H, Lx’, k)}
//: / H + (Lx |-> d’:Dict{d’= d} > Lx’)
Prototype Inheritance
x[k] getKey(*x, k)
99
JavaScript EncodingsFlow-Sensitive RefinementsNested Refinements
Primitive Operators(Avoid Implicit Coercion)
JavaScript Arrays(Several Unusual Issues)
Prototype Inheritance
100
OutlineMotivation and Thesis
Challenges of Dynamic LanguagesTour of Dependent JavaScript
Technical ContributionsEvaluation
Looking Ahead
101
313a
421(+35%)
LOCbefore/after
14 Excerpts from: JavaScript, Good Parts SunSpider Benchmark Suite Google Closure Library
Benchmarks
Chosen to Stretch the Limits of DJS
102
313a
421(+35%)
LOCbefore/after
14 Excerpts from: JavaScript, Good Parts SunSpider Benchmark Suite Google Closure Library
Benchmarks
9 Browser Extensions from: [Guha et al. Oakland ’11]
321a
383(+19%)
1,003a
1,027(+2%)
2 Examples from: Google Gadgets
1,637
1,831(+12%)
TOTALS
103
LOCbefore/after
14 Excerpts from: JavaScript, Good Parts SunSpider Benchmark Suite Google Closure Library
313a
421(+35%)
Benchmarks
9 Browser Extensions from: [Guha et al. Oakland ’11]
321a
383(+19%)
2 Examples from: Google Gadgets
1,003a
1,027(+2%)
1,637
1,831(+12%)
11 sec
Running Time
3 sec
19 sec
33 sec
TOTALS
1,831(+12%)
33 sec
>>> Skip Optimizations >>>
104
Reducing Annotation Burden• Bidirectional (local) type inference– Several aspects unique to this setting
• Optional var declaration invariants– Auto add to types of subsequent closures
var i /*: Int */ = 0;
…
//: f :: T1 / H1 + (Li |-> i1:Int)
//: T2 / H2 + (Li |-> i2:Int)
var f(…) { … i … }
function refers to mutable variable i, so heaps must describe
the location
105
Reducing Annotation Burden• Bidirectional (local) type inference– Several aspects unique to this setting
• Optional var declaration invariants– Auto add to types of subsequent closures
var i /*: Int */ = 0;
…
//: f :: T1 / H1
//: T2 / H2
var f(…) { … i … }
copy the annotation, if any
106
Reducing Annotation Burden• Bidirectional (local) type inference– Several aspects unique to this setting
• Optional var declaration invariants– Auto add to types of subsequent closures
var i /*: Int */ = 0;
…
//: f :: T1 / H1 + (Li |-> i1:Int)
//: T2 / H2 + (Li |-> i2:Int)
var f(…) { … i … }
copy the annotation, if any
107
Reducing Annotation Burden• Bidirectional (local) type inference– Several aspects unique to this setting
• Optional var declaration invariants– Auto add to types of subsequent closures
no annotation means “do not modify”var i = 0;
…
//: f :: T1 / H1 + (Li |-> i1:{x | x = 0})
//: T2 / H2 + (Li |-> i2:{x | x = 0})
var f(…) { … i … }
108
Improving Performance
17 :: {x:Int|x = 17 }
if b then “hi” else 17 :: {x|b = true x = “hi”
if b then 17 else “hi” :: {x∧b = false x = 17 }
track syntactic types for specialized cases …
… fall back on refinement types for general case
• Avoid making SMT queries when possible
109
Improving Performance• Can reduce precision for if-expressions
var n = 0;
if (b) {
n = n + 1;
else {
n = n + 2;
} Ln |-> m:{ x | b = true x = 1Ln |-> m: {x ∧ b = false x = 2 }
“exact joins” by default …
… annotation on var triggers “inexact joins”
110
Improving Performance• Can reduce precision for if-expressions
var n /*: Int */ = 0;
if (b) {
n = n + 1;
else {
n = n + 2;
}
Ln |-> m:Int
“exact joins” by default …
… annotation on var triggers “inexact joins”
111
Plenty of Room for Improvement
1,637
TOTALS
33 sec
1,831(+12%)
Relatively Modest OptimizationsHave Made Significant Impact
112
OutlineMotivation and Thesis
Challenges of Dynamic LanguagesTour of Dependent JavaScript
Technical ContributionsEvaluation
Looking Ahead
113
Immediate Directions• Type Soundness– proven for System D, but not for !D or DJS
• Expressiveness– e.g. mutual recursion, recursion through heap
• Usability– annotation burden, performance, error messages
• Integration with Run-Time Contracts– invariants before and after eval()
114
Expressiveness
Usability
Verification
Dependent JavaScript (DJS)
TypeScriptLightweight (unsound) static checking tools becoming popular
Translate for additional checking
and IDE support
“Gradual Gradual Typing”
115
Way Behind Tools for Java, etc.
• Static Types in DJS Enable Refactoring,• Documentation Tools, Code Completion
• Web-Based Interfaces to Collect Statistics• about Programming Patterns, Errors
Web Development Tools
116
candidateannotations
User or
Test Cases or
Heuristics
Iterative InferenceGlobal inference (e.g. via Liquid Types) would be nice,but no basic type structure to start with
unannotatedprogram
✓
✗
Verification Condition
Generation
Coarsen Types
117
Browser Extension Security
Security =Refinement
Type Checkingà la Swamy et al. (ESOP 2010)
and Guha et al. (Oakland 2011)
Dependent JavaScript (DJS)
Third-PartyCode
Policy
Browser APIs
Secure Reference Monitorvia Refinements
118
Thanks!
Nested Refinements +Dependent JavaScript
Ranjit JhalaPat Rondon
Dave HermanPanos Vekris
OtherProjects
Sorin LernerJan Voung
Jeff MeisterNikhil Swamy
Juan Chen
119
EXTRA SLIDES
“The Good Parts”
reflection
arrays
eval()*
lambdas
JavaScript
120
objects
121
var nums = [0,1,2];while (…) { nums[nums.length] = 17;}
A finite tuple…
… extended to unbounded collection
122
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”
123
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 }
124
{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”];
125
var tup = [17, “cacti”];
tup[tup.length] = true;
{a |a :: Arr(Any)
{a | packed(a) len(a) = 3
{a | …}
“The Good Parts”
reflection
arrays
eval()*
lambdas
JavaScript
126
objects
127
What About eval?
Arbitrary code loading
…
eval(“…”);
…
Old Types
128
What About eval?
…
eval(“…”);
//: #assume
…
Old Types
New TypesNew Types
Future Work: Integrate DJS with“Contract Checking” at Run-time
aka “Gradual Typing”
129Expressiveness
Usability Idioms ofDynamic Languages
VerificationHoare Logics
Model CheckingAbstract Interpretation
Theorem Proving…
130
Function Subtyping…{d|sel(d,“f”) :: }
(x:Any) { y|y = x }{d|sel(d,“f”) :: }(x:Num) Num<:
Higher-Order Refinements
131
Function Subtyping…
{d|sel(d,“f”) :: }(x:Num) Num
{d|sel(d,“f”) :: } (x:Any) { y|y = x }
Higher-Order Refinements
132
Function Subtyping…
{d|sel(d,“”)f :: }(x:Num) Num
{d|sel(d,“ ”f :: } (x:Any) { y|y = x }
… 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
Higher-Order Refinements
133
Heap Updates…
… With Quantifiers
var x = {};
x.f = 7;h1
h2
h0
∧ …
∧ 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)
Encode Heap w/ McCarthy Operators
Higher-Order Refinements
134
Subtyping with Nesting
e.g. tag(x) = tag(y)
e.g. tag(sel(d,k)) = “number”
1) Convert q to CNF clauses (q11 … ) … (qn1 … )
2) For each clause, discharge some literal qij as follows:
base predicate: p qij
anything except HasType(x,U)
135
Subtyping with Nesting
Implication
SMT Solver
Subtyping Arrow Rule
base predicate: p qij p x :: U
Implication
SMT Solver
Subtyping
p x :: U’
U’ <: U
p qij
1) Convert q to CNF clauses (q11 … ) … (qn1 … )
2) For each clause, discharge some literal qij as follows:
136
Type Soundness with Nesting
137
0 :: {|x.x+1:: IntInt }_|
x.x+1 :: {|:: IntInt }_|
f:{|:: IntInt }0 :: {|f:: IntInt }_|
SubstitutionLemma
_| v :: TxandIf x:Tx, Γ e[:: T_|
Γ[v/x] e[v/x] :: T[v/x]_|then
independent of 0, and just echoes the binding from the environment
138
0 :: {|x.x+1:: IntInt }_|
SMT =0 x.x+1:: IntInt✗{|=0 } < {|x.x+1:: IntInt }0 :: {|=0 }
SubstitutionLemma
_| v :: TxandIf x:Tx, Γ e[:: T_|
Γ[v/x] e[v/x] :: T[v/x]_|then
1st attempt
139
0 :: {|x.x+1:: IntInt }_|
{|=0 } < {|x.x+1:: IntInt }0 :: {|=0 }
SubstitutionLemma
_| v :: TxandIf x:Tx, Γ e[:: T_|
Γ[v/x] e[v/x] :: T[v/x]_|then✗SMT =0 :: U’
Arrow U’ <: IntInt
+2nd attempt✗
140
x.x+1:: IntInt|=I
x.x+1:: {|:: IntInt }|_
SMT Γ p q
Γ {|=p } < {|=q }_|
[S-Valid-Uninterpreted]
|=I Γ p q
Γ {|=p } < {|=q }_|
[S-Valid-Interpreted]
iff
• Rule not closed under substitution
• Interpret formulas by “hooking back” into type system
• Stratification to create ordering for induction
n
n-1
n
n
141
Type Soundness with NestingStratifiedSubstitutionLemma
_| v :: TxandIf x:Tx, Γ e[:: T_|
Γ[v/x] e[v/x] :: T[v/x]_|then n+1
n
n
StratifiedPreservation
e vandIf e[:: T_| 0
_|then v[:: Tm for some m
“Level 0” for type checking source programs,using only [S-Valid-Uninterpreted]
artifact of the metatheory
142
</Ph.D.>