1

Consider I have the following code:

function a() { console.log("A"); }
function b() { console.log("B"); }

function* test(code) {
    eval(code);
    yield abc;
}

var code = "var abc = 2; a(); yield 1; b();"

var testObj = test(code);
var first = testObj.next(); // expect 1
var second = testObj.next(); // expect 2

It gives the following error:

Uncaught SyntaxError: Unexpected number

Wrapping the code in another generation function is not possible as the variable "abc" will be gone. Is there any solution on that?

Update on 8 Aug: The variable scope needs to be preserved, because there will be multiple codes to be executed (each probably triggered by events).

function* test() {
    while (true) {
        eval(code);
    }
    yield abc;
}


var testObj = test();
// First Call
var code = "var a = 2;"
testObj.next();
// Second Call
code = "a += 1;"
testObj.next();
// Third Call
code = "console.log(a);"
testObj.next();  //Expect Output "3"
Eddie
  • 11
  • 2
  • 3
    Why would you ever do this? – Heretic Monkey Aug 07 '21 at 18:49
  • There's a better alternative to using `eval` in 99.9% of the cases... what are you trying to achieve with it? Sounds like an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) to me. – FZs Aug 07 '21 at 18:54
  • If you do `eval(code + 'yield abc;')`, the approach with a second generator function would work. What exactly do you need this for? – Bergi Aug 07 '21 at 21:20
  • @HereticMonkey My program is designed to run code written by the others loaded from a local file. For some reason, I need to stop the execution in the middle (use regex to match pattern and insert `yield` in between), while preserving all variables declared in `code` – Eddie Aug 08 '21 at 07:50
  • @Bergi Thanks for your suggestion. However, declaring another function would mess up variable scope which I need to avoid. – Eddie Aug 08 '21 at 07:51
  • @Eddie, Node has [vm](https://nodejs.org/dist/latest-v14.x/docs/api/vm.html) module to do so... The vm module enables compiling and running code within V8 Virtual Machine contexts – Nur Aug 08 '21 at 10:55
  • @Eddie Please be more specific about what gets messed up. If you want to run code written by others, what exactly do you pass to that code (and how - e.g. by making them available in scope) and how do you want to get results back (e.g. by accessing an `abc` variable that the code writes to)? What other requirements do you have? – Bergi Aug 08 '21 at 12:24
  • 1
    @Eddie Oh, and why do you even need to "stop execution in the middle"? Using regex on JS code is probably a bad idea in the first place. – Bergi Aug 08 '21 at 12:25
  • @Bergi Thanks for your opinion. In my application, there is a blocking(sync) JS function in the native app (PC and mobile). The function invoke a in-app browser (JS execution paused), complete some user interaction, and then obtain the result from the browser back as a JS variable. Now I am working on a web client, which I cannot block the JS thread, thus I wish to use `generator` and `yield` to achieve that As the code were written the others and already being used, I cannot just change the API and ask them to rewrite their code as it will break the compatibility – Eddie Aug 08 '21 at 13:50
  • @Bergi I understand using regex/string operation on JS code is a very bad idea, do you have any suggestions on that? – Eddie Aug 08 '21 at 13:53
  • @Nur Thanks for your comment. The application is purely client-side (using webpack) and no node server will be used. – Eddie Aug 08 '21 at 13:54
  • @Eddie A better solution would be to use a transpiler like [babel](https://babeljs.io) or [sweetjs](https://www.sweetjs.org) instead of regex. And actually I'd use `await` instead of `yield` for that use case, although admittedly the problem is just the same. – Bergi Aug 08 '21 at 14:07
  • @Eddie And yes, I would actually recommend to deprecate the synchronous api method and ask your users to write their code with `yield` (or `await`) themselves. You may offer an "automatic rewrite" as a compatibility feature, but you should consider that as a temporary solution, and migrate all the existing code. Dealing with synchronous-looking-but-asynchronous-under-the-hood-through-undocumented-magic code is an absolute pain in the ass. – Bergi Aug 08 '21 at 14:07
  • Still, I don't see why `const test = Function("return function*() {" + rewrite(code) + "; return …; };")(); const textObj = test(); …` would not work for you. Please [edit] your question to include a code sample where that approach would break scoping or something. – Bergi Aug 08 '21 at 14:09
  • @Bergi using `await` is not possible as there will be UI interactions in between. The user cannot do anything when the JS is waiting. As everything needs to be completely client side, I cannot use [babel](https://babeljs.io/) nor [sweetjs](https://www.sweetjs.org/) – Eddie Aug 08 '21 at 14:11
  • @Eddie `await` does allow UI interactions and other event loop tasks while waiting for a promise to settle asynchronously, that's the whole point. And yes, both babel and sweetjs work on the client side in the browser - just see their live playgrounds on their websites! – Bergi Aug 08 '21 at 14:15
  • @Bergi Thanks for your suggestion, let me check that out. I have also updated the code to illustrate why the scope needs to be preserved. – Eddie Aug 08 '21 at 14:19
  • `while (true)` actually will never reach that `yield abc`. Why do you need to evaluate multiple scripts but take only one result from them? Or is the actual problem that they need to *share* a scope, so that variables from the first script are still available in the second? And why do you need to treat these as separate, what would stop you from just concatenating the codes into one big string of code to evaluate? – Bergi Aug 08 '21 at 14:27
  • @Bergi each code fragment is seperate (e.g. triggered by clicking different buttons). We provide the software (with API) and the client create their own document/script/UI. As such, I cannot pre-determine nor combining the code in advance. Yes, I need to share the scope through the entire session. For example, it may be used create a calculator with their own UI and script. – Eddie Aug 08 '21 at 14:35
  • @Eddie Maybe it's too late to change now, but if the fragments are chosen while some fragments already ran, I would not let them share their state through some shared quasi-global scoped, but require an explicit state object (that can not just be passed around easily, but even be serialised and restored). And regarding sharing scope between multiple `eval` snippets, maybe [this approach](https://stackoverflow.com/q/67322922/1048572) is a viable solution for you. – Bergi Aug 08 '21 at 21:10
  • @Bergi Thanks for your great help, the link provided is really valuable. I have been searching for somebody doing similar thing for a long while – Eddie Aug 09 '21 at 13:01

0 Answers0