900 likes | 1.05k Views
Dependent Types for JavaScript. Ravi Chugh Ranjit Jhala. University of California, San Diego. David Herman. Mozilla Research. Dependent Types for JavaScript. Why. ?. Pervasive Used at Massive Scale. Why Add Types?. var x = {}; x.f ; x.f.f ;.
E N D
DependentTypes for JavaScript Ravi ChughRanjitJhala University of California, San Diego David Herman Mozilla Research
DependentTypes for JavaScript Why ? Pervasive Used at Massive Scale
Why Add Types? var x = {}; x.f; x.f.f; produces undefined rather than error… … but this raises TypeError
Why Add Types? var x = {}; x.f; x.f.f; There Are Run-time Errorsreading from undefined, applying non-function values, … Worse: Browsers Hide Them!
Why Add Types? Prevent Errors Prevent the Unexpected
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?
JavaScript scope manipulation implicit global object var lifting ‘,,,’ == new Array(4)
JavaScript scope manipulation prototypes objects implicit global object lambdas type-tests var lifting ‘,,,’ == new Array(4)
JavaScript scope manipulation “The Good Parts” prototypes objects implicit global object lambdas type-tests var lifting ‘,,,’ == new Array(4)
JavaScript “The Good Parts” Our Approach Key Idea: Use Logic! PriorTypeSystems
JavaScript “The Good Parts” DJS Dependent JavaScript
Outline • Motivation • Our Approach: Logic! • Evaluation
typeof true // “boolean” typeof 0.1 // “number”typeof 0 // “number” typeof {}// “object”typeof []// “object”typeof null// “object”
typeof returns run-time “tags” Tags are very coarse-grained types “undefined” • “boolean” • “string” • “number” • “object” • “function”
Refinement Types NumOrBool {x|p} Num Int Any {v|tag(v)=“number”∨tag(v)=“boolean”} {n|tag(n)=“number”} {i|tag(i)=“number”integer(i)} {x|true} “set of values x s.t. formula p is true”
Refinement Types NumOrBool Num Int Any {v|tag(v)=“number”∨tag(v)=“boolean”} {n|tag(n)=“number”} {i|tag(i)=“number”integer(i)} {x|true} • Syntactic Sugarfor Common Types
Refinement Types 3:: {n|n=3} 3:: {n|n>0} 3:: {n|tag(n)=“number”integer(n)} 3:: {n|tag(n)=“number”}
Refinement Types • Subtyping is Implication <: {n|n=3} <: {n|n>0} <: {n|tag(n)=“number”integer(n)} <: {n|tag(n)=“number”}
Refinement Types • Subtyping is Implication {n|n=3} {n|n>0} {n|tag(n)=“number”integer(n)} {n|tag(n)=“number”}
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } !true // false true negate()
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } 0 - 2 // -2 2 negate()
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } • WAT?! 0 - [] // 0 [] negate()
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } • Use types to prevent implicit coercion (-) :: (Num,Num)Num
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays //: negate :: (x:Any)Any varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } • Function type annotation inside comments
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays //: negate :: (x:Any)Any varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } ✓ x is boolean... so negationis well-typed • DJS is Path Sensitive
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays ✗ //: negate :: (x:Any)Any varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } x is arbitrarynon-boolean value… ✗ so DJS signals error! • DJS is Path Sensitive
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays //: negate :: (x:NumOrBool)Any varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } ✓
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays //: negate :: (x:NumOrBool)Any varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } ✓ • this time,x is a number… so subtractionis well-typed
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays ✓ //: negate :: (x:NumOrBool)Any varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } • but returntype is imprecise
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays ✓ //: negate :: (x:NumOrBool)NumOrBool varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; }
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays /*: negate :: (x:NumOrBool){v|tag(v)=tag(x)} */ ✓ varnegate = function(x) { if (typeofx==“boolean”) return !x; else return0 - x; } • output type depends on input value
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays /*: negate :: (x:NumOrBool){v|tag(v)=tag(x)} */ Programmer choosesdegree of precisionin specification
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!”;
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!”;
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays What is “Duck Typing”? • Can dynamically testthe presence of a methodbut not its type if (duck.quack) return“Duck says ” + duck.quack(); else return“This duck can’t quack!”;
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays {d|tag(d)=“Dict”∧ {v|has(d,“quack”) {v|sel(d,“quack”)::UnitStr} • Operators from McCarthy theory of arrays if (duck.quack) return“Duck says ” + duck.quack(); else return“This duck can’t quack!”;
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays {d|tag(d)=“Dict”∧ {v|has(d,“quack”) {v|sel(d,“quack”)::UnitStr} • Call produces Str, so concat well-typed ✓ if (duck.quack) return“Duck says ” + duck.quack(); else return“This duck can’t quack!”;
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • DJS is FlowSensitive x1:{d|d=upd(x0,“f”,7)} x0:Empty var x = {}; x.f = 7; x.f + 2; • McCarthy operator • DJS verifies that x.f is definitely a number
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • DJS is FlowSensitive x1:{d|d=upd(x0,“f”,7)} x0:Empty var x = {}; x.f = 7; x.f + 2; Strong updates to singleton objects Weak updates to collections of objects
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • Typical“Dynamic” • Features
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • Typical“Dynamic” • Features • JavaScript • tl;dr • Harder, butDJS handles them
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays child • Upon construction, each object links to a prototype object parent grandpa ... null
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • Semantics of Key Lookup var k = “first”; child[k]; child 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 parent grandpa ... null
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • Semantics of Key Lookup var k = “first”; child[k]; child 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|ifhas(child,k) then {v|ifv=sel(child,k) parent grandpa H(Rest of Heap) ... null
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • Semantics of Key Lookup var k = “first”; child[k]; child 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|ifhas(child,k) then {v|ifv=sel(child,k) {v|else ifhas(parent,k) then {v|ifv=sel(parent,k) parent grandpa H(Rest of Heap) ... null
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • Semantics of Key Lookup var k = “first”; child[k]; child 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|ifhas(child,k) then {v|ifv=sel(child,k) {v|else ifhas(parent,k) then {v|ifv=sel(parent,k) parent grandpa ??? H(Rest of Heap) ... null
Tag-Tests • Duck Typing • Mutable Objects • Prototypes • Arrays • Semantics of Key Lookup var k = “first”; child[k]; child {v|ifhas(child,k) then {v|ifv=sel(child,k) {v|else ifhas(parent,k) then {v|ifv=sel(parent,k) {v|else {v|ifv=HeapSel(H,grandpa,k))} parent grandpa ??? • Abstract predicateto summarize theunknown portionof the prototype chain H(Rest of Heap) ... null