JSCore Runtime - Execute JavaScript and WebAssembly natively with JavaScriptCore - A step towards compiled C extension modules for Pythonista #122
Replies: 3 comments 2 replies
-
|
This is genius, I've been working on web assembly bridges using the webview in ui but this is a way cleaner approach. I'll give this some experimenting and see what I think. I've already been really impressed with the speeds of running this. Great work |
Beta Was this translation helpful? Give feedback.
-
|
Just saw this comment...love it! 👏🏼👏🏼👏🏼 |
Beta Was this translation helpful? Give feedback.
-
jscore-runtime v0.0.8As I have just updated the release version of jscore-runtime adding partial support for WebAssembly System Interface (WASI) snapshot preview 1. I thought now might also be a good time to post an update here too, including some more details and commentary for anyone that might be interested. First things first, let’s explain a little bit more about what WASI is and how it fits into the picture of executing compiled WASM programs in Pythonista 3 with JavaScriptCore. A system interface is an abstraction layer and protocols representing a means of accessing the device / environment / platform it is running within. It specifies delegated components/functions that must fulfil specific requirements on a given target platform for code requiring this to function correctly. Without having to implement exactly how it is done because it is typically a system or platform specific implementation. This is like having a driver for an operating system/platform and the hardware potentially depending on what is specified. One side is acting like a slot of a particular shape, the other side is adapted to fit into this shape and so an executable can interact with the system, without necessarily needing to know exactly which platform it is running or have to be compiled or have provide code for each possible platform. Web Assembly .wasm executables are an interesting type of program / executable format, especially when it comes to sandboxed environments like iOS apps such as Pythonista. As partly it is intended to be able to repurpose existing code in a platform independent way as it was designed primarily to run inside browsers. Historically apps like Pythonista have had to work around code compilation, downloading, executable loading and sharing related app store policy restrictions. That while generally not entirely unreasoned from a security perspective and without going into too much detail, have on the other hand tended to hinder certain aspects of a development experience on iOS. For Pythonista for example this has meant obtaining Python modules with pip from pypi, was not built in due to ensuring compliance with this and compilation into an iOS native executable formats being restricted additionally imposes the limitation to installing pure Python modules only currently. This update bridges the gap significantly As it is now possible to attempt run a compiled context = jscore.wasm()
process = context.run_async(‘./program.wasm’, ‘arg0’, ‘argv’, env = {‘envVar’:’envValue’}, dirs=[‘./preopen_dir’])
stdin = process.stdin
stdout = process.stdout
stderr = process.stderr
# …
process.communicate()
# …To be clear, this update definitely doesn’t fix or address the installation restriction as of yet. But it does bring the project much closer towards having enough support to be able to consider it further. Ultimately with further implementation in the system interface it should be possible to run both an actual shell / terminal, install common Linux C tools, compilers etc all in an isolated environment inside Pythonista. You can find some examples of this being applied on sites which host terminals running in browser windows and frameworks like Pyodide which runs Python cross-compiled into WebAssembly entirely in a browser window. Although in our case we have Python 3.10 in Pythonista, but what we are missing is not just that of a shell, which has been solved before. Nor the ability to actually execute Cython like code as Pythonista is a Cython implementation. The difficulty with ‘impure’ Python or Python with C extensions in Pythonista is that they can’t be built or even if can be built then they cannot be loaded without a valid AppStore or Apple OS signature. As unapproved/unsigned C / Objective C / Swift compiled executable loading is restricted on devices which don’t also have a jailbreak. There is no As restrictions doesn’t apply in the same way for WebAssembly executables however, as they have to be loaded to work in browsers like Safari. We can use it to make more packages work, especially where they may simply use C extensions for offloading performance concerns that aren’t necessarily platform specific. More just have used C or another compiled to implement some code that Python is less suited for or is better suited to assembly operations such as an optimised matrix multiply. Ideally with no adjustments or at least comparatively less adjustments than just cutting out or simulating / reimplementing the functionality entirely. The a-shell app is a fairly good example of being able to compile and repurpose existing programs into WebAssembly and then run them in a terminal in practice on iOS. Including compiling from source directly with a compiler and linker also running in web assembly in the app. Pythonista with JavaScriptCore can follow a similar process, which jscore-runtime now provides a partial level of support for with this update. There are still outstanding and missing features not making it complete enough to consider stable yet, however I felt on balance much of the underlying support added also improved quite a bit upon the core runtime as well since the last release of v0.0.7. Fixing problems such as A particularly notable feature is WebAssembly memory can be manipulated completely arbitrarily in a single call for getting and setting def getBytes(self, offset, length):
return self._allocator.memory_get_bytes(self.memory, offset, length)
def setBytes(self, offset, data, length = None):
if length is None:
length = len(data)
data = bytes(data[:length])
self._allocator.memory_set_bytes(self.memory, offset, data)
return offset + lengthThe function memory_get_bytes(wasm_memory, offset, length) {
const buffer = new Uint8Array(wasm_memory.buffer, offset, length);
return new Uint8Array(buffer);
}
function memory_set_bytes(wasm_memory, offset, data) {
const buffer = new Uint8Array(wasm_memory.buffer, offset);
buffer.set(data);
}This also demonstrates one of the most powerful features of jscore-runtime allowing Python, JavaScript and WebAssembly to be used almost interchangeably of one another. JavaScript was an easy choice to implement this functionality as it was much more convenient, simpler and briefer than the same equivalent in Python. TypedArrays are returned in a special wrapper holding the JSValue reference which allows direct access in Python and it to be passed back to JavaScriptCore. This is also newer functionality as until this point most type marshalling was primarily conversion rather than more complete proxies for values. I anticipate this functionality can be expanded further to improve handling objects and shared / custom object prototypes/classes and instances in time, which are synchronised more manually currently. Although a mechanism does exist for this currently via the main JSValue wrapper’s While I could probably cover more of the fine grained detail, I’ll conclude here as this post is getting quite long. Finally will leave you with saying that there will be further updates to come. Alongside also a screenshot showing some output from this version of jscore-runtime executing and passing some of the official WASI testsuite tests for snapshot preview 1. Note the error messages are expected as the tests generating them are deliberately attempting to access invalid file descriptors.
|
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
-
I’m honestly thrilled to have reached a good point to share this new project I’ve been working on. It radically blurs the lines of what is possible in Pythonista 3, by implementing JavaScript and WebAssembly as closely integrated, independent execution runtimes with full Python interoperability support. It is an extensive Python mapping of the JavaScriptCore iOS API and implementation with just
objc_utiland standard libraries. Python scripts, programs and apps can now directly import and use JavaScript and WebAssembly functionality with close to native performance, a Python-like interface and seamless standard Python values conversions, including for Python callables such as functions and lambdas.https://github.com/M4nw3l/pythonista-jscore-runtime
A couple of quick examples before some further discussion:
JavaScript runtime:
WebAssembly runtime:
OK, so although they’re somewhat trivial there’s a fair bit going on in these, and they are aiming to illustrate briefly what the interfaces look like for using JavaScript and WebAssembly from Python programs, with working examples. Note: to run the WebAssembly example the simple.wasm file needs to be downloaded and copied into Pythonista with the “Run Pythonista Script->Import File” share sheet option.
Now to address the main question, how is this a step towards compiled C extension modules in Pythonista, isn’t this impossible?
The short answer is, well it’s not impossible anymore… Although really it has been technically possible for a little while apparently, since JavaScriptCore with working WebAssembly support has been available as an iOS API. However this support isn’t out of the box, there are a number of challenges to overcome in cross-compiling C or another compiled language to WebAssembly and then executing it, from entirely within Pythonista. Currently this project addresses the execution of WebAssembly with Pythonista, that has been compiled already from a source. It does not compile any WebAssembly currently, it just runs it. While JavaScript execution support also comes as the natural first step because it is part of WebAssembly’s integration model and JavaScriptCore is primarily a standalone JavaScript engine. Like V8 is the standalone JavaScript engine to Chrome, JavaScriptCore is the same equivalent component to WebKit/Safari.
Some effort to prepare/compile a WebAssembly module, load and use it from Pythonista is still required as well as some knowledge of a compiler capable of generating WebAssembly is needed currently. WebAssembly System Interface (WASI) is not implemented as of yet either. A long-term goal is to support a full compilation tool-chain, that also integrates with setuptools / pip ultimately from just Pythonista. Which is potentially achievable with WebAssembly ports of the appropriate tools such as cc/gcc/clang and LLVM etc and some suitable code generation. Though some parts of integrating Wasm modules as Python compiled extension modules may still unavoidably require more explicit implementation. Its aim is the rather lofty goal of Pythonista having at least the same equivalent of Pyto’s capability to cross-compile Python extension modules to WebAssembly. Except with this support being provided purely as installable python modules/pip packages for Pythonista as-is, without requiring any changes or updates to Pythonista itself.
For now I hope you enjoy experimenting with executing JavaScript and pre-compiled WebAssembly in your programs! Some more details and usage examples are given in the README. I would enjoy to hear how you get on if you try out the project, if you build something with it, or even if you encounter problems or just have any thoughts, feedback or suggestions for improvements as well?
Beta Was this translation helpful? Give feedback.
All reactions