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

Options for executing Javascript code #36

Closed
mehmetron opened this issue Sep 10, 2024 · 4 comments
Closed

Options for executing Javascript code #36

mehmetron opened this issue Sep 10, 2024 · 4 comments

Comments

@mehmetron
Copy link

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
Copy link
Owner

Pkcarreno commented Sep 10, 2024

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:

  • It standardizes JS execution between different browsers and different versions, which helps to get the same result when sharing the link (especially for when new features are added that are not backwards compatible).
  • It's a project where I wanted to take the opportunity to experiment with new things.
  • The problem with executing in iframe, new Function or eval was that I could not avoid infinite loops (even if I set a timeout, once the execution entered the loop it was too late and froze the browser).
  • quickjs-emscripten-sync solved the problem that took me the longest to fix, which was exposing external functions within the execution.

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.

@Pkcarreno Pkcarreno reopened this Sep 10, 2024
@mehmetron
Copy link
Author

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
Copy link
Owner

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).

@Pkcarreno
Copy link
Owner

I'm going to close this issue, if you have any other question don't hesitate to ask here ✌️

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