Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Basic working proxy that turns reified Data into things that look and…

… behave like regular js objects, but whose values are bound to a buffer.
  • Loading branch information...
commit a227084767792bb0c9109afb6f1a6870c516880d 1 parent a5022c5
@Benvie authored
View
190 proxy/index.js
@@ -0,0 +1,190 @@
+//This is so no stable it's not even funny so be prepared for breakage.
+//In node or chrome it requires you somehow load the included direct-proxies shim.
+//I haven't set this up to work in Firefox correctly yet but it should basically be done by loading the shim unmodified (without the V8 wrapper).
+//That said, it's pretty awesome if it you manage to get everything in the right place and it doesn't break.
+//The expected result is illustrated as follows:
+
+//var reify = require('./') //in this directory.
+//var Pixel = reify('Pixel', { x: 'Uint32', y: 'Uint32', color: { r: 'Uint8', g: 'Uint8', b: 'Uint8' } });
+//var red = Pixel({ x: 100, y: 100, color: { r: 255, g: 0, b: 0 } });
+//console.log(red); //should look indentical to the object passed to Pixel
+//console.log(reify.unwrap(red)._data) //should give the underlying DataBuffer
+
+if (!module.proxyLoaded && typeof Proxy === 'object') {
+ require('direct-proxies');
+ module.proxyLoaded = true;
+}
+
+var reified = require('reified');
+
+var wrapmap = new WeakMap;
+var typemap = new WeakMap;
+var proxies = new WeakMap;
+
+function unwrapIface(o){ return wrapmap.get(o) }
+function unwrapType(o){ return typemap.get(o) }
+function unwrap(o){ return proxies.get(o) }
+reified.unwrap = unwrap;
+
+function dataWrap(data){
+ var iface = {
+ array: function(){ return Array.apply(null, Array(data.length)) },
+ struct: function(){ return {} },
+ numeric: function(){ return new Number },
+ bitfield: function(){ return {} }
+ }[data.DataType]();
+
+ wrapmap.set(iface, data);
+ typemap.set(iface, data.constructor);
+ var proxy = Proxy(iface, handler(data));
+ proxies.set(proxy, data);
+ return proxy;
+}
+
+
+
+Array.isArray = function(orig){
+ return function isArray(){
+ var arr = arguments[0];
+ if (Object(arr) === arr && proxies.has(arr)) {
+ return unwrap(arr).DataType === 'array';
+ } else {
+ return orig(arr);
+ }
+ }
+}(Array.isArray);
+
+function unique(a){
+ return Object.keys(a.reduce(function(r,s){ r[s]=1; return r },{}));
+}
+
+function rewrap(target, property){
+ var val = unwrapIface(target)[property];
+ return val.DataType === 'numeric' ? val.reify() : dataWrap(val);
+}
+
+function typeWrap(o){
+ var proxy = Proxy(o, TypeHandler);
+ wrapmap.set(proxy, o);
+ return proxy;
+}
+
+var TypeHandler = {
+ apply: function(target, receiver, args){
+ return dataWrap(Reflect.construct(target, args));
+ },
+ construct: function(target, args){
+ return dataWrap(Reflect.construct(target, args));
+ }
+};
+
+function handler(of){
+ if (of.DataType in DataHandler) {
+ var handle = DataHandler[of.DataType];
+ } else {
+ var handle = DataHandler.prototype;
+ }
+ return Proxy({}, {
+ get: function(t, trap){
+ return function(target, name, args){
+ if (trap === 'apply') name = '[[Call]]';
+ if (trap === 'construct') args = name, name = '[[Construct]]';
+ if (trap in handle) {
+ var res = handle[trap].apply(handle, arguments);
+ } else {
+ var res = Reflect[trap].apply(handle, arguments);
+ }
+ //console.log(trap, name, res);
+ return res;
+ }
+ }
+ });
+}
+
+function DataHandler(type, traps){
+ DataHandler[type] = this;
+ this.type = type;
+ for (var k in traps) {
+ this[k] = traps[k];
+ }
+}
+
+DataHandler.prototype = {
+ getOwnPropertyNames: function(target){
+ return unique(unwrapType(target).keys.concat(Reflect.getOwnPropertyNames(target)));
+ },
+ keys: function(target){
+ return unique(unwrapType(target).keys.concat(Reflect.keys(target)));
+ },
+ getOwnPropertyDescriptor: function(target, name){
+ var ret = Reflect.getOwnPropertyDescriptor(target, name);
+ if (ret && ~unwrapType(target).keys.indexOf(name)) {
+ ret.value = rewrap(target, name);
+ }
+ return ret;
+ },
+ enumerate: function(target){
+ return Reflect.enumerate(target);
+ },
+ iterate: function(target){
+ return Reflect.iterate(target);
+ },
+ get: function(target, name, receiver){
+ if (name === '__proto__') {
+ return Object.getPrototypeOf(target);
+ } else if (~unwrapType(target).keys.indexOf(name)) {
+ return rewrap(target, name);
+ } else {
+ return Reflect.get(target, name, receiver);
+ }
+ },
+ set: function(target, name, value, receiver){
+ if (name === '__proto__') {
+ target.__proto__ = value;
+ } else if (~unwrapType(target).keys.indexOf(name)) {
+ unwrap(target)[name] = value;
+ } else {
+ Reflect.set(target, name, value, receiver);
+ }
+ },
+ apply: function(target, receiver, args){
+ return unwrapIface(target);
+ }
+};
+
+new DataHandler('array', {});
+
+new DataHandler('struct', {});
+
+
+var reify = module.exports = Proxy(reified, {
+ apply: function(target, receiver, args){
+ return typeWrap(Reflect.apply(target, receiver, args));
+ },
+ construct: function(target, args){
+ return dataWrap(Reflect.construct(target, args));
+ }
+});
+
+
+/*
+
+var traps = {
+ getOwnPropertyDescriptor : ['target', 'name'] , //-> desc | undefined
+ getOwnPropertyNames : ['target'] , //-> [ string ]
+ defineProperty : ['target', 'name', 'descriptor'] , //-> boolean
+ preventExtensions : ['target'] , //-> boolean
+ freeze : ['target'] , //-> boolean
+ seal : ['target'] , //-> boolean
+ deleteProperty : ['target', 'name'] , //-> boolean
+ hasOwn : ['target', 'name'] , //-> boolean
+ has : ['target', 'name'] , //-> boolean
+ get : ['target', 'name', 'receiver'] , //-> any
+ set : ['target', 'name', 'value', 'receiver'] , //-> boolean
+ enumerate : ['target'] , //-> [ string ]
+ iterate : ['target'] , //-> iterator
+ keys : ['target'] , //-> [ string ]
+ apply : ['target', 'receiver', 'args'] , //-> any
+ construct : ['target', 'args'] , //-> any
+};
+*/
View
3  proxy/node_modules/direct-proxies/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "harmony-reflect"]
+ path = harmony-reflect
+ url = https://github.com/tvcutsem/harmony-reflect
View
119 proxy/node_modules/direct-proxies/README.md
@@ -0,0 +1,119 @@
+# Direct Proxies API Shim for Node
+
+This module contains a minimal wrapper around the reference shim implementation of the new Direct Proxies API that replaces the old Harmony Proxy proposal as of December 2011. It loads the shim unchanged but then wraps the Proxy, Proxy.create, and Proxy.createFunction functions in order to capture references to proxies and their targets. It then wrap Object.getOwnPropertyDescriptor and forges descriptors for lookups on target objects, presenting them as always
+configurable. This preempts incompatabilities with the new API and the current implementation in V8 that would
+cause errors to be thrown every time there's a non-configurable property.
+
+The Direct-Proxies shim has recently been renamed to "harmony-reflect" and has moved to right here on GitHub. This is mainly thanks to the hard work of [Tom Van Cutsem](https://github.com/tvcutsem/harmony-reflect) in both producing the API to begin with along with a shim to provide the functionality on top of existing Proxy functionality.
+
+
+The official documentation and API is here: [ES Wiki - Direct Proxies](http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies)
+
+
+
+# Usage
+
+The functionality of the shim has been minimized to the barest possible. It will load the shim and
+
+```javascript
+
+var shim = require('direct-proxies');
+
+//main global
+var Proxy = shim('./harmony-reflect/reflect.js').Proxy;
+
+//a vm context
+var context = vm.createContext();
+var Proxy = shim('./harmony-reflect/reflect.js', context).Proxy;
+
+//create a context and shim it
+var result = shim('./harmony-reflect/reflect.js', true);
+//result.Proxy, result.context
+v
+
+// `Proxy` itself is now the creator for all proxies
+var proxy = Proxy(target, handler);
+
+
+// all traps now have their first parameter as `target` referencing the real object
+{ getOwnPropertyDescriptor: function(target,name) }
+
+
+// new global `Reflect` contains default traps
+global.Reflect:
+ { getOwnPropertyDescriptor, // non-own `getProperty[Names|Descriptor]` gone
+ getOwnPropertyNames,
+ defineProperty,
+ deleteProperty, // renamed from `delete`
+ freeze, // freeze/seal/preventExtensions separated
+ seal,
+ preventExtensions,
+ has,
+ hasOwn,
+ get,
+ set,
+ enumerate,
+ iterate,
+ keys,
+ apply, // apply/new are now traps on the handler
+ construct }
+
+
+// for virtualized objects you can use this for your handler.prototype
+// it implements some handlers but requires the others, like old fundamental traps
+global.Reflect.VirtualHandler.prototype
+ abstract { getOwnPropertyDescriptor,
+ abstract getOwnPropertyNames,
+ abstract defineProperty,
+ abstract deleteProperty,
+ freeze,
+ seal,
+ abstract preventExtensions,
+ has,
+ hasOwn,
+ get,
+ set,
+ enumerate,
+ iterate,
+ keys,
+ abstract apply,
+ construct }
+```
+
+
+# Compatability
+
+The current stable Node.js branch, 0.6.x, uses a version of V8 with bugs that makes it incompatible with this shim. Node's master branch (0.7.x unstable) uses a new V8 which resolves the issue and is **required** to make this work.
+
+
+If you are using Node 0.7.x and are still getting "Illegal Access" errors I have a pull request that solves this issue (along with greatly improving util.inspect) here https://github.com/joyent/node/pull/2360. It should be integrated into Node's master branch soon and hopefully will be available along with the fixed V8 implementation for Node 0.8.
+=======
+If you are using Node >0.6.6 and are still getting "Illegal Access" errors see this:
+https://github.com/joyent/node/pull/2109
+
+# Trap Reference
+
+```javascript
+var traps = {
+ getOwnPropertyDescriptor : ['target', 'name'] , //-> desc | undefined
+ getOwnPropertyNames : ['target'] , //-> [ string ]
+ defineProperty : ['target', 'name', 'descriptor'] , //-> boolean
+
+ preventExtensions : ['target'] , //-> boolean
+ freeze : ['target'] , //-> boolean
+ seal : ['target'] , //-> boolean
+
+ delete : ['target', 'name'] , //-> boolean
+ hasOwn : ['target', 'name'] , //-> boolean
+ has : ['target', 'name'] , //-> boolean
+ get : ['target', 'name', 'receiver'] , //-> any
+ set : ['target', 'name', 'val', 'receiver'] , //-> boolean
+
+ enumerate : ['target'] , //-> [ string ]
+ iterate : ['target'] , //-> iterator `next` fn
+ keys : ['target'] , //-> [ string ]
+
+ apply : ['target', 'receiver', 'args'] , //-> any
+ new : ['target', 'args'] , //-> any
+};
+```
1  proxy/node_modules/direct-proxies/harmony-reflect
@@ -0,0 +1 @@
+Subproject commit 694072c74024f1ee60d7a005c2145e14172f2091
View
103 proxy/node_modules/direct-proxies/index.js
@@ -0,0 +1,103 @@
+
+(function(load){
+
+ return module.exports = load(require('path').resolve(__dirname, 'harmony-reflect/reflect.js'));
+
+})(function(){
+
+ try { var vm = require('vm'), fs = require('fs'), path = require('path') }
+ catch (e) { return /*TODO other platforms*/ }
+
+ var exists = fs.existsSync || path.existsSync;
+ var resolve = function(file){
+ var resolved = path.resolve(file);
+ if ('_makeLong' in path) resolved = path._makeLong(resolved);
+ return exists(resolved) ? resolved : null;
+ }
+
+ var scriptCache = {};
+
+ return module.exports = function load(file, context, dontShimConfigurability){
+ var resolved = resolve(file)
+ if (!resolved) throw new Error('Path not found: ' + file);
+
+ if (!(resolved in scriptCache)) {
+ // syntax errors will happen here
+ scriptCache[resolved] = vm.createScript(fs.readFileSync(file));
+ }
+
+ var script = scriptCache[resolved], ret;
+
+ if (!context) {
+ context = global;
+ ret = { completion: script.runInThisContext(file) };
+ }
+
+ if (context === true) {
+
+ context = vm.createScript();
+ ret = { completion: script.runInContext(context),
+ context: context };
+
+ }
+
+ if (context && context.constructor === process.binding('evals').Context) {
+
+ ret = { completion: script.runInContext(context) };
+
+ }
+
+ if (dontShimConfigurability === true) return ret;
+
+
+ // #################################################################
+ // ### V8 Shim to handle non-configurable descriptors on Proxies ###
+ // #################################################################
+
+ ret.Proxy = (function(Proxy, context){
+ // map proxies to their targets
+ var proxies = new WeakMap;
+
+ // shim the Proxy shim to intercept proxy creation so we can map proxies
+ // to their target objects
+ var ShimmedProxy = function(target, handler){
+ return proxies.set(target, Proxy(target, handler));
+ }
+
+ // Intercept GOPD calls to check if the target is a proxy and get the proxy's target.
+ // If it is a proxy falsify the return descriptor so that it's always configurable. This
+ // preempts the issues that arise in V8 without having to directly modify the shim itself.
+ context.Object.getOwnPropertyDescriptor = function(gopd){
+
+ return function getOwnPropertyDescriptor(obj, prop){
+ var desc = gopd.call(this, obj, prop);
+ if (desc && proxies.has(obj)) {
+ desc.configurable = true;
+ }
+ return desc;
+ }
+
+ }(context.Object.getOwnPropertyDescriptor);
+
+ ShimmedProxy.create = context.Proxy.create;
+ ShimmedProxy.createFunction = context.Proxy.createFunction;
+ // Proxy is enumerable in V8 for some reason, and the shim doesn't bother to make
+ // Reflect non-enumerable either.
+ Object.defineProperties(context, {
+ Proxy: { value: ShimmedProxy, enumerable: false, configurable: true, writable: true },
+ Reflect: { enumerable: false },
+ StopIteration: { enumerable: false },
+ });
+
+
+ // refresh context to ensure it's in sync
+ if (context !== global) vm.runInContext('this', context);
+
+ return ShimmedProxy;
+
+ })(context.Reflect.Proxy, context);
+
+
+ return ret;
+ }
+}());
View
20 proxy/node_modules/direct-proxies/package.json
@@ -0,0 +1,20 @@
+{
+ "author": "Brandon Benvie <brandon@bbenvie.com> (http://bbenvie.com)",
+ "name": "direct-proxies",
+ "description": "Direct Proxy shim for Node/Chrome to get the changed Harmony Proxy API from the November 2011 TC-39 meeting.",
+ "keywords": ["harmony", "proxy", "meta", "shim", "language", "ecmascript", "direct proxies", "browser"],
+ "version": "0.1.6",
+ "repository": {
+ "url": "https://github.com/Benvie/Direct-Proxies-Shim-for-Node"
+ },
+ "main": "index.js",
+ "scripts": {
+ "test": ["harmony-reflect/test/testProxies.js",
+ "harmony-reflect/test/testReflect.js"]
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "dependencies": {},
+ "devDependencies": {}
+}
Please sign in to comment.
Something went wrong with that request. Please try again.