Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

V8: ES6 proxies don't support iteration protocol when targeting custom objects?

I'm using the V8 API to create JavaScript objects. Some of these objects support iteration by setting up a native (intercepted) function at the Symbol.iterator property.

Iterating such an object via for...of works perfectly. However, if I wrap it in a null proxy (e.g., let x = new Proxy(obj, {});), the resulting object is not iterable and throws a TypeError with the message "Illegal invocation" if an attempt is made to iterate over it.

Wrapping a standard array doesn't exhibit this issue. Is this a V8 bug?

like image 270
BitCortex Avatar asked Mar 10 '23 11:03

BitCortex


1 Answers

Wrapping a standard array doesn't exhibit this issue.

Yes, that's how array iterators work. They don't care about the kind of the object they are iterating - they simply access its .length and indexed properties (which are routed normally through the proxy).

However, other standard exotic objects don't behave that nice either. If you try to invoke [Symbol​.iterator]() on a typed array, map or set that is wrapped in a proxy, they'll bitch about being invoked on the wrong object.

Is this a V8 bug?

No, it's a bug in the application. You've got three choices:

  • Create an iterator that does not depend on the internal slots of your custom objects, but rather uses their public (proxy-interceptable) property interface. Make sure your [Symbol.iterator] method does not typecheck its receiver.
  • Check the type of the receiver in your iterator method, and if it is a proxy (i.e. has a [[ProxyTarget]] internal slot) then use that value. I would strongly advise against this, as it does not match the standard behaviour and breaches the proxy when bypassing the handler.
  • Don't use a null proxy:

    let x = new Proxy(obj, {
        get(target, key, receiver) {
           if (key === Symbol.iterator)
               return target[Symbol.iterator].bind(target);
           else
               return Reflect.get(target, key, receiver);
        }
    });
    
like image 105
Bergi Avatar answered Mar 13 '23 02:03

Bergi