maekawatoshiki / rapidus

ECMAScript implementation in Rust
MIT License
519 stars 21 forks source link

An easy way to pass Rust functions into the JavaScript context #31

Closed skyne98 closed 5 years ago

skyne98 commented 5 years ago

Hey there! Incredible work being done here, but I have a question, that can potentially become an important feature.

Is there a way to import functions written in rust (which are defined in the code embedding the rapidus) into the JavaScript context, for the to be used? If not, as for me, it is one of the most important features for an embeddable JavaScript engine. Also, easily operating on JavaScript values and passing callbacks, as well as calling JavaScript functions from Rust as callbacks.

I haven't read though too much code, so I might be wrong in my conclusions, but as for now it seems like the only way to "introduce" some rust to the engine is to declare it in the code with attributes.

Thanks for your amazing work, Alex.

skyne98 commented 5 years ago

As an example, there is a Jurassic JavaScript engine for .NET, which is quite performant (translate source into CIL on the fly) and easy to use embeddable engine.

maekawatoshiki commented 5 years ago

Currently, there is no easy way to import Rust functions into the JS context.

maekawatoshiki commented 5 years ago

You're right. We need such an easy way.

skyne98 commented 5 years ago

Hello there! Any updates on the topic?

maekawatoshiki commented 5 years ago

I'm focusing on developing in the branch 'new-gen.' Next generation of rapidus will provide easy way to use JS context from rust.

skyne98 commented 5 years ago

@maekawatoshiki, sounds exciting!

sisshiki1969 commented 5 years ago

Hi Alex,

The current version of Rapidus (in master branch) can dynamically load the precompiled modules written in Rust, and the modules can be called from JS code using the CommonJS-like syntax. You can pass JS Values to the Rust modules easily, but I think it's not easy to call JS callbacks from Rust modules, because the JS code should be executed on VM.

Please note Jurassic is a compiler, which translate the whole JS script into .NET bytecode ahead of code execution. This allows high interoperability between compiled JavaScript code and .NET code (such as #C). On the other hand, Rapidus is an interpreter using VM with original bytecode, so it is difficult to do the same thing, but not impossible.

Implementing this feature is important, and would be an exciting challenge.

skyne98 commented 5 years ago

@sisshiki1969, thanks for you input on the topic! Sure, Jurassic isn't the best example, considering its implementation. However, Rapidus seems to be using LLVM under the hood. Doesn't it mean that making both Rust -> JS and JS -> Rust functions should just be a matter of calling unsafe function pointers?

sisshiki1969 commented 5 years ago

No, the problem is more complicated.

In Rapidus, a whole JS script is compiled to custom bytecode ahead of code execution, and then, the bytecode is executed on VM. This bytecode is different from LLVM-IR, and LLVM does not participate in these processes. Rapidus also has a Just-in-time compiler, which translate some parts of the bytecodes (specifically, loops or functions executed repeatedly) to machine code. This translation is done dynamically during bytecode execution, using LLVM JIT module indirectly. Moreover, JIT compile is currently available only in limited situations (i.e can not handle JS objects, and exceptions). This means an internal representation of a certain JS function may be bytecode in some case, or native machine code in the others. VM handles this complex situation.

Thus, it's not a simple matter that Rust functions call JS functions "directly".