reified
Version:
JS Binary Data API. Structs, arrays, bitfields, and numbers. Reify and Reference like nobody's business.
182 lines (157 loc) • 5.83 kB
JavaScript
//This is so not 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.
//This isn't set up to work in Firefox yet but it should be done by loading the shim without the V8 wrapper.
if (typeof global.Proxy === 'object') {
var Reflect = require('direct-proxies');
var Proxy = Reflect.Proxy;
}
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){
return Proxy({}, {
get: function(t, trap){
return function(target, name, val){
var res = DataHandler[trap].apply(t, arguments);
if (1) {
if (trap === 'apply') name = undefined;
if (trap === 'get') val = undefined;
if (trap === 'construct') val = name, name = undefined;
var log = [trap];
name && log.push(name);
val && log.push(val);
res !== undefined && log.push(res);
console.log.apply(console, log)
}
return res;
}
}
});
}
function NormalDesc(v){ this.value = v }
NormalDesc.prototype = { enumerable: true, configurable: true, writable: true }
function HiddenDesc(v){ this.value = v }
HiddenDesc.prototype = { configurable: true, writable: true }
var DataHandler = {
__proto__: Reflect,
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){
if (~unwrapType(target).keys.indexOf(name)) {
return new NormalDesc(rewrap(target, name));
} else {
return Reflect.getOwnPropertyDescriptor(target, name);
}
},
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)) {
unwrapIface(target)[name] = value;
} else {
Reflect.set(target, name, value, receiver);
}
},
apply: function(target, receiver, args){
return unwrapIface(target);
}
};
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 RGB = reify('RGB', { r: 'Uint8', g: 'Uint8', b: 'Uint8' });
var red = RGB({r: 0, g: 0, b: 0 });
red.r = 100;
console.log(red);
/*
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
};
*/