rafi16jan/micropython-wasm

process_char does result in repl response

Opened this issue · 12 comments

Ran the following code but didn't see any repl response...
Am I doing something wrong?
Running mp_js.do_str does execute print statements though....

const mp_js = require('micropython') \\ I waited till promises fulfilled for each
mp_js.init(64*1024)
mp_js.init_repl()
let b = new TextEncoder().encode('print(\'hello world\')')
for(let i = 0; i < b.length; i++){
   mp_js.process_char(b[ i ])
}
mp_js.process_char("\r".charCodeAt(0))
mp_js.process_char("\n".charCodeAt(0))

init_repl was indeed problematic because of the emterpreter stuff. What is your use case to use repl instead of do_str?

@rafi16jan I'm creating a custom web based repl for micropython.js but (for reason's that are a bit complicated) I can't use the document.getElementById('mp_js_stdout'); method....

I think there may be a way around this...
We can add a global callback that is called in the _mp_js_write method
I'm not too familiar with wasm, but I'm sure there's a more elegent way of adding this callback set in the mp_js object

var mainProgram = function()
{
  global.replCallback = null;
//.....
}

function _mp_js_write(ptr, len) {
          for (var i = 0; i < len; ++i) {
              if (typeof window === 'undefined') {
                  var b = Buffer.alloc(1);
                  b.writeInt8(getValue(ptr + i, 'i8'));
                  //process.stdout.write(b);
                  global.mpjsPrintStdout(b);
                  if(global.replCallback)
                    global.replCallback(b); //if user sets callback we can capture response from process_char
              } else {
                 // .....
              }
          }
      }

example use:

>> node
> const mp_js =  require("micropython")
> mp_js.init(64 * 1024);
> mp_js.init_repl()
> replCallback = function(b) { console.log("repl resp: " + b); }
> mp_js.process_char(65)
> 0
> repl resp: A
pmp-p commented

I'm not too familiar with wasm

Hi, then i suggest you intercept file descriptors "à la plan9" it's both compatible with emsdk and wasi. adding anything to JS layer is not portable (depends on complex polyfill) and lose speed of wasm.

@rafi16jan I'm creating a custom web based repl for micropython.js but (for reason's that are a bit complicated) I can't use the document.getElementById('mp_js_stdout'); method....

I think there may be a way around this...
We can add a global callback that is called in the _mp_js_write method
I'm not too familiar with wasm, but I'm sure there's a more elegent way of adding this callback set in the mp_js object

var mainProgram = function()
{
  global.replCallback = null;
//.....
}

function _mp_js_write(ptr, len) {
          for (var i = 0; i < len; ++i) {
              if (typeof window === 'undefined') {
                  var b = Buffer.alloc(1);
                  b.writeInt8(getValue(ptr + i, 'i8'));
                  //process.stdout.write(b);
                  global.mpjsPrintStdout(b);
                  if(global.replCallback)
                    global.replCallback(b); //if user sets callback we can capture response from process_char
              } else {
                 // .....
              }
          }
      }

example use:

>> node
> const mp_js =  require("micropython")
> mp_js.init(64 * 1024);
> mp_js.init_repl()
> replCallback = function(b) { console.log("repl resp: " + b); }
> mp_js.process_char(65)
> 0
> repl resp: A

Back then to catch stdout on browser I use this hack (because that is the default behaviour) take a look at this file. What if you add an event listener to mp_js_stdout like this:

document.getElementById('mp_js_stdout').addEventListener('print', content => console.log(content));

If it does prints on the JS console then I will make a fix to wrap process_char just like I wrap do_str to be able to get awaitable response.

@rafi16jan sure, I'll try your suggestion.
So, to confirm. If I apply:

document.getElementById('mp_js_stdout').addEventListener('print', content => console.log(content));

And there is a print to the js console, you will be able to create a waitable resp for process_char?

Hi @pmp-p, long time no see it's been a while since I take a look at your repos. Indeed, take a note that this repo is currently not for production software (performance critical or commercial use) as it was a quick PoC to make a synchronous as well as asynchronous JS Proxy object. Long story short, it turns out the new Asyncify is bad too. It still doesn't support reentrancy and it still effects performance and bundle size (although better than it previous iterations).

@JoshuaBThompson If you need more freedom to make FFI to the WASM side and etc, or if you doesn't need any asynchronous JS calls from micropython it is better to build the WASM binary yourself without emterpreter because the bundle size is even more compact than this version. If any chance that you still need async calls, I read sometime ago the micropython team already make a branch that supports halting the micropython execution stack and resuming it, making it possible to wait an async task from JS. I believe @pmp-p also already had the PoC of that branch and even implemented asyncio on it.

Actually, my first attempt at implementing async calls on micropython is by creating a decorator that transform functions to a series of yield calls. But unfortunately inspect and ast module is not available on micropython. I eventually abandoned it as transforming the function on runtime is a bad idea especially if the decorator is called so many times. On RPython it's actually the opposite, all the python code is translated at compile-time. So it's a good idea to decorate things on compile-time even the heavy transforms as the code that shipped at runtime is the transformed/decorated one. And it have great FFI, top notch performance stdlib, and it's Python! (although a strict subset, but still). But I think it's impossible to build a repl out of it 😄 .

@rafi16jan sure, I'll try your suggestion.
So, to confirm. If I apply:

document.getElementById('mp_js_stdout').addEventListener('print', content => console.log(content));

And there is a print to the js console, you will be able to create a waitable resp for process_char?

Yes I will

@rafi16jan
having some issues loading this test...
using browserify to load micropython.js into browser but keep getting:

Uncaught TypeError: fs.readFileSync is not a function

can you provide an example of using this library with browser?

pmp-p commented

@rafi16jan yeah, sadly asyncify is probably not the straight and easy way for dynamic dispatched languages except maybe for cPython which has more powerfull introspection/modifications tools than MicroPython.

Do you care about establishing a kind of "common scripting interface" for all those experiments we are conducting and maybe not restrained to Python interpreters ?

if you do please be welcomed here https://gitter.im/Wasm-Python/wapy

@pmp-p I do actually, my main goal from the start when I experimented with micropython is to build more common interface to interface function calls to JS side (lol, feels weird repeating words) because I feels like pyodide lacks that not to mention the bundle size is hundred of megabytes. And if we target this common interface to not be restrained to specific python interpreter/implementation it's a good way to showcase what pros and cons each of python interpreter/implementation have and their benchmarks so people have more options to make decision. And it's beneficial too for each of the implementation, because if the common interface is updated the repo owner of each implementation should just merge it upstream and sends back PR of specific implementation handling code if there are fundamental implementation differences (like RPython doesn't have __call__, etc).

@rafi16jan
having some issues loading this test...
using browserify to load micropython.js into browser but keep getting:

Uncaught TypeError: fs.readFileSync is not a function

can you provide an example of using this library with browser?

Unfortunately the only bundler that works now is webpack and parcel, I have to debug specific bundler to make it works because it utilizes readFileSync. Does browserify have some form of readFileSync in their implementation?