Skip to content

Instantly share code, notes, and snippets.

@noonat
Created October 2, 2010 21:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save noonat/608034 to your computer and use it in GitHub Desktop.
Save noonat/608034 to your computer and use it in GitHub Desktop.
// 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