Pkcarreno / jsod

Write, run, and share JavaScript code instantly
https://pkcarreno.github.io/jsod/
MIT License
1 stars 1 forks source link

Options for executing Javascript code #36

Open mehmetron opened 1 week ago

mehmetron commented 1 week ago

This is a really interesting editor. Especially the design, it looks very nice and intuitive!

I was just curious why you chose to use quickjs to execute the code. You could've also executed the Javascript in an iframe or a webworker.

The implementation would've also been simpler, although, you would've had to use asynchronous postMessage to share the custom API.

Pkcarreno commented 1 week ago

Thank you very much! I'm glad you like it

Actually, in the first iterations I made use of iframe, new Function, eval and even used WebContainer, and it worked quite well. In my research to see what ways I could run JS in the browser, I discovered quickjs-emscripten.

I have 4 reasons why I finally decided to use it:

Also, mention that the execution of quickjs is inside a Web Worker.

Currently, the biggest shortcoming of the project is the lack of integration with WebAPIs, which is the main difference with respect to having used iframe, new Function or eval, and for that I have to deal with the execution of external asynchronous functions and at the moment quickjs-emscripten-sync does not have that ability, I have made several tests without much success. Right now I have the project in maintenance mode, as I'm full of work, so I'm just updating dependencies and fixing small bugs. At some point when I have some more free time and if by then there has been no update in quickjs-emscripten-sync I plan to go back to experiment with just quickjs-emscripten and then improve the integration with the WebAPIs.

Likewise, I appreciate any suggestions as quickjs-emscripten is still a learning process for me and my plan is to go ahead with it.

mehmetron commented 6 days ago

Regarding dealing with infinite loops, I think this babel plugin that can be used with babel-standalone, could solve this problem: https://github.com/frontarm/demoboard/blob/master/packages/demoboard-worker/src/transforms/babel/babel-plugin-prevent-infinite-loops.ts

So you're saying, the functions we expose to the execution environment, can't be asynchronous? That is very interesting. I hadn't thought about that as a possible limiting factor. I'm assuming a Javascript based interpreter like JS-Interpreter, wouldn't have this problem.

Pkcarreno commented 5 days ago

I already tried using a preventive tactic like that. I'm sorry I can't get the article I based it on, but it was able to handle many cases to prevent infinite loops. You see, the problem with preventing infinite loops is that, while you can handle the common cases, there are cases where it is very complicated to get to. Add to that the fact that you have to always keep that logic updated as new functions come out in JS.

In that script you share, it is capable of handling while, for and do statements; however, I don't see that it is capable of handling recursive function calling.

On the other hand, if you look at the beginning of the script it manually marks a maximum number of iterations to 10001, which is fine except if the user intentionally wants to exceed that number of iterations, which would be solved by raising that number, and here the problem is that the more you raise that number the more there is the possibility that the browser freezes, in such a case, it comes out of the loop, but it may take X time until that happens.

Without going any further, in the current implementation, there is a similar parameter that counts cycles, although all the loops that I have tested are captured first by the timeout which you can freely modify in the settings.

Now, it is a method that could be perfectly feasible: to interpret and manage manually each case that may result in an infinite loop. At that time it occurred to me that maybe someone else had already gone through that situation and had created a library to deal with these exceptions, however, I did not get anything updated, there were attempts, but they had already several years without receiving an update and then I decided to follow the opposite approach which is to manage post execution, basically find a way to use a timeout without freezing the browser first.

The case of the interpreter you mention I had already seen it and it works well, but I was not comfortable with it being limited to ES5. I wanted, as much as possible, to make it as current a version of JS as possible. For example, in their demo you can try changing the var in the sample code to let which is an ES6 feature. In that case there was also the possibility of using Ducktape.

The limitation of executing asynchronous code is an implementation issue, since I am using quickjs-emscripten-sync and it makes use of the synchronous method of execution, although quickjs-emscripten provides an asynchronous method as well. With the latter I had some problems when integrating and so for the first instance I went for quickjs-emscripten-sync, because of its ease of implementation. Likewise, you can execute asynchronous code; what is not currently possible is to execute asynchronous functions external to the execution.

This is an example with the same example code of js-interpreter with let (ES6) and iterating 10002 times (js-interpreter succeeds, although it doesn't show all values, I don't know if it is a limitation of Alert).