Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function("import(specifier)") #89

Open
nicolo-ribaudo opened this issue Jun 27, 2023 · 3 comments
Open

Function("import(specifier)") #89

nicolo-ribaudo opened this issue Jun 27, 2023 · 3 comments

Comments

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented Jun 27, 2023

The function constructor captures the active script or module when creating a function object, at step 15 of https://tc39.es/ecma262/#sec-ordinaryfunctioncreate.

This means that, given this a.js module:

Function("import('./b.js')")();

it will call the host hook with a.js as the referrer and ./b.js as the specifier.

I would thus expect it to call the importHook when running in a new Module already in layer 0. If instead it goes through the evaluators from layer 3, they should have a way to get the correct referrer module.

@nicolo-ribaudo
Copy link
Member Author

Note that the current layer 0 spec already calls the importHook in this case, but as far as I remembered we so far assumed that it wouldn't do it.

@Jamesernator
Copy link

Jamesernator commented Jun 30, 2023

Honestly this seems like a benefit to evaluators as it means there doesn't need to be multiple copies of Function floating about the same realm.

i.e. In the current proposal the pattern would be to do this:

const evaluator = new Evaluator({
    globalThis: {
        ...globalThis,
        SOME_GLOBAL: "SOME_GLOBAL",
    },
});
evaluator.globalThis.Function = evaluator.Function;

evaluator.eval(`
   const fn = Function("return SOME_GLOBAL")
   console.log(fn()); // SOME_GLOBAL
`);

however in doing this we wind up with multiple copies of Function floating around in the same realm:

const evaluator = new Evaluator({ globalThis: { ...globalThis } });
evaluator.globalThis.Function = evaluator.Function;

evaluator.eval(`
   // Multiple Function constructors are floating around
   console.log(parseInt.constructor === Function); // false
`);

However with the existing behaviour we need not bother create a new Function at all, it'll just work when run inside the associated evaluator:

const evaluator = new Evaluator({
    globalThis: {
        ...globalThis.
        someGlobal: "SOME_GLOBAL",
    },
});

// With the current spec, this should just work
evaluator.eval(`
   const fn = Function("return SOME_GLOBAL")
   console.log(fn()); // SOME_GLOBAL
`);

// No Function discontinuity
evaluator.eval(`
   console.log(parseInt.constructor === Function); // true
`);

The only thing lost is the ability to do new evaluator.Function(...) from the outside, but this could be fixed easily by providing a evaluator.createFunction or similar that changes what evaluator context the created function belongs to.

@Jamesernator
Copy link

Jamesernator commented Jun 30, 2023

Frankly I think new Module(...) and eval should do the same so that we don't need to install these objects on the evaluator's globalThis at all. No power would actually be lost in such an approach, if we want to expose the outer eval/Function/Module we can just wrap it (just like we could do with import(...) today):

const evaluator = new Evaluator({ ...globalThis, SOME_GLOBAL: "SOME_GLOBAL" });

evaluator.eval(`
   // Would just work as eval would capture current script/module and get it's associated
   // evaluator
   console.log(eval.call(null, "SOME_GLOBAL"));
`);

// But we can still customize eval by wrapping with a closure that ensures the nearest script/module record is our current level
const evaluator2 = new Evaluator({
   globalThis: {
        ...globalThis, 
        eval: (arg) => eval.call(null, arg),
        SOME_GLOBAL: "SOME_GLOBAL",
   },
});

globalThis.SOME_GLOBAL = "NOT_IN_EVALUATOR";

evaluator2.eval(`
    console.log(eval.call(null, "SOME_GLOBAL")); // NOT_IN_EVALUATOR
`);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants