Giter Club home page Giter Club logo

harmony-reflect's Introduction

NPM version Dependencies

This is a shim for the ECMAScript 6 Reflect and Proxy objects.

This library does two things:

  • It defines an ES6-compliant Reflect global object that exports the ECMAScript 6 reflection API.
  • If harmony-era (pre-ES6) Proxy support is available, it patches Proxy to be up-to-date with the ES6 spec.

July 2016 update: the most recent version of all major browsers and node.js now support fully ES6-compliant Reflect and Proxy objects. This shim is primarily useful if you want ES6 Reflect support on older browsers or versions of node.js < v6.0.0, or if you want ES6 Proxy support on versions of node.js < v6.0.0.

May 2016 update: the recently released V8 v4.9 includes native support for ES2015 Proxies and Reflect, making this library obsolete for environments that embed V8 4.9 or newer (like Chrome 49 and Node v6.0). Node v5.10.x or lower still requires this polyfill for proper ES6 Proxy support.

Read Why should I use this library?

Installation

For node.js, install via npm:

npm install harmony-reflect

Then:

node --harmony-proxies
> var Reflect = require('harmony-reflect');

See release notes for changes to the npm releases.

To use in a browser, just download the single reflect.js file. After loading

<script src="reflect.js"></script>

a global object Reflect is defined that contains reflection methods as defined in the ES6 spec.

This library also updates the "harmony-era" Proxy object in the V8 engine (also used in node.js) to follow the latest ECMAScript 2015 spec. To create such a proxy, call:

var proxy = new Proxy(target, handler);

See below for a list of spec incompatibilities and other gotcha's.

API Docs

This module exports an object named Reflect and updates the global Proxy object (if it exists) to be compatible with the latest ECMAScript 6 spec.

The ECMAScript 6 Proxy API allows one to intercept various operations on Javascript objects.

Compatibility

The Reflect API, with support for proxies, was tested on:

  • Firefox (>= v4.0)
  • node --harmony_proxies (>= v0.7.8)
  • iojs --harmony_proxies (>= 2.3.0)
  • v8 --harmony_proxies (>= v3.6)
  • Any recent js spidermonkey shell

If you need only Reflect and not an up-to-date Proxy object, this library should work on any modern ES5 engine (including all browsers).

Compatibility notes:

  • Chrome (>= v19 && <= v37) used to support proxies behind a flag (chrome://flags/#enable-javascript-harmony) but Chrome v38 removed the Proxy constructor. As a result, this library cannot patch the harmony-era Proxy object on Chrome v38 or above. If you're working with Chromium directly, it's still possible to enable proxies using chromium-browser --js-flags="--harmony_proxies".
  • In older versions of V8, the Proxy constructor was enabled by default when starting V8 with --harmony. For recent versions of V8, Proxy must be explicitly enabled with --harmony_proxies.

Dependencies

  • ECMAScript 5/strict
  • To emulate direct proxies: * old Harmony Proxies * Harmony WeakMaps

After loading reflect.js into your page or other JS environment, be aware that the following globals are patched to be able to recognize emulated direct proxies:

Object.getOwnPropertyDescriptor
Object.defineProperty
Object.defineProperties
Object.getOwnPropertyNames
Object.getOwnPropertySymbols
Object.keys
Object.{get,set}PrototypeOf
Object.assign
Object.{freeze,seal,preventExtensions}
Object.{isFrozen,isSealed,isExtensible}
Object.prototype.valueOf
Object.prototype.isPrototypeOf
Object.prototype.toString
Object.prototype.hasOwnProperty
Function.prototype.toString
Date.prototype.toString
Array.isArray
Array.prototype.concat
Proxy
Reflect

โš ๏ธ In node.js, when you require('harmony-reflect'), only the current module's globals are patched. If you pass an emulated direct proxy to an external module, and that module uses the unpatched globals, the module may not interact with the proxy according to the latest ES6 Proxy API, instead falling back on the old pre-ES6 Proxy API. This can cause bugs, e.g. the built-in Array.isArray will return false when passed a proxy-for-array, while the patched Array.isArray will return true. I know of no good fix to reliably patch the globals for all node modules. If you do, let me know.

Examples

The examples directory contains a number of examples demonstrating the use of proxies:

  • membranes: wrappers that transitively isolate two object-graphs.
  • observer: a self-hosted implementation of the ES7 Object.observe notification mechanism.
  • profiler: a simple profiler to collect usage statistics of an object.

Other example uses of proxies (not done by me, but using this library):

  • supporting negative array indices a la Python
  • tpyo: using proxies to correct typo's in JS property names
  • persistent objects: shows how one might go about using proxies to save updates to objects in a database incrementally
  • defaultdict: default values for new keys in objects (as known from Python)

For more examples of proxies, and a good overview of their design rationale, I recommend reading Axel Rauschmayer's blog post on proxies.

Proxy Handler API

The sister project proxy-handlers defines a number of predefined Proxy handlers as "abstract classes" that your code can "subclass" The goal is to minimize the number of traps that your proxy handlers must implement.

Spec Incompatibilities and other gotcha's

This library differs from the ECMAScript 2016 spec as follows:

  • In ES7, the enumerate() trap, and the corresponding Reflect.enumerate() method, have been removed. This shim still supports the trap.
  • The ES7 (and ES6) spec contains a bug that leads to missing invariant checks in the getOwnPropertyDescriptor, defineProperty and deleteProperty traps. This library already contains the patch referred to in this issue.

This library differs from the ECMAScript 2015 spec as follows:

  • In ES6, Proxy is a constructor function that requires the use of new. That is, you must write new Proxy(target, handler). This library exports Proxy as an ordinary function which may be called with or without using the new operator.

  • In ES6, Function.prototype.toString and Date.prototype.toString do not operate transparently on Proxies. This shim patches those functions so that stringifying a Proxy-for-a-function or a Proxy-for-a-date "unwraps" the proxy and instead stringifies the target of the Proxy. This behavior may change in the future to be more spec-compatible.

  • This library does not shim Symbol objects. On modern V8 or io.js which supports Symbol objects natively, due to a bug in V8, Symbols and Proxies don't play well together. Read more.

  • Proxies-for-arrays are serialized as JSON objects rather than as JSON arrays. That is, JSON.stringify(new Proxy([], {})) returns "{}" rather than "[]". Read more.

harmony-reflect's People

Contributors

angus-c avatar johnjbarton avatar mathiasbynens avatar metamatt avatar not-an-aardvark avatar polotek avatar rreverser avatar tvcutsem avatar zombie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

harmony-reflect's Issues

Delete trap on nested proxies.

I am having an issue when using the delete operator on an object wrapped using nested proxies. An example program is:

var Reflect = require("harmony-reflect");

var handler = {
    deleteProperty: function(target,name,receiver) {
        return Reflect.deleteProperty(target,name,receiver);
    }
};

var o = {x: 1, y: 2};
var inner = new Proxy(o, handler);
var outer = new Proxy(inner, handler);

delete outer.x;

Which in turn produces this error trace:

/Users/jackw/Developer/harmony-test/node_modules/harmony-reflect/reflect.js:1717
      return handler.deleteProperty(target, name);
                     ^
TypeError: undefined is not a function
    at Object.global.Reflect.deleteProperty (/Users/jackw/Developer/harmony-test/node_modules/harmony-reflect/reflect.js:1717:22)
    at Object.handler.deleteProperty (/Users/jackw/Developer/harmony-test/test.js:5:24)
    at Object.Validator [as delete] (/Users/jackw/Developer/harmony-test/node_modules/harmony-reflect/reflect.js:818:20)
    at Object.<anonymous> (/Users/jackw/Developer/harmony-test/test.js:13:8)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)

Assuming this is a bug and not an incorrect example on my part, I believe the issue may be related to the difference in naming between the Validator trap delete and the Reflect function deleteProperty.

Extending proxy classes

I am in a situation where i want to proxy a base class ( not it's instance ) and let other classes extend that base class and call magical static methods.

var handler = {
  get: function(target,name){
    if(target[name]){
      return target[name]
    }else{
      // call some magic method
      return 'foo'
    }
  },
}

class Model{

}

var proxyModel  = new Proxy(Model,handler)

class User extends proxyModel{

}

But it fails saying 17270 segmentation fault iojs --harmony_proxies test/poly.js

Strange 'illegal access' error in stack trace

I noticed that when using your attributes.js example I get a strange <error: illegal access> error in the stack trace of any method that I call when the class is wrapped in AttributeProxy. It doesn't "seem" to affect anything, but was wondering if this is normal? Thanks.

In node.js:

require('harmony-reflect');

function AttributeProxy(target) {
  return Proxy(target, {
    get: function(target, name, receiver) {
      if (name in target) { return target[name]; }
      // everything not found on the target object itself should
      // be looked up in the target's _attributes map
      return target._attributes[name];
    },
    set: function(target, name, val, receiver) {
      if (name in target) {
        target[name] = val;
        return true;
      }
      // everything not found on the target object itself should
      // be added to the target's _attributes map
      target._attributes[name] = val;
      return true;
    }
  });
}

var Person = function() {
   this._attributes = {};
   return AttributeProxy(this);
};

Person.prototype.walk = function() {
  console.log('Person is walking');
  console.log(new Error().stack);
};

var Female = function() {
  // call "super" constructor
  return Person.call(this);
}
// make Female inherit from Person
Female.prototype = Object.create(Person.prototype);
Female.prototype.shop = function() {
  console.log('Female is shopping');
  console.log(new Error().stack);
}

console.log(new Error().stack);

// tests
var person = new Person();
person.hair = 'black';
person.walk(); // methods are called normally

var female = new Female();
female.hair = 'blonde';
female.walk(); // methods are called normally
female.shop(); // methods are called normally

Output:

Error: Regular Stack Trace
    at Object.<anonymous> (/var/guestweb/new.jump.omnitarget.com/attributes_test.js:45:13)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:492:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)
Person is walking
Error: Person.walk
    at <error: illegal access>
    at Object.<anonymous> (/var/guestweb/new.jump.omnitarget.com/attributes_test.js:50:8)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:492:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)
Person is walking
Error: Person.walk
    at <error: illegal access>
    at Object.<anonymous> (/var/guestweb/new.jump.omnitarget.com/attributes_test.js:54:8)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:492:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)
Female is shopping
Error: Female.shop
    at <error: illegal access>
    at Object.<anonymous> (/var/guestweb/new.jump.omnitarget.com/attributes_test.js:55:8)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:492:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

(proxied function).prototype = ...

Environment: Node.js

Example:

const f = function () {
};

const pf = Proxy(f, ...);

pf.prototype = {}; // This statement will cast exception

Using proxies as prototypes

Iโ€™m trying to proxy every object in a group of objects where some might be the prototypes of others. Similar to this minimal example:

proto = {..}
proxiedProto = Proxy(proto, {
    set: function(target, name, value) {
        target[name] = value; return true; 
    }
});

obj = Object.create(proxiedProto);
proxiedObj = Proxy(obj, {
    set: function(target, name, value) {
        target[name] = value; return true; 
    }
});

The proposal for Direct Proxies in the ECMAScript wiki (http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies#interaction_with_prototypal_inheritance) suggests that setting a property of obj (for example in the set-trap of proxiedObj) will fire the set-trap of proxiedProto:

When a proxy is used as a prototype, some of its traps may get triggered not because the proxy itself was โ€œtouchedโ€ by external code, but rather an object that inherits (directly or indirectly) from the proxy

Now, Iโ€™m wondering about two things related to this quote:

First: Using the current version of this repository, whether the prototypeโ€™s set-trap fires or not seems to depend on whether the property in question was already defined in the prototype's target or not, at least in Chrome 30.0.1599.101 on my computer, as in the following two examples:

Example 1: as expected after reading the wiki:

proto = {age: 1}

proxiedProto = Proxy(proto, {
    set: function(target, name, value) {
        target[name] = value; return true;
    }
})

obj = Object.create(proxiedProto)
obj.age = 2 

console.log(obj.age === 2) // true
console.log(obj.hasOwnProperty('age')) // false
console.log(proto.age === 1) // false

Example 2: not as (Iโ€™ve) expected, only difference is that proto now is an empty object (without an โ€˜ageโ€™ property):

proto = {}

proxiedProto = Proxy(proto, {
    set: function(target, name, value) {
        target[name] = value; return true;
    }
})

obj = Object.create(proxiedProto)
obj.age = 2

console.log(obj.age === 2) // true
console.log(obj.hasOwnProperty('age')) // true
console.log(proto.age === undefined) // true 

Is this a bug?

Second: Given that the prototypeโ€™s trap should fire as in Example 1, is there a way to change the target in proxiedObjโ€™s proxy-handler without triggering the set-trap of proxiedProto? Or is it basically not possible to change an inheriting object when the prototype is a proxy with a set-trap?


And there's one more question I've related to proxies as prototypes (not sure if it would be better to file another issue, let me know in that case): When proxies are used as prototypes, this can't be changed later-on. For example:

proxiedProto = Proxy({}, 
    {set: function(target, name, value) {
        target[name] = value; return true;
    }
});

obj = {};
console.log(obj.__proto__ === Object.prototype); // true

obj.__proto__ = proxiedProto;
console.log(obj.__proto__ === proxiedProto); // true

obj.__proto__ = Object.prototype;
console.log(obj.__proto__ === Object.prototype); // false
console.log(obj.__proto__ === proxiedProto); // true

Maybe that's also related to #18. At least it's similarly just a matter with Chrome and not in Firefox (25.0).

Array.concat() works incorrectly on proxies under V8

Something is wrong with Array.concat when it operates on a proxy. This may be a special case of issue #6 but I'm opening a new issue so we can more easily track the details.

var a = ['x', 'y'];
var h = {};
var aProxy = Proxy(a, h);
var aConcat = [].concat(a);
var aProxyConcat = [].concat(aProxy);

d8> print(aConcat.length);
2

d8> print(aProxyConcat.length);
1

I've confirmed that this bug exists with the latest version of harmony-reflect (0.0.7) and with today's trunk version of V8.

Date, RegExp, some Array prototype built-ins fail on proxies

In the direct proxies spec, built-ins on Date and RegExp should auto-unwrap the proxy:

var d = new Date()               
var dp = Proxy(d,{})
Date.prototype.getTime.call(dp) // or simply: dp.getTime()
// should work, but currently fails

Similarly for e.g. RegExp.prototype.exec and RegExp.prototype.test
Similarly also for the Date and RegExp toString() method, as explained elsewhere.

All non-generic Date.prototype and RegExp.prototype methods should be patched to auto-unwrap proxies.

The spidermonkey shell uses non-standard toSource() invocations that also currently fail on proxies. Those should probably also auto-unwrap.

Patched __proto__ (that uses Object.setPrototypeOf) fails and throws TypeError

There seems to be an issue with how proto is patched to use Object.setPrototypeOf.
Currently, setting the proto property of an object causes a TypeError:

TypeError: Generic use of proto accessor not allowed

The error occurs, for example, when using test/testProxies.html to run testProxies.js in the testSetPrototypeOf.

Is there another way to patch proto?

I used Chrome 29.0.1547.22 beta (with Experimental JavaScript for the old Harmony proxies and Harmony Weak Maps).

My current solution is to prevent the patch (not executing the function after _var _proto__setter), change Object.setPrototypeOf to directly set the proto property, and then consistently use

Object.setPrototypeOf(target, newProto)

instead of

target.proto = newProto

in the project... :-/

automatic unwrapping

I believe this behavior was introduced in harmony-reflect v0.0.5 while fixing #13.

The auto-unwrapping assumes Object.prototype.toString will only be called on objects. But lots of code calls it on other types to figure out what type they're dealing with.

Here's what one would normally expect for this behavior:

magi@ubuntu ~/s/d/foo> node --harmony
> ({}).toString.apply(1)
'[object Number]'
> ({}).toString.apply(true)
'[object Boolean]'
> ({}).toString.apply('asdf')
'[object String]'
> ({}).toString.apply(null)
'[object Null]'
> ({}).toString.apply(undefined)
'[object Undefined]'
> ({}).toString.apply([])
'[object Array]'
> ({}).toString.apply({})
'[object Object]'

Here's what you get with harmony-proxies v0.0.5:

magi@ubuntu ~/s/d/foo> node --harmony
> require('harmony-reflect')
{ snip }
> ({}).toString.apply(1)
TypeError: Invalid value used as weak map key
    at WeakMap.get (native)
    at Number.builtin (/home/magi/node_modules/harmony-reflect/reflect.js:1500:34)
    at repl:1:16
> ({}).toString.apply(true)
TypeError: Invalid value used as weak map key
    at WeakMap.get (native)
    at Boolean.builtin (/home/magi/node_modules/harmony-reflect/reflect.js:1500:34)
    at repl:1:16
> ({}).toString.apply('asdf')
TypeError: Invalid value used as weak map key
    at WeakMap.get (native)
    at String.builtin (/home/magi/node_modules/harmony-reflect/reflect.js:1500:34)
    at repl:1:16
> ({}).toString.apply(null)
TypeError: Invalid value used as weak map key
    at WeakMap.get (native)
    at builtin (/home/magi/node_modules/harmony-reflect/reflect.js:1500:34)
    at repl:1:16
> ({}).toString.apply(undefined)
TypeError: Invalid value used as weak map key
    at WeakMap.get (native)
    at builtin (/home/magi/node_modules/harmony-reflect/reflect.js:1500:34)
    at repl:1:16
> ({}).toString.apply([])
'[object Array]'
> ({}).toString.apply({})
'[object Object]'

Remove deprecated methods/give option to turn this off

I'd like to use this module as a direct proxy shim, especially for educational purposes. However, I don't want people learning about and using the deprecated traps and reflect APIs.

I could manually monkey-patch the code, but it would be nice if there was a way to turn it off.

proxies not supported on this platform

I get this exception when calling Proxy(...).

Error: proxies not supported on this platform
at new global.Proxy (http://localhost:55503/Core/shared-html-widgets/3rdparty/reflect.js:1992:11)
at Object.ViewModelMarshallingService._getOrCreateRemoteObject (http://localhost:55503/Core/js/angular/services/view-model-marshalling-service.js?bust=1418052137254:489:37)

Here's are my Chrome version info:

Google Chrome 39.0.2171.71 (Build officiel) m
Rรฉvision 465742dffbc8f2edcb5dacd2afc8b095199172fc-refs/branch-heads/2171_62@{#12}
Systรจme d'exploitation Windows
Blink 537.36 (@185310)
JavaScript V8 3.29.88.17
Flash 15.0.0.239
Agent utilisateur Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Ligne de commande "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --harmony-proxies --flag-switches-begin --javascript-harmony --flag-switches-end

setPrototypeOf should throw TypeError if typeof newProto === 'undefined'

The first line of [[setPrototypeOf]] fails if the argument is undefined:
https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v

But reflect.js does not:

  function testSetPrototypeOf() {
    var obj = {};
    var shouldThrow;
    try {
      Object.setPrototypeOf(obj, undefined);
    } catch (ex) {
      shouldThrow = ex;
    }
    assert(shouldThrow);
  }

I believe the issue here is that reflect.js implements Object.setPrototypeOf(obj, newProto) using obj.__proto__ setter, but somehow these operations are not consistent on Chrome at least.

ES5 shim for proxies

I've been exploring various concepts for trying to bring ES6 features to ES5 supporting engines (without source rewriting). One example is of bringing Names to ES5 (http://bbenvie.com/articles/2012-06-06/ES6-Private-Names-Shim), and collections (https://github.com/Benvie/ES6-Harmony-Collections-Shim) are trivial to implement at an API compatible level and many other features are as well. Obviously anything related to syntax changes are out. Proxies represent an interesting in between, where the syntax is fully compatible but the underlying semantics are...complicated.

In fact, it's trivial to shim most of the features of proxies as this very repo shows. The direct proxy concept is implemented on top of the original proxy api almost entirely (from a user standpoint) due to the fact that almost all of the operations in JS are already only accessible via function calls, and some of the ones that aren't still defer to JS code anyway (toString, valueOf). The only ones that are non-obvious to shim are the literal operations: get, set, delete, has, in. Using ES5 and premeditation, you can even handle get and set for existing properties.

I don't have answers to this but I thought it could potentially be in the spirit of this specific project.

Using stringify on proxy object

hi

any reason why this is not working?

var obj  = {};
obj.name = "Func";
var t = JSON.stringify(obj); //working

var prx = new Proxy({}, {});
prx.name = "Func";
var t = JSON.stringify(prx); //not working

"cannot report inconsistent value for non-writable, non-configurable property"

I hope you won't mind a user-level question here.

I am using Chrome with this library to wrap 'window' in a proxy so that I might record and replay the DOM operations. On the operation 'window.document', I fail. I need to return the proxy of window.document so subsequent operations on the return will also enter the proxy code. However, window.document is own frozen data of window and the test in reflect.js line 1063 throws a TypeError. As far as I can see from the code, I could not succeed in wrapping returns from window.document, and yet my case seems quite similar to the ones discussed as use cases for Proxy. Any hints?

License?

A LICENSE file would be helpful, otherwise I can't include this repo as a submodule.

harmony-reflect borks node repl

Installing harmony-reflect (version 1.4.2) in a node repl crashes it on TAB completion:

$ node --harmony_proxies
> require("harmony-reflect")
{ getOwnPropertyDescriptor: [Function],
  defineProperty: [Function],
  deleteProperty: [Function],
  getPrototypeOf: [Function],
  setPrototypeOf: [Function],
  preventExtensions: [Function],
  isExtensible: [Function],
  has: [Function],
  get: [Function],
  set: [Function],
  enumerate: [Function],
  ownKeys: [Function],
  apply: [Function],
  construct: [Function] }
> ** pressing TAB key **  readline.js:925
            throw err;
            ^

TypeError: Object prototype may only be an Object or null: true
    at setPrototypeOf (native)
    at Function.Object.setPrototypeOf (.../node_modules/harmony-reflect/reflect.js:1655:14)
    at Object.prim_defineProperty.set (.../node_modules/harmony-reflect/reflect.js:1634:21)
    at completionGroupsLoaded (repl.js:875:21)
    at REPLServer.complete (repl.js:767:11)
    at REPLServer.complete [as completer] (repl.js:315:10)
    at REPLServer.Interface._tabComplete (readline.js:375:8)
    at REPLServer.Interface._ttyWrite (readline.js:872:16)
    at ReadStream.onkeypress (readline.js:106:10)
    at emitTwo (events.js:100:13)
$ node -v
v5.5.0

how to trap prototype method calls of ES6 class object?

Give ES6 class, how to trap prototype method calls?

class Account {
    constructor(name,  amount) {
        this.name = name;
        this.amount = amount;
    }
    withdraw(arg) {
        this.amount = this.amount - arg;
    }

    deposit(arg) {
        this.amount = this.amount + arg;
    }
}
var proxyAccount = new Proxy(new Account('sumo', 500) , {} );
proxyAccount.deposit(100); //want to trap this method call
proxyAccount.withdraw(50);

valueOf,toString,... failing with proxies

Hello :]

require('harmony-reflect');

var myProxy = new Proxy({
    valueOf : function () {
        return 0;
    }
} ,{
    get : function (target, name) {
        return myProxy;
    }
});

console.log(myProxy * 1); // should say 0

And the error I get :

TypeError: Cannot convert object to primitive value
    at repl:1:21
    at REPLServer.self.eval (repl.js:110:21)
    at repl.js:249:20
    at REPLServer.self.eval (repl.js:122:7)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.EventEmitter.emit (events.js:95:17)
    at Interface._onLine (readline.js:202:10)
    at Interface._line (readline.js:531:8)
    at Interface._ttyWrite (readline.js:760:14)
    at ReadStream.onkeypress (readline.js:99:10)

Looks like something is wrapped here by the library when it shouldn't but I'm not sure of this.
Any idea ?

Possibly implement invoke trap

This is only possible to do in SpiderMonkey but it still may be worth doing.

var proxy = new Proxy({}, {});
proxy.method();

Will cause the following

1.) proxy.[[Get]]('method'), returns undefined
2.) proxy.[[Get]]('__noSuchMethod__')

If you implement the get trap to check for 'noSuchMethod' that can be reflected as the invoke trap.

Patches list in comments is not up-to-date

As commented in #50, // Patches: section in comments does not include:

  • defineProperty
  • defineProperties
  • getPrototypeOf
  • getOwnPropertyNames
  • setPrototypeOf
  • (maybe others, I used simple regex to find those)

Proxy not patched by shim when already defined as a function

Previously this shim would not patch up Proxy if it was already defined as a function (i.e. typeof Proxy === "function"). The rationale was that if Proxy is already defined as a function, the platform supports direct proxies and patching Proxy is not necessary.

Current firefox versions (FF 26+) do implement Proxy as a function and do support the direct proxies API, but various bugs remain so that patching Proxy is still valuable.

Commit ab5ca4c updates the library to always patch the global Proxy object if it is available, regardless of whether it is a function or not.

Add basic Proxy support for non-supported platforms

Would it be possible to add at least traps for Object.* static methods on platforms that do not support real proxies?

I'm currently playing with some code where at least such basic support would be useful, and from my understanding it shouldn't be hard to implement it since overrides for those methods are already implemented and just need some basic Proxy implementation for storing and accessing handlers.

Wrong description of the repo or lack of some text in readme

Just wanted to point out that this library doesn't work as Proxy polyfill which might be implied from the project description or, maybe, I missed something in the readme which states how to bring Proxy into environment, that don't have those natively.

confused about apply

I'm trying to basically write a dsl, translating dot and bracket notation into operations on a remote database. With a bit of help i came up with;

function QueryBuilder() {
if (this instanceof QueryBuilder == false) return new QueryBuilder();
var self = this;
var fn = function () {
//ignored
};
fn.self = self;
this.proxy = new Proxy(fn, QueryBuilderProxyHandler);
this.parts = [];
return this;
}

var QueryBuilderProxyHandler = {
get: function(target, name){
target.self.parts.push(name);
return target.self.proxy;
},
apply: function(target, thisValue, args) {
target.self.parts.push({that:thisValue, args: args});
return target.self.proxy;
}
}

var thing = new QueryBuilder();

thing.proxy.test.this.out(1);

console.log(thing);

This logs the operations, but is shared and requires multiple calls.
Next I updated an old example from https://github.com/DavidBruant/HarmonyProxyLab to use the new api;
(function() {
var DeclarativeObject, declo;

DeclarativeObject = function() {
var constructorTrap, firstHandler, getTrap, middleHandler, propNames;
propNames = void 0;
getTrap = function(rec, name) {
propNames.push(name);
return Proxy(constructorTrap, middleHandler);
};
middleHandler = {
get: getTrap
};
constructorTrap = function() {
return propNames.slice();
};
firstHandler = {
get: function(rec, name) {
propNames = [];
return getTrap.apply(this, arguments);
}
};
return Proxy({}, firstHandler);
};

declo = new DeclarativeObject();

console.log(declo.a.b.c.ef(48));

console.log(declo.xo['cc'].xo.ke());

}).call(this);

This works, but as soon as i try adding the apply method all i get are TypeErrors. What is the proper way to do this?

Not working with react-native

I haven't figured out why yet, but requiring 'harmony-reflect' in react-native is giving me "SyntaxError: Unexpected end of script".

I am using harmony and the Proxy object is present.

TypeError when calling enumerate on nested Proxy

The enumerate trap fails when called on a Proxy wrapping another Proxy:

require('harmony-reflect');

function wrap(obj) {
    return new Proxy(obj, {});
}

var proxy = wrap({a: 1, b: 2});
for (var prop in proxy) console.log(prop);

proxy = wrap(proxy);
for (var prop in proxy) console.log(prop);

Output:

$ node --harmony src/enumtest.js
a
b

TypeError: Object a,b has no method 'next'
    at Object.Validator.enumerate (node_modules/harmony-reflect/reflect.js:1141:28)
    at Object.<anonymous> (enumtest.js:11:18)
...

When explicitly implementing the trap (as return Reflect.enumerate(target)), the error changes to:

TypeError: enumerate trap should return an iterator, got: a,b
    at Object.Validator.enumerate (node_modules/harmony-reflect/reflect.js:1155:13)
    at Object.<anonymous> (enumtest.js:17:18)

This is with node v0.10.29 and [email protected].

Cannot properly Proxy 'os' methods

I cannot get the following example to work:

require('harmonize')(['harmony_proxies']);
require('harmony-reflect');

var os = require('os');

var p = new Proxy(os, {});

console.log(os.type())
console.log(p.type())

This is what I get as output:

Linux
(...)file.js:9
console.log(p.type())
              ^

TypeError: Illegal invocation
    at Object.<anonymous> (/home/onaips/node-lazy-require/oi.js:9:15)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Function.Module.runMain (module.js:467:10)
    at startup (node.js:134:18)
    at node.js:961:3

Looks like the this context is not being properly set. Is this a bug or I shouldn't be doing it?

Object.create puzzle

I'm trying to figure out what I am doing wrong. In the following code I create aProxy which only logs the get() call. Then I call Object.create(aProxy). I'm trying to puzzle out the resulting object's behavior and deal with it.

(function() {
    var obj = {};
    var handler = {
        get: function(target, name, receiver) {
            console.log('get ' + name);
            return target[name];
        }
    };
    var aProxy = Proxy(obj, handler);
    var created = Object.create(aProxy);
    console.log('created.__proto__', created.__proto__);
    console.log('created.__proto__ === aProxy ', created.__proto__ === aProxy);
    console.log('Object.getPrototypeOf(created) === aProxy ', Object.getPrototypeOf(created) === aProxy);
})();

When the code runs, the handler is called twice and we log an object, false and true:

get __proto__ testObjectCreated.html:9
created.__proto__ Object {} testObjectCreated.html:15
get __proto__ testObjectCreated.html:9
created.__proto__ === aProxy  false testObjectCreated.html:17
Object.getPrototypeOf(created) === aProxy  true testObjectCreated.html:18

This code acts as if created.__proto__ first calls [[Get]] on created, finds nothing in own properties, then calls [[Get]] on the first proto chain link, finds a proxy, triggers the .get trap, logs, then returns Object as the proto value of {} and logs it.

The subsequent lines show that created.__proto__ is not equivalent to Object.getPrototypeOf(created); the former is actually created.__proto__._proto__ while the latter is the correct created.__proto__.

Since created is not a proxy, to workaround this we need to discover in the get handler that we are a proxy for proto and return ourselves. Then we have to avoid getting the wrong result if we call aProxy.__proto__.

Have you encountered this and do you have any suggestions?

Proxy to Array fools most methods of checking whether Array is Array

I'm not quite sure how to motivate this problem without describing 3 different phenomena at once so please bear with me:

  1. JSON.stringify does not work on Proxy objects (it fails with "Illegal Access" exception).
  2. So I've been using Crockford's json2.js when I need to JSON-serialize Proxy objects. This works fine except if I have a Proxy to an Array (or to an object with an embedded Array), the generated JSON is just an object with numeric keys, not an Array (literally, more like { "0": 'foo', "1": 'bar' } instead of ['foo', 'bar'].
  3. The second thing is because the test json2.js uses to determine whether something is an array is the Object.prototype.toString.apply(candidate) === '[object Array]' test. Proxy to Array returns [object Object].

It's the third of these that I think is arguably a bug in the proxy implementation (or I'd like to know why not, and how to work around it!).

magi@ubuntu ~/src> node --harmony
> require('harmony-reflect')
> JSON2 = require('./json2')
> a = ['foo','bar']
[ 'foo', 'bar' ]
> p = Proxy(a, {})
{ '0': 'foo',
  '1': 'bar' }
> JSON.stringify(p)
illegal access
> JSON2.stringify(p)
'{"0":"foo","1":"bar"}'

Note there that the REPL's display of p shows it as an object with numeric keys, as I described in (2) above. So not only json2.js but also util.inspect (which the REPL uses) is treating p as a general Object, not as an Array.

Also note that the builtin JSON.stringify on a proxy just fails with "illegal access".

Here's the full battery of tests I can think of for whether something is an array (all of these return true for a, but mostly not for p):

> a instanceof Array
true
> Array.isArray(a)
true
> require('util').isArray(a)
true
> Object.prototype.toString.apply(a)
'[object Array]'
> p instanceof Array
true
> Array.isArray(p)
false
> require('util').isArray(p)
false
> Object.prototype.toString.apply(p)
'[object Object]'

So the instanceof test works on the proxy, but all the other tests fail. And a lot of code out there wants to use the Object.prototype.toString test. Now, the reasons for using the Object.prototype.toString test instead of the instanceof test might not be relevant to Node (namely, browser-side stuff with multiple frames or iframes). But is there any hope that Object.prototype.toString should be returning Array and not Object for arrays?

Some further details:

  • I was hoping that (1) would be fixed in more recent versions of Node, from https://chromiumcodereview.appspot.com/11312063 and https://code.google.com/p/v8/source/detail?r=12851.
  • (I don't know how to tell from Chromium or V8 bug reports what V8 version a fix goes into, but from the general timeframe of the fix, I was hoping this would be in V8 3.15.)
  • I just tried building Node.js from source, which currently comes with V8 3.16.17, and JSON.stringify(proxy) still fails as shown below.

Can't trap invoke for all methods of a given proxy

I suspect this is because the get trap is overriding the apply trap because the method is accessed before it is invoked. But wanted to check it's not due to my own bad code. Thanks!...

require('harmony-reflect');
function getApplyProxy(fn) {
  console.log(fn); //logged as expected
  //get trap also required?...
  return Proxy(
    fn,
    { apply: function() {
       console.log(arguments)
    }}
  )
}

var fnMonitor = Proxy(
  {},
  { get: function(t,p) {
    return (typeof t[p] == 'function' ? getApplyProxy(t[p]) : t[p]);
  }}
);

var Obj = function() {};
Obj.prototype = fnMonitor;

var a = new Obj;
a.fn = function() {};
a.fn(); //undefined(!)

Error when using Reflect.construct on ES2015 classes

var Reflect = require('harmony-reflect')

var Foo = class{}

Reflect.construct(Foo, [])

This throws the following error:

TypeError: Class constructors cannot be invoked without 'new'
    at new <anonymous> (path/to/harmony-reflect/test/testReflect.js:313:19)
    at Object.global.Reflect.construct (path/to/harmony-reflect/reflect.js:1976:43)
    at path/to/harmony-reflect/test/testReflect.js:325:29
    at test (path/to/harmony-reflect/test/testReflect.js:328:4)
    at Object.<anonymous> (path/to/code/harmony-reflect/test/testReflect.js:333:3)
    at Module._compile (module.js:413:34)
    at Object.Module._extensions..js (module.js:422:10)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Function.Module.runMain (module.js:447:10)

"illegal access" when using JSON.stringify on a Proxy

I am getting a strange error when trying to stringify a Proxy object in node.js:

$ node --harmony
> var hr = require('harmony-reflect');
undefined
> JSON.stringify(new Proxy({}, {}));
illegal access

Just the plain string illegal access is thrown (not an Error object). Tested with node.js v0.10.26 and v0.11.13, same behavior.

Collected documentation/discussion of implementation issues

I'm not sure where this would best exist, if anywhere at all, but it seems like it's already been useful just pulling together notes on a few bugs and issues with the various implementations and continuing iteration of those. While they should always be reported as bugs to the tracker for the implementation first, it still seems useful to note them and workarounds for the short term, and also as a kind of historical documentation of the various issues that inevitably come up along the way in implementing new APIs, especially one this grandiose. Individual bug tracs have a tendency to get deeply buried amongst the mountain of issues filed in engine repo history logs without much context to draw from after the fact.

Possible incompatibility gulp/jscs

I'm having trouble with another package that began requiring harmony-reflect.
I started getting the following error (more details):

TypeError: Object prototype may only be an Object or null: true

I haven't been able to determine with what package the problem lies, but if you have any insight, it would be appreciated!

Original issue: jscs-dev/node-jscs#2026

Get/Set receivers don't work with inheritance in v8

I'm sure this is a known issue. The v8 legacy proxy implementation seems to have a bug with get and/or set. When using a proxy as a prototype, the get and set traps should get the actual receiver of the property access as an argument. But that doesn't seem to be the case. The traps always receive just the proxy object. Here's a test.

// for node.js
if(typeof require == 'function') {
  var load = require;
}

load('../reflect.js');

var RCVR = null;

function test() {
  var target = {}
  var proxy = Proxy(target, {
    get: function(target, name, receiver) {
      console.log('GET');
      RCVR = receiver;
    },
    set: function(target, name, val, receiver) {
      console.log('SET');
      RCVR = receiver;
      return true;
    }
  });
  var child = Object.create(proxy);

  child.foo;
  console.log('get :' + (RCVR === child));

  child.bar = 'bar';
  console.log('set :' + (RCVR === child));
}

I spent some time trying to come up with a way to work around this bug in v8 to get inheritance working with this proxy shim. But I can't see one. this is really unfortunate because without this, nice use cases like MethodSink just don't work. Should I file this bug with v8? It's unlikely that they'll fix it considering the old proposal is likely going away.

There may also be something wrong with this test. I ran it in FF (where all the current tests pass) and it fails there. For some reason the get/set traps don't seem to be called at all in FF. I don't get the GET output that confirms the trap. Any idea what I'm doing wrong?

Objects with TTL, safe to enumerate/iterate?

I'm writting a module that creates objects whose keys have a timeout and when it expires the keys are automatically deleted.

The code is very easy to follow:

"use strict";

require ("harmony-reflect");

var harmony = module.exports = {};

harmony.create = function (args){
    args = args || {};
    if (!("ttl" in args)) return {};
    if (typeof args.ttl !== "number" || args.ttl < 0){
        throw new Error ("Invalid ttl value.");
    }

    var timers = {};

    return Proxy ({}, {
        set: function (target, key, value, receiver){
            if (!(key in target) && args.ttl){
                timers[key] = setTimeout (function (){
                    delete target[key];
                    delete timers[key];
                    if (args.expire) args.expire (key);
                }, args.ttl);
            }
            return Reflect.set (target, key, value, receiver);
        },
        deleteProperty: function (target, key){
            var id = timers[key];
            if (id){
                clearTimeout (id);
                delete timers[key];
            }
            return Reflect.deleteProperty (target, key);
        }
    });
};

When a new key is added to the proxy a timeout is set and when the user manually deletes the key the timeout is cleared.

But I have a question. If I enumerate the keys with a for-in loop, what happens if the key is deleted because the timer expires WHILE the loop is being executed?

For example:

var ttl = require (...);
var o = ttl.create ({ ttl: 1000, expire: function (key){ ... } }); // o is the proxy

o.a = 1;
o.b = 2;
o.c = 3;

for (var p in o){
  ...
  // o.c expires and is deleted BEFORE p gets the value of "c".
}

What will happen in this case? The for-in loop checks the existence of the key before it is copied to the p variable?

Thanks

Es6 Classes: Class constructors cannot be invoked without 'new'

Es6 Classes: Class constructors cannot be invoked without 'new' try following

  "use strict"

  var Reflect = require('harmony-reflect');

  class UsersController{

    constructor(name){
      this.name = name
    }

    hello(){
      return this.name
    }

  }

  var obj = Reflect.construct(UsersController, ["somename"])

  console.log(obj.hello())

get handler with Symbol

I fail to use the Proxy get handler with a ES6 Symbol as property name:

var Reflect = require('harmony-reflect');

var p = new Proxy(Object.create(null), {

    get: function(target, property) {

        return 'bar';
    }
});


console.log( p['foo'] );
console.log( p[Symbol('fooSym')] );

Error: proxies not supported on this platform in OS X Mountain Lion

I built node 0.7.8 on OS X Mountain Lion and am getting the error:

Error: proxies not supported on this platform

when I try to call Proxy().

Here is the code - derived from the example, that causes this to happen.

function makeProfiler(target) {
var count = Object.create(null);
return {
proxy: harmonyReflect.Proxy(target, {
get: function(target, name, rcvr) {
count[name] = (count[name] || 0) + 1;
return harmonyReflect.Reflect.get(target, name, rcvr);
}
}),
stats: count
};
}

function runApp(o) {
o.foo; o.foo; o.foo;
o.bar; o.bar;
o.baz;
}

var harmonyReflect = require( 'harmony-reflect');
var target = { foo: 42, bar: 24 };
var profiler = makeProfiler(target);
runApp(profiler.proxy);

Here is the full stack trace:

Error: proxies not supported on this platform
at Object.Proxy (/Users/dylanbarrell/Documents/src/node/node_modules/harmony-reflect/reflect.js:1702:11)
at makeProfiler (repl:4:23)
at repl:1:16
at REPLServer.eval (repl.js:110:21)
at repl.js:259:20
at REPLServer.eval (repl.js:117:5)
at Interface. (repl.js:249:12)
at Interface.emit (events.js:87:17)
at Interface._onLine (readline.js:178:10)
at Interface._line (readline.js:499:8)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.