nestedrefinement...
TRANSCRIPT
Nested Refinement Types for JavaScript
Ravi Chugh
Disserta/on Defense − September 3, 2013
Types for JavaScript
“Dynamic” or Untyped Features Enable Rapid Prototyping
2
Types for JavaScript
“Dynamic” or Untyped Features Enable Rapid Prototyping
http://stackoverflow.com/tags02-‐03-‐13
Count Tag
411,345 C#
361,080 Java
337,896 PHP
321,757 JavaScript
175,765 C++
160,768 Python
83,486 C
64,345 Ruby
9,827 Haskell
1,347 OCaml
3
Types for JavaScript
“Dynamic” or Untyped Features Enable Rapid Prototyping
1. Development Tools 2. Reliability & Security
3. Performance
4
1. Development Tools
5
op>onal “type systems” unsound but provide some IDE support
2. Reliability & Security
6
lightweight sta>c checkers
run-‐>me invariant checkers
secure language subsets
3. Performance
7
fast language subset to facilitate op/miza/ons
Just-‐In-‐Time (JIT) engines perform sta/c/dynamic analysis to predict types
8
Types for JavaScript
Why So Hard?
9
implicit global object
scope manipula/on
JavaScript
var li]ing
‘,,,’ == new Array(4)
“The Good Parts”
objects
reflec/on
arrays
eval()*
lambdas
JavaScript
implicit global object
scope manipula/on
var li]ing
‘,,,’ == new Array(4)
10
“The Good Parts”
objects
reflec/on
arrays
eval()*
lambdas
JavaScript
11
12 Expressiveness
Usability Idioms of Dynamic Languages
Types
Verifica>on Hoare Logics
Model Checking Abstract Interpreta/on
Theorem Proving …
+ Logic
Thesis Statement:
“Refinement types can reason about
dynamic languages to verify the absence of run-‐/me errors …
13 Expressiveness
Usability Idioms of Dynamic Languages
Types
Verifica>on Hoare Logics
Model Checking Abstract Interpreta/on
Theorem Proving …
+ Logic
Thesis Statement:
… without resor/ng to undecidable
logics”
14 Expressiveness
Usability Idioms of Dynamic Languages
Types
Verifica>on Hoare Logics
Model Checking Abstract Interpreta/on
Theorem Proving …
+ Logic Dependent
JavaScript (DJS) [POPL 2012, OOPSLA 2012]
Outline
15
Mo/va/on and Thesis
Challenges of Dynamic Languages
Tour of Dependent JavaScript
Technical Contribu/ons
Evalua/on
Looking Ahead
“The Good Parts”
objects
reflec/on
arrays
eval()*
lambdas
JavaScript
16
Core Technical Challenges
reflec/on
17
objects
Core Technical Challenges
Java JavaScript
vs.
18
JavaScript function negate(x) { if (typeof x == “number”) { return 0 - x; } else { return !x;} }
run-‐/me type-‐test
19
JavaScript function negate(x) { if (typeof x == “number”) { return 0 - x; } else { return !x;} }
but boolean on else-‐branch
x should be numeric on then-‐branch…
20
JavaScript function negate(x) { if (typeof x == “number”) { return 0 - x; } else { return !x;} }
Java
✗
21
Java interface 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
JavaScript function negate(x) { if (typeof x == “number”) { … } else { …} }
Java interface 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
JavaScript function negate(x) { if (typeof x == “number”) { … } else { …} }
Lightweight Reflec>on
25
ad-‐hoc union type
Java 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 + “…”;
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 Dic>onaries
27
Java 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 + “…”;
Java 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 + “…”;
var john = {};
john.first = “John”; john.last = “McCarthy”; john.greeting = …;
JavaScript
incrementally extend object
“sealed” a]er ini/aliza/on
28
var john = {};
john.first = “John”; john.last = “McCarthy”; john.greeting = …;
// much later in the code john.anotherKey = …;
addMoreKeys(john);
if (…) { addEvenMoreKeys(john); }
JavaScript
Objects are Extensible29
Java 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 + “…”;
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 from superclass
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 chain declared
JavaScript
var john = { “first” : “John”, “last” : “McCarthy”, “greeting” : … };
var twJohn = { “year” : 1971, “__proto__” : john };
twJohn.greeting();
inheritance chain computed
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 Dic>onaries
Lightweight Reflec>on
Core Technical Challenges
33
Refinement Types
Dependent JavaScript (DJS) [POPL ’12, OOPSLA ’12]
C#
✓ ✓ ✓ ✓
✓ * ✓
✗
✗
✗
✗
✗
✗
Java
Haskell
Scala
lightweight reflec/on
objects are dic/onaries
objects are extensible
prototype inheritance
34
Outline
35
Mo/va/on and Thesis
Challenges of Dynamic Languages
Tour of Dependent JavaScript
Technical Contribu/ons
Evalua/on
Looking Ahead
36
typeof true // “boolean”
typeof 0.1 // “number” typeof 0 // “number”
typeof {} // “object” typeof [] // “object” typeof null // “object”
37
typeof returns run-‐/me “tags”
Tags are very coarse-‐grained types
“undefined”
“boolean”
“string”
“number”
“object”
“function”
Refinement Types
38
{ 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 ≡
Syntac/c Sugar for 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 Implica/on
Refinement Types
41
{ n | n = 3 } { n | n > 0 } { n | tag(n) = “number” ∧ integer(n) } { n | tag(n) = “number” }
⇒⇒⇒⇒
Subtyping is Implica/on
Refinement Types
42
Refinement Types
SMT Solver or Theorem
Prover
Refinement Type
Checker
p ⇒ q ?
Yes / No
43
Subtyping is Implica/on
44
Mutable Objects Dic/onary Objects Tag-‐Tests Prototypes
45
Mutable Objects Dic/onary Objects Tag-‐Tests Prototypes
function negate(x) { if (typeof x == “number”) { return 0 - x; } else { return !x;} }
Type Annota>on in Comments
//: negate :: (x:NumOrBool) → NumOrBool
{ v | tag(v) = “number” ∨ tag(v) = “boolean” }
NumOrBool ≡
Syntac>c Sugar for Common Types
46
Mutable Objects Dic/onary Objects Tag-‐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 Objects Dic/onary Objects Tag-‐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 Objects Dic/onary Objects Tag-‐Tests Prototypes
function negate(x) { if (typeof x == “number”) { return 0 - x; } else { return !x;} }
//: negate :: (x:NumOrBool) → NumOrBool✓
Refinement Types Enable Path Sensi/vity
49
Mutable Objects Dic/onary Objects Tag-‐Tests Prototypes
function negate(x) { if (typeof x == “number”) { return 0 - x; } else { return !x;} }
Output type depends 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 Objects Tag-‐Tests Dic/onary 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 Objects Tag-‐Tests Dic/onary 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 test the presence of a method
but not its type
Mutable Objects Tag-‐Tests Dic/onary 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 Objects Tag-‐Tests Dic/onary 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 Objects Tag-‐Tests Dic/onary Objects Prototypes
55
var x = {};x.f = 7;x.f + 2;
x0:Empty
x1:{d|d = upd(x0,“f”,7)}
DJS is Flow Sensi/ve
McCarthy operator DJS verifies that x.f is definitely a number
Dic/onary Objects Tag-‐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 Sensi/ve
Strong updates to singleton objects
Weak updates to collec/ons of objects
Dic/onary Objects Tag-‐Tests Mutable Objects Prototypes
child
parent
...
grandpa
null
Upon construc/on, each object links to a prototype object
57
Dic/onary Objects Tag-‐Tests Prototypes Mutable 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];Seman/cs of Key Lookup
58
Dic/onary Objects Tag-‐Tests Prototypes Mutable Objects
...
child
parent
grandpa
null
H
(Rest of Heap)
var k = “first”; child[k];Seman/cs 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
Dic/onary Objects Tag-‐Tests Prototypes Mutable Objects
...
child
parent
grandpa
null
H
(Rest of Heap)
var k = “first”; child[k];Seman/cs 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
Dic/onary Objects Tag-‐Tests Prototypes Mutable 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];Seman/cs of Key Lookup
61
Dic/onary Objects Tag-‐Tests Prototypes Mutable 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];Seman/cs of Key Lookup
Abstract predicate to summarize the unknown por>on
of the prototype chain
62
Dic/onary Objects Tag-‐Tests Prototypes Mutable 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
Dic/onary Objects Tag-‐Tests Prototypes Mutable 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
Dic/onary Objects Tag-‐Tests Prototypes Mutable Objects
Key Idea:
Reduce prototype seman/cs to decidable
theory of arrays
Prototype Chain Unrolling
65
Dic/onary Objects Tag-‐Tests Prototypes Mutable Objects
“The Good Parts”
reflec/on
arrays
eval()*
lambdas
JavaScript
66
objects
✓ ✓
✓
EXTRA SLIDES
Outline
67
Mo/va/on and Thesis
Challenges of Dynamic Languages
Tour of Dependent JavaScript
Technical Contribu/ons
Evalua/on
Looking Ahead
lambdas dic/onaries
tag tests
muta/on
prototypes
68
Transla/on à la Guha et al. (ECOOP 2010)
Dependent JavaScript (DJS)
Our Strategy:
Build an expressive type system for core language
69
lambdas dic/onaries
tag tests
muta/on
prototypes
(POPL 2012)
(OOPSLA 2012)
System D
System !D
System DJS (OOPSLA 2012)
Dependent JavaScript (DJS) Dependent JavaScript (DJS)
Prior Refinement Types
70
Types
T ::= { x:B | p } | x:T1 → T2 | [ f1:T1; … ] | Map[A,B] | Ref(T)
base type (e.g. Int, Bool, Str) dependent func/on type
object type (a.k.a. record, struct)
dic/onary type (a.k.a. map, hash table)
reference type (a.k.a. pointer)
Predicates refine only base types
71
Prior Refinement
Types
C# System DJS
✓ ✓ ✓ ✓
✓ * ✓
✗
✗
✗
✗
✗
✗
Java
Haskell
Scala
lightweight reflec/on
objects are dic/onaries
objects are extensible
prototype inheritance
72
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
* ✓ ✗
✗
objects are dic/onaries
objects are extensible
prototype inheritance
✓ ✓ ✓
73
Key Challenge:
Func/on Subtyping as Logical Implica/on
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
74
42 + negate (17)
⊆ { f | f :: }(x:Num) → Num “expected func/on”
{ f | f :: } (x:NumOrBool) → { y|tag(y) = tag(x) }
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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 Systems Disallow Func/on Types Inside Logic!
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
76
T ::= { x | p } | T1 → T2
Types Refinement Logic
Sa/sfiability Modulo Theories (SMT)
SAT + Decidable Theories
p ⇒ q ✓
✗ SMT Solver (e.g. Z3)
[Prior State-‐of-‐the-‐Art]
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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 Connec/ves • Equality • Linear Arithme/c • Uninterpreted Func/ons • McCarthy Arrays
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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 Reflec>on
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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 Dic>onary Types with Dynamic Keys …
{ d | tag(sel(d,k)) = “boolean” ∧
{ d | tag(sel(d,“f”)) = “function” ∧
{ d | sel(d,“f”) ??? }
But Disallows Func/ons in Dic/onaries!
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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 Logic Goal: Without Giving up Decidability
T ::= { x | p } | T1 → T2
Solu/on: Nested Refinements!
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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 Encoding 2. Precise Subtyping 3. Type Soundness Proof
Types Refinement Logic
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
82
{ f | f :: } (x:NumOrBool) → { y|tag(y) = tag(x) }
{ f | f :: }(x:Num) → Num⇒
Decidable Encoding
Uninterpreted Predicate in Logic
Dis/nct Uninterpreted Constants in Logic…
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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
Dis/nct Uninterpreted Constants in Logic…
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
84
Decidable Encoding
{ f | f :: } (x:NumOrBool) → { y|tag(y) = tag(x) }
{ f | f :: }(x:Num) → Num⇒
✗ SMT Solver
Trivially Decidable, but Imprecise
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
p ⇒ f ::(x:S1) → S2
85
⊆ { f | f :: }(x:T1) → T2{ f | p }
Precise Subtyping
Step 1:
Step 2:
Find such that (x:S1) → S2
SMT Solver ✓
T1 S1⊆ ✓ ✓ S2 T2⊆ x:T1 ∧
Compare and (x:S1) → S2 (x:T1) → T2
✓
“contra-‐variance”
“co-‐variance”
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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) }SMT
Solver ✓
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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) } Num⊆ x:Num ∧
✓ SMT Solver ✓
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
88
Precise Subtyping
Step 1:
Step 2:
Decidable Reasoning via SMT
Precise Func/on Subtyping via Syntac/c Techniques
+
⊆ { f | f :: }{ f | f = negate } ✓ (x:Num) → Num
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
89
Type Soundness Proof
Logic 0
Conven/onal Proofs Require Logic to be an Oracle …
… but Nes/ng Introduces Circularity That Prevents Typical Induc/ve Proofs
Type System 0
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
Type System ∞
90
Type Soundness Proof
Logic 0
Logic 1
Logic 2
Type System 1
Type System 2
Type System 0
… …
Proof Technique: Stra>fy into Infinite Hierarchy
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
91
Key to Encode Dic/onary Objects (Even in Sta/cally 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 Logic
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
92
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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 Encodings Flow-‐Sensi/ve Refinements Nested 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 loca>on Lx and x :: Ref(Lx)Type of pointer doesn’t change …
But types of heap loca/ons can!
✓
94
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
x:T1 / h1 → T2 / h2
output type
input type
95
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
x:T1 / h1 → T2 / h2
output type
input type
input heap
output heap
96
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested Refinements
x:T1 / h1 → T2 / h2Our Formula/on Extends:
Alias Types (Smith et al., ESOP 2000) syntac>c types for higher-‐order programs
Low-‐Level Liquid Types (Rondon et al., POPL 2010) refinement types for first-‐order programs
97
JavaScript Encodings Flow-‐Sensi/ve Refinements Nested 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 Encodings Flow-‐Sensi/ve Refinements Nested 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 Encodings Flow-‐Sensi/ve Refinements Nested Refinements
Primi>ve Operators (Avoid Implicit Coercion)
JavaScript Arrays (Several Unusual Issues)
Prototype Inheritance
Outline
100
Mo/va/on and Thesis
Challenges of Dynamic Languages
Tour of Dependent JavaScript
Technical Contribu/ons
Evalua/on
Looking Ahead
101
313
a
421
(+35%)
LOC before/a]er
14 Excerpts from: JavaScript, Good Parts SunSpider Benchmark Suite Google Closure Library
Benchmarks
Chosen to Stretch the Limits of DJS
102
313
a
421
(+35%)
LOC before/a]er
14 Excerpts from: JavaScript, Good Parts SunSpider Benchmark Suite Google Closure Library
Benchmarks
9 Browser Extensions from: [Guha et al. Oakland ’11]
321
a
383
(+19%)
1,003
a
1,027
(+2%) 2 Examples from: Google Gadgets
1,637 1,831
(+12%) TOTALS
103
LOC before/a]er
14 Excerpts from: JavaScript, Good Parts SunSpider Benchmark Suite Google Closure Library
313
a
421
(+35%)
Benchmarks
9 Browser Extensions from: [Guha et al. Oakland ’11]
321
a
383
(+19%)
2 Examples from: Google Gadgets
1,003
a
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 >>>
Reducing Annota>on Burden • Bidirec/onal (local) type inference – Several aspects unique to this sezng
• Op/onal var declara/on invariants – Auto add to types of subsequent closures
104
var i /*: Int */ = 0;…//: f :: T1 / H1 + (Li |-> i1:Int) //: → T2 / H2 + (Li |-> i2:Int)var f(…) { … i … }
func/on refers to mutable variable i, so heaps must describe
the loca/on
Reducing Annota>on Burden • Bidirec/onal (local) type inference – Several aspects unique to this sezng
• Op/onal var declara/on invariants – Auto add to types of subsequent closures
105
var i /*: Int */ = 0;…//: f :: T1 / H1//: → T2 / H2var f(…) { … i … }
copy the annota/on, if any
Reducing Annota>on Burden • Bidirec/onal (local) type inference – Several aspects unique to this sezng
• Op/onal var declara/on invariants – Auto add to types of subsequent closures
106
var i /*: Int */ = 0;…//: f :: T1 / H1 + (Li |-> i1:Int) //: → T2 / H2 + (Li |-> i2:Int)var f(…) { … i … }
copy the annota/on, if any
Reducing Annota>on Burden • Bidirec/onal (local) type inference – Several aspects unique to this sezng
• Op/onal var declara/on invariants – Auto add to types of subsequent closures
107
no annota/on 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 … }
Improving Performance
108
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 syntac>c types for specialized cases …
… fall back on refinement types for general case
• Avoid making SMT queries when possible
Improving Performance • Can reduce precision for if-‐expressions
109
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 …
… annota/on on var triggers “inexact joins”
Improving Performance • Can reduce precision for if-‐expressions
110
var n /*: Int */ = 0;if (b) { n = n + 1;else { n = n + 2;}
Ln |-> m:Int
“exact joins” by default …
… annota/on on var triggers “inexact joins”
111
Plenty of Room for Improvement
1,637 TOTALS 33 sec 1,831
(+12%)
Rela/vely Modest Op/miza/ons Have Made Significant Impact
Outline
112
Mo/va/on and Thesis
Challenges of Dynamic Languages
Tour of Dependent JavaScript
Technical Contribu/ons
Evalua/on
Looking Ahead
Immediate Direc>ons
• Type Soundness – proven for System D, but not for !D or DJS
• Expressiveness – e.g. mutual recursion, recursion through heap
• Usability – annota/on burden, performance, error messages
• Integra/on with Run-‐Time Contracts – invariants before and a]er eval()
113
114
Expressiveness
Usability
Verifica>on
Dependent JavaScript (DJS)
TypeScript Lightweight (unsound) sta/c checking tools becoming popular
Translate for addi/onal checking and IDE support
“Gradual Gradual Typing”
115
Way Behind Tools for Java, etc.
• Sta/c Types in DJS Enable Refactoring, • Documenta/on Tools, Code Comple/on
• Web-‐Based Interfaces to Collect Sta>s>cs • about Programming Pa{erns, Errors
Web Development Tools
candidate annota/ons
User or
Test Cases or
Heuris>cs
116
Itera>ve Inference Global inference (e.g. via Liquid Types) would be nice, but no basic type structure to start with
unannotated program
✓
✗
Verifica>on Condi>on Genera>on
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-‐Party Code
Policy
Browser APIs
Secure Reference Monitor via Refinements
Thanks!
118
Nested Refinements + Dependent JavaScript
Ranjit Jhala Pat Rondon Dave Herman Panos Vekris
Other Projects
Sorin Lerner Jan Voung Jeff Meister Nikhil Swamy Juan Chen
119
EXTRA SLIDES
“The Good Parts”
reflec/on
arrays
eval()*
lambdas
JavaScript
120
objects
121
var nums = [0,1,2];while (…) { nums[nums.length] = 17;}
A finite tuple…
… extended to unbounded collec/on
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 length of 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”
reflec/on
arrays
eval()*
lambdas
JavaScript
126
objects
What About eval?
127
Arbitrary code loading
…
eval(“…”);
…
Old Types
What About eval?
128
…
eval(“…”);//: #assume …
Old Types
New Types New Types
Future Work: Integrate DJS with “Contract Checking” at Run-‐/me
aka “Gradual Typing”
129 Expressiveness
Usability Idioms of Dynamic Languages
Verifica>on Hoare Logics
Model Checking Abstract Interpreta/on
Theorem Proving …
Func/on Subtyping…
130
{ d | sel(d,“f”) :: } (x:Any) → { y|y = x }{ d | sel(d,“f”) :: }(x:Num) → Num<:
Higher-‐Order Refinements
Func/on Subtyping…
131
{ d | sel(d,“f”) :: }(x:Num) → Num{ d | sel(d,“f”) :: } (x:Any) → { y|y = x }
⇒
Higher-‐Order Refinements
⇒
Func/on Subtyping…
132
{ d | sel(d,“”)f :: }(x:Num) → Num{ d | sel(d,“ ”f :: } (x:Any) → { y|y = x }
⇒
… With Quan/fiers ∀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 Quan/fiers
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
Subtyping with Nes>ng
134
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)
Subtyping with Nes>ng
135
Implica/on
SMT Solver
Subtyping Arrow Rule
base predicate: p ⇒ qij p ⇒ x :: U
Implica/on
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:
Type Soundness with Nes>ng
136
137
0 :: { ν | λx.x+1 :: Int → Int }_ |
λx.x+1 :: { ν | ν :: Int → Int }_ |
f:{ ν | ν :: Int → Int } 0 :: { ν | f :: Int → Int }_ |
Subs/tu/on Lemma
_ | v :: Txand If 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 :: Int → Int }_ |
SMT ν = 0 ⇒ λx.x+1 :: Int → Int✗
{ ν | ν = 0 } < { ν | λx.x+1 :: Int → Int }0 :: { ν | ν = 0 }
Subs/tu/on Lemma
_ | v :: Txand If x:Tx, Γ e[:: T_ |
Γ[v/x] e[v/x] :: T[v/x]_ | then
1st a{empt
139
0 :: { ν | λx.x+1 :: Int → Int }_ |
{ ν | ν = 0 } < { ν | λx.x+1 :: Int → Int }0 :: { ν | ν = 0 }
Subs/tu/on Lemma
_ | v :: Txand If x:Tx, Γ e[:: T_ |
Γ[v/x] e[v/x] :: T[v/x]_ | then ✗ SMT ν = 0 ⇒ ν :: U’
Arrow U’ <: Int → Int
+2nd a{empt ✗
140
λx.x+1 :: Int → Int|=I
λx.x+1 :: { ν | ν :: Int → Int }|_
SMT Γ ∧ p ⇒ q
Γ { ν | ν = p } < { ν | ν = q }_ |
[S-‐Valid-‐Uninterpreted]
|=I Γ ∧ p ⇒ q
Γ { ν | ν = p } < { ν | ν = q }_ |
[S-‐Valid-‐Interpreted]
iff
• Rule not closed under subs/tu/on
• Interpret formulas by “hooking back” into type system
• Stra/fica/on to create ordering for induc/on
n
n-1
n
n
Type Soundness with Nes>ng
141
Stra/fied Subs/tu/on Lemma
_ | v :: Txand If x:Tx, Γ e[:: T_ |
Γ[v/x] e[v/x] :: T[v/x]_ | then n+1
nn
Stra/fied Preserva/on
e vand If e[:: T_ | 0_ | then v[:: Tm for some m
“Level 0” for type checking source programs,
using only [S-‐Valid-‐Uninterpreted]
ar/fact of the metatheory
142
</Ph.D.>