0

As part of some code refactoring, I am wanting to trace the sequence of calls to an API, since we seem to have broken something in the process. For this reason we are looking to proxy the API calls, to log what is called, using the Javascript Proxy object, but we are getting "TypeError: Illegal invocation" when we try.

The code (based on MDN docs):

function createProxy (glObj) {
  const handler = {
    get (target, prop, receiver) {
      console.log('FN', prop);
      const x = Reflect.get(target, prop, receiver);
      return x;
    }
  };

  return new Proxy(glObj, handler);
}

And how it is being used:

function webGlInit () {
  const elementId = 'canvas';
  const canvas = document.querySelector(`#${elementId}`); // $('#canvas')[0];

  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;

  const glOptions = {
    // antialias: false,
    alpha: false,
    premultipliedAlpha: false
  };

  const gl = canvas.getContext('webgl', glOptions) || canvas.getContext('experimental-webgl', glOptions);

  const proxyGl = createProxy(gl);
  // the line where "TypeError: Illegal invocation" is happening
  proxyGl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);

Any ideas on what we are doing wrong?

1

1 Answer 1

0

Unfortunately (I would consider that a flaw since Proxy/Reflect should be transparent) you should bind methods to the target:

function createProxy (glObj) {
  const handler = {
    get (target, prop, receiver) {
      if(typeof target[prop] === 'function'){
         return target[prop].bind(target);
      }      

      const x = Reflect.get(target, prop, receiver);
      return x;
    }
  };

  return new Proxy(glObj, handler);
}

That would cover most cases, but you cannot rebind a function. If you need binding (this also demonstrates how native objects are good with this (RegExp Iterator in this case)):

function createProxy (glObj) {
  const handler = {
    get (target, prop, receiver) {
      if(typeof target[prop] === 'function'){
         return function(){
            return target[prop].apply(this === receiver ? target : this, arguments);
         };
      }      

      const x = Reflect.get(target, prop, receiver);
      return x;
    }
  };

  return new Proxy(glObj, handler);
}

const a = createProxy({name:'a', printName(){ console.log(this.name) }});
const b = {name: 'b'};

a.printName();
a.printName.bind(b)();
a.printName.call(b);

const iterator = createProxy('----'.matchAll(/-/g));
for(const c of iterator){
  console.log(...c);
}

Here we check if the call was made on the proxy we bind to the target, otherwise we use bound this.

This will also remove name, length props of a method, but they are used rarely, if you need them I guess even a more advanced solution could be figured out.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.