Proxies are Awesome!

Preview:

DESCRIPTION

My JSConf.eu talk about next-gen JavaScript metaprogramming features, starting with ES5's new Object APIs and then focusing on the forthcoming Proxy object, approved for the next ECMA-262 Edition. This is beautiful work from Tom Van Cutsem and Mark Miller, with Andreas Gal helping on the implementation front -- proxies are already shipping in Firefox 4 betas.

Citation preview

Proxiesare Awesome!

Brendan Eich(w/ Mark Miller & Tom Van Cutsem)

Tuesday, September 28, 2010

ECMAScript 5 (ES5)

Tuesday, September 28, 2010

ES5 Review

• Array extras

• JSON (based on json2.js)

• Strict Mode (“use strict”)

• Object meta-programming API

• accessor properties (getters & setters)

• mutability and enumerability controls

• “javascript.lang.reflect”

• In Firefox 4, IE9 (w/o strict?), WebKit nightlies

Tuesday, September 28, 2010

ES5 Property Descriptors• Data vs. accessor properties

• Property attributes

Object.getOwnPropertyDescriptor(point, ‘x’);

Object.getOwnPropertyDescriptor(point, ‘r’);

{ value: 5, writable: true, enumerable: true, configurable: true }

{ get: function () { return Math.sqrt(this.x*this.x + ...); }, set: undefined, enumerable: true, configurable: true }

var point = { x: 5, y: 8, get r() { return Math.sqrt(this.x*this.x + this.y*this.y); } };

Tuesday, September 28, 2010

var point = Object.create( Object.prototype, { x: { value: 5, ... }, y: { value: 8, ... }, r: { get: function() {...}, enumerable: true, ... }, z: { value: 0, enumerable: true, ... } });

Property Descriptor Mapsvar point = { x: 5, y: 8, get r() { return Math.sqrt(this.x*this.x + this.y*this.y); } };

Object.defineProperty(point, ‘z’, { value: 0, enumerable: true, writable: false, configurable: false });

Tuesday, September 28, 2010

var point = Object.create( Object.prototype, { x: { value: 5, ... }, y: { value: 8, ... }, r: { get: function() {...}, enumerable: true, ... }, z: { value: 0, enumerable: true, ... } });

Property Descriptor Mapsvar point = { x: 5, y: 8, get r() { return Math.sqrt(this.x*this.x + this.y*this.y); } };

Object.defineProperty(point, ‘z’, { value: 0, enumerable: true, writable: false, configurable: false });

name pdx {...}

y {...}

r {...}

z {...}

Tuesday, September 28, 2010

Tamper-proofing Objectsvar point = { x: 5, y: 8, get r() { return Math.sqrt(this.x*this.x + this.y*this.y); } };

Object.freeze(point);point.x = 7; // can’t assign properties

Object.seal(point);delete point.x; // can’t delete properties

Object.preventExtensions(point);point.z = 0; // can’t add new properties

Tuesday, September 28, 2010

ES-Harmony Proxies

Tuesday, September 28, 2010

Dynamic Proxies

• Generic handling of property access:

• Generic wrappers: security, aspects, logging, profiling, ...

• Stratified form of SpiderMonkey’s __noSuchMethod__

js> o = {__noSuchMethod__: function (id, args) { print(id, args); }}

({__noSuchMethod__:(function (id, args) {print(id, args);})})

js> o.m(1,2,3)

m 1,2,3

• Generic handling of other operations applicable to objects:

• Virtual objects: persistent objects, remote objects, ...

• Emulate the dreaded “host objects”

Tuesday, September 28, 2010

Example w/ just ES5: loggingfunction makePoint(x, y) { return { x: x, y: y, ... };}

Tuesday, September 28, 2010

Example w/ just ES5: loggingfunction makePoint(x, y) { return { x: x, y: y, ... };}function makeLoggedPoint(p) { return { get x() { log(‘get’,‘x’,p); return p.x; }, set x(v) { log(‘set’,‘x’,p,v); p.x = v; }, // get y, set y, ... };}var lp = makeLoggedPoint(makePoint(1,2));

Tuesday, September 28, 2010

Example w/ just ES5: loggingfunction makePoint(x, y) { return { x: x, y: y, ... };}function makeLoggedPoint(p) { return { get x() { log(‘get’,‘x’,p); return p.x; }, set x(v) { log(‘set’,‘x’,p,v); p.x = v; }, // get y, set y, ... };}var lp = makeLoggedPoint(makePoint(1,2));

Too ad hoc. What about:• logging other data types• profiling, persistence, access control, ...

Tuesday, September 28, 2010

Logging: static ES5 “proxies”function makeLogger(obj) { var proxy = Object.create(Object.getProtoypeOf(obj), {}); Object.getOwnPropertyNames(obj).forEach(function(name) { var pd = Object.getOwnPropertyDescriptor(obj, name); Object.defineProperty(proxy, name, { get: function() { log(‘get’, name, obj); return obj[name]; }, set: function(v) { log(‘set’, name, obj, v); obj[name] = v; }, // copy attributes from pd }); }); return proxy;}

Tuesday, September 28, 2010

Logging: static ES5 “proxies”function makeLogger(obj) { var proxy = Object.create(Object.getProtoypeOf(obj), {}); Object.getOwnPropertyNames(obj).forEach(function(name) { var pd = Object.getOwnPropertyDescriptor(obj, name); Object.defineProperty(proxy, name, { get: function() { log(‘get’, name, obj); return obj[name]; }, set: function(v) { log(‘set’, name, obj, v); obj[name] = v; }, // copy attributes from pd }); }); return proxy;}

• proxy doesn’t reflect structural changes made to ‘obj’• structural changes made to proxy are not reflected in ‘obj’• structural changes: • add/delete properties• change property attributes

Tuesday, September 28, 2010

Logging: dynamic (harmony) proxies

function makeLogger(obj) { var proxy = Proxy.create({ get: function(rcvr, name) { log(‘get’, name, obj); return obj[name]; }, set: function(rcvr, name, val) { log(‘set’, name, obj, val); obj[name] = val; return true; }, ... }, Object.getPrototypeOf(obj)); return proxy;}

Tuesday, September 28, 2010

Logging: dynamic (harmony) proxies

function makeLogger(obj) { var proxy = Proxy.create({ get: function(rcvr, name) { log(‘get’, name, obj); return obj[name]; }, set: function(rcvr, name, val) { log(‘set’, name, obj, val); obj[name] = val; return true; }, ... }, Object.getPrototypeOf(obj)); return proxy;}

meta

base

proxy

handler

Tuesday, September 28, 2010

Stratified APIvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

Tuesday, September 28, 2010

Stratified APIvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

proxy.foo

handler.get(proxy, ‘foo’)

Tuesday, September 28, 2010

Stratified APIvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

proxy.foo

handler.get(proxy, ‘foo’)

proxy.foo = 42

handler.set(proxy, ‘foo’, 42)

Tuesday, September 28, 2010

Stratified APIvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

proxy.foo

handler.get(proxy, ‘foo’)

proxy.foo = 42

handler.set(proxy, ‘foo’, 42)

proxy.foo(1,2,3)

handler.get(proxy, ‘foo’).apply(proxy,[1,2,3])

Tuesday, September 28, 2010

Stratified APIvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

proxy.foo

handler.get(proxy, ‘foo’)

proxy.foo = 42

handler.set(proxy, ‘foo’, 42)

proxy.get

handler.get(proxy, ‘get’)

proxy.foo(1,2,3)

handler.get(proxy, ‘foo’).apply(proxy,[1,2,3])

Tuesday, September 28, 2010

Stratified APIvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

proxy.foo

handler.get(proxy, ‘foo’)

proxy.foo = 42

handler.set(proxy, ‘foo’, 42)

proxy.get

handler.get(proxy, ‘get’)

proxy.foo(1,2,3)

handler.get(proxy, ‘foo’).apply(proxy,[1,2,3])

proto

Tuesday, September 28, 2010

Not just property accessesvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

Tuesday, September 28, 2010

Not just property accessesvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

‘foo’ in proxy

handler.has(‘foo’)

Tuesday, September 28, 2010

Not just property accessesvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

‘foo’ in proxy

handler.has(‘foo’)

delete proxy.foo

handler.delete(‘foo’)

Tuesday, September 28, 2010

Not just property accessesvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

‘foo’ in proxy

handler.has(‘foo’)

delete proxy.foo

handler.delete(‘foo’)

for (var prop in proxy) { ... }

var props = handler.enumerate();for (var i=0;i<props.length;i++) { var prop = props[i]; ...}

Tuesday, September 28, 2010

Not just property accessesvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

‘foo’ in proxy

handler.has(‘foo’)

delete proxy.foo

handler.delete(‘foo’)

for (var prop in proxy) { ... }

var props = handler.enumerate();for (var i=0;i<props.length;i++) { var prop = props[i]; ...}handler.defineProperty(‘foo’, pd)

Object.defineProperty(proxy,‘foo’, pd)

Tuesday, September 28, 2010

But not quite everything, eithervar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

Tuesday, September 28, 2010

But not quite everything, eithervar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

proxy === obj

Tuesday, September 28, 2010

But not quite everything, eithervar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

proxy === obj

Object.getPrototypeOf(proxy) => proto proto

Tuesday, September 28, 2010

But not quite everything, eithervar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

proxy instanceof SomeFunction

proxy === obj

Object.getPrototypeOf(proxy) => proto proto

Tuesday, September 28, 2010

But not quite everything, eithervar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

typeof proxy => “object”

proxy instanceof SomeFunction

proxy === obj

Object.getPrototypeOf(proxy) => proto proto

Tuesday, September 28, 2010

Full Handler API

handler.getOwnPropertyDescriptor(name)handler.getPropertyDescriptor(name)handler.defineProperty(name, pd)handler.getOwnPropertyNames()handler.delete(name) handler.enumerate()handler.fix()handler.has(name)handler.hasOwn(name)handler.get(receiver, name)handler.set(receiver, name, val)handler.keys()handler.iterate()

Object.getOwnPropertyDescriptor(proxy)Object.getPropertyDescriptor(proxy)

Object.defineProperty(proxy,name,pd)Object.getOwnPropertyNames(proxy) 

delete proxy.namefor (name in proxy) { ... } 

Object.{freeze|seal|...}(proxy)name in proxy

({}).hasOwnProperty.call(proxy, name)receiver.name

receiver.name = valObject.keys(proxy)

for (name in proxy) { ... }

proxy

handler

Tuesday, September 28, 2010

Fundamental vs Derived Traps

handler.getOwnPropertyDescriptor(name)handler.getPropertyDescriptor(name)handler.defineProperty(name, pd)handler.getOwnPropertyNames()handler.delete(name) handler.enumerate() -> [string]handler.fix()

handler.has(name)handler.hasOwn(name)handler.get(receiver, name)handler.set(receiver, name, val)handler.keys()handler.iterate() -> iterator

Object.getOwnPropertyDescriptor(proxy)Object.getPropertyDescriptor(proxy)

Object.defineProperty(proxy,name,pd)Object.getOwnPropertyNames(proxy) 

delete proxy.namefor (name in proxy) { ... } 

Object.{freeze|seal|...}(proxy)

name in proxy({}).hasOwnProperty.call(proxy, name)

receiver.namereceiver.name = valObject.keys(proxy)

for (name in proxy) { ... }

proxy

handler

Fundamental traps

Derived traps

Tuesday, September 28, 2010

Function ProxiesJavaScript functions are objects. Additionally, they are also callable and constructible

var call = function() { ... };var construct = function() { ... };var funproxy = Proxy.createFunction(handler, call, construct);

funproxy

handlercall construct

Tuesday, September 28, 2010

Function ProxiesJavaScript functions are objects. Additionally, they are also callable and constructible

var call = function() { ... };var construct = function() { ... };var funproxy = Proxy.createFunction(handler, call, construct);

funproxy

handlercall construct

funproxy(1,2,3)

call(1,2,3)

Tuesday, September 28, 2010

Function ProxiesJavaScript functions are objects. Additionally, they are also callable and constructible

var call = function() { ... };var construct = function() { ... };var funproxy = Proxy.createFunction(handler, call, construct);

funproxy

handlercall construct

funproxy(1,2,3)

call(1,2,3)

new funproxy(1,2,3)

construct(1,2,3)

Tuesday, September 28, 2010

Function ProxiesJavaScript functions are objects. Additionally, they are also callable and constructible

var call = function() { ... };var construct = function() { ... };var funproxy = Proxy.createFunction(handler, call, construct);

funproxy

handlercall construct

funproxy(1,2,3)

call(1,2,3)

new funproxy(1,2,3)

construct(1,2,3)

funproxy.prototype

handler.get(funproxy,‘prototype’)

Tuesday, September 28, 2010

Function ProxiesJavaScript functions are objects. Additionally, they are also callable and constructible

var call = function() { ... };var construct = function() { ... };var funproxy = Proxy.createFunction(handler, call, construct);

funproxy

handlercall construct

funproxy(1,2,3)

call(1,2,3)

new funproxy(1,2,3)

construct(1,2,3)

funproxy.prototype

handler.get(funproxy,‘prototype’)

typeof funproxy => “function”

Tuesday, September 28, 2010

Function ProxiesJavaScript functions are objects. Additionally, they are also callable and constructible

var call = function() { ... };var construct = function() { ... };var funproxy = Proxy.createFunction(handler, call, construct);

funproxy

handlercall construct

funproxy(1,2,3)

call(1,2,3)

new funproxy(1,2,3)

construct(1,2,3)

funproxy.prototype

handler.get(funproxy,‘prototype’)

typeof funproxy => “function”Object.getPrototypeOf(funproxy) => Function.prototype

Tuesday, September 28, 2010

Dilemma: Invoke vs Get+Call• Fundamental vs derived traps = tradeoff in

performance (method allocation or caching) vs. consistency

• invoke can intercept arguments

• but notably, JS methods can be extracted as functions and called later (functional FTW!)

• breaks invariant o.m.call(o) <=> o.m()

var p = Proxy.create({  get:    function(receiver, name) { ... },  invoke: function(receiver, name, args) { ... },  ... });

p.x; // get(p,'x')p.m(a); // invoke(p, 'm', [a])

Tuesday, September 28, 2010

Selective Interception

meta

base

object

Tuesday, September 28, 2010

Selective Interception

meta

base

object

VM territory (C++)

JavaScript territory

Tuesday, September 28, 2010

Selective Interception

meta

base

object

VM territory (C++)

JavaScript territory

VM handler

Tuesday, September 28, 2010

Selective Interception

meta

base

object

VM territory (C++)

JavaScript territory

host object

VM handler VM handler

Tuesday, September 28, 2010

Selective Interception

meta

base

object

VM territory (C++)

JavaScript territory

host object

VM handler VM handler

proxy

handler

Tuesday, September 28, 2010

Selective Interception

meta

base

object

VM territory (C++)

JavaScript territory

host object

VM handler VM handler

proxy

handler

Tuesday, September 28, 2010

Selective Interception

meta

base

object

VM territory (C++)

JavaScript territory

Self-hosted

host object

VM handler VM handler

proxy

handler

Tuesday, September 28, 2010

Selective Interception

meta

base

object

VM territory (C++)

JavaScript territory

Self-hosted

host object

VM handler VM handler

proxy

handler

Tuesday, September 28, 2010

Example: no-op forwarding proxy

function ForwardingHandler(obj) { this.target = obj;}ForwardingHandler.prototype = {  has: function(name) { return name in this.target; },  get: function(rcvr,name) { return this.target[name]; },  set: function(rcvr,name,val) { this.target[name]=val;return true; },  delete: function(name) { return delete this.target[name]; }  enumerate: function() {    var props = []; for (name in this.target) { props.push(name); }; return props;  }, ...}

var proxy = Proxy.create(new ForwardingHandler(o),                         Object.getPrototypeOf(o));

proxy

handler

target

Tuesday, September 28, 2010

Example: counting property access

function makeSimpleProfiler(target) { var forwarder = new ForwardingHandler(target); var count = Object.create(null); forwarder.get = function(rcvr, name) { count[name] = (count[name] || 0) + 1; return this.target[name]; }; return { proxy: Proxy.create(forwarder, Object.getPrototypeOf(target)), get stats() { return count; } }}

Tuesday, September 28, 2010

Example: counting property access

function makeSimpleProfiler(target) { var forwarder = new ForwardingHandler(target); var count = Object.create(null); forwarder.get = function(rcvr, name) { count[name] = (count[name] || 0) + 1; return this.target[name]; }; return { proxy: Proxy.create(forwarder, Object.getPrototypeOf(target)), get stats() { return count; } }}

var subject = { ... };var profiler = makeSimpleProfiler(subject);runApp(profiler.proxy);display(profiler.stats);

Tuesday, September 28, 2010

Fixing a Proxyvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

Tuesday, September 28, 2010

Fixing a Proxyvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handler

Object.freeze(proxy)

Object.seal(proxy)

Object.preventExtensions(proxy)

Tuesday, September 28, 2010

Fixing a Proxyvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handlervar pdmap = handler.fix();if (pdmap === undefined) throw TypeError();become(proxy, Object.freeze( Object.create(proto, pdmap)));

Object.freeze(proxy)

Object.seal(proxy)

Object.preventExtensions(proxy)

Tuesday, September 28, 2010

Fixing a Proxyvar proxy = Proxy.create(handler, proto);

meta

base

proxy

handlervar pdmap = handler.fix();if (pdmap === undefined) throw TypeError();become(proxy, Object.freeze( Object.create(proto, pdmap)));

Object.freeze(proxy)

Object.seal(proxy)

Object.preventExtensions(proxy)

Trapping Fixedfix

Tuesday, September 28, 2010

Fixing a Proxyvar proxy = Proxy.create(handler, proto);

meta

base

proxy

var pdmap = handler.fix();if (pdmap === undefined) throw TypeError();become(proxy, Object.freeze( Object.create(proto, pdmap)));

Object.freeze(proxy)

Object.seal(proxy)

Object.preventExtensions(proxy)

Trapping Fixedfix

Tuesday, September 28, 2010

Meta-level Shifting

handler.getOwnPropertyDescriptor(name)handler.getPropertyDescriptor(name)handler.defineProperty(name, pd)handler.getOwnPropertyNames()handler.delete(name) handler.enumerate()handler.fix()handler.has(name)handler.hasOwn(name)handler.get(receiver, name)handler.set(receiver, name, val)handler.keys()handler.iterate()

Object.getOwnPropertyDescriptor(proxy)Object.getPropertyDescriptor(proxy)

Object.defineProperty(proxy,name,pd)Object.getOwnPropertyNames(proxy) 

delete proxy.namefor (name in proxy) { ... } 

Object.{freeze|seal|...}(proxy)name in proxy

({}).hasOwnProperty.call(proxy, name)receiver.name

receiver.name = valObject.keys(proxy)

for (name in proxy) { ... } 

base-level: many operations on objects

meta-level: all operations reified as invocations of traps

proxy

handler

Tuesday, September 28, 2010

Meta-level Shifting

meta-level: all operations reified as invocations of traps

meta-meta-level: all operations reified as invocations of ‘get’ trap

handler

μhandler

handler.getOwnPropertyDescriptor(name)handler.getPropertyDescriptor(name)handler.defineOwnProperty(name, pd)handler.delete(name) handler.getOwnPropertyNames()handler.enumerate()handler.fix()handler.has(name)handler.hasOwn(name)handler.get(receiver, name)handler.set(receiver, name, val)handler.keys()handler.iterate()

μhandler.get(handler, ‘getOwnP..’)(name)μhandler.get(handler, ‘getProp..’)(name)μhandler.get(handler, ‘define...’)(name,pd)μhandler.get(handler, ‘delete’)(name)μhandler.get(handler, ‘getOwnP..’)()μhandler.get(handler, ‘enumerate’)()μhandler.get(handler, ‘fix’)()μhandler.get(handler, ‘has’)(name)μhandler.get(handler, ‘hasOwn’)(name)μhandler.get(handler, ‘get’)(receiver,name)μhandler.get(handler, ‘set’)(receiver,name,val)μhandler.get(handler, ‘keys’)()μhandler.get(handler, ‘iterate’)()

a proxy whose handler is a proxy

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

Example: Membranes

• Firefox security wrappers (anti-XSS, XUL, etc.)

• Google Caja: capability-secure subset

• Object-capability model: an object is powerless unless given a reference to other objects

• References can be made revocable through a membrane

Tuesday, September 28, 2010

function makeSimpleMembrane(initTarget) {  var enabled = true;

}

Example: Membranes

Tuesday, September 28, 2010

function makeSimpleMembrane(initTarget) {  var enabled = true;

}

Example: Membranes

  return { wrapper: wrap(initTarget), revoke: function() { enabled = false; } };

Tuesday, September 28, 2010

function makeSimpleMembrane(initTarget) {  var enabled = true;

}

  function wrap(target) {    if (Object.isPrimitive(target)) { return target; }    var baseHandler = new ForwardingHandler(target);    var revokeHandler = Proxy.create({      get: function(rcvr, name) { return wrapFunction(baseHandler[name]); }    });

  }

Example: Membranes

  return { wrapper: wrap(initTarget), revoke: function() { enabled = false; } };

Tuesday, September 28, 2010

function makeSimpleMembrane(initTarget) {  var enabled = true;

}

  function wrap(target) {    if (Object.isPrimitive(target)) { return target; }    var baseHandler = new ForwardingHandler(target);    var revokeHandler = Proxy.create({      get: function(rcvr, name) { return wrapFunction(baseHandler[name]); }    });

  }

Example: Membranes

  return { wrapper: wrap(initTarget), revoke: function() { enabled = false; } };

if (typeof target === “function”) { return Proxy.createFunction(revokeHandler, wrapFunction(target)); }    return Proxy.create(revokeHandler, wrap(Object.getPrototypeOf(target)));

Tuesday, September 28, 2010

function makeSimpleMembrane(initTarget) {  var enabled = true;

}

  function wrap(target) {    if (Object.isPrimitive(target)) { return target; }    var baseHandler = new ForwardingHandler(target);    var revokeHandler = Proxy.create({      get: function(rcvr, name) { return wrapFunction(baseHandler[name]); }    });

  }

Example: Membranes

function wrapFunction(f) {    return function() { // variable-argument function if (!enabled) { throw new Error("revoked"); }     return wrap(f.apply(wrap(this), arguments.map(wrap))); } }

  return { wrapper: wrap(initTarget), revoke: function() { enabled = false; } };

if (typeof target === “function”) { return Proxy.createFunction(revokeHandler, wrapFunction(target)); }    return Proxy.create(revokeHandler, wrap(Object.getPrototypeOf(target)));

Tuesday, September 28, 2010

Prior Work

meta

base

proxy

handler

Tuesday, September 28, 2010

Prior Work

meta

base

java.lang.reflect.Proxy

InvocationHandler

proxy

ProxyHandler

mirage

mirror

proxy

handler

Tuesday, September 28, 2010

Prior Work

meta

base

java.lang.reflect.Proxy

InvocationHandler

proxy

ProxyHandler

mirage

mirror

proxy

handler

# traps 13 3 130

Tuesday, September 28, 2010

Making JavaScript Extensible

• Extending JavaScript today: “Host objects”(the IE DOM; anything implemented in C++)

• Proxies are sufficiently powerful to emulate most of the behavior of host objects in JavaScript itself

• Two possible avenues to close the gap:

• Make proxies even more powerful

• Make host objects only as powerful as proxies

Tuesday, September 28, 2010

Status

• Presented at ECMA TC-39 meetings

• Approved for inclusion in ES-Harmony

• http://wiki.ecmascript.org/doku.php?id=harmony:proxies

• In Firefox 4 already, thanks to Andreas Gal!

• The basis of all of Gecko’s security wrappers

• Used by Zaphod (Narcissus as JS engine add-on, source at http://github.com/taustin/Zaphod/)

Tuesday, September 28, 2010

Lessons for Web Standards• Standards need savvy academic research

• Standards must evolve quickly on the Web

• They can’t evolve without prototype trials

• These experiments need tons of user-testing

• To reach users at scale, prototypes must ship

• Ecma TC39 committed to prototyping specs before finalizing standards

• Committee members work together, no blind-siding, to uphold Harmony (it’s social!)

Tuesday, September 28, 2010

Micro-benchmark Results

Tuesday, September 28, 2010

Micro-benchmark: Overhead

Tuesday, September 28, 2010

Proxies: Summary

• Proxies enable:

• Generic wrappers: access control, profiling, adaptors, test injection, etc.

• Virtual objects: persistent objects, remote objects, emulated host objects, ...

• API:

• Robust: stratified, not all operations intercepted

• Secure: can’t trap non-proxy or fixed objects

• Performance: no overhead for non-proxy objects

Tuesday, September 28, 2010

Conclusions

• ES5 provides new meta-programming APIs

• ES-Harmony Proxies: robust dynamic meta-programming for virtual objects, wrappers

• Proxies help put developers in control of extending JavaScript, instead of Ecma TC39

• JavaScript: the Revenge of Smalltalk!

Tuesday, September 28, 2010