Commit 57b71185 authored by mercury233's avatar mercury233

do not track node_modules

parent 68673d01
function byteField(p, offset) {
this.length = 1;
this.get = function () {
return p.buf[offset];
}
this.set = function (val) {
p.buf[offset] = val;
}
}
function boolField(p, offset, length) {
this.length = length;
this.get = function () {
return (p.buf[offset] > 0 );
}
this.set = function (val) {
p.buf[offset] = val ? 1 : 0;
}
}
function intField(p, offset, length, le, signed) {
this.length = length;
function bec(cb) {
for (var i = 0; i < length; i++)
cb(i, length - i - 1);
}
function lec(cb) {
for (var i = 0; i < length; i++)
cb(i, i);
}
function getUVal(bor) {
var val = 0;
bor(function (i, o) {
val += Math.pow(256, o) * p.buf[offset + i];
})
return val;
}
function getSVal(bor) {
var val = getUVal(bor);
if ((p.buf[offset + ( le ? (length - 1) : 0)] & 0x80) == 0x80) {
val -= Math.pow(256, length);
}
return val;
}
function setVal(bor, val) {
bor(function (i, o) {
p.buf[offset + i] = Math.floor(val / Math.pow(256, o)) & 0xff;
});
}
this.get = function () {
var bor = le ? lec : bec;
return ( signed ? getSVal(bor) : getUVal(bor));
}
this.set = function (val) {
var bor = le ? lec : bec;
setVal(bor, val);
}
}
function charField(p, offset, length, encoding) {
this.length = length;
this.encoding = encoding;
this.get = function () {
var result = p.buf.toString(this.encoding, offset, offset + length);
var strlen = result.indexOf("\0");
if (strlen == -1) {
return result;
} else {
return result.slice(0, strlen);
}
}
this.set = function (val) {
val += "\0";
if (val.length > length)
val = val.substring(0, length);
p.buf.write(val, offset, this.encoding);
}
}
function structField(p, offset, struct) {
this.length = struct.length();
this.get = function () {
return struct;
}
this.set = function (val) {
struct.set(val);
}
this.allocate = function () {
struct._setBuff(p.buf.slice(offset, offset + struct.length()));
}
}
function arrayField(p, offset, len, type) {
var as = Struct();
var args = [].slice.call(arguments, 4);
args.unshift(0);
for (var i = 0; i < len; i++) {
if (type instanceof Struct) {
as.struct(i, type.clone());
} else if (type in as) {
args[0] = i;
as[type].apply(as, args);
}
}
this.length = as.length();
this.allocate = function () {
as._setBuff(p.buf.slice(offset, offset + as.length()));
}
this.get = function () {
return as;
}
this.set = function (val) {
as.set(val);
}
}
function Struct() {
if (!(this instanceof Struct))
return new Struct;
var priv = {
buf: {},
allocated: false,
len: 0,
fields: {},
closures: []
}, self = this;
function checkAllocated() {
if (priv.allocated)
throw new Error('Cant change struct after allocation');
}
this.word8 = function (key) {
checkAllocated();
priv.closures.push(function (p) {
p.fields[key] = new byteField(p, p.len);
p.len++;
});
return this;
};
// Create handlers for various Bool Field Variants
[1, 2, 3, 4].forEach(function (n) {
self['bool' + (n == 1 ? '' : n)] = function (key) {
checkAllocated();
priv.closures.push(function (p) {
p.fields[key] = new boolField(p, p.len, n);
p.len += n;
});
return this;
}
});
// Create handlers for various Integer Field Variants
[1, 2, 3, 4, 6, 8].forEach(function (n) {
[true, false].forEach(function (le) {
[true, false].forEach(function (signed) {
var name = 'word' + (n * 8) + ( signed ? 'S' : 'U') + ( le ? 'le' : 'be');
self[name] = function (key) {
checkAllocated();
priv.closures.push(function (p) {
p.fields[key] = new intField(p, p.len, n, le, signed);
p.len += n;
});
return this;
};
});
});
});
this.chars = function (key, length, encoding) {
checkAllocated();
priv.closures.push(function (p) {
p.fields[key] = new charField(p, p.len, length, encoding || 'ascii');
p.len += length;
});
return this;
}
this.struct = function (key, struct) {
checkAllocated();
priv.closures.push(function (p) {
p.fields[key] = new structField(p, p.len, struct.clone());
p.len += p.fields[key].length;
});
return this;
}
function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
this.array = function (key, length, type) {
checkAllocated();
var args = [].slice.call(arguments, 1);
args.unshift(null);
args.unshift(null);
priv.closures.push(function (p) {
args[0] = p;
args[1] = p.len;
p.fields[key] = construct(arrayField, args);
p.len += p.fields[key].length;
});
return this;
}
var beenHere = false;
function applyClosures(p) {
if (beenHere)
return;
p.closures.forEach(function (el) {
el(p);
});
beenHere = true;
}
function allocateFields() {
for (var key in priv.fields) {
if ('allocate' in priv.fields[key])
priv.fields[key].allocate();
}
}
this._setBuff = function (buff) {
priv.buf = buff;
applyClosures(priv);
allocateFields();
priv.allocated = true;
}
this.allocate = function () {
applyClosures(priv);
priv.buf = new Buffer(priv.len);
allocateFields();
priv.allocated = true;
return this;
}
this._getPriv = function () {
return priv;
}
this.clone = function () {
var c = new Struct;
var p = c._getPriv();
p.closures = priv.closures;
return c;
}
this.length = function () {
applyClosures(priv);
return priv.len;
}
this.get = function (key) {
if (key in priv.fields) {
return priv.fields[key].get();
} else
throw new Error('Can not find field ' + key);
}
this.set = function (key, val) {
if (arguments.length == 2) {
if (key in priv.fields) {
priv.fields[key].set(val);
} else
throw new Error('Can not find field ' + key);
} else if (Buffer.isBuffer(key)) {
this._setBuff(key);
} else {
for (var k in key) {
this.set(k, key[k]);
}
}
}
this.buffer = function () {
return priv.buf;
}
function getFields() {
var fields = {};
Object.keys(priv.fields).forEach(function (key) {
Object.defineProperty(fields, key, {
get: function () {
var res = self.get(key);
if (res instanceof Struct) return res.fields;
else return res;
},
set: function (newVal) {
self.set(key, newVal);
},
enumerable: true
});
});
return fields;
};
var _fields;
Object.defineProperty(this, 'fields', {
get: function () {
if (_fields) return _fields;
return (_fields = getFields());
},
enumerable: true,
configurable: true
});
}
exports.Struct = Struct;
Copyright (c) 2010 Sam Shull http://www.google.com/profiles/brickysam26
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
node-proxy is an implementation of Harmony Proxies http://wiki.ecmascript.org/doku.php?id=harmony:proxies
that allows the developer to create "catch-all" property handlers for an object or a function in node.js.
Author: Sam Shull
Repository: http://github.com/samshull/node-proxy
Issues: http://github.com/samshull/node-proxy/issues
*** This does not work appropriately in node versions 0.1.100 - 0.1.102. You will need to install node_version.h in $PREFIX/include/node
Methods:
Object create(ProxyHandler handler [, Object proto ] ) throws Error, TypeError
Function createFunction(ProxyHandler handler, Function callTrap [, Function constructTrap ] ) throws Error, TypeError
Boolean isTrapping(Object obj) throws Error
Additional Methods (for ECMAScript 5 compatibliity): @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf
Boolean freeze(Object obj) throws Error, TypeError
Boolean seal(Object obj) throws Error, TypeError
Boolean preventExtensions(Object obj) throws Error, TypeError
Boolean isFrozen(Object obj) throws Error, TypeError
Boolean isSealed(Object obj) throws Error, TypeError
Boolean isExtensible(Object obj) throws Error, TypeError
PropertyDescriptor getOwnPropertyDescriptor(Object obj, String name) throws Error, TypeError
Boolean defineProperty(Object obj, String name, PropertyDescriptor pd) throws Error, TypeError
Boolean defineProperties(Object obj, Object descriptors) throws Error, TypeError
More methods:
Object hidden(Object obj, String name [, Object value ] ) throws Error
- Set or retrieve a hidden property on an Object
Object clone(Object obj) throws Error
- Create a shallow copy of an Object
Boolean isProxy(Object obj)
- determine if an object was created by Proxy
Boolean setPrototype(Object obj, Object obj) throws Error
-set the prototype of a given object to the second given object
{
'targets': [
{
'target_name': 'nodeproxy',
'sources': [
'src/node-proxy.cc',
],
}
]
}
/*
* This package exports one function: namespace
* in order to create a system of namespace loading
* from a given directory. Directories and files are
* loaded into a proxy object in order to provide a
* system for loading additional files and directories
* the namespace.
*
* This system does not proxy scalar values on an object
* because of the
*
*/
var Proxy = require("node-proxy"),
fs = require("fs"),
sys = require("sys"),
path = require("path"),
undef, extensions;
function isFunction(fn) {
return !!fn &&
Object.prototype.toString.call(fn) ==
"[object Function]";
}
function isScalar(fn) {
switch (typeof fn) {
case "string": return true;
case "number": return true;
}
return false;
}
function inPath(file) {
var i = 0, l = extensions.length;
for (;i<l;++i) {
if (path.existsSync(file + "." + extensions[i])) {
return true;
}
}
return false;
}
exports.namespace = function namespace(filePath, properties) {
properties = properties || {};
var scalar = isScalar(properties),
func = isFunction(filePath),
handlers = {
get: function(rec, name) {
if (name === "valueOf" || name === "toString") {
return function(){
return properties[name]();
};
}
if (!(name in properties)) {
if (func) {
handlers[name] = filePath(name, properties);
} else {
//sys.puts(name);
var file = path.join(filePath, name),
stat, obj;
if (inPath(file)) {
obj = require(file);
}
if (!obj) {
try{
// would work if it was a directory
stat = fs.statSync(file);
} catch(e) {}
}
if (stat || obj) {
properties[name] = obj ?
namespace(file, obj) :
// this allows you to use an
// object as a namespace as well
namespace(file);
} else {
return undef;
}
}
} else if (!isScalar(properties[name]) && !Proxy.isProxy(properties[name])) {
properties[name] = namespace(path.join(filePath, name), properties[name]);
}
//sys.puts("returning");
return properties[name];
},
has: function(name) {
return name === "valueOf" ||
name === "toString" ||
inPath(path.join(filePath, name));
},
set: function(rec, name, value) {
properties[name] = value;
},
"delete": function(name) {
return false;
},
enumerate: function() {
return Object.keys(properties);
},
fix: function() {
return undef;
}
};
return isFunction(properties) ?
Proxy.createFunction(handlers, function() {
return properties.apply(this, arguments);
}) :
Proxy.create(handlers, properties.constructor.prototype);
};
extensions = exports.namespace.extensions = ["js", "node"];
/*
* This is an example of creating an object that autoloads
* files and directories from a specified folder into an object
* in order to emulate a namespace loader.
*
* The preferred naming convention is property names
* of Objects begin lower case, while directory and file names
* should begin upper case in order to avoid naming conflicts
*
*/
var sys = require("sys"),
assert = require("assert"),
namespace = require(__dirname + "/autoload-namespace").namespace,
org = namespace(__dirname + "/org");
sys.puts("test 1");
assert.equal(typeof(org), "object", "org is not an object");
sys.puts("test 2");
assert.equal(typeof(org.w3c), "object", "org.w3c is not an object");
sys.puts("test 3");
assert.equal(typeof(org.w3c.DOM), "object", "org.w3c.DOM is not an object");
sys.puts("test 4");
assert.equal(typeof(org.w3c.DOM.Document), "object", "org.w3c.DOM.Document is not an object");
sys.puts("test 5");
assert.equal(typeof(org.w3c.DOM.Document.string), "string", "org.w3c.DOM.Document.string is not a string");
sys.puts("test 6");
assert.equal(org.w3c.DOM.Document.string.toString(), "String", "org.w3c.DOM.Document.string is not equal to 'String'");
sys.puts("test 7");
assert.equal(typeof(org.w3c.DOM.Document.String.Test), "object", "org.w3c.DOM.Document.String.Test is not an object");
sys.puts("test 8");
assert.equal(typeof(org.w3c.DOM.Document.String.Test.success), "string", "org.w3c.DOM.Document.String.Test.success is not an string");
sys.puts("test 9");
assert.equal(org.w3c.DOM.Document.String.Test.success, "success", "org.w3c.DOM.Document.String.Test.success is not equal to 'success'");
sys.puts(typeof(org.w3c.DOM.Document.create));
sys.puts(typeof(function(){}));
sys.puts(Object.prototype.toString.call(org.w3c.DOM.Document.create));
sys.puts(org.w3c.DOM.Document.create instanceof Function);
org.w3c.DOM.Document.create(sys);
\ No newline at end of file
exports.string = "String";
exports.object = {};
exports.regexp = /regex/;
exports.create = function(sys) {
sys.puts("A proxified function was called inside of Document.js")
};
\ No newline at end of file
module.exports = exports = require('../build/Release/nodeproxy.node');
\ No newline at end of file
{
"name": "node-proxy",
"version": "0.6.0",
"description": "A module for node implementing __noSuchMethod__-type handlers, such as object overloading, as part of the Harmony Catch-All Proxies specification found at http://wiki.ecmascript.org/doku.php?id=harmony:proxies",
"keywords": [
"interceptor",
"proxy",
"overload",
"__noSuchMethod__"
],
"contributors": [
{
"name": "Sam Shull",
"email": "http://www.google.com/profiles/brickysam26"
},
{
"name": "richardms",
"email": "https://github.com/richardms"
},
{
"name": "Andreas Botsikas",
"email": "http://webinos.org/bio-andreas-botsikas/"
}
],
"licenses": [
{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.html"
}
],
"bugs": {
"url": "http://github.com/samshull/node-proxy/issues"
},
"implements": [
"http://wiki.ecmascript.org/doku.php?id=harmony:proxies"
],
"engines": {
"node": ">=0.6",
"npm": ">= 1.1.17"
},
"repositories": [
{
"type": "git",
"url": "http://github.com/samshull/node-proxy"
}
],
"main": "./lib/node-proxy.js",
"scripts": {
"install": "node-gyp configure build",
"test": "node test/test.js"
},
"readme": "node-proxy is an implementation of Harmony Proxies http://wiki.ecmascript.org/doku.php?id=harmony:proxies\nthat allows the developer to create \"catch-all\" property handlers for an object or a function in node.js.\n\nAuthor: Sam Shull \nRepository: http://github.com/samshull/node-proxy \nIssues: http://github.com/samshull/node-proxy/issues \n\n*** This does not work appropriately in node versions 0.1.100 - 0.1.102. You will need to install node_version.h in $PREFIX/include/node\n\nMethods:\n\nObject create(ProxyHandler handler [, Object proto ] ) throws Error, TypeError\n\nFunction createFunction(ProxyHandler handler, Function callTrap [, Function constructTrap ] ) throws Error, TypeError\n\nBoolean isTrapping(Object obj) throws Error\n\n\nAdditional Methods (for ECMAScript 5 compatibliity): @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf\n\nBoolean freeze(Object obj) throws Error, TypeError\n\nBoolean seal(Object obj) throws Error, TypeError\n\nBoolean preventExtensions(Object obj) throws Error, TypeError\n\nBoolean isFrozen(Object obj) throws Error, TypeError\n\nBoolean isSealed(Object obj) throws Error, TypeError\n\nBoolean isExtensible(Object obj) throws Error, TypeError\n\nPropertyDescriptor getOwnPropertyDescriptor(Object obj, String name) throws Error, TypeError\n\nBoolean defineProperty(Object obj, String name, PropertyDescriptor pd) throws Error, TypeError\n\nBoolean defineProperties(Object obj, Object descriptors) throws Error, TypeError\n\n\nMore methods:\n\nObject hidden(Object obj, String name [, Object value ] ) throws Error\n- Set or retrieve a hidden property on an Object\n\nObject clone(Object obj) throws Error\n- Create a shallow copy of an Object\n\nBoolean isProxy(Object obj)\n- determine if an object was created by Proxy\n\nBoolean setPrototype(Object obj, Object obj) throws Error\n-set the prototype of a given object to the second given object\n",
"readmeFilename": "README.md",
"repository": {
"type": "git",
"url": "http://github.com/samshull/node-proxy"
},
"_id": "node-proxy@0.6.0",
"dist": {
"shasum": "cb44fdeb7efc10ad4e0fb4b6478da3b19bd9fa81"
},
"_from": "node-proxy@>=0.5.1",
"_resolved": "https://registry.npmjs.org/node-proxy/-/node-proxy-0.6.0.tgz"
}
/**
*
*
*
* @author Sam Shull <http://samshull.blogspot.com/>
* @version 0.1
*
* @copyright Copyright (c) 2009 Sam Shull <http://samshull.blogspot.com/>
* @license <http://www.opensource.org/licenses/mit-license.html>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*
* CHANGES:
*/
#include "./node-proxy.h"
namespace v8 { // this was much easier
// fundamental traps
Persistent<String> NodeProxy::getOwnPropertyDescriptor;
Persistent<String> NodeProxy::getPropertyDescriptor;
Persistent<String> NodeProxy::getOwnPropertyNames;
Persistent<String> NodeProxy::getPropertyNames;
Persistent<String> NodeProxy::defineProperty;
Persistent<String> NodeProxy::delete_;
Persistent<String> NodeProxy::fix;
// derived traps
Persistent<String> NodeProxy::has;
Persistent<String> NodeProxy::hasOwn;
Persistent<String> NodeProxy::get;
Persistent<String> NodeProxy::set;
Persistent<String> NodeProxy::enumerate;
Persistent<String> NodeProxy::keys;
// string identifiers
Persistent<String> NodeProxy::callTrap;
Persistent<String> NodeProxy::constructorTrap;
Persistent<String> NodeProxy::value;
Persistent<String> NodeProxy::writable;
Persistent<String> NodeProxy::enumerable;
Persistent<String> NodeProxy::configurable;
Persistent<String> NodeProxy::name;
Persistent<String> NodeProxy::trapping;
Persistent<String> NodeProxy::sealed;
Persistent<String> NodeProxy::frozen;
Persistent<String> NodeProxy::extensible;
Persistent<String> NodeProxy::seal;
Persistent<String> NodeProxy::freeze;
Persistent<String> NodeProxy::preventExtensions;
Persistent<String> NodeProxy::isTrapping;
Persistent<String> NodeProxy::isSealed;
Persistent<String> NodeProxy::isFrozen;
Persistent<String> NodeProxy::isExtensible;
Persistent<String> NodeProxy::isProxy;
Persistent<String> NodeProxy::hidden;
Persistent<String> NodeProxy::hiddenPrivate;
Persistent<ObjectTemplate> NodeProxy::ObjectCreator;
Persistent<ObjectTemplate> NodeProxy::FunctionCreator;
/**
*
*
*
*
*/
NodeProxy::NodeProxy() {
}
/**
*
*
*
*
*/
NodeProxy::~NodeProxy() {
}
/**
* Used for creating a shallow copy of an object
*
*
* @param mixed
* @returns mixed
* @throws Error
*/
Handle<Value> NodeProxy::Clone(const Arguments& args) {
HandleScope scope;
if (args.Length() < 1) {
return THREXC("clone requires at least one (1) argument.");
}
if (args[0]->IsString()) {
return args[0]->ToObject()->Clone()->ToString();
} else if (args[0]->IsBoolean()) {
return args[0]->ToObject()->Clone()->ToBoolean();
} else if (args[0]->IsNumber()
|| args[0]->IsInt32()
|| args[0]->IsUint32()) {
return args[0]->ToObject()->Clone()->ToNumber();
} else if (args[0]->IsArray()) {
return Local<Array>::Cast(args[0]->ToObject()->Clone());
} else if (args[0]->IsDate()) {
return Local<Date>::Cast(args[0]->ToObject()->Clone());
} else if (args[0]->IsFunction()) {
return Local<Function>::Cast(args[0])->Clone();
} else if (args[0]->IsNull()) {
return Local<Value>::New(Null());
} else if (args[0]->IsUndefined()) {
return Local<Value>::New(Undefined());
} else if (args[0]->IsObject()) {
return args[0]->ToObject()->Clone();
}
return THREXC("clone cannot determine the type of the argument.");
}
/**
* Set or Retrieve the value of a hidden
* property on a given object
* Passing two arguments to this function
* returns the value of the hidden property
* While passing three arguments to this function
* results in the setting of the hidden property
* and returns a Boolean value indicating successful
* setting of value
*
* @param Object
* @param String name
* @param mixed value - optional
* @returns mixed
* @throws Error
*/
Handle<Value> NodeProxy::Hidden(const Arguments& args) {
HandleScope scope;
if (args.Length() < 2) {
return THREXC("hidden requires at least two (2) arguments.");
}
Local<Object> obj = args[0]->ToObject();
if (args.Length() < 3) {
return obj->GetHiddenValue(
String::Concat(NodeProxy::hidden,
args[1]->ToString()));
}
return Boolean::New(
obj->SetHiddenValue(String::Concat(NodeProxy::hidden,
args[1]->ToString()),
args[2]));
}
/**
* Set the prototype of an object
*
* @param Object
* @param Object
* @returns Boolean
* @throws Error
*/
Handle<Value> NodeProxy::SetPrototype(const Arguments& args) {
HandleScope scope;
if (args.Length() < 2) {
return THREXC("setPrototype requires at least two (2) arguments.");
}
return Boolean::New(args[0]->ToObject()->SetPrototype(args[1]));
}
/**
* Determine if an Object was created by Proxy
*
* @param Object
* @returns Boolean
*/
Handle<Value> NodeProxy::IsProxy(const Arguments& args) {
HandleScope scope;
if (args.Length() < 1) {
return THREXC("isProxy requires at least one (1) argument.");
}
Local<Object> obj = args[0]->ToObject();
if (obj->InternalFieldCount() > 0) {
Local<Value> temp = obj->GetInternalField(0);
return Boolean::New(!temp.IsEmpty() && temp->IsObject());
}
return False();
}
/**
* Create an object that has ProxyHandler intercepts attached and
* optionally implements the prototype of another object
*
* * ProxyHandler intercepts override the property handlers for any
* * given prototype. So, the ProxyHandler will be invoked for access
* * to the prototype's properties as well
*
* @param ProxyHandler - @see NodeProxy::ValidateProxyHandler
* @param Object - optional, the prototype object to implement
* @returns Object
* @throws Error, TypeError
*/
Handle<Value> NodeProxy::Create(const Arguments& args) {
HandleScope scope;
Local<Object> proxyHandler;
if (args.Length() < 1) {
return THREXC("create requires at least one (1) argument.");
}
if (!args[0]->IsObject()) {
return THR_TYPE_ERROR(
"create requires the first argument to be an Object.");
}
proxyHandler = args[0]->ToObject();
if (args.Length() > 1 && !args[1]->IsObject()) {
return THR_TYPE_ERROR(
"create requires the second argument to be an Object.");
}
// manage locking states
proxyHandler->SetHiddenValue(NodeProxy::trapping, True());
proxyHandler->SetHiddenValue(NodeProxy::extensible, True());
proxyHandler->SetHiddenValue(NodeProxy::sealed, False());
proxyHandler->SetHiddenValue(NodeProxy::frozen, False());
Local<Object> instance = ObjectCreator->NewInstance();
instance->SetInternalField(0, proxyHandler);
if (args.Length() > 1) {
instance->SetPrototype(args[1]);
}
return scope.Close(instance);
}
/**
* Create a function that has ProxyHandler intercepts attached and
* sets a call trap function for invokation as well as an optional
* constructor trap
*
*
* @param ProxyHandler - @see NodeProxy::ValidateProxyHandler
* @param Function - call trap
* @param Function - optional, constructor trap
* @returns Function
* @throws Error, TypeError
*/
Handle<Value> NodeProxy::CreateFunction(const Arguments& args) {
HandleScope scope;
if (args.Length() < 2) {
return THREXC("createFunction requires at least two (2) arguments.");
}
if (!args[0]->IsObject()) {
return THR_TYPE_ERROR(
"createFunction requires the first argument to be an Object.");
}
Local<Object> proxyHandler = args[0]->ToObject();
if (!args[1]->IsFunction()) {
return THR_TYPE_ERROR(
"createFunction requires the second argument to be a Function.");
}
if (args.Length() > 2 && !args[2]->IsFunction()) {
return THR_TYPE_ERROR(
"createFunction requires the second argument to be a Function.");
}
proxyHandler->SetHiddenValue(NodeProxy::callTrap, args[1]);
proxyHandler->SetHiddenValue(NodeProxy::constructorTrap,
args.Length() > 2
? args[2]
: Local<Value>::New(Undefined()));
// manage locking states
proxyHandler->SetHiddenValue(NodeProxy::trapping, True());
proxyHandler->SetHiddenValue(NodeProxy::extensible, True());
proxyHandler->SetHiddenValue(NodeProxy::sealed, False());
proxyHandler->SetHiddenValue(NodeProxy::frozen, False());
Local<Object> fn = FunctionCreator->NewInstance();
fn->SetPrototype(args[1]->ToObject()->GetPrototype());
fn->SetInternalField(0, proxyHandler);
return scope.Close(fn);
}
/**
* Used as a handler for freeze, seal, and preventExtensions
* to lock the state of a Proxy created object
*
* @param Object
* @returns Boolean
* @throws Error, TypeError
*/
Handle<Value> NodeProxy::Freeze(const Arguments& args) {
HandleScope scope;
Local<String> name = args.Callee()->GetName()->ToString();
if (args.Length() < 1) {
return THREXCW(String::Concat(name,
String::New(" requires at least one (1) argument.")));
}
Local<Object> obj = args[0]->ToObject();
if (obj->InternalFieldCount() < 1) {
return THR_TYPE_ERROR(
"Locking functions expect first "
"argument to be intialized by Proxy");
}
Local<Value> hide = obj->GetInternalField(0);
if (hide.IsEmpty() || !hide->IsObject()) {
return THR_TYPE_ERROR(
"Locking functions expect first "
"argument to be intialized by Proxy");
}
Local<Object> handler = hide->ToObject();
// if the object already meets the requirements of the function call
if (name->Equals(NodeProxy::freeze)) {
if (handler->GetHiddenValue(NodeProxy::frozen)->BooleanValue()) {
return True();
}
} else if (name->Equals(NodeProxy::seal)) {
if (handler->GetHiddenValue(NodeProxy::sealed)->BooleanValue()) {
return True();
}
} else if (name->Equals(NodeProxy::preventExtensions)) {
if (handler->GetHiddenValue(NodeProxy::extensible)->BooleanValue()) {
return True();
}
}
// if this object is not trapping, just set the appropriate parameters
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
if (name->Equals(NodeProxy::freeze)) {
handler->SetHiddenValue(NodeProxy::frozen, True());
handler->SetHiddenValue(NodeProxy::sealed, True());
handler->SetHiddenValue(NodeProxy::extensible, False());
return True();
} else if (name->Equals(NodeProxy::seal)) {
handler->SetHiddenValue(NodeProxy::sealed, True());
handler->SetHiddenValue(NodeProxy::extensible, False());
return True();
} else if (name->Equals(NodeProxy::preventExtensions)) {
handler->SetHiddenValue(NodeProxy::extensible, False());
return True();
}
}
// Harmony Proxy handling of fix
Local<Function> fix = Local<Function>::Cast(handler->Get(NodeProxy::fix));
#ifdef _WIN32
// On windows you get "error C2466: cannot allocate an array of constant size 0" and we use a pointer
Local<Value>* argv;
#else
Local<Value> argv[0];
#endif
Local<Value> pieces = fix->Call(args[0]->ToObject(), 0, argv);
if (pieces.IsEmpty() || !pieces->IsObject()) {
return THR_TYPE_ERROR("Cannot lock object.");
}
Local<Object> parts = pieces->ToObject();
// set the appropriate parameters
if (name->Equals(NodeProxy::freeze)) {
parts->SetHiddenValue(NodeProxy::frozen, True());
parts->SetHiddenValue(NodeProxy::sealed, True());
parts->SetHiddenValue(NodeProxy::extensible, False());
} else if (name->Equals(NodeProxy::seal)) {
parts->SetHiddenValue(NodeProxy::sealed, True());
parts->SetHiddenValue(NodeProxy::extensible, False());
} else if (name->Equals(NodeProxy::preventExtensions)) {
parts->SetHiddenValue(NodeProxy::extensible, False());
}
parts->SetHiddenValue(NodeProxy::trapping, False());
// overwrite the handler, making handler available for GC
obj->SetInternalField(0, parts);
return True();
}
/**
* Used as a handler for determining isTrapped,
* isFrozen, isSealed, and isExtensible
*
* @param Object
* @returns Boolean
* @throws Error, TypeError
*/
Handle<Value> NodeProxy::IsLocked(const Arguments& args) {
HandleScope scope;
Local<String> name = args.Callee()->GetName()->ToString();
if (args.Length() < 1) {
return THREXCW(String::Concat(name,
String::New(" requires at least one (1) argument.")));
}
Local<Object> arg = args[0]->ToObject();
if (arg->InternalFieldCount() < 1) {
return THR_TYPE_ERROR(
"Locking functions expect first argument "
"to be intialized by Proxy");
}
Local<Value> hide = arg->GetInternalField(0);
if (hide.IsEmpty() || !hide->IsObject()) {
return THR_TYPE_ERROR(
"Locking functions expect first argument "
"to be intialized by Proxy");
}
Local<Object> obj = hide->ToObject();
if (name->Equals(NodeProxy::isExtensible)) {
return obj->GetHiddenValue(NodeProxy::extensible)->ToBoolean();
} else if (name->Equals(NodeProxy::isSealed)) {
return obj->GetHiddenValue(NodeProxy::sealed)->ToBoolean();
} else if (name->Equals(NodeProxy::isTrapping)) {
return obj->GetHiddenValue(NodeProxy::trapping)->ToBoolean();
} else if (name->Equals(NodeProxy::isFrozen)) {
return obj->GetHiddenValue(NodeProxy::frozen)->ToBoolean();
}
return False();
}
/**
* Part of ECMAScript 5, but only for use on
* Objects and Functions created by Proxy
*
* @param Object
* @param String - the name of the property
* @returns PropertyDescriptor
* @throws Error, TypeError
*/
Handle<Value> NodeProxy::GetOwnPropertyDescriptor(const Arguments& args) {
HandleScope scope;
if (args.Length() < 2) {
return THREXC("getOwnPropertyDescriptor requires "
"at least two (2) arguments.");
}
if (!args[1]->IsString() && !args[1]->IsNumber()) {
return THR_TYPE_ERROR("getOwnPropertyDescriptor requires "
"the second argument to be a String or a Number.");
}
Local<Object> obj = args[0]->ToObject();
Local<String> name = args[1]->ToString();
if (obj->InternalFieldCount() < 1) {
return THR_TYPE_ERROR("getOwnPropertyDescriptor expects "
"first argument to be intialized by Proxy");
}
Local<Value> temp = obj->GetInternalField(0);
if (temp.IsEmpty() || !temp->IsObject()) {
return THR_TYPE_ERROR("getOwnPropertyDescriptor expects "
"first argument to be intialized by Proxy");
}
Local<Object> handler = temp->ToObject();
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
return handler->Get(name);
}
Local<Function> getOwn =
Local<Function>::Cast(
handler->Get(NodeProxy::getOwnPropertyDescriptor));
Local<Value> argv[1] = {args[1]};
return getOwn->Call(obj, 1, argv);
}
/**
* Part of ECMAScript 5, but only for use on
* Objects and Functions created by Proxy
*
* @param Object
* @param String - the name of the property
* @param PropertyDescriptor
* @returns Boolean
* @throws Error, TypeError
*/
Handle<Value> NodeProxy::DefineProperty(const Arguments& args) {
HandleScope scope;
if (args.Length() < 3) {
return THREXC("defineProperty requires at least three (3) arguments.");
}
if (!args[1]->IsString() && !args[1]->IsNumber()) {
return THR_TYPE_ERROR("defineProperty requires the "
"second argument to be a String or a Number.");
}
if (!args[2]->IsObject()) {
return THR_TYPE_ERROR("defineProperty requires the third argument "
"to be an Object of the type PropertyDescriptor.");
}
Local<Object> obj = args[0]->ToObject();
if (obj->InternalFieldCount() < 1) {
return THR_TYPE_ERROR("defineProperty expects first "
"argument to be intialized by Proxy");
}
Local<Value> temp = obj->GetInternalField(0);
if (temp.IsEmpty() || !temp->IsObject()) {
return THR_TYPE_ERROR("defineProperty expects first argument "
"to be intialized by Proxy");
}
Local<String> name = args[1]->ToString();
Local<Object> handler = temp->ToObject();
if (handler->GetHiddenValue(NodeProxy::sealed)->BooleanValue() ||
!handler->Has(NodeProxy::defineProperty)) {
return False();
}
if (!handler->GetHiddenValue(NodeProxy::extensible)->BooleanValue() &&
!handler->Has(name)) {
return False();
}
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
Local<Object> desc = handler->Get(name)->ToObject();
if (desc->Get(NodeProxy::configurable)->BooleanValue()) {
return Boolean::New(
handler->Set(name, args[2]->ToObject()));
}
return False();
}
Local<Function> def = Local<Function>::Cast(
handler->Get(NodeProxy::defineProperty));
Local<Value> argv[2] = {args[1], args[2]->ToObject()};
return def->Call(obj, 2, argv)->ToBoolean();
}
/**
* Part of ECMAScript 5, but only for use on
* Objects and Functions created by Proxy
*
* @param Object
* @param Object - name/PropertyDescriptor pairs
* @returns Boolean
* @throws Error, TypeError
*/
Handle<Value> NodeProxy::DefineProperties(const Arguments& args) {
HandleScope scope;
if (args.Length() < 2) {
return THREXC("defineProperty requires at least three (3) arguments.");
}
if (!args[1]->IsObject()) {
return THR_TYPE_ERROR("defineProperty requires the third argument "
"to be an Object of the type PropertyDescriptor.");
}
Local<Object> obj = args[0]->ToObject();
if (obj->InternalFieldCount() < 1) {
return THR_TYPE_ERROR("defineProperty expects first "
"argument to be intialized by Proxy");
}
Local<Value> temp = obj->GetInternalField(0);
if (!temp.IsEmpty() && temp->IsObject()) {
Local<Object> props = args[1]->ToObject();
Local<Object> handler = temp->ToObject();
if (handler->GetHiddenValue(NodeProxy::sealed)->BooleanValue()) {
return False();
}
bool extensible = handler->GetHiddenValue(
NodeProxy::extensible)->BooleanValue();
Local<Array> names = props->GetPropertyNames();
uint32_t i = 0, l = names->Length();
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
for (;i < l; ++i) {
Local<Object> name = names->CloneElementAt(i);
if (handler->Has(name->ToString()) &&
handler->Get(name->ToString())->IsObject()
) {
Local<Object> tempObj =
handler->Get(name->ToString())->ToObject();
if (tempObj->Get(NodeProxy::configurable)->BooleanValue()) {
if (!handler->Set(name->ToString(),
props->Get(name->ToString()))) {
return THREXCW(
String::Concat(
String::New("Unable to define property: "),
name->ToString()));
}
}
} else {
return THREXCW(String::Concat(
String::New("Unable to define property: "),
name->ToString()));
}
}
return True();
}
Local<Function> def =
Local<Function>::Cast(handler->Get(NodeProxy::defineProperty));
TryCatch firstTry;
for (;i < l; ++i) {
Local<Value> name = names->Get(i);
if (extensible || obj->Has(name->ToString())) {
Local<Value> pd = props->Get(name->ToString());
Local<Value> argv[2] = {name, pd};
def->Call(obj, 2, argv);
if (firstTry.HasCaught()) {
return firstTry.ReThrow();
}
}
}
return True();
}
return False();
}
/**
* Function used for a constructor and invocation
* handler of a Proxy created function
* Calls the appropriate function attached when the Proxy was created
*
* @param ...args
* @returns mixed
* @throws Error
*/
Handle<Value> NodeProxy::New(const Arguments& args) {
HandleScope scope;
if (args.Callee()->InternalFieldCount() < 1 && args.Data().IsEmpty()) {
return THR_TYPE_ERROR("defineProperty expects first "
"argument to be intialized by Proxy");
}
Local<Value> info, ret, data = args.Holder()->GetInternalField(0);
if (data.IsEmpty() || !data->IsObject()) {
return THREXC("Invalid reference to Proxy#constructor");
}
Local<Function> fn;
Local<Object> obj = data->ToObject();
if (args.IsConstructCall()) {
info = obj->GetHiddenValue(NodeProxy::constructorTrap);
if (!info.IsEmpty() && info->IsFunction()) {
fn = Local<Function>::Cast(info);
} else {
fn = Local<Function>::Cast(
obj->GetHiddenValue(NodeProxy::callTrap));
}
} else {
fn = Local<Function>::Cast(obj->GetHiddenValue(NodeProxy::callTrap));
}
int i = 0, l = args.Length();
Local<Value>* argv = new Local<Value>[l];
for (; i < l; ++i) {
argv[i] = args[i];
}
ret = fn->Call(args.This(), args.Length(), argv);
if (args.IsConstructCall()) {
if (!ret.IsEmpty()) {
return ret;
}
return args.This();
}
return ret;
}
/**
* Invoked for accessing the named properties of an object
*
*
*
*/
Handle<Value> NodeProxy::GetNamedProperty(Local<String> name,
const AccessorInfo &info) {
HandleScope scope;
if (info.This()->InternalFieldCount() < 1 || info.Data().IsEmpty()) {
return THR_TYPE_ERROR("SetNamedProperty intercepted "
"by non-Proxy object");
}
Local<Value> argv1[1] = {name};
Local<Value> data = info.This()->InternalFieldCount() > 0 ?
info.This()->GetInternalField(0) :
info.Data();
if (!data->IsObject()) {
return Undefined();
}
Local<Function> fn;
Local<Object> handler = data->ToObject();
// if the Proxy isn't trapping, return
// the value set on the property descriptor
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
return CallPropertyDescriptorGet(handler->Get(name), info.This(), argv1);
}
Local<Value> get = handler->Get(NodeProxy::get);
if (get->IsFunction()) {
fn = Local<Function>::Cast(get);
Local<Value> argv[2] = {info.This(), name};
return fn->Call(handler, 2, argv);
}
Local<Value> getPropertyDescriptor = handler->Get(NodeProxy::getPropertyDescriptor);
if (getPropertyDescriptor->IsFunction()) {
fn = Local<Function>::Cast(getPropertyDescriptor);
return CallPropertyDescriptorGet(fn->Call(handler, 1, argv1), info.This(), argv1);
}
Local<Value> getOwnPropertyDescriptor = handler->Get(NodeProxy::getOwnPropertyDescriptor);
if (getOwnPropertyDescriptor->IsFunction()) {
fn = Local<Function>::Cast(getOwnPropertyDescriptor);
return CallPropertyDescriptorGet(fn->Call(handler, 1, argv1), info.This(), argv1);
}
}
Local<Value> NodeProxy::CallPropertyDescriptorGet(Local<Value> descriptor, Handle<Object> context, Local<Value> args[1]) {
if (descriptor->IsObject()) {
Local<Value> get = descriptor->ToObject()->Get(NodeProxy::get);
if (get->IsFunction()) {
Local<Function> fn = Local<Function>::Cast(get);
return fn->Call(context, 1, args);
}
return descriptor->ToObject()->Get(NodeProxy::value);
}
return Local<Value>::New(Undefined());
}
/**
* Invoked for setting the named properties of an object
*
*
*
*/
Handle<Value> NodeProxy::SetNamedProperty(Local<String> name,
Local<Value> value,
const AccessorInfo &info) {
HandleScope scope;
if (info.This()->InternalFieldCount() < 1 || info.Data().IsEmpty()) {
return THR_TYPE_ERROR("SetNamedProperty intercepted "
"by non-Proxy object");
}
Local<Value> argv2[2] = {name, value};
Local<Value> data = info.This()->InternalFieldCount() > 0 ?
info.This()->GetInternalField(0) :
info.Data();
if (!data->IsObject()) {
return Undefined();
}
Local<Object> handler = data->ToObject();
// if the Proxy isn't trapping, return the
// value set on the property descriptor
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
if (handler->GetHiddenValue(NodeProxy::extensible)->BooleanValue() ||
handler->Has(name)
) {
Local<Value> pd = handler->Get(name);
if (!pd->IsObject()) {
return Undefined();
}
Local<Object> pd_obj = pd->ToObject();
if (!pd_obj->GetHiddenValue(
NodeProxy::writable)->BooleanValue()
) {
return THREXCW(
String::Concat(
String::New("In accessible property: "),
name));
}
Local<Value> set = pd_obj->Get(NodeProxy::set);
if (set->IsFunction()) {
Local<Function> fn = Local<Function>::Cast(set);
fn->Call(info.This(), 2, argv2);
return value;
}
if (pd_obj->Set(NodeProxy::value, value)) {
return value;
}
return Undefined();
}
return Undefined();
}
// does the ProxyHandler have a set method?
Local<Value> set = handler->Get(NodeProxy::set);
if (set->IsFunction()) {
Local<Function> set_fn = Local<Function>::Cast(set);
Local<Value> argv3[3] = {info.This(), name, value};
set_fn->Call(handler, 3, argv3);
return value;
}
Local<Value> getOwnPropertyDescriptor = handler->Get(NodeProxy::getOwnPropertyDescriptor);
if (getOwnPropertyDescriptor->IsFunction()) {
Local<Function> gopd_fn = Local<Function>::Cast(getOwnPropertyDescriptor);
Local<Value> argv[1] = {name};
return CallPropertyDescriptorSet(gopd_fn->Call(handler, 1, argv), info.This(), name, value);
}
Local<Value> getPropertyDescriptor = handler->Get(NodeProxy::getPropertyDescriptor);
if (getPropertyDescriptor->IsFunction()) {
Local<Function> gpd_fn = Local<Function>::Cast(getPropertyDescriptor);
Local<Value> argv[1] = {name};
return CallPropertyDescriptorSet(gpd_fn->Call(handler, 1, argv), info.This(), name, value);
}
return Undefined();
}
Local<Value> NodeProxy::CallPropertyDescriptorSet(Local<Value> descriptor, Handle<Object> context, Local<Value> name, Local<Value> value) {
if (descriptor->IsObject()) {
Local<Object> pd = descriptor->ToObject();
Local<Value> set = pd->Get(NodeProxy::set);
if (set->IsFunction()) {
Local<Function> fn = Local<Function>::Cast(set);
Local<Value> args[2] = { name, value };
return fn->Call(context, 2, args);
} else if (pd->Get(NodeProxy::writable)->BooleanValue()) {
if (pd->Set(NodeProxy::value, value)) {
return value;
}
}
}
return Local<Value>::New(Undefined());
}
/**
* Invoked for determining if an object has a specific property
*
*
*
*/
Handle<Boolean> NodeProxy::QueryNamedProperty(Local<String> name,
const AccessorInfo &info) {
HandleScope scope;
if (info.This()->InternalFieldCount() < 1 || !info.Data().IsEmpty()) {
Local<Value> argv[1] = {name}, temp;
Local<Value> data = info.This()->InternalFieldCount() > 0 ?
info.This()->GetInternalField(0) :
info.Data();
Local<Function> fn;
if (!data->IsObject()) {
return False();
}
Local<Object> handler = data->ToObject();
// if the Proxy isn't trapping,
// return the value set on the property descriptor
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
return Boolean::New(handler->Has(name));
}
// check the ProxyHandler for the has method
Local<Value> hasOwn = handler->Get(NodeProxy::hasOwn);
if (hasOwn->IsFunction()) {
fn = Local<Function>::Cast(hasOwn);
return fn->Call(handler, 1, argv)->ToBoolean();
}
// check the ProxyHandler for the has method
Local<Value> has = handler->Get(NodeProxy::has);
if (has->IsFunction()) {
fn = Local<Function>::Cast(has);
return fn->Call(handler, 1, argv)->ToBoolean();
}
Local<Value> getOwnPropertyDescriptor = handler->Get(NodeProxy::getOwnPropertyDescriptor);
if (getOwnPropertyDescriptor->IsFunction()) {
fn = Local<Function>::Cast(temp);
return fn->Call(handler, 1, argv)->ToBoolean();
}
Local<Value> getPropertyDescriptor = handler->Get(NodeProxy::getPropertyDescriptor);
if (getPropertyDescriptor->IsFunction()) {
fn = Local<Function>::Cast(getPropertyDescriptor);
return fn->Call(handler, 1, argv)->ToBoolean();
}
}
return False();
}
/**
* Invoked for determining if an object has a specific property
*
*
*
*/
Handle<Integer> NodeProxy::QueryNamedPropertyInteger(Local<String> name,
const AccessorInfo &info) {
HandleScope scope;
Local<Integer> DoesntHavePropertyResponse;
Local<Integer> HasPropertyResponse = Integer::New(None);
if (info.This()->InternalFieldCount() > 0 || !info.Data().IsEmpty()) {
Local<Value> data = info.This()->InternalFieldCount() > 0 ?
info.This()->GetInternalField(0) :
info.Data();
if (!data->IsObject()) {
return DoesntHavePropertyResponse;
}
Local<Object> handler = data->ToObject();
// if the Proxy isn't trapping,
// return the value set on the property descriptor
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
if (handler->Has(name)) {
Local<Value> pd = handler->Get(name);
if (pd->IsObject()) {
return GetPropertyAttributeFromPropertyDescriptor(
pd->ToObject());
}
return HasPropertyResponse;
}
return DoesntHavePropertyResponse;
}
Local<Value> argv[1] = {name};
Local<Value> hasOwn = handler->Get(NodeProxy::hasOwn);
if (hasOwn->IsFunction()) {
Local<Function> hasOwn_fn = Local<Function>::Cast(hasOwn);
return hasOwn_fn->Call(handler, 1, argv)->BooleanValue() ?
HasPropertyResponse :
DoesntHavePropertyResponse;
}
Local<Value> has = handler->Get(NodeProxy::has);
if (has->IsFunction()) {
Local<Function> has_fn = Local<Function>::Cast(has);
return has_fn->Call(handler, 1, argv)->BooleanValue() ?
HasPropertyResponse :
DoesntHavePropertyResponse;
}
Local<Value> getOwnPropertyDescriptor = handler->Get(NodeProxy::getOwnPropertyDescriptor);
if (getOwnPropertyDescriptor->IsFunction()) {
Local<Function> gopd_fn = Local<Function>::Cast(getOwnPropertyDescriptor);
Local<Value> gopd_pd = gopd_fn->Call(handler, 1, argv);
if (gopd_pd->IsObject()) {
return GetPropertyAttributeFromPropertyDescriptor(
gopd_pd->ToObject());
}
}
Local<Value> getPropertyDescriptor = handler->Get(NodeProxy::getPropertyDescriptor);
if (handler->Has(NodeProxy::getPropertyDescriptor)) {
Local<Function> gpd_fn = Local<Function>::Cast(getPropertyDescriptor);
Local<Value> gpd_pd = gpd_fn->Call(handler, 1, argv);
if (gpd_pd->IsObject()) {
return GetPropertyAttributeFromPropertyDescriptor(
gpd_pd->ToObject());
} else if (gpd_pd->IsUndefined()) {
return DoesntHavePropertyResponse;
}
}
}
return DoesntHavePropertyResponse;
}
/**
* Find the appropriate PropertyAttribute
* for a given PropertyDescriptor object
*
*
*/
Handle<Integer>
NodeProxy::GetPropertyAttributeFromPropertyDescriptor(Local<Object> pd) {
HandleScope scope;
uint32_t ret = None;
if (pd->Get(NodeProxy::configurable)->IsBoolean() &&
!pd->Get(NodeProxy::configurable)->BooleanValue()) {
// return Integer::New(DontDelete);
ret &= DontDelete;
}
if (pd->Get(NodeProxy::enumerable)->IsBoolean() &&
!pd->Get(NodeProxy::enumerable)->BooleanValue()) {
// return Integer::New(DontEnum);
ret &= DontEnum;
}
if (pd->Get(NodeProxy::writable)->IsBoolean() &&
!pd->Get(NodeProxy::writable)->BooleanValue()) {
// return Integer::New(ReadOnly);
ret &= ReadOnly;
}
return Integer::New(ret);
}
/**
* Invoked when deleting the named property of an object
*
*
*
*/
Handle<Boolean> NodeProxy::DeleteNamedProperty(Local<String> name,
const AccessorInfo &info) {
HandleScope scope;
if (info.This()->InternalFieldCount() > 0 || !info.Data().IsEmpty()) {
Local<Value> data = info.This()->InternalFieldCount() > 0 ?
info.This()->GetInternalField(0) :
info.Data();
if (!data->IsObject()) {
return False();
}
Local<Object> handler = data->ToObject();
// if the Proxy isn't trapping,
// return the value set on the property descriptor
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
if (!handler->GetHiddenValue(NodeProxy::frozen)->BooleanValue()) {
Local<Value> pd = handler->Get(name);
if (pd->IsObject()) {
Local<Object> pd_obj = pd->ToObject();
if (pd_obj->Get(NodeProxy::configurable)->IsBoolean() &&
pd_obj->Get(NodeProxy::configurable)->BooleanValue()
) {
return Boolean::New(handler->Delete(name));
}
}
}
return False();
}
Local<Value> delete_ = handler->Get(NodeProxy::delete_);
if (delete_->IsFunction()) {
Local<Function> fn = Local<Function>::Cast(delete_);
Local<Value> argv[1] = {name};
return fn->Call(handler, 1, argv)->ToBoolean();
}
}
return False();
}
/**
* Invoked for enumerating all properties of an object
*
*
*
*/
Handle<Array> NodeProxy::EnumerateNamedProperties(const AccessorInfo &info) {
HandleScope scope;
if (info.This()->InternalFieldCount() > 0 || !info.Data().IsEmpty()) {
Local<Value> data = info.This()->InternalFieldCount() > 0 ?
info.This()->GetInternalField(0) :
info.Data();
if (!data->IsObject()) {
return Array::New();
}
Local<Object> handler = data->ToObject();
#ifdef _WIN32
// On windows you get "error C2466: cannot allocate an array of constant size 0" and we use a pointer
Local<Value>* argv;
#else
Local<Value> argv[0];
#endif
// if the Proxy isn't trapping,
// return the value set on the property descriptor
if (!handler->GetHiddenValue(NodeProxy::trapping)->BooleanValue()) {
return handler->GetPropertyNames();
}
Local<Value> enumerate = handler->Get(NodeProxy::enumerate);
if (enumerate->IsFunction()) {
Local<Function> enumerate_fn = Local<Function>::Cast(enumerate);
Local<Value> names = enumerate_fn->Call(handler, 0, argv);
if (names->IsArray()) {
return Local<Array>::Cast(names->ToObject());
}
}
Local<Value> keys = handler->Get(NodeProxy::keys);
if (keys->IsFunction()) {
Local<Function> keys_fn = Local<Function>::Cast(enumerate);
Local<Value> names = keys_fn->Call(handler, 0, argv);
if (names->IsArray()) {
return Local<Array>::Cast(names->ToObject());
}
}
Local<Value> getPropertyNames = handler->Get(NodeProxy::getPropertyNames);
if (getPropertyNames->IsFunction()) {
Local<Function> gpn_fn = Local<Function>::Cast(getPropertyNames);
Local<Value> names = gpn_fn->Call(handler, 0, argv);
if (names->IsArray()) {
return Local<Array>::Cast(names->ToObject());
}
}
}
return Array::New();
}
/**
* Invoked for accessing the given indexed property of an object
*
*
*
*/
Handle<Value> NodeProxy::GetIndexedProperty(uint32_t index,
const AccessorInfo &info) {
HandleScope scope;
return GetNamedProperty(Local<String>::Cast(
Integer::NewFromUnsigned(index)),
info);
}
/**
* Invoked for setting the given indexed property of an object
*
*
*
*/
Handle<Value> NodeProxy::SetIndexedProperty(uint32_t index,
Local<Value> value,
const AccessorInfo &info) {
HandleScope scope;
return SetNamedProperty(Local<String>::Cast(
Integer::NewFromUnsigned(index)),
value,
info);
}
/**
* Invoked for determining if an object has a given indexed property
*
*
*
*/
Handle<Boolean> NodeProxy::QueryIndexedProperty(uint32_t index,
const AccessorInfo &info) {
HandleScope scope;
return QueryNamedProperty(
Local<String>::Cast(Integer::NewFromUnsigned(index)),
info);
}
Handle<Integer> NodeProxy::QueryIndexedPropertyInteger(uint32_t index,
const AccessorInfo &info) {
HandleScope scope;
return QueryNamedPropertyInteger(
Local<String>::Cast(Integer::NewFromUnsigned(index)),
info);
}
/**
* Invoked for deleting a given indexed property
*
*
*
*/
Handle<Boolean> NodeProxy::DeleteIndexedProperty(uint32_t index,
const AccessorInfo &info) {
HandleScope scope;
return DeleteNamedProperty(
Local<String>::Cast(Integer::NewFromUnsigned(index)),
info);
}
/**
* Initialize the NodeProxy Strings and functions
*
*
*
*/
void NodeProxy::Init(Handle<Object> target) {
HandleScope scope;
// required properties
NodeProxy::getOwnPropertyDescriptor =
PROXY_NODE_PSYMBOL("getOwnPropertyDescriptor");
NodeProxy::getPropertyDescriptor =
PROXY_NODE_PSYMBOL("getPropertyDescriptor");
NodeProxy::getOwnPropertyNames =
PROXY_NODE_PSYMBOL("getOwnPropertyNames");
NodeProxy::getPropertyNames =
PROXY_NODE_PSYMBOL("getPropertyNames");
NodeProxy::defineProperty =
PROXY_NODE_PSYMBOL("defineProperty");
NodeProxy::delete_ =
PROXY_NODE_PSYMBOL("delete");
NodeProxy::fix =
PROXY_NODE_PSYMBOL("fix");
// optional properties
NodeProxy::has =
PROXY_NODE_PSYMBOL("has");
NodeProxy::hasOwn =
PROXY_NODE_PSYMBOL("hasOwn");
NodeProxy::get =
PROXY_NODE_PSYMBOL("get");
NodeProxy::set =
PROXY_NODE_PSYMBOL("set");
NodeProxy::enumerate =
PROXY_NODE_PSYMBOL("enumerate");
NodeProxy::keys =
PROXY_NODE_PSYMBOL("keys");
// createFunction
NodeProxy::callTrap =
PROXY_NODE_PSYMBOL("callTrap");
NodeProxy::constructorTrap =
PROXY_NODE_PSYMBOL("constructorTrap");
// properties of PropertyDescriptor
NodeProxy::value =
PROXY_NODE_PSYMBOL("value");
NodeProxy::writable =
PROXY_NODE_PSYMBOL("writable");
NodeProxy::enumerable =
PROXY_NODE_PSYMBOL("enumerable");
NodeProxy::configurable =
PROXY_NODE_PSYMBOL("configurable");
// misc
NodeProxy::name =
PROXY_NODE_PSYMBOL("name");
// hidden property names
NodeProxy::trapping =
PROXY_NODE_PSYMBOL("trapping");
NodeProxy::sealed =
PROXY_NODE_PSYMBOL("sealed");
NodeProxy::frozen =
PROXY_NODE_PSYMBOL("frozen");
NodeProxy::extensible =
PROXY_NODE_PSYMBOL("extensible");
// fixable calls
NodeProxy::seal =
PROXY_NODE_PSYMBOL("seal");
NodeProxy::freeze =
PROXY_NODE_PSYMBOL("freeze");
NodeProxy::preventExtensions =
PROXY_NODE_PSYMBOL("preventExtensions");
// fixed checks
NodeProxy::isSealed =
PROXY_NODE_PSYMBOL("isSealed");
NodeProxy::isFrozen =
PROXY_NODE_PSYMBOL("isFrozen");
NodeProxy::isExtensible =
PROXY_NODE_PSYMBOL("isExtensible");
NodeProxy::isTrapping =
PROXY_NODE_PSYMBOL("isTrapping");
NodeProxy::isProxy =
PROXY_NODE_PSYMBOL("isProxy");
// namespacing for hidden properties of visible objects
NodeProxy::hidden =
PROXY_NODE_PSYMBOL("NodeProxy::hidden::");
NodeProxy::hiddenPrivate =
PROXY_NODE_PSYMBOL("NodeProxy::hiddenPrivate::");
// function creation
// main functions
Local<String> createName =
String::New("create");
Local<Function> create =
FunctionTemplate::New(Create)->GetFunction();
create->SetName(createName);
target->Set(createName, create, DontDelete);
Local<String> createFunctionName =
String::New("createFunction");
Local<Function> createFunction =
FunctionTemplate::New(CreateFunction)->GetFunction();
create->SetName(createFunctionName);
target->Set(createFunctionName, createFunction, DontDelete);
// freeze function assignment
Local<Function> freeze =
FunctionTemplate::New(Freeze)->GetFunction();
freeze->SetName(NodeProxy::freeze);
target->Set(NodeProxy::freeze, freeze, DontDelete);
Local<Function> seal =
FunctionTemplate::New(Freeze)->GetFunction();
seal->SetName(NodeProxy::seal);
target->Set(NodeProxy::seal, seal, DontDelete);
Local<Function> prevent =
FunctionTemplate::New(Freeze)->GetFunction();
prevent->SetName(NodeProxy::preventExtensions);
target->Set(NodeProxy::preventExtensions, prevent, DontDelete);
// check function assignment
Local<Function> isfrozen =
FunctionTemplate::New(IsLocked)->GetFunction();
isfrozen->SetName(NodeProxy::isFrozen);
target->Set(NodeProxy::isFrozen, isfrozen, DontDelete);
Local<Function> issealed =
FunctionTemplate::New(IsLocked)->GetFunction();
issealed->SetName(NodeProxy::isSealed);
target->Set(NodeProxy::isSealed, issealed, DontDelete);
Local<Function> isextensible =
FunctionTemplate::New(IsLocked)->GetFunction();
isextensible->SetName(NodeProxy::isExtensible);
target->Set(NodeProxy::isExtensible, isextensible, DontDelete);
// part of harmony proxies
Local<Function> istrapping =
FunctionTemplate::New(IsLocked)->GetFunction();
istrapping->SetName(NodeProxy::isTrapping);
target->Set(NodeProxy::isTrapping, istrapping, DontDelete);
// ECMAScript 5
Local<String> getOwnPropertyDescriptorName =
String::New("getOwnPropertyDescriptor");
Local<Function> getOwnPropertyDescriptor =
FunctionTemplate::New(GetOwnPropertyDescriptor)->GetFunction();
getOwnPropertyDescriptor->SetName(getOwnPropertyDescriptorName);
target->Set(getOwnPropertyDescriptorName,
getOwnPropertyDescriptor,
DontDelete);
Local<String> definePropertyName =
String::New("defineProperty");
Local<Function> defineProperty =
FunctionTemplate::New(DefineProperty)->GetFunction();
defineProperty->SetName(definePropertyName);
target->Set(definePropertyName, defineProperty, DontDelete);
Local<String> definePropertiesName =
String::New("defineProperties");
Local<Function> defineProperties =
FunctionTemplate::New(DefineProperties)->GetFunction();
defineProperties->SetName(definePropertiesName);
target->Set(definePropertiesName, defineProperties, DontDelete);
// additional functions
Local<String> cloneName =
String::New("clone");
Local<Function> clone =
FunctionTemplate::New(Clone)->GetFunction();
clone->SetName(cloneName);
target->Set(cloneName, clone, DontDelete);
Local<String> hiddenName =
String::New("hidden");
Local<Function> hidden =
FunctionTemplate::New(Hidden)->GetFunction();
hidden->SetName(hiddenName);
target->Set(hiddenName, hidden, DontDelete);
Local<String> setPrototypeName =
String::New("setPrototype");
Local<Function> setPrototype =
FunctionTemplate::New(SetPrototype)->GetFunction();
setPrototype->SetName(setPrototypeName);
target->Set(setPrototypeName, setPrototype, DontDelete);
Local<Function> isProxy_ =
FunctionTemplate::New(IsProxy)->GetFunction();
hidden->SetName(NodeProxy::isProxy);
target->Set(NodeProxy::isProxy, isProxy_, DontDelete);
Local<ObjectTemplate> temp = ObjectTemplate::New();
temp->SetInternalFieldCount(1);
// named property handlers
temp->SetNamedPropertyHandler(GetNamedProperty,
SetNamedProperty,
// different versions of V8 require different return types
// 0.1.97 is where the switch occurred in v8,
// but NODE_*_VERSION wasn't added until 0.1.100
#ifndef NODE_MAJOR_VERSION
QueryNamedProperty,
#elif PROXY_NODE_VERSION_AT_LEAST(0, 1, 98)
QueryNamedPropertyInteger,
#else
QueryNamedProperty,
#endif
DeleteNamedProperty,
EnumerateNamedProperties);
// indexed property handlers
temp->SetIndexedPropertyHandler(GetIndexedProperty,
SetIndexedProperty,
// different versions of V8 require different return types
// 0.2.0 is where the switch occurred
#ifndef NODE_MAJOR_VERSION
QueryIndexedProperty,
#elif PROXY_NODE_VERSION_AT_LEAST(0, 2, 0)
QueryIndexedPropertyInteger,
#else
QueryIndexedProperty,
#endif
DeleteIndexedProperty);
ObjectCreator = Persistent<ObjectTemplate>::New(temp) ;
Local<ObjectTemplate> instance = ObjectTemplate::New();
instance->SetCallAsFunctionHandler(New, Undefined());
instance->SetInternalFieldCount(1);
instance->SetNamedPropertyHandler(GetNamedProperty,
SetNamedProperty,
// different versions of V8 require different return types
// 0.1.97 is where the switch occurred in v8,
// but NODE_*_VERSION wasn't added until 0.1.100
#ifndef NODE_MAJOR_VERSION
QueryNamedProperty,
#elif PROXY_NODE_VERSION_AT_LEAST(0, 1, 98)
QueryNamedPropertyInteger,
#else
QueryNamedProperty,
#endif
DeleteNamedProperty,
EnumerateNamedProperties);
instance->SetIndexedPropertyHandler(GetIndexedProperty,
SetIndexedProperty,
// different versions of V8 require different return types
// 0.2.0 is where the switch occurred
#ifndef NODE_MAJOR_VERSION
QueryIndexedProperty,
#elif PROXY_NODE_VERSION_AT_LEAST(0, 2, 0)
QueryIndexedPropertyInteger,
#else
QueryIndexedProperty,
#endif
DeleteIndexedProperty);
FunctionCreator = Persistent<ObjectTemplate>::New(instance) ;
scope.Close(Undefined());
}
} // end namespace
/**
* Required by Node for initializing the module
*
*/
extern "C" void init(v8::Handle<v8::Object> target) {
v8::NodeProxy::Init(target);
}
/* Required by windows Node version to detect the entry method */
NODE_MODULE(nodeproxy, init);
\ No newline at end of file
/**
*
*
*
* @author Sam Shull <http://samshull.blogspot.com/>
* @version 0.1
*
* @copyright Copyright (c) 2009 Sam Shull <http://samshull.blogspot.com/>
* @license <http://www.opensource.org/licenses/mit-license.html>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*
* CHANGES:
*/
#ifndef NODE_PROXY_H // NOLINT
#define NODE_PROXY_H
#include <v8.h>
#include <node.h>
#include <node_version.h>
#define THREXCW(str) ThrowException(Exception::Error(str))
#define THREXC(str) THREXCW(String::New(str))
#define THR_TYPE_ERROR(str) \
ThrowException(Exception::TypeError(String::New(str)))
#define PROXY_NODE_PSYMBOL(s) \
Persistent<String>::New(String::NewSymbol(s))
// had to redefine NODE_VERSION_AT_LEAST here because of missing parenthesis
#define PROXY_NODE_VERSION_AT_LEAST(major, minor, patch) \
(((major) < NODE_MAJOR_VERSION) \
|| ((major) == NODE_MAJOR_VERSION && (minor) < NODE_MINOR_VERSION) \
|| ((major) == NODE_MAJOR_VERSION && (minor) == NODE_MINOR_VERSION && \
(patch) <= NODE_PATCH_VERSION))
// using namespace v8;
namespace v8 {
class NodeProxy {
public:
// fundamental traps
static Persistent<String> getOwnPropertyDescriptor;
static Persistent<String> getPropertyDescriptor;
static Persistent<String> getOwnPropertyNames;
static Persistent<String> getPropertyNames;
static Persistent<String> defineProperty;
static Persistent<String> delete_;
static Persistent<String> fix;
// derived traps
static Persistent<String> has;
static Persistent<String> hasOwn;
static Persistent<String> get;
static Persistent<String> set;
static Persistent<String> enumerate;
static Persistent<String> keys;
// string identifiers
static Persistent<String> callTrap;
static Persistent<String> constructorTrap;
static Persistent<String> value;
static Persistent<String> writable;
static Persistent<String> enumerable;
static Persistent<String> configurable;
static Persistent<String> name;
static Persistent<String> trapping;
static Persistent<String> sealed;
static Persistent<String> frozen;
static Persistent<String> extensible;
static Persistent<String> seal;
static Persistent<String> freeze;
static Persistent<String> preventExtensions;
static Persistent<String> isTrapping;
static Persistent<String> isSealed;
static Persistent<String> isFrozen;
static Persistent<String> isExtensible;
static Persistent<String> isProxy;
static Persistent<String> hidden;
static Persistent<String> hiddenPrivate;
static Persistent<ObjectTemplate> ObjectCreator;
static Persistent<ObjectTemplate> FunctionCreator;
static void Init(Handle<Object> target);
protected:
NodeProxy();
~NodeProxy();
static Handle<Integer>
GetPropertyAttributeFromPropertyDescriptor(Local<Object> pd);
static Local<Value> CorrectPropertyDescriptor(Local<Object> pd);
static Handle<Value> ValidateProxyHandler(Local<Object> handler);
static Handle<Value> Clone(const Arguments& args);
static Handle<Value> Hidden(const Arguments& args);
static Handle<Value> Create(const Arguments& args);
static Handle<Value> SetPrototype(const Arguments& args);
static Handle<Value> CreateFunction(const Arguments& args);
static Handle<Value> Freeze(const Arguments& args);
static Handle<Value> IsLocked(const Arguments& args);
static Handle<Value> IsProxy(const Arguments& args);
static Handle<Value> GetOwnPropertyDescriptor(const Arguments& args);
static Handle<Value> DefineProperty(const Arguments& args);
static Handle<Value> DefineProperties(const Arguments& args);
static Handle<Value> New(const Arguments& args);
static Handle<Value>
GetNamedProperty(Local<String> name, const AccessorInfo &info);
static Handle<Value>
SetNamedProperty(Local<String> name,
Local<Value> value,
const AccessorInfo &info);
static Handle<Boolean>
QueryNamedProperty(Local<String> name,
const AccessorInfo &info);
static Handle<Integer>
QueryNamedPropertyInteger(Local<String> name,
const AccessorInfo &info);
static Handle<Boolean>
DeleteNamedProperty(Local<String> name,
const AccessorInfo &info);
static Handle<Array>
EnumerateNamedProperties(const AccessorInfo &info);
static Handle<Value>
GetIndexedProperty(uint32_t index,
const AccessorInfo &info);
static Handle<Value>
SetIndexedProperty(uint32_t index,
Local<Value> value,
const AccessorInfo &info);
static Handle<Boolean>
QueryIndexedProperty(uint32_t index,
const AccessorInfo &info);
static Handle<Integer>
QueryIndexedPropertyInteger(uint32_t index,
const AccessorInfo &info);
static Handle<Boolean>
DeleteIndexedProperty(uint32_t index,
const AccessorInfo &info);
static Local<Value> CallPropertyDescriptorGet(Local<Value> descriptor,
Handle<Object> context,
Local<Value> args[1]);
static Local<Value> CallPropertyDescriptorSet(Local<Value> descriptor,
Handle<Object> context,
Local<Value> name,
Local<Value> value);
};
}
extern "C" void init(v8::Handle<v8::Object> target);
#endif // NODE_CLASSTEMPLATE_H // NOLINT
/*jslint forin: true, onevar: true, immed: true */
(function () {
process.env['NODE_PATH'] += ':' + __dirname + "/../lib";
var sys = require('util'),
assert = require('assert'),
undef,
called, p,
Proxy = require("../lib/node-proxy.js"),
createProxyFunction = function(handlers, callTrap, constructorTrap) {
called = "createProxyFunction";
return Proxy.createFunction({
"delete": function (name){
called = "delete";
var r = true;
if (name in handlers) {
r = (delete handlers[name]);
}
return r;
},
enumerate: function (){
called = "enumerate";
return Object.keys(handlers);
},
fix: function (){
called = "fix";
return handlers;
},
has: function (name){
called = "has";
return (name in handlers);
},
get: function (receiver, name){
called = "get";
if (!(name in handlers)) {
return undef;
}
return "get" in handlers[name] && typeof(handlers[name].get) == "function" ?
handlers[name].get.call(receiver) :
(handlers[name].value || undef);
},
set: function (receiver, name, val){
called = "set";
if (!(name in handlers)) {
defineProperty.call(this, name, {
configurable:true,
writable:true,
enumerable:true,
value:val,
get:function (){return val;},
set:function (v){val=v;}
});
called = "set";
return true;
}
if (!handlers[name].configurable) {
return false;
}
if ("set" in handlers[name]) {
handlers[name].set.call(receiver, val);
}
handlers[name].value = val;
return true;
}
}, callTrap);
},
createProxy = function (handlers) {
called = "createProxy";
function defineProperty(name, pd){
called = "defineProperty";
if (name in handlers && !handlers[name].configurable) {
return null;
}
handlers[name] = pd;
return null;
}
return Proxy.create({
getOwnPropertyDescriptor:function (name){
called = "getOwnPropertyDescriptor";
return handlers.hasOwnProperty(name) ? handlers[name] : undef;
},
getPropertyDescriptor:function (name){
called = "getPropertyDescriptor";
return name in handlers ? handlers[name] : undef;
},
defineProperty: defineProperty,
getOwnPropertyNames:function (){
called = "getOwnPropertyNames";
return Object.getOwnPropertyNames(handlers);
},
"delete":function (name){
called = "delete";
var r = true;
if (name in handlers) {
r = (delete handlers[name]);
}
return r;
},
enumerate:function (){
called = "enumerate";
return Object.keys(handlers);
},
fix:function (){
called = "fix";
return handlers;
},
has:function (name){
called = "has";
//sys.puts("has called on: "+name);
//sys.puts(name in handlers)
return (name in handlers);
},
hasOwn:function (name){
called = "hasOwn";
return handlers.hasOwnProperty(name);
},
get:function (receiver, name){
called = "get";
//sys.puts(arguments.callee.caller)
if (!(name in handlers)) {
return undef;
}
return "get" in handlers[name] && typeof(handlers[name].get) == "function" ?
handlers[name].get.call(receiver) :
(handlers[name].value || undef);
},
set:function (receiver, name, val){
called = "set";
if (!(name in handlers)) {
defineProperty.call(this, name, {
configurable:true,
writable:true,
enumerable:true,
value:val,
get:function (){return val;},
set:function (v){val=v;}
});
called = "set";
return true;
}
if (!handlers[name].configurable) {
return false;
}
if ("set" in handlers[name]) {
handlers[name].set.call(receiver, val);
}
handlers[name].value = val;
return true;
}
});
},
proxyTest,
clone,
cloneProxy,
proxyTrapTest,
proxyTrapTestInstance,
firstValue = "firstProp",
names,
count,
regex = /regex/,
base = {},
handlers = {
first: {
get:function (){return firstValue;},
set:function (val){firstValue = val;}
}
},
funcHandlers = {
test: {
get:function(){return "working";}
}
},
callTrap = function() {
called = "callTrap";
return this;
},
tests = {
"Base Proxy methods": {
"Proxy.create": function() {
proxyTest = createProxy(handlers);
assert.equal(called, "createProxy", "createProxy was not the last method called");
assert.ok(typeof proxyTest == "object");
},
"Proxy.createFunction": function() {
proxyTrapTest = createProxyFunction(funcHandlers, callTrap);
assert.equal(called, "createProxyFunction", "createProxyFunction was not the last method called");
//assert.ok(typeof proxyTrapTest == "function", "proxyTrapTest is not a function");
},
"Proxy.createFunction with optional constructor trap": function() {
//proxyTrapTest = createProxyFunction(funcHandlers, callTrap, constructTrap);
},
"Proxy.isTrapping on proxy object": function() {
assert.ok(Proxy.isTrapping(proxyTest), "proxyTest is not trapping");
},
},
"Testing proxy function instance": {
"proxy function is callable": function() {
proxyTrapTest();
assert.equal(called, "callTrap", "callTrap was not the last function called");
},
"proxy function has accessible properties": function() {
assert.ok("test" in proxyTrapTest, "'test' not in proxyTrapTest");
},
"proxy function get properties": function() {
assert.equal(proxyTrapTest.test, "working", "'test' not in proxyTrapTest");
},
"proxy function as constructor": function() {
proxyTrapTestInstance = new proxyTrapTest();
assert.equal(called, "callTrap", "callTrap was not last call");
},
"proxy function instance property handling": function() {
assert.ok("test" in proxyTrapTestInstance, "no 'test' in proxyTrapTestInstance");
assert.equal(called, "has", "did not call has");
}
},
"Testing proxy object instance": {
"has property 'first'": function() {
assert.ok("first" in proxyTest, "proxyTest does not have a property named 'first'");
//assert.equal(called, "has", "the has method was not the last method called");
},
"get property 'first'": function(){
assert.equal(proxyTest.first, firstValue);
assert.equal(called, "get", "the get method was not the last method called");
},
"set property 'first' to new value": function() {
proxyTest.first = "changed";
assert.equal(called, "set", "the set method was not the last method called");
assert.equal(proxyTest.first, firstValue, "proxyTest.first != firstValue");
},
"set new property 'second'": function() {
proxyTest.second = "secondProp";
assert.equal(called, "set", "the set method was not the last method called");
},
"has new property 'second'": function() {
assert.ok("second" in proxyTest, "proxyTest does not have the property 'second'");
},
"get newly set property 'second'": function() {
assert.equal(proxyTest.second, "secondProp", "proxyTest.second is not equal to 'secondProp'");
},
"iterate property names": function() {
count = 0;
for (p in proxyTest){++count;}
assert.equal(count, 2, "there are not 2 properties on proxyTest");
},
"Object.getOwnPropertyNames on proxy object": function() {
names = Object.getOwnPropertyNames(proxyTest);
assert.equal(called, "enumerate", "Object.getOwnPropertyNames did not invoke enumerate");
},
"Object.getOwnPropertyNames returned an Array": function() {
assert.ok(names instanceof Array);
},
"Object.getOwnPropertyNames return value has the correct length": function() {
assert.equal(names.length, 2, "2 property names were not returned");
},
"Object.getOwnPropertyNames has the correct values": function() {
assert.equal(names[0], "first", "The first property name is not 'first'");
assert.equal(names[1], "second", "The second property name is not 'second'");
},
"Object.keys on proxy object": function() {
names = Object.keys(proxyTest);
assert.equal(called, "enumerate", "Object.keys did not invoke 'enumerate'");
},
"Object.keys returned an Array": function() {
assert.ok(names instanceof Array);
},
"Object.keys return value has the correct length": function() {
assert.equal(names.length, 2, "2 property names were not returned");
},
"Object.keys has the correct values": function() {
assert.equal(names[0], "first", "The first property name is not 'first'");
assert.equal(names[1], "second", "The second property name is not 'second'");
},
"delete 'second'": function() {
assert.ok((delete proxyTest.second), "Delete the property 'second' from the proxy");
assert.equal(called, "delete", "the delete method was not the last method called");
},
"proxy instance no longer has property 'second'": function() {
assert.ok(!Object.prototype.hasOwnProperty.call(proxyTest, "second"), "proxyTest still hasOwnProperty the property 'second'");
assert.ok(!("second" in proxyTest), "proxyTest still has the property 'second'");
}
},
"Fundamental traps": {
"PropertyDescriptor context for get should be the receiver": function() {
var tester = 1,
proxy = Proxy.create({
getPropertyDescriptor: function(name) {
if (name === "tester") {
return {
get: function(name) {
assert.ok(this === proxy, "PropertyDescriptor context is not the receiver");
return tester;
}
};
}
}
});
assert.ok(proxy.tester === tester, "PropertyDescriptor failed to properly set the test variable");
},
"PropertyDescriptor context for set should be the receiver": function() {
var tester = 1,
proxy = Proxy.create({
getPropertyDescriptor: function(name) {
if (name === "tester") {
return {
set: function(name, value) {
assert.ok(this === proxy, "PropertyDescriptor context is not the receiver");
tester = value;
}
};
}
}
});
proxy.tester = 2;
},
// TODO: write more PropertyDescriptor tests:
// value, configurable, enumerable, writable, ...
"PropertyDescriptor should get value if get method is not supplied": function() {
var pd = { value: 2 },
proxy = Proxy.create({
getPropertyDescriptor: function(name) {
return pd;
}
});
assert.ok(proxy.tester === pd.value, "PropertyDescriptor did not return appropriate value");
},
"PropertyDescriptor should set value if set method is not supplied": function() {
var pd = {
value: 2
},
proxy = Proxy.create({
getPropertyDescriptor: function(name) {
return pd;
}
});
proxy.tester = 3;
assert.ok(proxy.tester === pd.value, "PropertyDescriptor value was not changed");
}
// TODO: write more fundamental trap tests:
// getOwnPropertyDescriptor, getPropertyNames, getOwnPropertyNames, ...
},
"Derived traps": {
"proxy context should be the PropertyHandler for derived trap 'get'": function() {
var tester = 5,
handler = {
get: function(receiver, name) {
assert.ok(this === handler, "PropertyHandler is not the appropriate context");
return tester;
}
},
proxy = Proxy.create(handler);
assert.ok(proxy.tester === tester, "PropertyHandler get method was not called properly");
},
"proxy context should be the PropertyHandler for derived trap 'has'": function() {
var tester = 5,
handler = {
has: function(name) {
assert.ok(this === handler, "PropertyHandler is not the appropriate context");
return (name === "tester2");
}
},
proxy = Proxy.create(handler);
assert.ok("tester2" in proxy, "PropertyHanlder is not responding correctly to has");
},
"proxy context should be the PropertyHandler for derived trap 'enumerate'": function() {
var handler = {
enumerate: function() {
assert.ok(this === handler, "PropertyHandler is not the appropriate context");
return ["tester"];
}
},
proxy = Proxy.create(handler), p;
for (p in proxy) {
assert.ok(p === "tester", "PropertyHandler responded with incorrect enumerate value: '" + p + "'");
}
},
"proxy context should be the PropertyHandler for derived trap 'set'": function() {
var tester = 5,
called = false;
handler = {
set: function(receiver, name, value) {
called = true;
assert.ok(this === handler, "PropertyHandler is not the appropriate context");
tester = value;
}
},
proxy = Proxy.create(handler);
proxy.tester = 6;
assert.ok(called, "PropertyHandler set method was not called properly");
}
// TODO: write more derived trap tests:
// keys, hasOwn, ...
},
"ECMAScript 5 implementation methods": {
"Proxy.defineProperty on proxy object": function() {
Proxy.defineProperty(proxyTest, 'third', {
get: function() {
return "third";
}
});
assert.equal(called, "defineProperty", "defineProperty was not called: "+called);
},
"proxy has newly defined property": function() {
assert.ok("third" in proxyTest);
},
"proxy's newly defined property have correct return value": function() {
assert.equal(proxyTest.third, "third", "proxyTest.third != 'third'");
},
"proxy's newly defined property are reflected in underlying handlers": function() {
assert.ok("third" in handlers, "'third' is not in handlers");
},
"Proxy.defineProperties on proxy object": function() {
Proxy.defineProperties(proxyTest, {
fourth: {
get: function() {
return "fourth";
}
},
fifth: {
get: function() {
return "fifth";
}
}
});
assert.equal(called, "defineProperty", "defineProperty was not called: "+called);
},
"proxy has newly defined properties": function() {
assert.ok("fourth" in proxyTest);
assert.ok("fifth" in proxyTest);
},
"proxy's newly defined properties have correct return value": function() {
assert.equal(proxyTest.fourth, "fourth", "proxyTest.fourth != 'fourth'");
assert.equal(proxyTest.fifth, "fifth", "proxyTest.fifth != 'fifth'");
},
"proxy's newly defined properties are reflected in underlying handlers": function() {
assert.ok("fourth" in handlers, "'fourth' is not in handlers");
assert.ok("fifth" in handlers, "'fifth' is not in handlers");
}
},
"Additional method tests": {
"Proxy.isProxy proxy object": function() {
assert.ok(Proxy.isProxy(proxyTest), "proxyTest is not a Proxy");
},
"Proxy.isProxy non-proxy object": function() {
assert.ok(!Proxy.isProxy({}), "object is a Proxy");
},
"Proxy.setPrototype of proxy object": function() {
assert.ok(Proxy.setPrototype(proxyTest, RegExp.prototype), "unable to set prototype of RegExp on proxyTest");
},
"proxy object is instanceof RegExp": function() {
assert.ok(proxyTest instanceof RegExp, "proxyTest is not an instanceof RegExp");
},
"Proxy.setPrototype of non-proxy object": function() {
assert.ok(Proxy.setPrototype(base, Number.prototype), "unable to set prototype of Number on base");
},
"non-proxy object is instanceof RegExp": function() {
assert.ok(base instanceof Number, "base is not an instanceof Number");
},
"Proxy.clone proxy object": function() {
cloneProxy = Proxy.clone(proxyTest);
assert.ok(typeof cloneProxy == "object", "cloneProxy does not result in an object");
},
"cloned proxy maintains prototype of base proxy": function() {
assert.ok(cloneProxy instanceof RegExp, "cloneProxy is not an instanceof RegExp");
},
"Proxy.clone non-proxy object": function() {
clone = Proxy.clone(base);
assert.ok(clone !== base, "clone is identical to base object");
},
"cloned object maintains prototype of base": function() {
assert.ok(clone instanceof Number, "clone is not an instance of a Number");
},
"set hidden property on cloned object": function() {
assert.ok(Proxy.hidden(clone, "hiddenTest", regex), "unable to set hidden property 'hiddenTest' on clone");
},
"get hidden property on cloned object": function() {
assert.ok(Proxy.hidden(clone, "hiddenTest") === regex, "unable to retrieve hidden property 'hiddenTest' on clone");
},
}
}, section, sectionName, test, testIndex, sectionIndex = 0, totalTests = 0, passedTests = 0, failedTests = 0;
sys.puts("Running tests...\n");
for (sectionName in tests) {
++sectionIndex;
sys.puts("\n" + sectionIndex + ": "+ sectionName);
testIndex = 0;
section = tests[sectionName];
for (test in section) {
++totalTests;
++testIndex;
sys.print(" " + test + ": ");
try{
section[test]();
++passedTests;
sys.puts("PASS");
} catch(e) {
++failedTests;
sys.puts("FAIL: "+ e.message);
}
}
}
sys.puts("\nPassed " + passedTests + " of " + totalTests + " tests");
sys.puts("\nFailed " + failedTests + " of " + totalTests + " tests");
sys.puts("");
process.exit(0);
}());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment