as-bind icon indicating copy to clipboard operation
as-bind copied to clipboard

as-bind seems to not be able to handle proxies?

Open surma opened this issue 3 years ago β€’ 1 comments

I’m not 100% sure I dug out the very core issue here, but I put a sufficiently minimal repro in a gist, which will hopefully help analyze the bug.

I import a function which takes a string as a parameter, which is working fine with as-bind. However, some of my functions rely on this being the object the imported function is defined on. So I thought I’d bind() all functions before importing. as-bind handles that, too. Then I thought I should do the binding lazily, so I added a Proxy that does the binding on GET and caches it in a Map. Now, as-bind is failing to do its job and I just get a pointer.


import { AsBind } from "as-bind";
// Bundler magic compiles my ASC for me
import wasmUrl from "asc:./main.ts";

// Manually binds all methods found on `obj` to `obj.
function manualbind(obj) {
  const returnobj = {};
  for (const key of Object.keys(obj)) {
    if (typeof obj[key] === "function") {
      returnobj[key] = obj[key].bind(obj);
    }
    returnobj[key] = obj[key];
  }
  return returnobj;
}

// *Lazily* binds all methods found on `obj` to `obj.
function proxybind(obj) {
  const bindCache = new Map();
  return new Proxy(obj, {
    get(target, p) {
      if (bindCache.has(p)) {
        return bindCache.get(p);
      }
      if (typeof target?.[p] === "function") {
        const bound = target[p].bind(target);
        bindCache.set(p, bound);
        return bound;
      }
      return target[p];
    },
  });
}

function myalert(s) {
  alert(`message: ${JSON.stringify(s)}, has a this: ${this !== null}`);
}

async function main() {
  // Works fine!
  const instance = await AsBind.instantiate(fetch(wasmUrl), {
    main: { myalert },
  });
  instance.exports.run("vanilla");

  // Works fine!
  const instance2 = await AsBind.instantiate(fetch(wasmUrl), {
    main: manualbind({ myalert }),
  });
  instance2.exports.run("manualbind");

  // Breaks. I only get a pointer value and not the string.
  const instance3 = await AsBind.instantiate(fetch(wasmUrl), {
    main: proxybind({ myalert }),
  });
  instance3.exports.run("proxybind");
}
main();

Can you take a look and let me know if it’s something that I am doing wrong or something we can fix in as-bind?

surma avatar Mar 06 '21 17:03 surma

@surma Thank you for the issue! So I took a look at this by:

  • Downloading your source
  • Importing as-bind from lib/lib.js directly
  • Added some logging in as-bind

And I was able to figure out, it seems like the proxy skips or overrides the bindImportFunction that AsBind does (I know this since the "functionThis" logs I added are never called): https://github.com/torch2424/as-bind/blob/master/lib/asbind-instance/bind-function.js#L7

Screenshot from 2021-03-15 12-11-42

So, I'll be honest, I'm not super strong at using Proxies. So I'm not quite sure where to start fixing this :hushed: But! Perhaps it's because we aren't using arrow functions to bind our stuff? But, we need that so we can set function properties :thinking:

Let me know if this helps, or if you need me to poke around a bit more :hushed: I can buckle down and try to figure out how Proxies work haha! :joy:

torch2424 avatar Mar 15 '21 19:03 torch2424