Download - Proxies are Awesome!
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