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