JavaScript 2 and the Open Web
Brendan Eich
Note how JS is paired with Flash -- poor, mundane HTML, CSS, DOM
Dilbert - the Big Time
I once described Doug Crockford as "the Yoda of Lambda JavaScript programming"
Yoda on ES4
Not good.
Yoda in Trouble
Anyway Doug's too tall for the part, even if it's a muppet. Let's try a taller wizard...
The Bridge of EcmaDoom
I can cast myself too (renegade veteran from the losing side of the last war against an evil empire...).
Mal, Latin for "Bad"
They'll swing back to the belief that they can make people... better. And I do not hold to that.
- Mal Reynolds,
Serenity
Ok, enough mock flattery -- what's really going on with the ES4 fracas?
Cui Bono
- Who decides what is "better"?
- Browser, plugin, and OS vendors?
- Web developers?
- All of the above, ideally
- Without taking too long
- Or making a mess
- Preventing change could spare us from "worse"
- Or help proprietary "change" to take off for the worst
Inevitable Evolution
- Web browsers are evolving
- They need to, against Silverlight, AIR, and OS stacks
- Browsers (and plugins!) need all three of
- Better Security
- Better APIs for everything (see 1)
- Better programming language support (see 1 and 2)
- No two-legged stools, all three are needed
Evolving Toward "Better"
- Security: hard problem, humans too much in the loop
- Good ideas (Caja) being tried now
- Soundness still elusive
- APIs: served by WHAT-WG (Apple, Mozilla, Opera)
- Languages: only realistic hope in the short run is JS2
Alternative Languages
- Why not new/other programming languages?
- JS not going away in our lifetimes
- JS code is growing, not being rewritten
- No room for multiple language runtimes in mobile browser
- Apple, Mozilla, Opera attest to this in my hearing
- One multi-language runtime? Eventually, not soon enough
- A patent minefield...
- How many hard problems can we (everyone!) solve at once and quickly?
Why JS2
- JS1 is too small => complexity tax on library and app authors
- JS1 has too few primitives => hard idiom optimization problem
- JS1 lacks integrity features => better security has to be bolted on
- JS1 is not taught much => Java U. still cranking out programmers
- JS2 aims to cover the whole user curve, "out of the box"
Normal Distribution
Wait a Minute!
Perhaps you object (strenously):
- "I like my JS small, it is not complex with the right kung-fu!"
- "Most runtime is in the DOM, who cares about JS optimizations"
- "Security through smallness, and anyway: fix security first"
- "People are learning, Yoda is teaching them"
OK, Granted
- Some truth in these claims, just not enough in my view
- The odds ratios multiply to a pretty small success likelihood
- Meanwhile, Silverlight is charging hard with C# (DLR fan-bait aside)
- Flash and AIR likewise rely on ActionScript 3, not JS1, to compete
- And really, JS1 users who are hitting its limits need relief soon
Why Not Evolve?
- We're not proto-humans from 2001: A Space Odyssey
- Making space ships out of bones
- Or modules out of lambdas
- We're highly-evolved tool users, with opposable thumbs -- we can:
- Make better use of an existing tool (JS1)
- Improve the tool itself (JS2)
- Why not do both?
But... But...
You may still object:
- "JS should remain small no matter what!"
- "Classes suck, I hate my pointy-haired Java boss"
- "Aren't you rejecting your own elegant (yet messy) creation?"
- "Who replaced you with a pod-person? We don't even know you any longer!"
What I Seek
- To make JS (not people)... better
- Better for its present and near-future uses on the web
- Especially for building Ajax libraries and applications
- JS programs are increasing in size and complexity
- They face increasing workload -- lots of objects, runtime
- JS users deserve improvements since ES3 (eight years ago)
In Particular
- JS users deserve an optional type system
- Instead of tedious (often skipped) error checking
- So APIs can prove facts about their arguments
- Without requiring all calling code to be typed
- (... at first, or ever!)
- They deserve integrity guarantees such as
const
- They deserve real namespaces, packages, compilation units
- They deserve some overdue "bug fixes" to the ES3 standard
- They deserve generous syntactic sugar on top
The Deeper Answer
Why JS2 as a major evolutionary jump matters:
- So browsers and plugins lose their "native-code innovation" lock
- Downloaded JS2 code can patch around old native code bugs
- Or reimplement a whole buggy subsystem, at near-native (or better!) speed
- No more waiting for IE (or Firefox, or Safari, or Opera)
- Distributed extensibility, web developers win
Believe It
- Yes, this was the Java dream that died in 1995, or 1997
- This time for sure (Tamarin may be the most widely deployed VM ever)
- It's coming true with JS -- if only it can evolve enough in time
Non-Issues
- JS1 performance on synthetic pure-JS (no DOM) benchmarks
- Trace-based JITing accelerates JS1 at least an order of magnitude
- Work from Michael Franz's group at UC Irvine (Mozilla supported)
- No
int
type annotations required
- Preliminary results (from submitted papers) based on Tamarin-to-Java bytecode translation, with a custom tracing JIT targeting the JVM (whew!), next...
Tracing JIT Benchmarks
Tracing JIT Benchmarks (2)
Non-Issues (2)
- Making JS2 look like any other language
- Stuart, yesterday: fans already fake JS1 to resemble Ruby, Python, ...
- But: JS2 learns from other languages
- AS3: nominal types, namespaces, packages
- Python: iterators and generators, catch-alls
- Dylan, Cecil: generic methods
- Only those who don't learn history are doomed to repeat it
- Problems will be shaken out in ES4 "beta"
- No rubber-stamped standards! (cough OOXML)
Integrity in JS2
- Object, Array, etc., globals can be replaced in JS1
- JSON CSRF hazards pointed out by Joe Walker
- ECMA spec says this matters, or not, half the time
- JS2 makes the standard class bindings immutable
- Objects are mutable, extensible
- Even with privileged/private members via closures
- Too easy to forge instance of special type
- JS2 has
class
exactly to solve this problem
- JS2 lets users make fixtures, fixed ("don't delete") properties
- JS1 user-defined properties can be replaced/hijacked
Evolutionary Programming
Version 1 of a webmail client, in almost pure JS1
function send(msg) {
validateMessage(msg);
msg.id = sendToServer(JSON.encode(msg));
database[msg.id] = msg;
}
function fetch() {
handleMessage(-1); // -1 means "get new mail"
}
Evolutionary Programming (2)
function get(n) {
if (uint(n) !== n) // JS1: n>>>0 === n
throw new TypeError;
if (n in database)
return database[n];
return handleMessage(n);
}
var database = [];
Evolutionary Programming (3)
function handleMessage(n) {
let msg = JSON.decode(fetchFromServer(n));
if (typeof msg != "object")
throw new TypeError;
if (msg.result == "no data")
return null;
validateMessage(msg);
return database[msg.id] = msg;
}
Evolutionary Programming (4)
function validateMessage(msg) {
function isAddress(a)
typeof a == "object" && a != null &&
typeof a.at == "object" && msg != null &&
typeof a.at[0] == "string" && typeof a.at[1] == "string" &&
typeof a.name == "string";
if (!(typeof msg == "object" && msg != null &&
typeof msg.id == "number" && uint(msg.id) === msg.id &&
typeof msg.to == "object" && msg != null &&
msg.to instanceof Array && msg.to.every(isAddress) &&
isAddress(msg.from) && typeof msg.subject == "string" &&
typeof msg.body == "string"))
throw new TypeError;
}
Evolution, Second Stage
Version 2: Structural types for validation.
type Addr = { at: [string, string], name: string };
type Msg = {
to: [Addr], from: Addr, subject: string, body: string, id: uint
};
function send(msg: like Msg) {
msg.id = sendToServer(JSON.encode(msg));
database[msg.id] = msg;
}
function fetch()
handleMessage(-1);
Evolution, Second Stage (2)
function get(n: uint) {
if (n in database)
return database[n];
return handleMessage(n);
}
function handleMessage(n) {
let msg = JSON.decode(fetchFromServer(n));
if (msg is like { result: string } && msg.result == "no data")
return null;
if (msg is like Msg)
return database[msg.id] = msg;
throw new TypeError;
}
Evolution, Third Stage
Version 3a: Integrity through structural type fixtures (other functions are unchanged since Version 2)
type MsgNoId = {
to: [Addr], from: Addr, subject: string, body: string
};
function send(msg: like MsgNoId) {
msg.id = sendToServer(JSON.encode(msg));
database[msg.id] = copyMessage(msg);
}
Evolution, Third Stage (2)
function handleMessage(n) {
let msg = JSON.decode(fetchFromServer(n));
if (msg is like { result: string } && msg.result == "no data")
return null;
if (msg is like Msg)
return database[id] = copyMessage(msg);
throw new TypeError;
}
Evolution, Third Stage (3)
function copyMessage(msg) {
function newAddr({ at: [user, host], name })
new Addr([user, host]: [string, string], name);
let { to, from, subject, body, id } = msg;
return new Msg(to.map(newAddr), newAddr(from),
subject, body, id);
}
Alternative Third Stage
Version 3b (other functions are unchanged since Version 3a)
function send(msg: like MsgNoId) {
msg.id = sendToServer(JSON.encode(msg))
database[msg.id] = msg wrap Msg
}
function handleMessage(n) {
let msg = JSON.decode(fetchFromServer(n))
if (msg is like { result: string } && msg.result == "no data")
return null
return database[msg.id] = msg wrap Msg
}
Observations on Evolution
- At no point so far did clients have to use types
- Code shrank by half from stage 1 to 3a, more to 3b
- First stage just used a tiny bit of JS2 (
uint
)
- Second stage added structural types and
is like
tests
- Sanity-checking the "shape" of API arguments
- But trusting the client not to mutate behind the library's back!
- Third stage copied into structural type instances with fixtures -- integrity against confused/malicious client
- Alternative third stage used
wrap
instead
Observations on Evolution (2)
- A "modularization" stage would use
package
or namespace
- If copying or wrapping too costly, drop
like
from formal params, and either:
- Change client to pass structural type instances
- Or use nominal types (
class
, interface
) throughout
- Either way, client changes required at this point
- Use optional strict mode for verification before deployment
- (Many thanks to Lars Thomas Hansen for the example code)
Conclusions
- JS2 focused on programming in the large and code migration:
- Evolutionary programming with structural types
- Gradual typing from
like
to wrap
or fixed types
- Rapid prototypes start out untyped, just like today
- We believe most web JS can remain untyped, with good performance
- Library APIs and implementations can buy integrity and efficiency by the yard
- Higher integrity with efficiency may induce more "islands" of typed code (e.g., real-time games)
What Else Is New?
- ScreamingMonkey lives! It runs a self-hosted ES4 compiler that generates bytecode from the compiler's own source
- Much optimization work remains to be done
- But the C# chess demo from MIX07, ported to ES4, runs now
- ScreamingMonkey chess demo is ~15x faster than the JScript version (per fresh e-mail today from Mark Hammond)
- Demos of two other new APIs, the <video> tag and 3D <canvas>, follow...
Video Tag Demo
- Implements the WHAT-WG <video> tag proposal
- Opera and now Apple have implemented too
- page-defined DHTML playback controls
- Uses Ogg Theora and Vorbis for video and audio
- Embeds <video> in SVG <foreignObject> for transforms
- Developed by Chris Double
Canvas3D Demo
- Alternative OpenGL-ES rendering context for <canvas>
- Now available as a Firefox 3 addon!
- Embeds OpenGL's shader language in <script>, read via DOM
- Example KMZ (Google Earth) viewer
- Developed by Vladimir Vukicevic