Created
October 2, 2010 21:54
-
-
Save noonat/608034 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ECMAScript 5 object proxy as JS | |
(function(exports) { | |
function getPropertyDescriptor(obj, propertyName) { | |
print("getPropertyDescriptor", obj, propertyName); | |
var desc = Object.getOwnPropertyDescriptor(obj, propertyName); | |
if (desc !== undefined) { | |
return desc; | |
} else { | |
var proto = Object.getPrototypeOf(obj); | |
if (proto !== undefined && proto !== null) { | |
return undefined; | |
} else { | |
return Object.getOwnPropertyDescriptor(proto, propertyName); | |
} | |
} | |
}; | |
function isAccessorDescriptor(desc) { | |
print("isAccessorDescriptor", desc); | |
if (desc == null) { | |
return false; | |
} else if (desc.get == null && desc.set == null) { | |
return false; | |
} else { | |
return true; | |
} | |
}; | |
function isDataDescriptor(desc) { | |
print("isDataDescriptor", desc); | |
if (desc == null) { | |
return false; | |
} else if (desc.value === undefined && desc.writable === undefined) { | |
return false; | |
} else { | |
return true; | |
} | |
}; | |
function isGenericDescriptor(desc) { | |
print("isGenericDescriptor", desc); | |
if (desc == null) { | |
return false; | |
} else if (isAccessorDescriptor(desc)) { | |
return false; | |
} else if (isDataDescriptor(desc)) { | |
return false; | |
} else { | |
return true; | |
} | |
}; | |
function isPrimitive(value) { | |
print("isPrimitive", value); | |
if (value === null || typeof value !== 'object') { | |
return true; | |
} else { | |
return false; | |
} | |
}; | |
exports.ObjectProxy = function ObjectProxy() { | |
print("ObjectProxy constructor"); | |
}; | |
// ECMAScript [[GetOwnProperty]](P) | |
ObjectProxy.prototype.getOwnProperty = function(propertyName) { | |
print("ObjectProxy.getOwnProperty", propertyName); | |
return Object.getOwnPropertyDescriptor(this, propertyName); | |
}; | |
// ECMAScript [[GetProperty]](P) | |
ObjectProxy.prototype.getProperty = function(propertyName) { | |
print("ObjectProxy.getProperty", propertyName); | |
return getPropertyDescriptor(this, propertyName); | |
}; | |
// ECMAScript [[Get]](P) | |
ObjectProxy.prototype.get = function(propertyName) { | |
print("ObjectProxy.get", propertyName); | |
var desc = this.getProperty(propertyName); | |
if (desc == null) { | |
return undefined; | |
} else if (isDataDescriptor(desc)) { | |
return desc.value; | |
} else if (desc.getter != null) { | |
return desc.getter.call(this); | |
} else { | |
return undefined; | |
} | |
}; | |
// ECMAScript [[CanPut]](P) | |
ObjectProxy.prototype.canPut = function(propertyName) { | |
print("ObjectProxy.canPut", propertyName); | |
var desc = this.getOwnProperty(propertyName); | |
if (desc != null) { | |
if (isAccessorDescriptor(desc)) { | |
return !!desc.set; | |
} else { | |
return !!desc.writable; | |
} | |
} else { | |
var proto = Object.getPrototypeOf(this); | |
if (proto == null) { | |
return Object.isExtensible(this); | |
} else { | |
var inherited = Object.getPropertyOf(proto, propertyName); | |
if (inherited == null) { | |
return Object.isExtensible(this); | |
} else if (isAccessorDescriptor(inherited)) { | |
return !!inherited.set; | |
} else { | |
if (isExtensible(this)) { | |
return !!inherited.writable; | |
} else { | |
return false; | |
} | |
} | |
} | |
} | |
}; | |
// ECMAScript [[Put]](P, V, Throw) | |
ObjectProxy.prototype.put = function(propertyName, value, canThrow) { | |
print("ObjectProxy.put", propertyName, value, canThrow); | |
if (this.canPut(propertyName)) { | |
var ownDesc = this.getOwnProperty(propertyName); | |
if (isDataDescriptor(ownDesc)) { | |
var valueDesc = {value: value}; | |
Object.defineProperty(this, propertyName, valueDesc, canThrow); | |
} else { | |
var desc = this.getProperty(propertyNAme); | |
if (isAccessorProperty(desc)) { | |
desc.set.call(this, value); | |
} else { | |
var newDesc = { | |
value: value, | |
writable: true, | |
enumerable: true, | |
configurable: true | |
}; | |
Object.defineProperty(this, propertyName, newDesc, canThrow); | |
} | |
} | |
} else { | |
if (shouldThrow) { | |
throw new TypeError; | |
} else { | |
return; | |
} | |
} | |
}; | |
// ECMAScript [[HasProperty]](P) | |
ObjectProxy.prototype.hasProperty = function(propertyName) { | |
print("ObjectProxy.hasProperty", propertyName); | |
if (this.getProperty(propertyName) != null) { | |
return true; | |
} else { | |
return false; | |
} | |
}; | |
// ECMAScript [[Delete]](P, Throw) | |
ObjectProxy.prototype['delete'] = function(propertyName, canThrow) { | |
print("ObjectProxy.delete", propertyName, canThrow); | |
var desc = this.getOwnProperty(propertyName); | |
if (desc != null) { | |
if (desc.configurable) { | |
// FIXME: how to remove property? | |
delete this[propertyName]; | |
return true; | |
} else { | |
if (canThrow) { | |
throw new TypeError; | |
} else { | |
return false; | |
} | |
} | |
} else { | |
return true; | |
} | |
}; | |
// ECMAScript [[DefaultValue]](H) | |
ObjectProxy.prototype.defaultValue = function(hint) { | |
print("ObjectProxy.defaultValue", hint); | |
}; | |
// ECMAScript [[HasInstance]](V) | |
ObjectProxy.prototype.hasInstance = function(value) { | |
print("ObjectProxy.hasInstance", value); | |
if (typeof value !== 'object' || value === null) { | |
return false; | |
} else { | |
while (value != null) { | |
value = Object.getPrototypeOf(value); | |
if (this === value) { | |
return true; | |
} | |
} | |
return false; | |
} | |
}; | |
exports.proxyObject = function(target, hooks) { | |
var parent, prototype; | |
return org.mozilla.javascript.Scriptable({ | |
'delete': function(name) { | |
print('delete', name); | |
delete target[name]; | |
}, | |
get: function(name, start) { | |
print('get', name); | |
if (typeof target[name] === 'undefined') { | |
return org.mozilla.javascript.Scriptable.NOT_FOUND; | |
} else { | |
return target[name]; | |
} | |
}, | |
getClassName: function() { | |
print('getClassName'); | |
return (target.constructor && target.constructor.name) || 'Object'; | |
}, | |
getDefaultValue: function(hint) { | |
print('getDefaultValue', hint); | |
if (hint === java.lang.String || hint === null) { | |
return String(target); | |
} else if (hint === java.lang.Number) { | |
return Number(target); | |
} else if (hint === java.lang.Boolean) { | |
return Boolean(target); | |
} else { | |
return target; | |
} | |
}, | |
getIds: function() { | |
print('getIds'); | |
return Object.keys(target); | |
}, | |
getParentScope: function() { | |
print('getParentScope'); | |
return parent; | |
}, | |
getPrototype: function() { | |
print('getPrototype'); | |
return Object.getPrototypeOf(target); | |
}, | |
has: function(name, start) { | |
print('has', name); | |
return target.hasOwnProperty(name); | |
}, | |
hasInstance: function(constructor) { | |
print('hasInstance', constructor); | |
return target instanceof constructor; | |
}, | |
put: function(name, start, value) { | |
print('put', name, value); | |
target[name] = value; | |
}, | |
setParentScope: function(newParent) { | |
print('setParentScope', newParent); | |
parent = newParent; | |
}, | |
setPrototype: function(newPrototype) { | |
print('setPrototype', newPrototype); | |
try { | |
target.prototype = newPrototype; | |
} catch (error) { | |
// ignore | |
} | |
} | |
}); | |
}; | |
})(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment