├── lib └── node-proxy.js ├── examples └── autoload-namespace │ ├── org │ └── w3c │ │ └── DOM │ │ ├── Document │ │ └── String │ │ │ └── Test.js │ │ └── Document.js │ ├── autoload-test.js │ └── autoload-namespace.js ├── .gitignore ├── binding.gyp ├── .travis.yml ├── LICENSE.txt ├── package.json ├── README.md ├── src ├── node-proxy.h └── node-proxy.cc └── test ├── test.js └── mocha-test-notworking.js /lib/node-proxy.js: -------------------------------------------------------------------------------- 1 | module.exports = exports = require('bindings')('nodeproxy.node'); 2 | -------------------------------------------------------------------------------- /examples/autoload-namespace/org/w3c/DOM/Document/String/Test.js: -------------------------------------------------------------------------------- 1 | exports.success = "success"; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.node 3 | src/build 4 | src/.lock-wscript 5 | build/ 6 | node_modules/ 7 | -------------------------------------------------------------------------------- /examples/autoload-namespace/org/w3c/DOM/Document.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | exports.string = "String"; 4 | exports.object = {}; 5 | exports.regexp = /regex/; 6 | exports.create = function(sys) { 7 | sys.puts("A proxified function was called inside of Document.js") 8 | }; -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'nodeproxy', 5 | 'sources': [ 6 | 'src/node-proxy.cc', 7 | ], 8 | "include_dirs" : [ 9 | "", 18 | "richardms ", 19 | "Andreas Botsikas ", 20 | "Kenneth Bentley ", 21 | "Gabor Mezo " 22 | ], 23 | "licenses": [ 24 | { 25 | "type": "MIT", 26 | "url": "http://www.opensource.org/licenses/mit-license.html" 27 | } 28 | ], 29 | "bugs": { 30 | "url": "http://github.com/samshull/node-proxy/issues" 31 | }, 32 | "implements": [ 33 | "http://wiki.ecmascript.org/doku.php?id=harmony:proxies" 34 | ], 35 | "engines": { 36 | "node": ">= 0.10.36", 37 | "npm": ">= 1.1.17" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "http://github.com/samshull/node-proxy" 42 | }, 43 | "main": "./lib/node-proxy.js", 44 | "scripts": { 45 | "install": "node-gyp configure build", 46 | "test": "node test/test.js" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-proxy is an implementation of Harmony Proxies http://wiki.ecmascript.org/doku.php?id=harmony:proxies 2 | that allows the developer to create "catch-all" property handlers for an object or a function in node.js. 3 | 4 | Author: Sam Shull 5 | Repository: http://github.com/samshull/node-proxy 6 | Issues: http://github.com/samshull/node-proxy/issues 7 | 8 | Methods: 9 | 10 | Object create(ProxyHandler handler [, Object proto ] ) throws Error, TypeError 11 | 12 | Function createFunction(ProxyHandler handler, Function callTrap [, Function constructTrap ] ) throws Error, TypeError 13 | 14 | Boolean isTrapping(Object obj) throws Error 15 | 16 | 17 | Additional Methods (for ECMAScript 5 compatibliity): @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf 18 | 19 | Boolean freeze(Object obj) throws Error, TypeError 20 | 21 | Boolean seal(Object obj) throws Error, TypeError 22 | 23 | Boolean preventExtensions(Object obj) throws Error, TypeError 24 | 25 | Boolean isFrozen(Object obj) throws Error, TypeError 26 | 27 | Boolean isSealed(Object obj) throws Error, TypeError 28 | 29 | Boolean isExtensible(Object obj) throws Error, TypeError 30 | 31 | PropertyDescriptor getOwnPropertyDescriptor(Object obj, String name) throws Error, TypeError 32 | 33 | Boolean defineProperty(Object obj, String name, PropertyDescriptor pd) throws Error, TypeError 34 | 35 | Boolean defineProperties(Object obj, Object descriptors) throws Error, TypeError 36 | 37 | 38 | More methods: 39 | 40 | Object hidden(Object obj, String name [, Object value ] ) throws Error 41 | - Set or retrieve a hidden property on an Object 42 | 43 | Object clone(Object obj) throws Error 44 | - Create a shallow copy of an Object 45 | 46 | Boolean isProxy(Object obj) 47 | - determine if an object was created by Proxy 48 | 49 | Boolean setPrototype(Object obj, Object obj) throws Error 50 | -set the prototype of a given object to the second given object 51 | -------------------------------------------------------------------------------- /examples/autoload-namespace/autoload-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an example of creating an object that autoloads 3 | * files and directories from a specified folder into an object 4 | * in order to emulate a namespace loader. 5 | * 6 | * The preferred naming convention is property names 7 | * of Objects begin lower case, while directory and file names 8 | * should begin upper case in order to avoid naming conflicts 9 | * 10 | */ 11 | 12 | var sys = require("sys"), 13 | assert = require("assert"), 14 | namespace = require(__dirname + "/autoload-namespace").namespace, 15 | org = namespace(__dirname + "/org"); 16 | 17 | sys.puts("test 1"); 18 | assert.equal(typeof(org), "object", "org is not an object"); 19 | sys.puts("test 2"); 20 | assert.equal(typeof(org.w3c), "object", "org.w3c is not an object"); 21 | sys.puts("test 3"); 22 | assert.equal(typeof(org.w3c.DOM), "object", "org.w3c.DOM is not an object"); 23 | sys.puts("test 4"); 24 | assert.equal(typeof(org.w3c.DOM.Document), "object", "org.w3c.DOM.Document is not an object"); 25 | sys.puts("test 5"); 26 | assert.equal(typeof(org.w3c.DOM.Document.string), "string", "org.w3c.DOM.Document.string is not a string"); 27 | sys.puts("test 6"); 28 | assert.equal(org.w3c.DOM.Document.string.toString(), "String", "org.w3c.DOM.Document.string is not equal to 'String'"); 29 | sys.puts("test 7"); 30 | assert.equal(typeof(org.w3c.DOM.Document.String.Test), "object", "org.w3c.DOM.Document.String.Test is not an object"); 31 | sys.puts("test 8"); 32 | assert.equal(typeof(org.w3c.DOM.Document.String.Test.success), "string", "org.w3c.DOM.Document.String.Test.success is not an string"); 33 | sys.puts("test 9"); 34 | assert.equal(org.w3c.DOM.Document.String.Test.success, "success", "org.w3c.DOM.Document.String.Test.success is not equal to 'success'"); 35 | sys.puts(typeof(org.w3c.DOM.Document.create)); 36 | sys.puts(typeof(function(){})); 37 | sys.puts(Object.prototype.toString.call(org.w3c.DOM.Document.create)); 38 | sys.puts(org.w3c.DOM.Document.create instanceof Function); 39 | org.w3c.DOM.Document.create(sys); -------------------------------------------------------------------------------- /examples/autoload-namespace/autoload-namespace.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This package exports one function: namespace 3 | * in order to create a system of namespace loading 4 | * from a given directory. Directories and files are 5 | * loaded into a proxy object in order to provide a 6 | * system for loading additional files and directories 7 | * the namespace. 8 | * 9 | * This system does not proxy scalar values on an object 10 | * because of the 11 | * 12 | */ 13 | var Proxy = require("node-proxy"), 14 | fs = require("fs"), 15 | sys = require("sys"), 16 | path = require("path"), 17 | undef, extensions; 18 | 19 | function isFunction(fn) { 20 | return !!fn && 21 | Object.prototype.toString.call(fn) == 22 | "[object Function]"; 23 | } 24 | 25 | function isScalar(fn) { 26 | switch (typeof fn) { 27 | case "string": return true; 28 | case "number": return true; 29 | } 30 | return false; 31 | } 32 | 33 | function inPath(file) { 34 | var i = 0, l = extensions.length; 35 | 36 | for (;i 6 | * @version 0.1 7 | * 8 | * @copyright Copyright (c) 2009 Sam Shull 9 | * @license 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | * THE SOFTWARE. 28 | * 29 | * 30 | * CHANGES: 31 | */ 32 | 33 | #ifndef NODE_PROXY_H // NOLINT 34 | #define NODE_PROXY_H 35 | 36 | 37 | #include 38 | #include 39 | #include 40 | #include "nan.h" 41 | 42 | using namespace v8; 43 | using namespace node; 44 | 45 | // had to redefine NODE_VERSION_AT_LEAST here because of missing parenthesis 46 | #define PROXY_NODE_VERSION_AT_LEAST(major, minor, patch) \ 47 | (((major) < NODE_MAJOR_VERSION) \ 48 | || ((major) == NODE_MAJOR_VERSION && (minor) < NODE_MINOR_VERSION) \ 49 | || ((major) == NODE_MAJOR_VERSION && (minor) == NODE_MINOR_VERSION && \ 50 | (patch) <= NODE_PATCH_VERSION)) 51 | 52 | class NodeProxy { 53 | public: 54 | static Nan::Persistent ObjectCreator; 55 | static Nan::Persistent FunctionCreator; 56 | static void Init(Handle target); 57 | 58 | protected: 59 | NodeProxy(); 60 | ~NodeProxy(); 61 | static Local 62 | GetPropertyAttributeFromPropertyDescriptor(Local pd); 63 | static Local CorrectPropertyDescriptor(Local pd); 64 | static NAN_METHOD(ValidateProxyHandler); 65 | static NAN_METHOD(Clone); 66 | static NAN_METHOD(Hidden); 67 | static NAN_METHOD(Create); 68 | static NAN_METHOD(SetPrototype); 69 | static NAN_METHOD(CreateFunction); 70 | static NAN_METHOD(Freeze); 71 | static NAN_METHOD(IsLocked); 72 | static NAN_METHOD(IsProxy); 73 | static NAN_METHOD(GetOwnPropertyDescriptor); 74 | static NAN_METHOD(DefineProperty); 75 | static NAN_METHOD(DefineProperties); 76 | static NAN_METHOD(New); 77 | static NAN_PROPERTY_GETTER(GetNamedProperty); 78 | static NAN_PROPERTY_SETTER(SetNamedProperty); 79 | static NAN_PROPERTY_QUERY(QueryNamedPropertyInteger); 80 | static NAN_PROPERTY_DELETER(DeleteNamedProperty); 81 | static NAN_PROPERTY_ENUMERATOR(EnumerateNamedProperties); 82 | static NAN_INDEX_GETTER(GetIndexedProperty); 83 | static NAN_INDEX_SETTER(SetIndexedProperty); 84 | static NAN_INDEX_QUERY(QueryIndexedPropertyInteger); 85 | static NAN_INDEX_DELETER(DeleteIndexedProperty); 86 | 87 | static NAN_INLINE Local CallPropertyDescriptorGet(Local descriptor, 88 | Handle context, 89 | Local args[1]); 90 | static NAN_INLINE Local CallPropertyDescriptorSet(Local descriptor, 91 | Handle context, 92 | Local name, 93 | Local value); 94 | }; 95 | 96 | 97 | extern "C" void init(v8::Handle target); 98 | 99 | #endif // NODE_CLASSTEMPLATE_H // NOLINT 100 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /*jslint forin: true, onevar: true, immed: true */ 2 | 3 | (function () { 4 | process.env['NODE_PATH'] += ':' + __dirname + "/../lib"; 5 | 6 | var assert = require('assert'), 7 | undef, 8 | called, p, 9 | Proxy = require("../lib/node-proxy.js"), 10 | createProxyFunction = function(handlers, callTrap, constructorTrap) { 11 | called = "createProxyFunction"; 12 | 13 | return Proxy.createFunction({ 14 | "delete": function (name){ 15 | called = "delete"; 16 | var r = true; 17 | if (name in handlers) { 18 | r = (delete handlers[name]); 19 | } 20 | return r; 21 | }, 22 | enumerate: function (){ 23 | called = "enumerate"; 24 | return Object.keys(handlers); 25 | }, 26 | fix: function (){ 27 | called = "fix"; 28 | return handlers; 29 | }, 30 | has: function (name){ 31 | called = "has"; 32 | return (name in handlers); 33 | }, 34 | get: function (receiver, name){ 35 | called = "get"; 36 | if (!(name in handlers)) { 37 | return undef; 38 | } 39 | return "get" in handlers[name] && typeof(handlers[name].get) == "function" ? 40 | handlers[name].get.call(receiver) : 41 | (handlers[name].value || undef); 42 | }, 43 | set: function (receiver, name, val){ 44 | called = "set"; 45 | if (!(name in handlers)) { 46 | defineProperty.call(this, name, { 47 | configurable:true, 48 | writable:true, 49 | enumerable:true, 50 | value:val, 51 | get:function (){return val;}, 52 | set:function (v){val=v;} 53 | }); 54 | called = "set"; 55 | return true; 56 | } 57 | if (!handlers[name].configurable) { 58 | return false; 59 | } 60 | if ("set" in handlers[name]) { 61 | handlers[name].set.call(receiver, val); 62 | } 63 | 64 | handlers[name].value = val; 65 | return true; 66 | } 67 | }, callTrap); 68 | }, 69 | createProxy = function (handlers) { 70 | called = "createProxy"; 71 | 72 | function defineProperty(name, pd){ 73 | called = "defineProperty"; 74 | if (name in handlers && !handlers[name].configurable) { 75 | return null; 76 | } 77 | handlers[name] = pd; 78 | return null; 79 | } 80 | 81 | return Proxy.create({ 82 | getOwnPropertyDescriptor:function (name){ 83 | called = "getOwnPropertyDescriptor"; 84 | return handlers.hasOwnProperty(name) ? handlers[name] : undef; 85 | }, 86 | getPropertyDescriptor:function (name){ 87 | called = "getPropertyDescriptor"; 88 | return name in handlers ? handlers[name] : undef; 89 | }, 90 | defineProperty: defineProperty, 91 | getOwnPropertyNames:function (){ 92 | called = "getOwnPropertyNames"; 93 | return Object.getOwnPropertyNames(handlers); 94 | }, 95 | "delete":function (name){ 96 | called = "delete"; 97 | var r = true; 98 | if (name in handlers) { 99 | r = (delete handlers[name]); 100 | } 101 | return r; 102 | }, 103 | enumerate:function (){ 104 | called = "enumerate"; 105 | return Object.keys(handlers); 106 | }, 107 | fix:function (){ 108 | called = "fix"; 109 | return handlers; 110 | }, 111 | has:function (name){ 112 | called = "has"; 113 | //console.log("has called on: "+name); 114 | //console.log(name in handlers) 115 | return (name in handlers); 116 | }, 117 | hasOwn:function (name){ 118 | called = "hasOwn"; 119 | return handlers.hasOwnProperty(name); 120 | }, 121 | get:function (receiver, name){ 122 | called = "get"; 123 | //console.log(arguments.callee.caller) 124 | if (!(name in handlers)) { 125 | return undef; 126 | } 127 | return "get" in handlers[name] && typeof(handlers[name].get) == "function" ? 128 | handlers[name].get.call(receiver) : 129 | (handlers[name].value || undef); 130 | }, 131 | set:function (receiver, name, val){ 132 | called = "set"; 133 | if (!(name in handlers)) { 134 | defineProperty.call(this, name, { 135 | configurable:true, 136 | writable:true, 137 | enumerable:true, 138 | value:val, 139 | get:function (){return val;}, 140 | set:function (v){val=v;} 141 | }); 142 | called = "set"; 143 | return true; 144 | } 145 | if (!handlers[name].configurable) { 146 | return false; 147 | } 148 | if ("set" in handlers[name]) { 149 | handlers[name].set.call(receiver, val); 150 | } 151 | 152 | handlers[name].value = val; 153 | return true; 154 | } 155 | }); 156 | }, 157 | proxyTest, 158 | clone, 159 | cloneProxy, 160 | proxyTrapTest, 161 | proxyTrapTestInstance, 162 | firstValue = "firstProp", 163 | names, 164 | count, 165 | regex = /regex/, 166 | base = {}, 167 | handlers = { 168 | first: { 169 | get:function (){return firstValue;}, 170 | set:function (val){firstValue = val;} 171 | } 172 | }, 173 | funcHandlers = { 174 | test: { 175 | get:function(){return "working";} 176 | } 177 | }, 178 | callTrap = function() { 179 | called = "callTrap"; 180 | return this; 181 | }, 182 | tests = { 183 | "Base Proxy methods": { 184 | "Proxy.create": function() { 185 | proxyTest = createProxy(handlers); 186 | assert.equal(called, "createProxy", "createProxy was not the last method called"); 187 | assert.ok(typeof proxyTest == "object"); 188 | }, 189 | 190 | "Proxy.createFunction": function() { 191 | proxyTrapTest = createProxyFunction(funcHandlers, callTrap); 192 | assert.equal(called, "createProxyFunction", "createProxyFunction was not the last method called"); 193 | //assert.ok(typeof proxyTrapTest == "function", "proxyTrapTest is not a function"); 194 | }, 195 | 196 | "Proxy.createFunction with optional constructor trap": function() { 197 | //proxyTrapTest = createProxyFunction(funcHandlers, callTrap, constructTrap); 198 | }, 199 | 200 | "Proxy.isTrapping on proxy object": function() { 201 | assert.ok(Proxy.isTrapping(proxyTest), "proxyTest is not trapping"); 202 | }, 203 | }, 204 | 205 | "Testing proxy function instance": { 206 | "proxy function is callable": function() { 207 | proxyTrapTest(); 208 | assert.equal(called, "callTrap", "callTrap was not the last function called"); 209 | }, 210 | 211 | "proxy function has accessible properties": function() { 212 | assert.ok("test" in proxyTrapTest, "'test' not in proxyTrapTest"); 213 | }, 214 | 215 | "proxy function get properties": function() { 216 | assert.equal(proxyTrapTest.test, "working", "'test' not in proxyTrapTest"); 217 | }, 218 | 219 | "proxy function as constructor": function() { 220 | proxyTrapTestInstance = new proxyTrapTest(); 221 | assert.equal(called, "callTrap", "callTrap was not last call"); 222 | }, 223 | 224 | "proxy function instance property handling": function() { 225 | assert.ok("test" in proxyTrapTestInstance, "no 'test' in proxyTrapTestInstance"); 226 | assert.equal(called, "has", "did not call has"); 227 | } 228 | }, 229 | 230 | "Testing proxy object instance": { 231 | "has property 'first'": function() { 232 | assert.ok("first" in proxyTest, "proxyTest does not have a property named 'first'"); 233 | //assert.equal(called, "has", "the has method was not the last method called"); 234 | }, 235 | 236 | "get property 'first'": function(){ 237 | assert.equal(proxyTest.first, firstValue); 238 | assert.equal(called, "get", "the get method was not the last method called"); 239 | }, 240 | 241 | "set property 'first' to new value": function() { 242 | proxyTest.first = "changed"; 243 | assert.equal(called, "set", "the set method was not the last method called"); 244 | assert.equal(proxyTest.first, firstValue, "proxyTest.first != firstValue"); 245 | }, 246 | 247 | "set new property 'second'": function() { 248 | proxyTest.second = "secondProp"; 249 | assert.equal(called, "set", "the set method was not the last method called"); 250 | }, 251 | 252 | "has new property 'second'": function() { 253 | assert.ok("second" in proxyTest, "proxyTest does not have the property 'second'"); 254 | }, 255 | 256 | "get newly set property 'second'": function() { 257 | assert.equal(proxyTest.second, "secondProp", "proxyTest.second is not equal to 'secondProp'"); 258 | }, 259 | 260 | "iterate property names": function() { 261 | count = 0; 262 | for (p in proxyTest){++count;} 263 | assert.equal(count, 2, "there are not 2 properties on proxyTest"); 264 | }, 265 | 266 | "Object.getOwnPropertyNames on proxy object": function() { 267 | names = Object.getOwnPropertyNames(proxyTest); 268 | assert.equal(called, "enumerate", "Object.getOwnPropertyNames did not invoke enumerate"); 269 | }, 270 | 271 | "Object.getOwnPropertyNames returned an Array": function() { 272 | assert.ok(names instanceof Array); 273 | }, 274 | 275 | "Object.getOwnPropertyNames return value has the correct length": function() { 276 | assert.equal(names.length, 2, "2 property names were not returned"); 277 | }, 278 | 279 | "Object.getOwnPropertyNames has the correct values": function() { 280 | assert.equal(names[0], "first", "The first property name is not 'first'"); 281 | assert.equal(names[1], "second", "The second property name is not 'second'"); 282 | }, 283 | 284 | "Object.keys on proxy object": function() { 285 | names = Object.keys(proxyTest); 286 | assert.equal(called, "enumerate", "Object.keys did not invoke 'enumerate'"); 287 | }, 288 | 289 | "Object.keys returned an Array": function() { 290 | assert.ok(names instanceof Array); 291 | }, 292 | 293 | "Object.keys return value has the correct length": function() { 294 | assert.equal(names.length, 2, "2 property names were not returned"); 295 | }, 296 | 297 | "Object.keys has the correct values": function() { 298 | assert.equal(names[0], "first", "The first property name is not 'first'"); 299 | assert.equal(names[1], "second", "The second property name is not 'second'"); 300 | }, 301 | 302 | "delete 'second'": function() { 303 | assert.ok((delete proxyTest.second), "Delete the property 'second' from the proxy"); 304 | assert.equal(called, "delete", "the delete method was not the last method called"); 305 | 306 | }, 307 | 308 | "proxy instance no longer has property 'second'": function() { 309 | assert.ok(!Object.prototype.hasOwnProperty.call(proxyTest, "second"), "proxyTest still hasOwnProperty the property 'second'"); 310 | assert.ok(!("second" in proxyTest), "proxyTest still has the property 'second'"); 311 | } 312 | }, 313 | 314 | "Fundamental traps": { 315 | "PropertyDescriptor context for get should be the receiver": function() { 316 | var tester = 1, 317 | proxy = Proxy.create({ 318 | getPropertyDescriptor: function(name) { 319 | if (name === "tester") { 320 | return { 321 | get: function(name) { 322 | assert.ok(this === proxy, "PropertyDescriptor context is not the receiver"); 323 | return tester; 324 | } 325 | }; 326 | } 327 | } 328 | }); 329 | assert.ok(proxy.tester === tester, "PropertyDescriptor failed to properly set the test variable"); 330 | }, 331 | 332 | "PropertyDescriptor context for set should be the receiver": function() { 333 | var tester = 1, 334 | proxy = Proxy.create({ 335 | getPropertyDescriptor: function(name) { 336 | if (name === "tester") { 337 | return { 338 | set: function(name, value) { 339 | assert.ok(this === proxy, "PropertyDescriptor context is not the receiver"); 340 | tester = value; 341 | } 342 | }; 343 | } 344 | } 345 | }); 346 | proxy.tester = 2; 347 | }, 348 | 349 | // TODO: write more PropertyDescriptor tests: 350 | // value, configurable, enumerable, writable, ... 351 | 352 | "PropertyDescriptor should get value if get method is not supplied": function() { 353 | var pd = { value: 2 }, 354 | proxy = Proxy.create({ 355 | getPropertyDescriptor: function(name) { 356 | return pd; 357 | } 358 | }); 359 | assert.ok(proxy.tester === pd.value, "PropertyDescriptor did not return appropriate value"); 360 | }, 361 | 362 | "PropertyDescriptor should set value if set method is not supplied": function() { 363 | var pd = { 364 | value: 2 365 | }, 366 | proxy = Proxy.create({ 367 | getPropertyDescriptor: function(name) { 368 | return pd; 369 | } 370 | }); 371 | proxy.tester = 3; 372 | assert.ok(proxy.tester === pd.value, "PropertyDescriptor value was not changed"); 373 | } 374 | 375 | // TODO: write more fundamental trap tests: 376 | // getOwnPropertyDescriptor, getPropertyNames, getOwnPropertyNames, ... 377 | }, 378 | 379 | "Derived traps": { 380 | "proxy context should be the PropertyHandler for derived trap 'get'": function() { 381 | var tester = 5, 382 | handler = { 383 | get: function(receiver, name) { 384 | assert.ok(this === handler, "PropertyHandler is not the appropriate context"); 385 | return tester; 386 | } 387 | }, 388 | proxy = Proxy.create(handler); 389 | assert.ok(proxy.tester === tester, "PropertyHandler get method was not called properly"); 390 | }, 391 | 392 | "proxy context should be the PropertyHandler for derived trap 'has'": function() { 393 | var tester = 5, 394 | handler = { 395 | has: function(name) { 396 | assert.ok(this === handler, "PropertyHandler is not the appropriate context"); 397 | return (name === "tester2"); 398 | } 399 | }, 400 | proxy = Proxy.create(handler); 401 | assert.ok("tester2" in proxy, "PropertyHanlder is not responding correctly to has"); 402 | }, 403 | 404 | "proxy context should be the PropertyHandler for derived trap 'enumerate'": function() { 405 | var handler = { 406 | enumerate: function() { 407 | assert.ok(this === handler, "PropertyHandler is not the appropriate context"); 408 | return ["tester"]; 409 | } 410 | }, 411 | proxy = Proxy.create(handler), p; 412 | for (p in proxy) { 413 | assert.ok(p === "tester", "PropertyHandler responded with incorrect enumerate value: '" + p + "'"); 414 | } 415 | }, 416 | 417 | "proxy context should be the PropertyHandler for derived trap 'set'": function() { 418 | var tester = 5, 419 | called = false; 420 | handler = { 421 | set: function(receiver, name, value) { 422 | called = true; 423 | assert.ok(this === handler, "PropertyHandler is not the appropriate context"); 424 | tester = value; 425 | } 426 | }, 427 | proxy = Proxy.create(handler); 428 | proxy.tester = 6; 429 | assert.ok(called, "PropertyHandler set method was not called properly"); 430 | } 431 | 432 | // TODO: write more derived trap tests: 433 | // keys, hasOwn, ... 434 | }, 435 | 436 | "ECMAScript 5 implementation methods": { 437 | "Proxy.defineProperty on proxy object": function() { 438 | Proxy.defineProperty(proxyTest, 'third', { 439 | get: function() { 440 | return "third"; 441 | } 442 | }); 443 | assert.equal(called, "defineProperty", "defineProperty was not called: "+called); 444 | }, 445 | 446 | "proxy has newly defined property": function() { 447 | assert.ok("third" in proxyTest); 448 | }, 449 | 450 | "proxy's newly defined property have correct return value": function() { 451 | assert.equal(proxyTest.third, "third", "proxyTest.third != 'third'"); 452 | }, 453 | 454 | "proxy's newly defined property are reflected in underlying handlers": function() { 455 | assert.ok("third" in handlers, "'third' is not in handlers"); 456 | }, 457 | 458 | "Proxy.defineProperties on proxy object": function() { 459 | Proxy.defineProperties(proxyTest, { 460 | fourth: { 461 | get: function() { 462 | return "fourth"; 463 | } 464 | }, 465 | fifth: { 466 | get: function() { 467 | return "fifth"; 468 | } 469 | } 470 | }); 471 | assert.equal(called, "defineProperty", "defineProperty was not called: "+called); 472 | }, 473 | 474 | "proxy has newly defined properties": function() { 475 | assert.ok("fourth" in proxyTest); 476 | assert.ok("fifth" in proxyTest); 477 | }, 478 | 479 | "proxy's newly defined properties have correct return value": function() { 480 | assert.equal(proxyTest.fourth, "fourth", "proxyTest.fourth != 'fourth'"); 481 | assert.equal(proxyTest.fifth, "fifth", "proxyTest.fifth != 'fifth'"); 482 | }, 483 | 484 | "proxy's newly defined properties are reflected in underlying handlers": function() { 485 | assert.ok("fourth" in handlers, "'fourth' is not in handlers"); 486 | assert.ok("fifth" in handlers, "'fifth' is not in handlers"); 487 | } 488 | }, 489 | 490 | "Additional method tests": { 491 | "Proxy.isProxy proxy object": function() { 492 | assert.ok(Proxy.isProxy(proxyTest), "proxyTest is not a Proxy"); 493 | }, 494 | 495 | "Proxy.isProxy non-proxy object": function() { 496 | assert.ok(!Proxy.isProxy({}), "object is a Proxy"); 497 | }, 498 | 499 | "Proxy.setPrototype of proxy object": function() { 500 | assert.ok(Proxy.setPrototype(proxyTest, RegExp.prototype), "unable to set prototype of RegExp on proxyTest"); 501 | }, 502 | 503 | "proxy object is instanceof RegExp": function() { 504 | assert.ok(proxyTest instanceof RegExp, "proxyTest is not an instanceof RegExp"); 505 | }, 506 | 507 | "Proxy.setPrototype of non-proxy object": function() { 508 | assert.ok(Proxy.setPrototype(base, Number.prototype), "unable to set prototype of Number on base"); 509 | }, 510 | 511 | "non-proxy object is instanceof RegExp": function() { 512 | assert.ok(base instanceof Number, "base is not an instanceof Number"); 513 | }, 514 | 515 | "Proxy.clone proxy object": function() { 516 | cloneProxy = Proxy.clone(proxyTest); 517 | assert.ok(typeof cloneProxy == "object", "cloneProxy does not result in an object"); 518 | }, 519 | 520 | "cloned proxy maintains prototype of base proxy": function() { 521 | assert.ok(cloneProxy instanceof RegExp, "cloneProxy is not an instanceof RegExp"); 522 | }, 523 | 524 | "Proxy.clone non-proxy object": function() { 525 | clone = Proxy.clone(base); 526 | assert.ok(clone !== base, "clone is identical to base object"); 527 | 528 | }, 529 | 530 | "cloned object maintains prototype of base": function() { 531 | assert.ok(clone instanceof Number, "clone is not an instance of a Number"); 532 | }, 533 | 534 | "set hidden property on cloned object": function() { 535 | assert.ok(Proxy.hidden(clone, "hiddenTest", regex), "unable to set hidden property 'hiddenTest' on clone"); 536 | }, 537 | 538 | "get hidden property on cloned object": function() { 539 | assert.ok(Proxy.hidden(clone, "hiddenTest") === regex, "unable to retrieve hidden property 'hiddenTest' on clone"); 540 | }, 541 | } 542 | }, section, sectionName, test, testIndex, sectionIndex = 0, totalTests = 0, passedTests = 0, failedTests = 0; 543 | 544 | console.log("Running tests...\n"); 545 | 546 | for (sectionName in tests) { 547 | ++sectionIndex; 548 | console.log("\n" + sectionIndex + ": "+ sectionName); 549 | 550 | testIndex = 0; 551 | section = tests[sectionName]; 552 | 553 | for (test in section) { 554 | ++totalTests; 555 | ++testIndex; 556 | process.stdout.write(" " + test + ": "); 557 | 558 | try{ 559 | section[test](); 560 | ++passedTests; 561 | console.log("PASS"); 562 | } catch(e) { 563 | ++failedTests; 564 | console.log("FAIL: "+ e.message); 565 | } 566 | } 567 | } 568 | 569 | console.log("\nPassed " + passedTests + " of " + totalTests + " tests"); 570 | console.log("\nFailed " + failedTests + " of " + totalTests + " tests"); 571 | console.log(""); 572 | 573 | process.exit(0); 574 | }()); 575 | -------------------------------------------------------------------------------- /test/mocha-test-notworking.js: -------------------------------------------------------------------------------- 1 | /*jslint forin: true, onevar: true, immed: true */ 2 | 3 | describe('weak()', function () { 4 | process.env['NODE_PATH'] += ':' + __dirname + "/../lib"; 5 | 6 | var assert = require('assert'), 7 | undef, 8 | called, p, 9 | Proxy = require("../lib/node-proxy.js"), 10 | createProxyFunction = function(handlers, callTrap, constructorTrap) { 11 | called = "createProxyFunction"; 12 | 13 | return Proxy.createFunction({ 14 | "delete": function (name){ 15 | called = "delete"; 16 | var r = true; 17 | if (name in handlers) { 18 | r = (delete handlers[name]); 19 | } 20 | return r; 21 | }, 22 | enumerate: function (){ 23 | called = "enumerate"; 24 | return Object.keys(handlers); 25 | }, 26 | fix: function (){ 27 | called = "fix"; 28 | return handlers; 29 | }, 30 | has: function (name){ 31 | called = "has"; 32 | return (name in handlers); 33 | }, 34 | get: function (receiver, name){ 35 | called = "get"; 36 | if (!(name in handlers)) { 37 | return undef; 38 | } 39 | return "get" in handlers[name] && typeof(handlers[name].get) == "function" ? 40 | handlers[name].get.call(receiver) : 41 | (handlers[name].value || undef); 42 | }, 43 | set: function (receiver, name, val){ 44 | called = "set"; 45 | if (!(name in handlers)) { 46 | defineProperty.call(this, name, { 47 | configurable:true, 48 | writable:true, 49 | enumerable:true, 50 | value:val, 51 | get:function (){return val;}, 52 | set:function (v){val=v;} 53 | }); 54 | called = "set"; 55 | return true; 56 | } 57 | if (!handlers[name].configurable) { 58 | return false; 59 | } 60 | if ("set" in handlers[name]) { 61 | handlers[name].set.call(receiver, val); 62 | } 63 | 64 | handlers[name].value = val; 65 | return true; 66 | } 67 | }, callTrap); 68 | }, 69 | createProxy = function (handlers) { 70 | called = "createProxy"; 71 | 72 | function defineProperty(name, pd){ 73 | called = "defineProperty"; 74 | if (name in handlers && !handlers[name].configurable) { 75 | return null; 76 | } 77 | handlers[name] = pd; 78 | return null; 79 | } 80 | 81 | return Proxy.create({ 82 | getOwnPropertyDescriptor:function (name){ 83 | called = "getOwnPropertyDescriptor"; 84 | return handlers.hasOwnProperty(name) ? handlers[name] : undef; 85 | }, 86 | getPropertyDescriptor:function (name){ 87 | called = "getPropertyDescriptor"; 88 | return name in handlers ? handlers[name] : undef; 89 | }, 90 | defineProperty: defineProperty, 91 | getOwnPropertyNames:function (){ 92 | called = "getOwnPropertyNames"; 93 | return Object.getOwnPropertyNames(handlers); 94 | }, 95 | "delete":function (name){ 96 | called = "delete"; 97 | var r = true; 98 | if (name in handlers) { 99 | r = (delete handlers[name]); 100 | } 101 | return r; 102 | }, 103 | enumerate:function (){ 104 | called = "enumerate"; 105 | return Object.keys(handlers); 106 | }, 107 | fix:function (){ 108 | called = "fix"; 109 | return handlers; 110 | }, 111 | has:function (name){ 112 | called = "has"; 113 | //console.log("has called on: "+name); 114 | //console.log(name in handlers) 115 | return (name in handlers); 116 | }, 117 | hasOwn:function (name){ 118 | called = "hasOwn"; 119 | return handlers.hasOwnProperty(name); 120 | }, 121 | get:function (receiver, name){ 122 | called = "get"; 123 | //console.log(arguments.callee.caller) 124 | if (!(name in handlers)) { 125 | return undef; 126 | } 127 | return "get" in handlers[name] && typeof(handlers[name].get) == "function" ? 128 | handlers[name].get.call(receiver) : 129 | (handlers[name].value || undef); 130 | }, 131 | set:function (receiver, name, val){ 132 | called = "set"; 133 | if (!(name in handlers)) { 134 | defineProperty.call(this, name, { 135 | configurable:true, 136 | writable:true, 137 | enumerable:true, 138 | value:val, 139 | get:function (){return val;}, 140 | set:function (v){val=v;} 141 | }); 142 | called = "set"; 143 | return true; 144 | } 145 | if (!handlers[name].configurable) { 146 | return false; 147 | } 148 | if ("set" in handlers[name]) { 149 | handlers[name].set.call(receiver, val); 150 | } 151 | 152 | handlers[name].value = val; 153 | return true; 154 | } 155 | }); 156 | }, 157 | proxyTest, 158 | clone, 159 | cloneProxy, 160 | proxyTrapTest, 161 | proxyTrapTestInstance, 162 | firstValue = "firstProp", 163 | names, 164 | count, 165 | regex = /regex/, 166 | base = {}, 167 | handlers = { 168 | first: { 169 | get:function (){return firstValue;}, 170 | set:function (val){firstValue = val;} 171 | } 172 | }, 173 | funcHandlers = { 174 | test: { 175 | get:function(){return "working";} 176 | } 177 | }, 178 | callTrap = function() { 179 | called = "callTrap"; 180 | return this; 181 | }; 182 | describe("Base Proxy methods", function() { 183 | proxyTrapTest = createProxyFunction(funcHandlers, callTrap); 184 | describe("Proxy.create", function() { 185 | proxyTest = createProxy(handlers); 186 | assert.equal(called, "createProxy", "createProxy was not the last method called"); 187 | assert.ok(typeof proxyTest == "object"); 188 | }); 189 | 190 | describe("Proxy.createFunction", function() { 191 | //proxyTrapTest = createProxyFunction(funcHandlers, callTrap); 192 | assert.equal(called, "createProxyFunction", "createProxyFunction was not the last method called"); 193 | //assert.ok(typeof proxyTrapTest == "function", "proxyTrapTest is not a function"); 194 | }); 195 | 196 | describe("Proxy.createFunction with optional constructor trap", function() { 197 | //proxyTrapTest = createProxyFunction(funcHandlers, callTrap, constructTrap); 198 | }); 199 | 200 | describe("Proxy.isTrapping on proxy object", function() { 201 | assert.ok(Proxy.isTrapping(proxyTest), "proxyTest is not trapping"); 202 | }); 203 | 204 | describe("proxy function is callable", function() { 205 | console.log("...", proxyTrapTest) 206 | proxyTrapTest(); 207 | assert.equal(called, "callTrap", "callTrap was not the last function called"); 208 | }); 209 | 210 | describe("proxy function has accessible properties", function() { 211 | assert.ok("test" in proxyTrapTest, "'test' not in proxyTrapTest"); 212 | }); 213 | 214 | describe("proxy function get properties", function() { 215 | assert.equal(proxyTrapTest.test, "working", "'test' not in proxyTrapTest"); 216 | }); 217 | 218 | describe("proxy function as constructor", function() { 219 | proxyTrapTestInstance = new proxyTrapTest(); 220 | assert.equal(called, "callTrap", "callTrap was not last call"); 221 | }); 222 | 223 | describe("proxy function instance property handling", function() { 224 | assert.ok("test" in proxyTrapTestInstance, "no 'test' in proxyTrapTestInstance"); 225 | assert.equal(called, "has", "did not call has"); 226 | }); 227 | }); 228 | 229 | describe("Testing proxy object instance", function() { 230 | describe("has property 'first'", function() { 231 | assert.ok("first" in proxyTest, "proxyTest does not have a property named 'first'"); 232 | //assert.equal(called, "has", "the has method was not the last method called"); 233 | }); 234 | 235 | describe("get property 'first'", function(){ 236 | assert.equal(proxyTest.first, firstValue); 237 | assert.equal(called, "get", "the get method was not the last method called"); 238 | }); 239 | 240 | describe("set property 'first' to new value", function() { 241 | proxyTest.first = "changed"; 242 | assert.equal(called, "set", "the set method was not the last method called"); 243 | assert.equal(proxyTest.first, firstValue, "proxyTest.first != firstValue"); 244 | }); 245 | 246 | describe("set new property 'second'", function() { 247 | proxyTest.second = "secondProp"; 248 | assert.equal(called, "set", "the set method was not the last method called"); 249 | }); 250 | 251 | describe("has new property 'second'", function() { 252 | assert.ok("second" in proxyTest, "proxyTest does not have the property 'second'"); 253 | }); 254 | 255 | describe("get newly set property 'second'", function() { 256 | assert.equal(proxyTest.second, "secondProp", "proxyTest.second is not equal to 'secondProp'"); 257 | }); 258 | 259 | describe("iterate property names", function() { 260 | count = 0; 261 | for (p in proxyTest){++count;} 262 | assert.equal(count, 2, "there are not 2 properties on proxyTest"); 263 | }); 264 | 265 | describe("Object.getOwnPropertyNames on proxy object", function() { 266 | names = Object.getOwnPropertyNames(proxyTest); 267 | assert.equal(called, "enumerate", "Object.getOwnPropertyNames did not invoke enumerate"); 268 | }); 269 | 270 | describe("Object.getOwnPropertyNames returned an Array", function() { 271 | assert.ok(names instanceof Array); 272 | }); 273 | 274 | describe("Object.getOwnPropertyNames return value has the correct length", function() { 275 | assert.equal(names.length, 2, "2 property names were not returned"); 276 | }); 277 | 278 | describe("Object.getOwnPropertyNames has the correct values", function() { 279 | assert.equal(names[0], "first", "The first property name is not 'first'"); 280 | assert.equal(names[1], "second", "The second property name is not 'second'"); 281 | }); 282 | 283 | describe("Object.keys on proxy object", function() { 284 | names = Object.keys(proxyTest); 285 | assert.equal(called, "enumerate", "Object.keys did not invoke 'enumerate'"); 286 | }); 287 | 288 | describe("Object.keys returned an Array", function() { 289 | assert.ok(names instanceof Array); 290 | }); 291 | 292 | describe("Object.keys return value has the correct length", function() { 293 | assert.equal(names.length, 2, "2 property names were not returned"); 294 | }); 295 | 296 | describe("Object.keys has the correct values", function() { 297 | assert.equal(names[0], "first", "The first property name is not 'first'"); 298 | assert.equal(names[1], "second", "The second property name is not 'second'"); 299 | }); 300 | 301 | describe("delete 'second'", function() { 302 | assert.ok((delete proxyTest.second), "Delete the property 'second' from the proxy"); 303 | assert.equal(called, "delete", "the delete method was not the last method called"); 304 | 305 | }); 306 | 307 | describe("proxy instance no longer has property 'second'", function() { 308 | assert.ok(!Object.prototype.hasOwnProperty.call(proxyTest, "second"), "proxyTest still hasOwnProperty the property 'second'"); 309 | assert.ok(!("second" in proxyTest), "proxyTest still has the property 'second'"); 310 | }); 311 | }); 312 | 313 | describe("Fundamental traps", function() { 314 | describe("PropertyDescriptor context for get should be the receiver", function() { 315 | var tester = 1, 316 | proxy = Proxy.create({ 317 | getPropertyDescriptor: function(name) { 318 | if (name === "tester") { 319 | return { 320 | get: function(name) { 321 | assert.ok(this === proxy, "PropertyDescriptor context is not the receiver"); 322 | return tester; 323 | } 324 | }; 325 | } 326 | } 327 | }); 328 | assert.ok(proxy.tester === tester, "PropertyDescriptor failed to properly set the test variable"); 329 | }); 330 | 331 | describe("PropertyDescriptor context for set should be the receiver", function() { 332 | var tester = 1, 333 | proxy = Proxy.create({ 334 | getPropertyDescriptor: function(name) { 335 | if (name === "tester") { 336 | return { 337 | set: function(name, value) { 338 | assert.ok(this === proxy, "PropertyDescriptor context is not the receiver"); 339 | tester = value; 340 | } 341 | }; 342 | } 343 | } 344 | }); 345 | proxy.tester = 2; 346 | }); 347 | 348 | // TODO: write more PropertyDescriptor tests: 349 | // value, configurable, enumerable, writable, ... 350 | 351 | describe("PropertyDescriptor should get value if get method is not supplied", function() { 352 | var pd = { value: 2 }, 353 | proxy = Proxy.create({ 354 | getPropertyDescriptor: function(name) { 355 | return pd; 356 | } 357 | }); 358 | assert.ok(proxy.tester === pd.value, "PropertyDescriptor did not return appropriate value"); 359 | }); 360 | 361 | describe("PropertyDescriptor should set value if set method is not supplied", function() { 362 | var pd = { 363 | value: 2 364 | }, 365 | proxy = Proxy.create({ 366 | getPropertyDescriptor: function(name) { 367 | return pd; 368 | } 369 | }); 370 | proxy.tester = 3; 371 | assert.ok(proxy.tester === pd.value, "PropertyDescriptor value was not changed"); 372 | }); 373 | 374 | // TODO: write more fundamental trap tests: 375 | // getOwnPropertyDescriptor, getPropertyNam);es, getOwnPropertyNames, ... 376 | }); 377 | 378 | describe("Derived traps", function() { 379 | describe("proxy context should be the PropertyHandler for derived trap 'get'", function() { 380 | var tester = 5, 381 | handler = { 382 | get: function(receiver, name) { 383 | assert.ok(this === handler, "PropertyHandler is not the appropriate context"); 384 | return tester; 385 | } 386 | }, 387 | proxy = Proxy.create(handler); 388 | assert.ok(proxy.tester === tester, "PropertyHandler get method was not called properly"); 389 | }); 390 | 391 | describe("proxy context should be the PropertyHandler for derived trap 'has'", function() { 392 | var tester = 5, 393 | handler = { 394 | has: function(name) { 395 | assert.ok(this === handler, "PropertyHandler is not the appropriate context"); 396 | return (name === "tester2"); 397 | } 398 | }, 399 | proxy = Proxy.create(handler); 400 | assert.ok("tester2" in proxy, "PropertyHanlder is not responding correctly to has"); 401 | }); 402 | 403 | describe("proxy context should be the PropertyHandler for derived trap 'enumerate'", function() { 404 | var handler = { 405 | enumerate: function() { 406 | assert.ok(this === handler, "PropertyHandler is not the appropriate context"); 407 | return ["tester"]; 408 | } 409 | }, 410 | proxy = Proxy.create(handler), p; 411 | for (p in proxy) { 412 | assert.ok(p === "tester", "PropertyHandler responded with incorrect enumerate value: '" + p + "'"); 413 | } 414 | }); 415 | 416 | describe("proxy context should be the PropertyHandler for derived trap 'set'", function() { 417 | var tester = 5, 418 | called = false; 419 | handler = { 420 | set: function(receiver, name, value) { 421 | called = true; 422 | assert.ok(this === handler, "PropertyHandler is not the appropriate context"); 423 | tester = value; 424 | } 425 | }, 426 | proxy = Proxy.create(handler); 427 | proxy.tester = 6; 428 | assert.ok(called, "PropertyHandler set method was not called properly"); 429 | }); 430 | 431 | // TODO: write more derived trap tests: 432 | // keys, hasOwn, ...); 433 | }); 434 | 435 | describe("ECMAScript 5 implementation methods", function() { 436 | describe("Proxy.defineProperty on proxy object", function() { 437 | Proxy.defineProperty(proxyTest, 'third', { 438 | get: function() { 439 | return "third"; 440 | } 441 | }); 442 | assert.equal(called, "defineProperty", "defineProperty was not called: "+called); 443 | }); 444 | 445 | describe("proxy has newly defined property", function() { 446 | assert.ok("third" in proxyTest); 447 | }); 448 | 449 | describe("proxy's newly defined property have correct return value", function() { 450 | assert.equal(proxyTest.third, "third", "proxyTest.third != 'third'"); 451 | }); 452 | 453 | describe("proxy's newly defined property are reflected in underlying handlers", function() { 454 | assert.ok("third" in handlers, "'third' is not in handlers"); 455 | }); 456 | 457 | describe("Proxy.defineProperties on proxy object", function() { 458 | Proxy.defineProperties(proxyTest, { 459 | fourth: { 460 | get: function() { 461 | return "fourth"; 462 | } 463 | }, 464 | fifth: { 465 | get: function() { 466 | return "fifth"; 467 | } 468 | } 469 | }); 470 | assert.equal(called, "defineProperty", "defineProperty was not called: "+called); 471 | }); 472 | 473 | describe("proxy has newly defined properties", function() { 474 | assert.ok("fourth" in proxyTest); 475 | assert.ok("fifth" in proxyTest); 476 | }); 477 | 478 | describe("proxy's newly defined properties have correct return value", function() { 479 | assert.equal(proxyTest.fourth, "fourth", "proxyTest.fourth != 'fourth'"); 480 | assert.equal(proxyTest.fifth, "fifth", "proxyTest.fifth != 'fifth'"); 481 | }); 482 | 483 | describe("proxy's newly defined properties are reflected in underlying handlers", function() { 484 | assert.ok("fourth" in handlers, "'fourth' is not in handlers"); 485 | assert.ok("fifth" in handlers, "'fifth' is not in handlers"); 486 | }); 487 | }); 488 | 489 | describe("Additional method tests", function() { 490 | describe("Proxy.isProxy proxy object", function() { 491 | assert.ok(Proxy.isProxy(proxyTest), "proxyTest is not a Proxy"); 492 | }); 493 | 494 | describe("Proxy.isProxy non-proxy object", function() { 495 | assert.ok(!Proxy.isProxy({}), "object is a Proxy"); 496 | }); 497 | 498 | describe("Proxy.setPrototype of proxy object", function() { 499 | assert.ok(Proxy.setPrototype(proxyTest, RegExp.prototype), "unable to set prototype of RegExp on proxyTest"); 500 | }); 501 | 502 | describe("proxy object is instanceof RegExp", function() { 503 | assert.ok(proxyTest instanceof RegExp, "proxyTest is not an instanceof RegExp"); 504 | }); 505 | 506 | describe("Proxy.setPrototype of non-proxy object", function() { 507 | assert.ok(Proxy.setPrototype(base, Number.prototype), "unable to set prototype of Number on base"); 508 | }); 509 | 510 | describe("non-proxy object is instanceof RegExp", function() { 511 | assert.ok(base instanceof Number, "base is not an instanceof Number"); 512 | }); 513 | 514 | describe("Proxy.clone proxy object", function() { 515 | cloneProxy = Proxy.clone(proxyTest); 516 | assert.ok(typeof cloneProxy == "object", "cloneProxy does not result in an object"); 517 | }); 518 | 519 | describe("cloned proxy maintains prototype of base proxy", function() { 520 | assert.ok(cloneProxy instanceof RegExp, "cloneProxy is not an instanceof RegExp"); 521 | }); 522 | 523 | describe("Proxy.clone non-proxy object", function() { 524 | clone = Proxy.clone(base); 525 | assert.ok(clone !== base, "clone is identical to base object"); 526 | 527 | }); 528 | 529 | describe("cloned object maintains prototype of base", function() { 530 | assert.ok(clone instanceof Number, "clone is not an instance of a Number"); 531 | }); 532 | 533 | describe("set hidden property on cloned object", function() { 534 | assert.ok(Proxy.hidden(clone, "hiddenTest", regex), "unable to set hidden property 'hiddenTest' on clone"); 535 | }); 536 | 537 | describe("get hidden property on cloned object", function() { 538 | assert.ok(Proxy.hidden(clone, "hiddenTest") === regex, "unable to retrieve hidden property 'hiddenTest' on clone"); 539 | }); 540 | }); 541 | }); //, section, sectionName, test, testIndex, sectionIndex = 0, totalTests = 0, passedTests = 0, failedTests = 0; 542 | 543 | // console.log("Running tests...\n"); 544 | 545 | // for (sectionName in tests) { 546 | // ++sectionIndex; 547 | // console.log("\n" + sectionIndex + ": "+ sectionName); 548 | 549 | // testIndex = 0; 550 | // section = tests[sectionName]; 551 | 552 | // for (test in section) { 553 | // ++totalTests; 554 | // ++testIndex; 555 | // process.stdout.write(" " + test + ": "); 556 | 557 | // try{ 558 | // section[test](); 559 | // ++passedTests; 560 | // console.log("PASS"); 561 | // } catch(e) { 562 | // ++failedTests; 563 | // console.log("FAIL: "+ e.message); 564 | // } 565 | // } 566 | // } 567 | 568 | // console.log("\nPassed " + passedTests + " of " + totalTests + " tests"); 569 | // console.log("\nFailed " + failedTests + " of " + totalTests + " tests"); 570 | // console.log(""); 571 | 572 | // process.exit(0); 573 | -------------------------------------------------------------------------------- /src/node-proxy.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * 5 | * @author Sam Shull 6 | * @version 0.1 7 | * 8 | * @copyright Copyright (c) 2009 Sam Shull 9 | * @license 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | * THE SOFTWARE. 28 | * 29 | * 30 | * CHANGES: 31 | */ 32 | 33 | #include "./node-proxy.h" 34 | 35 | Nan::Persistent NodeProxy::ObjectCreator; 36 | Nan::Persistent NodeProxy::FunctionCreator; 37 | 38 | /** 39 | * 40 | * 41 | * 42 | * 43 | */ 44 | NodeProxy::NodeProxy() { 45 | } 46 | 47 | /** 48 | * 49 | * 50 | * 51 | * 52 | */ 53 | NodeProxy::~NodeProxy() { 54 | } 55 | 56 | /** 57 | * Used for creating a shallow copy of an object 58 | * 59 | * 60 | * @param mixed 61 | * @returns mixed 62 | * @throws Error 63 | */ 64 | NAN_METHOD(NodeProxy::Clone) { 65 | 66 | if (info.Length() < 1) { 67 | Nan::ThrowError("clone requires at least one (1) argument."); 68 | return; 69 | } 70 | 71 | if (info[0]->IsString()) { 72 | info.GetReturnValue().Set(info[0]->ToObject()->Clone()->ToString()); 73 | return; 74 | 75 | } else if (info[0]->IsBoolean()) { 76 | info.GetReturnValue().Set(info[0]->ToObject()->Clone()->ToBoolean()); 77 | return; 78 | 79 | } else if (info[0]->IsNumber() 80 | || info[0]->IsNumberObject() 81 | || info[0]->IsInt32() 82 | || info[0]->IsUint32()) { 83 | info.GetReturnValue().Set(Nan::New(info[0]->NumberValue())); 84 | return; 85 | 86 | } else if (info[0]->IsArray()) { 87 | info.GetReturnValue().Set(Local::Cast(info[0]->ToObject()->Clone())); 88 | return; 89 | 90 | } else if (info[0]->IsDate()) { 91 | info.GetReturnValue().Set(Local::Cast(info[0]->ToObject()->Clone())); 92 | return; 93 | 94 | } else if (info[0]->IsFunction()) { 95 | info.GetReturnValue().Set(Local::Cast(info[0])->Clone()); 96 | return; 97 | 98 | } else if (info[0]->IsNull()) { 99 | info.GetReturnValue().SetNull(); 100 | return; 101 | 102 | } else if (info[0]->IsUndefined()) { 103 | info.GetReturnValue().SetUndefined(); 104 | return; 105 | 106 | } else if (info[0]->IsObject()) { 107 | info.GetReturnValue().Set(info[0]->ToObject()->Clone()); 108 | return; 109 | } 110 | 111 | Nan::ThrowError("clone cannot determine the type of the argument."); 112 | return; 113 | } 114 | 115 | /** 116 | * Set or Retrieve the value of a hidden 117 | * property on a given object 118 | * Passing two arguments to this function 119 | * returns the value of the hidden property 120 | * While passing three arguments to this function 121 | * results in the setting of the hidden property 122 | * and returns a Boolean value indicating successful 123 | * setting of value 124 | * 125 | * @param Object 126 | * @param String name 127 | * @param mixed value - optional 128 | * @returns mixed 129 | * @throws Error 130 | */ 131 | NAN_METHOD(NodeProxy::Hidden) { 132 | 133 | if (info.Length() < 2) { 134 | Nan::ThrowError("hidden requires at least two (2) arguments."); 135 | return; 136 | } 137 | 138 | Local obj = info[0]->ToObject(); 139 | 140 | if (info.Length() < 3) { 141 | info.GetReturnValue().Set(obj->GetHiddenValue( 142 | String::Concat(Nan::New("NodeProxy::hidden:").ToLocalChecked(), 143 | Nan::To(info[1]).ToLocalChecked()))); 144 | return; 145 | } 146 | 147 | info.GetReturnValue().Set( 148 | Nan::New( 149 | obj->SetHiddenValue(String::Concat(Nan::New("NodeProxy::hidden:").ToLocalChecked(), 150 | Nan::To(info[1]).ToLocalChecked()), 151 | info[2]))); 152 | } 153 | 154 | /** 155 | * Set the prototype of an object 156 | * 157 | * @param Object 158 | * @param Object 159 | * @returns Boolean 160 | * @throws Error 161 | */ 162 | NAN_METHOD(NodeProxy::SetPrototype) { 163 | 164 | if (info.Length() < 2) { 165 | Nan::ThrowError("setPrototype requires at least two (2) arguments."); 166 | return; 167 | } 168 | info.GetReturnValue().Set(Nan::New(info[0]->ToObject()->SetPrototype(info[1]))); 169 | } 170 | 171 | /** 172 | * Determine if an Object was created by Proxy 173 | * 174 | * @param Object 175 | * @returns Boolean 176 | */ 177 | NAN_METHOD(NodeProxy::IsProxy) { 178 | 179 | if (info.Length() < 1) { 180 | Nan::ThrowError("isProxy requires at least one (1) argument."); 181 | return; 182 | } 183 | 184 | Local obj = info[0]->ToObject(); 185 | 186 | if (obj->InternalFieldCount() > 0) { 187 | Local temp = obj->GetInternalField(0); 188 | 189 | info.GetReturnValue().Set(Nan::New(!temp.IsEmpty() && temp->IsObject())); 190 | return; 191 | } 192 | 193 | info.GetReturnValue().Set(Nan::False()); 194 | } 195 | 196 | /** 197 | * Create an object that has ProxyHandler intercepts attached and 198 | * optionally implements the prototype of another object 199 | * 200 | * * ProxyHandler intercepts override the property handlers for any 201 | * * given prototype. So, the ProxyHandler will be invoked for access 202 | * * to the prototype's properties as well 203 | * 204 | * @param ProxyHandler - @see NodeProxy::ValidateProxyHandler 205 | * @param Object - optional, the prototype object to implement 206 | * @returns Object 207 | * @throws Error, TypeError 208 | */ 209 | NAN_METHOD(NodeProxy::Create) { 210 | 211 | Local proxyHandler; 212 | 213 | if (info.Length() < 1) { 214 | Nan::ThrowError("create requires at least one (1) argument."); 215 | return; 216 | } 217 | 218 | if (!info[0]->IsObject()) { 219 | Nan::ThrowTypeError( 220 | "create requires the first argument to be an Object."); 221 | return; 222 | } 223 | 224 | proxyHandler = info[0]->ToObject(); 225 | 226 | if (info.Length() > 1 && !info[1]->IsObject()) { 227 | Nan::ThrowTypeError( 228 | "create requires the second argument to be an Object."); 229 | return; 230 | } 231 | 232 | // manage locking states 233 | proxyHandler->SetHiddenValue(Nan::New("trapping").ToLocalChecked(), Nan::True()); 234 | proxyHandler->SetHiddenValue(Nan::New("extensible").ToLocalChecked(), Nan::True()); 235 | proxyHandler->SetHiddenValue(Nan::New("sealed").ToLocalChecked(), Nan::False()); 236 | proxyHandler->SetHiddenValue(Nan::New("frozen").ToLocalChecked(), Nan::False()); 237 | 238 | Local instance = Nan::New(ObjectCreator)->NewInstance(); 239 | 240 | instance->SetInternalField(0, proxyHandler); 241 | 242 | if (info.Length() > 1) { 243 | instance->SetPrototype(info[1]); 244 | } 245 | 246 | info.GetReturnValue().Set(instance); 247 | } 248 | 249 | /** 250 | * Create a function that has ProxyHandler intercepts attached and 251 | * sets a call trap function for invokation as well as an optional 252 | * constructor trap 253 | * 254 | * 255 | * @param ProxyHandler - @see NodeProxy::ValidateProxyHandler 256 | * @param Function - call trap 257 | * @param Function - optional, constructor trap 258 | * @returns Function 259 | * @throws Error, TypeError 260 | */ 261 | NAN_METHOD(NodeProxy::CreateFunction) { 262 | 263 | if (info.Length() < 2) { 264 | Nan::ThrowError("createFunction requires at least two (2) arguments."); 265 | return; 266 | } 267 | 268 | if (!info[0]->IsObject()) { 269 | Nan::ThrowTypeError("createFunction requires the first argument to be an Object."); 270 | return; 271 | } 272 | Local proxyHandler = info[0]->ToObject(); 273 | 274 | if (!info[1]->IsFunction()) { 275 | Nan::ThrowTypeError("createFunction requires the second argument to be a Function."); 276 | return; 277 | } 278 | 279 | if (info.Length() > 2 && !info[2]->IsFunction()) { 280 | Nan::ThrowTypeError("createFunction requires the second argument to be a Function."); 281 | return; 282 | } 283 | 284 | proxyHandler->SetHiddenValue(Nan::New("callTrap").ToLocalChecked(), info[1]); 285 | Local constructorTrap; 286 | if(info.Length() > 2) { 287 | constructorTrap = info[2]; 288 | } else { 289 | constructorTrap = Nan::Undefined(); 290 | } 291 | proxyHandler->SetHiddenValue(Nan::New("constructorTrap").ToLocalChecked(), constructorTrap); 292 | 293 | // manage locking states 294 | proxyHandler->SetHiddenValue(Nan::New("trapping").ToLocalChecked(), Nan::True()); 295 | proxyHandler->SetHiddenValue(Nan::New("extensible").ToLocalChecked(), Nan::True()); 296 | proxyHandler->SetHiddenValue(Nan::New("sealed").ToLocalChecked(), Nan::False()); 297 | proxyHandler->SetHiddenValue(Nan::New("frozen").ToLocalChecked(), Nan::False()); 298 | 299 | 300 | Local fn = Nan::New(FunctionCreator)->NewInstance(); 301 | fn->SetPrototype(info[1]->ToObject()->GetPrototype()); 302 | 303 | fn->SetInternalField(0, proxyHandler); 304 | 305 | info.GetReturnValue().Set(fn); 306 | } 307 | 308 | /** 309 | * Used as a handler for freeze, seal, and preventExtensions 310 | * to lock the state of a Proxy created object 311 | * 312 | * @param Object 313 | * @returns Boolean 314 | * @throws Error, TypeError 315 | */ 316 | NAN_METHOD(NodeProxy::Freeze) { 317 | 318 | Local name = info.Callee()->GetName()->ToString(); 319 | 320 | if (info.Length() < 1) { 321 | Nan::ThrowError(String::Concat(name, 322 | Nan::New(" requires at least one (1) argument.").ToLocalChecked())); 323 | return; 324 | } 325 | 326 | Local obj = info[0]->ToObject(); 327 | 328 | if (obj->InternalFieldCount() < 1) { 329 | Nan::ThrowTypeError( 330 | "Locking functions expect first " 331 | "argument to be intialized by Proxy"); 332 | return; 333 | } 334 | 335 | Local hide = obj->GetInternalField(0); 336 | 337 | if (hide.IsEmpty() || !hide->IsObject()) { 338 | Nan::ThrowTypeError( 339 | "Locking functions expect first " 340 | "argument to be intialized by Proxy"); 341 | return; 342 | } 343 | 344 | Local handler = hide->ToObject(); 345 | 346 | // if the object already meets the requirements of the function call 347 | if (name->Equals(Nan::New("freeze").ToLocalChecked())) { 348 | if (handler->GetHiddenValue(Nan::New("frozen").ToLocalChecked())->BooleanValue()) { 349 | info.GetReturnValue().Set(Nan::True()); 350 | return; 351 | } 352 | 353 | } else if (name->Equals(Nan::New("seal").ToLocalChecked())) { 354 | if (handler->GetHiddenValue(Nan::New("sealed").ToLocalChecked())->BooleanValue()) { 355 | info.GetReturnValue().Set(Nan::True()); 356 | return; 357 | } 358 | 359 | } else if (name->Equals(Nan::New("preventExtensions").ToLocalChecked())) { 360 | if (handler->GetHiddenValue(Nan::New("extensible").ToLocalChecked())->BooleanValue()) { 361 | info.GetReturnValue().Set(Nan::True()); 362 | return; 363 | } 364 | } 365 | 366 | // if this object is not trapping, just set the appropriate parameters 367 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 368 | if (name->Equals(Nan::New("freeze").ToLocalChecked())) { 369 | handler->SetHiddenValue(Nan::New("frozen").ToLocalChecked(), Nan::True()); 370 | handler->SetHiddenValue(Nan::New("sealed").ToLocalChecked(), Nan::True()); 371 | handler->SetHiddenValue(Nan::New("extensible").ToLocalChecked(), Nan::False()); 372 | info.GetReturnValue().Set(Nan::True()); 373 | return; 374 | 375 | } else if (name->Equals(Nan::New("seal").ToLocalChecked())) { 376 | handler->SetHiddenValue(Nan::New("sealed").ToLocalChecked(), Nan::True()); 377 | handler->SetHiddenValue(Nan::New("extensible").ToLocalChecked(), Nan::False()); 378 | info.GetReturnValue().Set(Nan::True()); 379 | return; 380 | 381 | } else if (name->Equals(Nan::New("preventExtensions").ToLocalChecked())) { 382 | handler->SetHiddenValue(Nan::New("extensible").ToLocalChecked(), Nan::False()); 383 | info.GetReturnValue().Set(Nan::True()); 384 | return; 385 | } 386 | } 387 | 388 | // Harmony Proxy handling of fix 389 | Local fix = Local::Cast(handler->Get(Nan::New("fix").ToLocalChecked())); 390 | #ifdef _WIN32 391 | // On windows you get "error C2466: cannot allocate an array of constant size 0" and we use a pointer 392 | Local* argv; 393 | #else 394 | Local argv[0]; 395 | #endif 396 | Local pieces = fix->Call(info[0]->ToObject(), 0, argv); 397 | 398 | if (pieces.IsEmpty() || !pieces->IsObject()) { 399 | Nan::ThrowTypeError("Cannot lock object."); 400 | return; 401 | } 402 | 403 | Local parts = pieces->ToObject(); 404 | 405 | // set the appropriate parameters 406 | if (name->Equals(Nan::New("freeze").ToLocalChecked())) { 407 | parts->SetHiddenValue(Nan::New("frozen").ToLocalChecked(), Nan::True()); 408 | parts->SetHiddenValue(Nan::New("sealed").ToLocalChecked(), Nan::True()); 409 | parts->SetHiddenValue(Nan::New("extensible").ToLocalChecked(), Nan::False()); 410 | 411 | } else if (name->Equals(Nan::New("seal").ToLocalChecked())) { 412 | parts->SetHiddenValue(Nan::New("sealed").ToLocalChecked(), Nan::True()); 413 | parts->SetHiddenValue(Nan::New("extensible").ToLocalChecked(), Nan::False()); 414 | 415 | } else if (name->Equals(Nan::New("preventExtensions").ToLocalChecked())) { 416 | parts->SetHiddenValue(Nan::New("extensible").ToLocalChecked(), Nan::False()); 417 | } 418 | 419 | parts->SetHiddenValue(Nan::New("trapping").ToLocalChecked(), Nan::False()); 420 | 421 | // overwrite the handler, making handler available for GC 422 | obj->SetInternalField(0, parts); 423 | 424 | info.GetReturnValue().Set(Nan::True()); 425 | } 426 | 427 | /** 428 | * Used as a handler for determining isTrapped, 429 | * isFrozen, isSealed, and isExtensible 430 | * 431 | * @param Object 432 | * @returns Boolean 433 | * @throws Error, TypeError 434 | */ 435 | NAN_METHOD(NodeProxy::IsLocked) { 436 | 437 | Local name = info.Callee()->GetName()->ToString(); 438 | 439 | if (info.Length() < 1) { 440 | Nan::ThrowError(String::Concat(name, 441 | Nan::New(" requires at least one (1) argument.").ToLocalChecked())); 442 | return; 443 | } 444 | 445 | Local arg = info[0]->ToObject(); 446 | 447 | if (arg->InternalFieldCount() < 1) { 448 | Nan::ThrowTypeError( 449 | "Locking functions expect first argument " 450 | "to be intialized by Proxy"); 451 | return; 452 | } 453 | 454 | Local hide = arg->GetInternalField(0); 455 | 456 | if (hide.IsEmpty() || !hide->IsObject()) { 457 | Nan::ThrowTypeError( 458 | "Locking functions expect first argument " 459 | "to be intialized by Proxy"); 460 | return; 461 | } 462 | 463 | Local obj = hide->ToObject(); 464 | 465 | if (name->Equals(Nan::New("isExtensible").ToLocalChecked())) { 466 | info.GetReturnValue().Set(obj->GetHiddenValue(Nan::New("extensible").ToLocalChecked())->ToBoolean()); 467 | return; 468 | 469 | } else if (name->Equals(Nan::New("isSealed").ToLocalChecked())) { 470 | info.GetReturnValue().Set(obj->GetHiddenValue(Nan::New("sealed").ToLocalChecked())->ToBoolean()); 471 | return; 472 | 473 | } else if (name->Equals(Nan::New("isTrapping").ToLocalChecked())) { 474 | info.GetReturnValue().Set(obj->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->ToBoolean()); 475 | return; 476 | 477 | } else if (name->Equals(Nan::New("isFrozen").ToLocalChecked())) { 478 | info.GetReturnValue().Set(obj->GetHiddenValue(Nan::New("frozen").ToLocalChecked())->ToBoolean()); 479 | return; 480 | } 481 | 482 | info.GetReturnValue().Set(Nan::False()); 483 | } 484 | 485 | /** 486 | * Part of ECMAScript 5, but only for use on 487 | * Objects and Functions created by Proxy 488 | * 489 | * @param Object 490 | * @param String - the name of the property 491 | * @returns PropertyDescriptor 492 | * @throws Error, TypeError 493 | */ 494 | NAN_METHOD(NodeProxy::GetOwnPropertyDescriptor) { 495 | 496 | if (info.Length() < 2) { 497 | Nan::ThrowError("getOwnPropertyDescriptor requires " 498 | "at least two (2) arguments."); 499 | return; 500 | } 501 | 502 | if (!info[1]->IsString() && !info[1]->IsNumber()) { 503 | Nan::ThrowTypeError("getOwnPropertyDescriptor requires " 504 | "the second argument to be a String or a Number."); 505 | return; 506 | } 507 | 508 | Local obj = info[0]->ToObject(); 509 | Local name = info[1]->ToString(); 510 | 511 | if (obj->InternalFieldCount() < 1) { 512 | Nan::ThrowTypeError("getOwnPropertyDescriptor expects " 513 | "first argument to be intialized by Proxy"); 514 | return; 515 | } 516 | 517 | Local temp = obj->GetInternalField(0); 518 | 519 | if (temp.IsEmpty() || !temp->IsObject()) { 520 | Nan::ThrowTypeError("getOwnPropertyDescriptor expects " 521 | "first argument to be intialized by Proxy"); 522 | return; 523 | } 524 | 525 | Local handler = temp->ToObject(); 526 | 527 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 528 | info.GetReturnValue().Set(handler->Get(name)); 529 | return; 530 | } 531 | 532 | Local getOwn = Local::Cast( 533 | handler->Get(Nan::New("getOwnPropertyDescriptor").ToLocalChecked())); 534 | 535 | Local argv[1] = {info[1]}; 536 | info.GetReturnValue().Set(getOwn->Call(obj, 1, argv)); 537 | } 538 | 539 | /** 540 | * Part of ECMAScript 5, but only for use on 541 | * Objects and Functions created by Proxy 542 | * 543 | * @param Object 544 | * @param String - the name of the property 545 | * @param PropertyDescriptor 546 | * @returns Boolean 547 | * @throws Error, TypeError 548 | */ 549 | NAN_METHOD(NodeProxy::DefineProperty) { 550 | 551 | if (info.Length() < 3) { 552 | Nan::ThrowError("defineProperty requires at least three (3) arguments."); 553 | } 554 | 555 | if (!info[1]->IsString() && !info[1]->IsNumber()) { 556 | Nan::ThrowTypeError("defineProperty requires the " 557 | "second argument to be a String or a Number."); 558 | return; 559 | } 560 | 561 | if (!info[2]->IsObject()) { 562 | Nan::ThrowTypeError("defineProperty requires the third argument " 563 | "to be an Object of the type PropertyDescriptor."); 564 | return; 565 | } 566 | 567 | Local obj = info[0]->ToObject(); 568 | 569 | if (obj->InternalFieldCount() < 1) { 570 | Nan::ThrowTypeError("defineProperty expects first " 571 | "argument to be intialized by Proxy"); 572 | return; 573 | } 574 | 575 | Local temp = obj->GetInternalField(0); 576 | 577 | if (temp.IsEmpty() || !temp->IsObject()) { 578 | Nan::ThrowTypeError("defineProperty expects first argument " 579 | "to be intialized by Proxy"); 580 | return; 581 | } 582 | 583 | Local name = info[1]->ToString(); 584 | Local handler = temp->ToObject(); 585 | 586 | if (handler->GetHiddenValue(Nan::New("sealed").ToLocalChecked())->BooleanValue() || 587 | !handler->Has(Nan::New("defineProperty").ToLocalChecked())) { 588 | info.GetReturnValue().Set(Nan::False()); 589 | return; 590 | } 591 | 592 | if (!handler->GetHiddenValue(Nan::New("extensible").ToLocalChecked())->BooleanValue() && 593 | !handler->Has(name)) { 594 | info.GetReturnValue().Set(Nan::False()); 595 | return; 596 | } 597 | 598 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 599 | Local desc = handler->Get(name)->ToObject(); 600 | 601 | if (desc->Get(Nan::New("configurable").ToLocalChecked())->BooleanValue()) { 602 | info.GetReturnValue().Set(Nan::New( 603 | handler->Set(name, info[2]->ToObject()))); 604 | return; 605 | } 606 | info.GetReturnValue().Set(Nan::False()); 607 | return; 608 | } 609 | 610 | Local def = Local::Cast( 611 | handler->Get(Nan::New("defineProperty").ToLocalChecked())); 612 | 613 | Local argv[2] = {info[1], info[2]->ToObject()}; 614 | 615 | info.GetReturnValue().Set(def->Call(obj, 2, argv)->ToBoolean()); 616 | } 617 | 618 | /** 619 | * Part of ECMAScript 5, but only for use on 620 | * Objects and Functions created by Proxy 621 | * 622 | * @param Object 623 | * @param Object - name/PropertyDescriptor pairs 624 | * @returns Boolean 625 | * @throws Error, TypeError 626 | */ 627 | NAN_METHOD(NodeProxy::DefineProperties) { 628 | 629 | if (info.Length() < 2) { 630 | Nan::ThrowError("defineProperty requires at least three (3) arguments."); 631 | return; 632 | } 633 | 634 | if (!info[1]->IsObject()) { 635 | Nan::ThrowTypeError("defineProperty requires the third argument " 636 | "to be an Object of the type PropertyDescriptor."); 637 | return; 638 | } 639 | 640 | Local obj = info[0]->ToObject(); 641 | 642 | if (obj->InternalFieldCount() < 1) { 643 | Nan::ThrowTypeError("defineProperty expects first " 644 | "argument to be intialized by Proxy"); 645 | return; 646 | } 647 | 648 | Local temp = obj->GetInternalField(0); 649 | 650 | if (!temp.IsEmpty() && temp->IsObject()) { 651 | Local props = info[1]->ToObject(); 652 | Local handler = temp->ToObject(); 653 | 654 | if (handler->GetHiddenValue(Nan::New("sealed").ToLocalChecked())->BooleanValue()) { 655 | info.GetReturnValue().Set(Nan::False()); 656 | return; 657 | } 658 | 659 | bool extensible = handler->GetHiddenValue( 660 | Nan::New("extensible").ToLocalChecked())->BooleanValue(); 661 | Local names = props->GetPropertyNames(); 662 | uint32_t i = 0, l = names->Length(); 663 | 664 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 665 | for (;i < l; ++i) { 666 | Local name = names->CloneElementAt(i); 667 | 668 | if (handler->Has(name->ToString()) && 669 | handler->Get(name->ToString())->IsObject() 670 | ) { 671 | Local tempObj = handler->Get(name->ToString())->ToObject(); 672 | 673 | if (tempObj->Get(Nan::New("configurable").ToLocalChecked())->BooleanValue()) { 674 | if (!handler->Set(name->ToString(), 675 | props->Get(name->ToString()))) { 676 | Nan::ThrowError( 677 | String::Concat( 678 | Nan::New("Unable to define property: ").ToLocalChecked(), 679 | name->ToString())); 680 | return; 681 | } 682 | } 683 | } else { 684 | Nan::ThrowError(String::Concat( 685 | Nan::New("Unable to define property: ").ToLocalChecked(), 686 | name->ToString())); 687 | return; 688 | } 689 | } 690 | info.GetReturnValue().Set(Nan::True()); 691 | return; 692 | } 693 | 694 | Local def = Local::Cast(handler->Get(Nan::New("defineProperty").ToLocalChecked())); 695 | 696 | TryCatch firstTry; 697 | for (;i < l; ++i) { 698 | Local name = names->Get(i); 699 | 700 | if (extensible || obj->Has(name->ToString())) { 701 | Local pd = props->Get(name->ToString()); 702 | Local argv[2] = {name, pd}; 703 | def->Call(obj, 2, argv); 704 | 705 | if (firstTry.HasCaught()) { 706 | firstTry.ReThrow(); 707 | return; 708 | } 709 | } 710 | } 711 | info.GetReturnValue().Set(Nan::True()); 712 | return; 713 | } 714 | info.GetReturnValue().Set(Nan::False()); 715 | } 716 | 717 | /** 718 | * Function used for a constructor and invocation 719 | * handler of a Proxy created function 720 | * Calls the appropriate function attached when the Proxy was created 721 | * 722 | * @param ...info 723 | * @returns mixed 724 | * @throws Error 725 | */ 726 | NAN_METHOD(NodeProxy::New) { 727 | 728 | if (info.Callee()->InternalFieldCount() < 1 && info.Data().IsEmpty()) { 729 | Nan::ThrowTypeError("defineProperty expects first " 730 | "argument to be intialized by Proxy"); 731 | return; 732 | } 733 | 734 | Local v, ret, data = info.Holder()->GetInternalField(0); 735 | 736 | if (data.IsEmpty() || !data->IsObject()) { 737 | Nan::ThrowError("Invalid reference to Proxy#constructor"); 738 | return; 739 | } 740 | 741 | Local fn; 742 | Local obj = data->ToObject(); 743 | 744 | if (info.IsConstructCall()) { 745 | v = obj->GetHiddenValue(Nan::New("constructorTrap").ToLocalChecked()); 746 | 747 | if (!v.IsEmpty() && v->IsFunction()) { 748 | fn = Local::Cast(v); 749 | } else { 750 | fn = Local::Cast( 751 | obj->GetHiddenValue(Nan::New("callTrap").ToLocalChecked())); 752 | } 753 | } else { 754 | fn = Local::Cast(obj->GetHiddenValue(Nan::New("callTrap").ToLocalChecked())); 755 | } 756 | 757 | int i = 0, l = info.Length(); 758 | Local* argv = new Local[l]; 759 | 760 | for (; i < l; ++i) { 761 | argv[i] = info[i]; 762 | } 763 | 764 | ret = fn->Call(info.This(), info.Length(), argv); 765 | 766 | if (info.IsConstructCall()) { 767 | if (!ret.IsEmpty()) { 768 | info.GetReturnValue().Set(ret); 769 | return; 770 | } 771 | info.GetReturnValue().Set(info.This()); 772 | return; 773 | } 774 | info.GetReturnValue().Set(ret); 775 | } 776 | 777 | /** 778 | * Invoked for accessing the named properties of an object 779 | * 780 | * 781 | * 782 | */ 783 | NAN_PROPERTY_GETTER(NodeProxy::GetNamedProperty) { 784 | 785 | if (info.This()->InternalFieldCount() < 1 || info.Data().IsEmpty()) { 786 | Nan::ThrowTypeError("SetNamedProperty intercepted " 787 | "by non-Proxy object"); 788 | return; 789 | } 790 | 791 | Local argv1[1] = {property}; 792 | Local data = info.This()->InternalFieldCount() > 0 ? 793 | info.This()->GetInternalField(0) : 794 | info.Data(); 795 | 796 | if (!data->IsObject()) { 797 | info.GetReturnValue().SetUndefined(); 798 | return; 799 | } 800 | 801 | Local fn; 802 | Local handler = data->ToObject(); 803 | 804 | // if the Proxy isn't trapping, return 805 | // the value set on the property descriptor 806 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 807 | info.GetReturnValue().Set(CallPropertyDescriptorGet(handler->Get(property), info.This(), argv1)); 808 | return; 809 | } 810 | 811 | Local get = handler->Get(Nan::New("get").ToLocalChecked()); 812 | if (get->IsFunction()) { 813 | fn = Local::Cast(get); 814 | Local argv[2] = {info.This(), property}; 815 | 816 | info.GetReturnValue().Set(fn->Call(handler, 2, argv)); 817 | return; 818 | } 819 | 820 | Local getPropertyDescriptor = handler->Get(Nan::New("getPropertyDescriptor").ToLocalChecked()); 821 | if (getPropertyDescriptor->IsFunction()) { 822 | fn = Local::Cast(getPropertyDescriptor); 823 | 824 | info.GetReturnValue().Set(CallPropertyDescriptorGet(fn->Call(handler, 1, argv1), info.This(), argv1)); 825 | return; 826 | } 827 | 828 | Local getOwnPropertyDescriptor = handler->Get(Nan::New("getOwnPropertyDescriptor").ToLocalChecked()); 829 | if (getOwnPropertyDescriptor->IsFunction()) { 830 | fn = Local::Cast(getOwnPropertyDescriptor); 831 | 832 | info.GetReturnValue().Set(CallPropertyDescriptorGet(fn->Call(handler, 1, argv1), info.This(), argv1)); 833 | return; 834 | } 835 | info.GetReturnValue().SetUndefined(); // <-- silence warnings for 0.10.x 836 | } 837 | 838 | NAN_INLINE Local NodeProxy::CallPropertyDescriptorGet(Local descriptor, Handle context, Local info[1]) { 839 | Nan::EscapableHandleScope scope; 840 | if (descriptor->IsObject()) { 841 | Local get = descriptor->ToObject()->Get(Nan::New("get").ToLocalChecked()); 842 | 843 | if (get->IsFunction()) { 844 | Local fn = Local::Cast(get); 845 | return fn->Call(context, 1, info); 846 | } 847 | 848 | return descriptor->ToObject()->Get(Nan::New("value").ToLocalChecked()); 849 | } 850 | 851 | return scope.Escape(Nan::Undefined()); 852 | } 853 | 854 | /** 855 | * Invoked for setting the named properties of an object 856 | * 857 | * 858 | * 859 | */ 860 | NAN_PROPERTY_SETTER(NodeProxy::SetNamedProperty) { 861 | 862 | if (info.This()->InternalFieldCount() < 1 || info.Data().IsEmpty()) { 863 | Nan::ThrowTypeError("SetNamedProperty intercepted " 864 | "by non-Proxy object"); 865 | return; 866 | } 867 | 868 | Local argv2[2] = {property, value}; 869 | Local data = info.This()->InternalFieldCount() > 0 ? 870 | info.This()->GetInternalField(0) : 871 | info.Data(); 872 | 873 | if (!data->IsObject()) { 874 | info.GetReturnValue().SetUndefined(); 875 | return; 876 | } 877 | 878 | Local handler = data->ToObject(); 879 | 880 | // if the Proxy isn't trapping, return the 881 | // value set on the property descriptor 882 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 883 | if (handler->GetHiddenValue(Nan::New("extensible").ToLocalChecked())->BooleanValue() || 884 | handler->Has(property) 885 | ) { 886 | Local pd = handler->Get(property); 887 | 888 | if (!pd->IsObject()) { 889 | info.GetReturnValue().SetUndefined(); 890 | return; 891 | } 892 | 893 | Local pd_obj = pd->ToObject(); 894 | 895 | if (!pd_obj->GetHiddenValue( 896 | Nan::New("writable").ToLocalChecked())->BooleanValue() 897 | ) { 898 | Nan::ThrowError( 899 | String::Concat( 900 | Nan::New("In accessible property: ").ToLocalChecked(), 901 | property)); 902 | return; 903 | } 904 | 905 | Local set = pd_obj->Get(Nan::New("set").ToLocalChecked()); 906 | if (set->IsFunction()) { 907 | Local fn = Local::Cast(set); 908 | fn->Call(info.This(), 2, argv2); 909 | 910 | info.GetReturnValue().Set(value); 911 | return; 912 | } 913 | 914 | if (pd_obj->Set(Nan::New("value").ToLocalChecked(), value)) { 915 | info.GetReturnValue().Set(value); 916 | return; 917 | } 918 | info.GetReturnValue().SetUndefined(); 919 | return; 920 | } 921 | info.GetReturnValue().SetUndefined(); 922 | return; 923 | } 924 | 925 | // does the ProxyHandler have a set method? 926 | Local set = handler->Get(Nan::New("set").ToLocalChecked()); 927 | if (set->IsFunction()) { 928 | Local set_fn = Local::Cast(set); 929 | Local argv3[3] = {info.This(), property, value}; 930 | set_fn->Call(handler, 3, argv3); 931 | 932 | info.GetReturnValue().Set(value); 933 | return; 934 | } 935 | 936 | Local getOwnPropertyDescriptor = handler->Get(Nan::New("getOwnPropertyDescriptor").ToLocalChecked()); 937 | if (getOwnPropertyDescriptor->IsFunction()) { 938 | Local gopd_fn = Local::Cast(getOwnPropertyDescriptor); 939 | Local argv[1] = {property}; 940 | info.GetReturnValue().Set(CallPropertyDescriptorSet(gopd_fn->Call(handler, 1, argv), info.This(), property, value)); 941 | return; 942 | } 943 | 944 | Local getPropertyDescriptor = handler->Get(Nan::New("getPropertyDescriptor").ToLocalChecked()); 945 | if (getPropertyDescriptor->IsFunction()) { 946 | Local gpd_fn = Local::Cast(getPropertyDescriptor); 947 | Local argv[1] = {property}; 948 | info.GetReturnValue().Set(CallPropertyDescriptorSet(gpd_fn->Call(handler, 1, argv), info.This(), property, value)); 949 | return; 950 | } 951 | 952 | info.GetReturnValue().SetUndefined(); 953 | } 954 | 955 | NAN_INLINE Local NodeProxy::CallPropertyDescriptorSet(Local descriptor, Handle context, Local name, Local value) { 956 | Nan::EscapableHandleScope scope; 957 | if (descriptor->IsObject()) { 958 | Local pd = descriptor->ToObject(); 959 | Local set = pd->Get(Nan::New("set").ToLocalChecked()); 960 | 961 | if (set->IsFunction()) { 962 | Local fn = Local::Cast(set); 963 | Local info[2] = { name, value }; 964 | return fn->Call(context, 2, info); 965 | 966 | } else if (pd->Get(Nan::New("writable").ToLocalChecked())->BooleanValue()) { 967 | if (pd->Set(Nan::New("value").ToLocalChecked(), value)) { 968 | return value; 969 | } 970 | } 971 | } 972 | 973 | return scope.Escape(Nan::Undefined()); 974 | } 975 | 976 | 977 | /** 978 | * Invoked for determining if an object has a specific property 979 | * 980 | * 981 | * 982 | */ 983 | NAN_PROPERTY_QUERY(NodeProxy::QueryNamedPropertyInteger) { 984 | 985 | Local DoesntHavePropertyResponse; 986 | Local HasPropertyResponse = Nan::New(None); 987 | 988 | if (info.This()->InternalFieldCount() > 0 || !info.Data().IsEmpty()) { 989 | Local data = info.This()->InternalFieldCount() > 0 ? 990 | info.This()->GetInternalField(0) : 991 | info.Data(); 992 | 993 | if (!data->IsObject()) { 994 | info.GetReturnValue().Set(DoesntHavePropertyResponse); 995 | return; 996 | } 997 | 998 | Local handler = data->ToObject(); 999 | 1000 | // if the Proxy isn't trapping, 1001 | // return the value set on the property descriptor 1002 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 1003 | if (handler->Has(property)) { 1004 | Local pd = handler->Get(property); 1005 | 1006 | if (pd->IsObject()) { 1007 | info.GetReturnValue().Set(GetPropertyAttributeFromPropertyDescriptor(pd->ToObject())); 1008 | return; 1009 | } 1010 | info.GetReturnValue().Set(HasPropertyResponse); 1011 | return; 1012 | } 1013 | info.GetReturnValue().Set(DoesntHavePropertyResponse); 1014 | return; 1015 | } 1016 | 1017 | Local argv[1] = {property}; 1018 | 1019 | Local hasOwn = handler->Get(Nan::New("hasOwn").ToLocalChecked()); 1020 | if (hasOwn->IsFunction()) { 1021 | Local hasOwn_fn = Local::Cast(hasOwn); 1022 | info.GetReturnValue().Set(hasOwn_fn->Call(handler, 1, argv)->BooleanValue() ? 1023 | HasPropertyResponse : 1024 | DoesntHavePropertyResponse); 1025 | return; 1026 | } 1027 | 1028 | Local has = handler->Get(Nan::New("has").ToLocalChecked()); 1029 | if (has->IsFunction()) { 1030 | Local has_fn = Local::Cast(has); 1031 | info.GetReturnValue().Set(has_fn->Call(handler, 1, argv)->BooleanValue() ? 1032 | HasPropertyResponse : 1033 | DoesntHavePropertyResponse); 1034 | return; 1035 | } 1036 | 1037 | Local getOwnPropertyDescriptor = handler->Get(Nan::New("getOwnPropertyDescriptor").ToLocalChecked()); 1038 | if (getOwnPropertyDescriptor->IsFunction()) { 1039 | Local gopd_fn = Local::Cast(getOwnPropertyDescriptor); 1040 | Local gopd_pd = gopd_fn->Call(handler, 1, argv); 1041 | 1042 | if (gopd_pd->IsObject()) { 1043 | info.GetReturnValue().Set(GetPropertyAttributeFromPropertyDescriptor(gopd_pd->ToObject())); 1044 | return; 1045 | } 1046 | } 1047 | 1048 | Local getPropertyDescriptor = handler->Get(Nan::New("getPropertyDescriptor").ToLocalChecked()); 1049 | if (handler->Has(Nan::New("getPropertyDescriptor").ToLocalChecked())) { 1050 | Local gpd_fn = Local::Cast(getPropertyDescriptor); 1051 | Local gpd_pd = gpd_fn->Call(handler, 1, argv); 1052 | 1053 | if (gpd_pd->IsObject()) { 1054 | info.GetReturnValue().Set(GetPropertyAttributeFromPropertyDescriptor(gpd_pd->ToObject())); 1055 | return; 1056 | } else if (gpd_pd->IsUndefined()) { 1057 | info.GetReturnValue().Set(DoesntHavePropertyResponse); 1058 | return; 1059 | } 1060 | } 1061 | } 1062 | 1063 | info.GetReturnValue().Set(DoesntHavePropertyResponse); 1064 | } 1065 | 1066 | /** 1067 | * Find the appropriate PropertyAttribute 1068 | * for a given PropertyDescriptor object 1069 | * 1070 | * 1071 | */ 1072 | Local 1073 | NodeProxy::GetPropertyAttributeFromPropertyDescriptor(Local pd) { 1074 | uint32_t ret = None; 1075 | 1076 | if (pd->Get(Nan::New("configurable").ToLocalChecked())->IsBoolean() && 1077 | !pd->Get(Nan::New("configurable").ToLocalChecked())->BooleanValue()) { 1078 | ret &= DontDelete; 1079 | } 1080 | 1081 | if (pd->Get(Nan::New("enumerable").ToLocalChecked())->IsBoolean() && 1082 | !pd->Get(Nan::New("enumerable").ToLocalChecked())->BooleanValue()) { 1083 | // return Nan::New(DontEnum); 1084 | ret &= DontEnum; 1085 | } 1086 | 1087 | if (pd->Get(Nan::New("writable").ToLocalChecked())->IsBoolean() && 1088 | !pd->Get(Nan::New("writable").ToLocalChecked())->BooleanValue()) { 1089 | // return Nan::New(ReadOnly); 1090 | ret &= ReadOnly; 1091 | } 1092 | 1093 | return Nan::New(ret); 1094 | } 1095 | 1096 | /** 1097 | * Invoked when deleting the named property of an object 1098 | * 1099 | * 1100 | * 1101 | */ 1102 | NAN_PROPERTY_DELETER(NodeProxy::DeleteNamedProperty) { 1103 | 1104 | if (info.This()->InternalFieldCount() > 0 || !info.Data().IsEmpty()) { 1105 | Local data = info.This()->InternalFieldCount() > 0 ? 1106 | info.This()->GetInternalField(0) : 1107 | info.Data(); 1108 | 1109 | if (!data->IsObject()) { 1110 | info.GetReturnValue().Set(Nan::False()); 1111 | return; 1112 | } 1113 | 1114 | Local handler = data->ToObject(); 1115 | // if the Proxy isn't trapping, 1116 | // return the value set on the property descriptor 1117 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 1118 | if (!handler->GetHiddenValue(Nan::New("frozen").ToLocalChecked())->BooleanValue()) { 1119 | Local pd = handler->Get(property); 1120 | 1121 | if (pd->IsObject()) { 1122 | Local pd_obj = pd->ToObject(); 1123 | 1124 | if (pd_obj->Get(Nan::New("configurable").ToLocalChecked())->IsBoolean() && 1125 | pd_obj->Get(Nan::New("configurable").ToLocalChecked())->BooleanValue() 1126 | ) { 1127 | info.GetReturnValue().Set(Nan::New(handler->Delete(property))); 1128 | return; 1129 | } 1130 | } 1131 | } 1132 | info.GetReturnValue().Set(Nan::False()); 1133 | return; 1134 | } 1135 | 1136 | Local delete_ = handler->Get(Nan::New("delete").ToLocalChecked()); 1137 | if (delete_->IsFunction()) { 1138 | Local fn = Local::Cast(delete_); 1139 | Local argv[1] = {property}; 1140 | info.GetReturnValue().Set(fn->Call(handler, 1, argv)->ToBoolean()); 1141 | return; 1142 | } 1143 | } 1144 | 1145 | info.GetReturnValue().Set(Nan::False()); 1146 | } 1147 | 1148 | /** 1149 | * Invoked for enumerating all properties of an object 1150 | * 1151 | * 1152 | * 1153 | */ 1154 | NAN_PROPERTY_ENUMERATOR(NodeProxy::EnumerateNamedProperties) { 1155 | 1156 | if (info.This()->InternalFieldCount() > 0 || !info.Data().IsEmpty()) { 1157 | Local data = info.This()->InternalFieldCount() > 0 ? 1158 | info.This()->GetInternalField(0) : 1159 | info.Data(); 1160 | 1161 | if (!data->IsObject()) { 1162 | info.GetReturnValue().Set(Nan::New()); 1163 | return; 1164 | } 1165 | 1166 | Local handler = data->ToObject(); 1167 | #ifdef _WIN32 1168 | // On windows you get "error C2466: cannot allocate an array of constant size 0" and we use a pointer 1169 | Local* argv; 1170 | #else 1171 | Local argv[0]; 1172 | #endif 1173 | 1174 | // if the Proxy isn't trapping, 1175 | // return the value set on the property descriptor 1176 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 1177 | info.GetReturnValue().Set(handler->GetPropertyNames()); 1178 | return; 1179 | } 1180 | 1181 | Local enumerate = handler->Get(Nan::New("enumerate").ToLocalChecked()); 1182 | if (enumerate->IsFunction()) { 1183 | Local enumerate_fn = Local::Cast(enumerate); 1184 | Local names = enumerate_fn->Call(handler, 0, argv); 1185 | 1186 | if (names->IsArray()) { 1187 | info.GetReturnValue().Set(Local::Cast(names->ToObject())); 1188 | return; 1189 | } 1190 | } 1191 | 1192 | Local keys = handler->Get(Nan::New("keys").ToLocalChecked()); 1193 | if (keys->IsFunction()) { 1194 | Local keys_fn = Local::Cast(enumerate); 1195 | Local names = keys_fn->Call(handler, 0, argv); 1196 | 1197 | if (names->IsArray()) { 1198 | info.GetReturnValue().Set(Local::Cast(names->ToObject())); 1199 | return; 1200 | } 1201 | } 1202 | 1203 | Local getPropertyNames = handler->Get(Nan::New("getPropertyNames").ToLocalChecked()); 1204 | if (getPropertyNames->IsFunction()) { 1205 | Local gpn_fn = Local::Cast(getPropertyNames); 1206 | Local names = gpn_fn->Call(handler, 0, argv); 1207 | 1208 | if (names->IsArray()) { 1209 | info.GetReturnValue().Set(Local::Cast(names->ToObject())); 1210 | return; 1211 | } 1212 | } 1213 | } 1214 | 1215 | info.GetReturnValue().Set(Nan::New()); 1216 | } 1217 | 1218 | /** 1219 | * Invoked for accessing the given indexed property of an object 1220 | * 1221 | * 1222 | * 1223 | */ 1224 | NAN_INDEX_GETTER(NodeProxy::GetIndexedProperty) { 1225 | 1226 | if (info.This()->InternalFieldCount() < 1 || info.Data().IsEmpty()) { 1227 | Nan::ThrowTypeError("SetNamedProperty intercepted " 1228 | "by non-Proxy object"); 1229 | return; 1230 | } 1231 | 1232 | Local idx = Nan::New(index); 1233 | Local argv1[1] = {idx}; 1234 | Local data = info.This()->InternalFieldCount() > 0 ? 1235 | info.This()->GetInternalField(0) : 1236 | info.Data(); 1237 | 1238 | if (!data->IsObject()) { 1239 | info.GetReturnValue().SetUndefined(); 1240 | return; 1241 | } 1242 | 1243 | Local fn; 1244 | Local handler = data->ToObject(); 1245 | 1246 | // if the Proxy isn't trapping, return 1247 | // the value set on the index descriptor 1248 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 1249 | info.GetReturnValue().Set(CallPropertyDescriptorGet(handler->Get(idx), info.This(), argv1)); 1250 | return; 1251 | } 1252 | 1253 | Local get = handler->Get(Nan::New("get").ToLocalChecked()); 1254 | if (get->IsFunction()) { 1255 | fn = Local::Cast(get); 1256 | Local argv[2] = {info.This(), idx}; 1257 | 1258 | info.GetReturnValue().Set(fn->Call(handler, 2, argv)); 1259 | return; 1260 | } 1261 | 1262 | Local getPropertyDescriptor = handler->Get(Nan::New("getPropertyDescriptor").ToLocalChecked()); 1263 | if (getPropertyDescriptor->IsFunction()) { 1264 | fn = Local::Cast(getPropertyDescriptor); 1265 | 1266 | info.GetReturnValue().Set(CallPropertyDescriptorGet(fn->Call(handler, 1, argv1), info.This(), argv1)); 1267 | return; 1268 | } 1269 | 1270 | Local getOwnPropertyDescriptor = handler->Get(Nan::New("getOwnPropertyDescriptor").ToLocalChecked()); 1271 | if (getOwnPropertyDescriptor->IsFunction()) { 1272 | fn = Local::Cast(getOwnPropertyDescriptor); 1273 | 1274 | info.GetReturnValue().Set(CallPropertyDescriptorGet(fn->Call(handler, 1, argv1), info.This(), argv1)); 1275 | return; 1276 | } 1277 | info.GetReturnValue().SetUndefined(); // <-- silence warnings for 0.10.x 1278 | } 1279 | 1280 | /** 1281 | * Invoked for setting the given indexed property of an object 1282 | * 1283 | * 1284 | * 1285 | */ 1286 | NAN_INDEX_SETTER(NodeProxy::SetIndexedProperty) { 1287 | 1288 | if (info.This()->InternalFieldCount() < 1 || info.Data().IsEmpty()) { 1289 | Nan::ThrowTypeError("SetNamedProperty intercepted " 1290 | "by non-Proxy object"); 1291 | return; 1292 | } 1293 | 1294 | Local idx = Nan::New(index); 1295 | Local argv2[2] = {idx, value}; 1296 | Local data = info.This()->InternalFieldCount() > 0 ? 1297 | info.This()->GetInternalField(0) : 1298 | info.Data(); 1299 | 1300 | if (!data->IsObject()) { 1301 | info.GetReturnValue().SetUndefined(); 1302 | return; 1303 | } 1304 | 1305 | Local handler = data->ToObject(); 1306 | 1307 | // if the Proxy isn't trapping, return the 1308 | // value set on the index descriptor 1309 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 1310 | if (handler->GetHiddenValue(Nan::New("extensible").ToLocalChecked())->BooleanValue() || 1311 | handler->Has(index) 1312 | ) { 1313 | Local pd = handler->Get(index); 1314 | 1315 | if (!pd->IsObject()) { 1316 | info.GetReturnValue().SetUndefined(); 1317 | return; 1318 | } 1319 | 1320 | Local pd_obj = pd->ToObject(); 1321 | 1322 | if (!pd_obj->GetHiddenValue( 1323 | Nan::New("writable").ToLocalChecked())->BooleanValue() 1324 | ) { 1325 | Nan::ThrowError( 1326 | String::Concat( 1327 | Nan::New("In accessible index: ").ToLocalChecked(), 1328 | Local::Cast(idx))); 1329 | return; 1330 | } 1331 | 1332 | Local set = pd_obj->Get(Nan::New("set").ToLocalChecked()); 1333 | if (set->IsFunction()) { 1334 | Local fn = Local::Cast(set); 1335 | fn->Call(info.This(), 2, argv2); 1336 | 1337 | info.GetReturnValue().Set(value); 1338 | return; 1339 | } 1340 | 1341 | if (pd_obj->Set(Nan::New("value").ToLocalChecked(), value)) { 1342 | info.GetReturnValue().Set(value); 1343 | return; 1344 | } 1345 | info.GetReturnValue().SetUndefined(); 1346 | return; 1347 | } 1348 | info.GetReturnValue().SetUndefined(); 1349 | return; 1350 | } 1351 | 1352 | // does the ProxyHandler have a set method? 1353 | Local set = handler->Get(Nan::New("set").ToLocalChecked()); 1354 | if (set->IsFunction()) { 1355 | Local set_fn = Local::Cast(set); 1356 | Local argv3[3] = {info.This(), idx, value}; 1357 | set_fn->Call(handler, 3, argv3); 1358 | 1359 | info.GetReturnValue().Set(value); 1360 | return; 1361 | } 1362 | 1363 | Local getOwnPropertyDescriptor = handler->Get(Nan::New("getOwnPropertyDescriptor").ToLocalChecked()); 1364 | if (getOwnPropertyDescriptor->IsFunction()) { 1365 | Local gopd_fn = Local::Cast(getOwnPropertyDescriptor); 1366 | Local argv[1] = {idx}; 1367 | info.GetReturnValue().Set(CallPropertyDescriptorSet(gopd_fn->Call(handler, 1, argv), info.This(), idx, value)); 1368 | return; 1369 | } 1370 | 1371 | Local getPropertyDescriptor = handler->Get(Nan::New("getPropertyDescriptor").ToLocalChecked()); 1372 | if (getPropertyDescriptor->IsFunction()) { 1373 | Local gpd_fn = Local::Cast(getPropertyDescriptor); 1374 | Local argv[1] = {idx}; 1375 | info.GetReturnValue().Set(CallPropertyDescriptorSet(gpd_fn->Call(handler, 1, argv), info.This(), idx, value)); 1376 | return; 1377 | } 1378 | 1379 | info.GetReturnValue().SetUndefined(); 1380 | } 1381 | 1382 | /** 1383 | * Invoked for determining if an object has a given indexed property 1384 | * 1385 | * 1386 | * 1387 | */ 1388 | NAN_INDEX_QUERY(NodeProxy::QueryIndexedPropertyInteger) { 1389 | 1390 | Local idx = Nan::New(index); 1391 | Local DoesntHavePropertyResponse; 1392 | Local HasPropertyResponse = Nan::New(None); 1393 | 1394 | if (info.This()->InternalFieldCount() > 0 || !info.Data().IsEmpty()) { 1395 | Local data = info.This()->InternalFieldCount() > 0 ? 1396 | info.This()->GetInternalField(0) : 1397 | info.Data(); 1398 | 1399 | if (!data->IsObject()) { 1400 | info.GetReturnValue().Set(DoesntHavePropertyResponse); 1401 | return; 1402 | } 1403 | 1404 | Local handler = data->ToObject(); 1405 | 1406 | // if the Proxy isn't trapping, 1407 | // return the value set on the property descriptor 1408 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 1409 | if (handler->Has(index)) { 1410 | Local pd = handler->Get(index); 1411 | 1412 | if (pd->IsObject()) { 1413 | info.GetReturnValue().Set(GetPropertyAttributeFromPropertyDescriptor(pd->ToObject())); 1414 | return; 1415 | } 1416 | info.GetReturnValue().Set(HasPropertyResponse); 1417 | return; 1418 | } 1419 | info.GetReturnValue().Set(DoesntHavePropertyResponse); 1420 | return; 1421 | } 1422 | 1423 | Local argv[1] = {idx}; 1424 | 1425 | Local hasOwn = handler->Get(Nan::New("hasOwn").ToLocalChecked()); 1426 | if (hasOwn->IsFunction()) { 1427 | Local hasOwn_fn = Local::Cast(hasOwn); 1428 | info.GetReturnValue().Set(hasOwn_fn->Call(handler, 1, argv)->BooleanValue() ? 1429 | HasPropertyResponse : 1430 | DoesntHavePropertyResponse); 1431 | return; 1432 | } 1433 | 1434 | Local has = handler->Get(Nan::New("has").ToLocalChecked()); 1435 | if (has->IsFunction()) { 1436 | Local has_fn = Local::Cast(has); 1437 | info.GetReturnValue().Set(has_fn->Call(handler, 1, argv)->BooleanValue() ? 1438 | HasPropertyResponse : 1439 | DoesntHavePropertyResponse); 1440 | return; 1441 | } 1442 | 1443 | Local getOwnPropertyDescriptor = handler->Get(Nan::New("getOwnPropertyDescriptor").ToLocalChecked()); 1444 | if (getOwnPropertyDescriptor->IsFunction()) { 1445 | Local gopd_fn = Local::Cast(getOwnPropertyDescriptor); 1446 | Local gopd_pd = gopd_fn->Call(handler, 1, argv); 1447 | 1448 | if (gopd_pd->IsObject()) { 1449 | info.GetReturnValue().Set(GetPropertyAttributeFromPropertyDescriptor(gopd_pd->ToObject())); 1450 | return; 1451 | } 1452 | } 1453 | 1454 | Local getPropertyDescriptor = handler->Get(Nan::New("getPropertyDescriptor").ToLocalChecked()); 1455 | if (handler->Has(Nan::New("getPropertyDescriptor").ToLocalChecked())) { 1456 | Local gpd_fn = Local::Cast(getPropertyDescriptor); 1457 | Local gpd_pd = gpd_fn->Call(handler, 1, argv); 1458 | 1459 | if (gpd_pd->IsObject()) { 1460 | info.GetReturnValue().Set(GetPropertyAttributeFromPropertyDescriptor(gpd_pd->ToObject())); 1461 | return; 1462 | } else if (gpd_pd->IsUndefined()) { 1463 | info.GetReturnValue().Set(DoesntHavePropertyResponse); 1464 | return; 1465 | } 1466 | } 1467 | } 1468 | 1469 | info.GetReturnValue().Set(DoesntHavePropertyResponse); 1470 | } 1471 | 1472 | /** 1473 | * Invoked for deleting a given indexed property 1474 | * 1475 | * 1476 | * 1477 | */ 1478 | NAN_INDEX_DELETER(NodeProxy::DeleteIndexedProperty) { 1479 | 1480 | if (info.This()->InternalFieldCount() > 0 || !info.Data().IsEmpty()) { 1481 | Local data = info.This()->InternalFieldCount() > 0 ? 1482 | info.This()->GetInternalField(0) : 1483 | info.Data(); 1484 | 1485 | if (!data->IsObject()) { 1486 | info.GetReturnValue().Set(Nan::False()); 1487 | return; 1488 | } 1489 | 1490 | Local idx = Nan::New(index); 1491 | Local handler = data->ToObject(); 1492 | // if the Proxy isn't trapping, 1493 | // return the value set on the property descriptor 1494 | if (!handler->GetHiddenValue(Nan::New("trapping").ToLocalChecked())->BooleanValue()) { 1495 | if (!handler->GetHiddenValue(Nan::New("frozen").ToLocalChecked())->BooleanValue()) { 1496 | Local pd = handler->Get(idx); 1497 | 1498 | if (pd->IsObject()) { 1499 | Local pd_obj = pd->ToObject(); 1500 | 1501 | if (pd_obj->Get(Nan::New("configurable").ToLocalChecked())->IsBoolean() && 1502 | pd_obj->Get(Nan::New("configurable").ToLocalChecked())->BooleanValue() 1503 | ) { 1504 | info.GetReturnValue().Set(Nan::New(handler->Delete(index))); 1505 | return; 1506 | } 1507 | } 1508 | } 1509 | info.GetReturnValue().Set(Nan::False()); 1510 | return; 1511 | } 1512 | 1513 | Local delete_ = handler->Get(Nan::New("delete").ToLocalChecked()); 1514 | if (delete_->IsFunction()) { 1515 | Local fn = Local::Cast(delete_); 1516 | Local argv[1] = {idx}; 1517 | info.GetReturnValue().Set(fn->Call(handler, 1, argv)->ToBoolean()); 1518 | return; 1519 | } 1520 | } 1521 | 1522 | info.GetReturnValue().Set(Nan::New(false)); 1523 | } 1524 | 1525 | /** 1526 | * Initialize the NodeProxy Strings and functions 1527 | * 1528 | * 1529 | * 1530 | */ 1531 | void NodeProxy::Init(Handle target) { 1532 | Nan::HandleScope scope; 1533 | 1534 | // function creation 1535 | 1536 | // main functions 1537 | Local create = Nan::New(Create)->GetFunction(); 1538 | Local _create = Nan::New("create").ToLocalChecked(); 1539 | create->SetName(_create); 1540 | target->Set(_create, create); 1541 | 1542 | Local createFunction = Nan::New(CreateFunction)->GetFunction(); 1543 | Local _createFunction = Nan::New("createFunction").ToLocalChecked(); 1544 | create->SetName(_createFunction); 1545 | target->Set(_createFunction, createFunction); 1546 | 1547 | // freeze function assignment 1548 | Local freeze = Nan::New(Freeze)->GetFunction(); 1549 | Local _freeze = Nan::New("freeze").ToLocalChecked(); 1550 | freeze->SetName(_freeze); 1551 | target->Set(_freeze, freeze); 1552 | 1553 | Local seal = Nan::New(Freeze)->GetFunction(); 1554 | Local _seal = Nan::New("seal").ToLocalChecked(); 1555 | seal->SetName(_seal); 1556 | target->Set(_seal, seal); 1557 | 1558 | Local prevent = Nan::New(Freeze)->GetFunction(); 1559 | Local _preventExtensions = Nan::New("preventExtensions").ToLocalChecked(); 1560 | prevent->SetName(_preventExtensions); 1561 | target->Set(_preventExtensions, prevent); 1562 | 1563 | // check function assignment 1564 | Local isfrozen = Nan::New(IsLocked)->GetFunction(); 1565 | Local _isFrozen = Nan::New("isFrozen").ToLocalChecked(); 1566 | isfrozen->SetName(_isFrozen); 1567 | target->Set(_isFrozen, isfrozen); 1568 | 1569 | Local issealed = Nan::New(IsLocked)->GetFunction(); 1570 | Local _isSealed = Nan::New("isSealed").ToLocalChecked(); 1571 | issealed->SetName(_isSealed); 1572 | target->Set(_isSealed, issealed); 1573 | 1574 | Local isextensible = Nan::New(IsLocked)->GetFunction(); 1575 | Local _isExtensible = Nan::New("isExtensible").ToLocalChecked(); 1576 | isextensible->SetName(_isExtensible); 1577 | target->Set(_isExtensible, isextensible); 1578 | 1579 | // part of harmony proxies 1580 | Local istrapping = Nan::New(IsLocked)->GetFunction(); 1581 | Local _isTrapping = Nan::New("isTrapping").ToLocalChecked(); 1582 | istrapping->SetName(_isTrapping); 1583 | target->Set(_isTrapping, istrapping); 1584 | 1585 | // ECMAScript 5 1586 | Local getOwnPropertyDescriptor = Nan::New(GetOwnPropertyDescriptor)->GetFunction(); 1587 | Local _getOwnPropertyDescriptor = Nan::New("getOwnPropertyDescriptor").ToLocalChecked(); 1588 | getOwnPropertyDescriptor->SetName(_getOwnPropertyDescriptor); 1589 | target->Set(_getOwnPropertyDescriptor, getOwnPropertyDescriptor); 1590 | 1591 | Local defineProperty = Nan::New(DefineProperty)->GetFunction(); 1592 | Local _defineProperty = Nan::New("defineProperty").ToLocalChecked(); 1593 | defineProperty->SetName(_defineProperty); 1594 | target->Set(_defineProperty, defineProperty); 1595 | 1596 | Local defineProperties = Nan::New(DefineProperties)->GetFunction(); 1597 | Local _defineProperties = Nan::New("defineProperties").ToLocalChecked(); 1598 | defineProperties->SetName(_defineProperties); 1599 | target->Set(_defineProperties, defineProperties); 1600 | 1601 | // additional functions 1602 | Local clone = Nan::New(Clone)->GetFunction(); 1603 | Local _clone = Nan::New("clone").ToLocalChecked(); 1604 | clone->SetName(_clone); 1605 | target->Set(_clone, clone); 1606 | 1607 | Local hidden = Nan::New(Hidden)->GetFunction(); 1608 | Local _hidden = Nan::New("hidden").ToLocalChecked(); 1609 | hidden->SetName(_hidden); 1610 | target->Set(_hidden, hidden); 1611 | 1612 | Local setPrototype = Nan::New(SetPrototype)->GetFunction(); 1613 | Local _setPrototype = Nan::New("setPrototype").ToLocalChecked(); 1614 | setPrototype->SetName(_setPrototype); 1615 | target->Set(_setPrototype, setPrototype); 1616 | 1617 | Local isProxy_ = Nan::New(IsProxy)->GetFunction(); 1618 | Local _isProxy = Nan::New("isProxy").ToLocalChecked(); 1619 | hidden->SetName(_isProxy); 1620 | target->Set(_isProxy, isProxy_); 1621 | 1622 | Local temp = Nan::New(); 1623 | 1624 | temp->SetInternalFieldCount(1); 1625 | 1626 | // named property handlers 1627 | Nan::SetNamedPropertyHandler( 1628 | temp, 1629 | GetNamedProperty, 1630 | SetNamedProperty, 1631 | QueryNamedPropertyInteger, 1632 | DeleteNamedProperty, 1633 | EnumerateNamedProperties); 1634 | 1635 | // indexed property handlers 1636 | // TODO: properly implement arrays 1637 | Nan::SetIndexedPropertyHandler( 1638 | temp, 1639 | GetIndexedProperty, 1640 | SetIndexedProperty, 1641 | QueryIndexedPropertyInteger, 1642 | DeleteIndexedProperty); 1643 | 1644 | ObjectCreator.Reset(temp); 1645 | 1646 | Local instance = Nan::New(); 1647 | Nan::SetCallAsFunctionHandler(instance, NodeProxy::New); 1648 | instance->SetInternalFieldCount(1); 1649 | 1650 | Nan::SetNamedPropertyHandler( 1651 | instance, 1652 | GetNamedProperty, 1653 | SetNamedProperty, 1654 | QueryNamedPropertyInteger, 1655 | DeleteNamedProperty, 1656 | EnumerateNamedProperties); 1657 | 1658 | Nan::SetIndexedPropertyHandler( 1659 | instance, 1660 | GetIndexedProperty, 1661 | SetIndexedProperty, 1662 | QueryIndexedPropertyInteger, 1663 | DeleteIndexedProperty); 1664 | 1665 | FunctionCreator.Reset(instance); 1666 | } 1667 | 1668 | /** 1669 | * Required by Node for initializing the module 1670 | * 1671 | */ 1672 | void init(Handle exports) { 1673 | NodeProxy::Init(exports); 1674 | } 1675 | /* Required by windows Node version to detect the entry method */ 1676 | NODE_MODULE(nodeproxy, init) 1677 | --------------------------------------------------------------------------------