Commit Graph

10 Commits

Author SHA1 Message Date
Damien George
95c19e05ff webassembly/objjsproxy: Lookup attributes without testing they exist.
In JavaScript when accessing an attribute such as `obj.attr` a value of
`undefined` is returned if the attribute does not exist.  This is unlike
Python semantics where an `AttributeError` is raised.  Furthermore, in some
cases in JavaScript (eg a Proxy instance) `attr in obj` can return false
yet `obj.attr` is still valid and returns something other than `undefined`.
So the source of truth for whether a JavaScript attribute exists is to just
right away attempt `obj.attr`.

To more closely match these JavaScript semantics when proxying a JavaScript
object through to Python, change the attribute lookup logic on a `JsProxy`
so that it immediately attempts `obj.attr` instead of first testing if the
attribute exists via `attr in obj`.

This allows JavaScript objects which dynamically create attributes to work
correctly on the Python side, with both `obj.attr` and `obj["attr"]`.  Note
that `obj["attr"]` already works in all cases because it immediately does
the subscript access without first testing if the attribute exists.

As a benefit, this new behaviour matches the Pyodide behaviour.

Signed-off-by: Damien George <damien@micropython.org>
2024-06-28 11:40:24 +10:00
Damien George
a053e63914 webassembly/objjsproxy: Implement proxying of JS iterable protocol.
This allows Python to iterate over JavaScript objects that provide
Symbol.iterator.

Signed-off-by: Damien George <damien@micropython.org>
2024-06-18 22:14:34 +10:00
Damien George
5c7a414574 webassembly: Add C-level finaliser to JsProxy object.
And clear the corresponding `proxy_js_ref[js_ref]` entry when the finaliser
runs.  This then allows the JavaScript side to (eventually) free the
corresponding JavaScript object.

Signed-off-by: Damien George <damien@micropython.org>
2024-05-22 17:06:18 +10:00
Damien George
d7f031397d webassembly/objjsproxy: Make jsproxy_it keep ref to jsproxy.
So that there is a one-to-one correspondence between js_ref and
JsProxy objects.

Signed-off-by: Damien George <damien@micropython.org>
2024-05-21 15:13:55 +10:00
Damien George
e860e32e24 webassembly/objjsproxy: Fix proxying in arguments to JS new function.
Signed-off-by: Damien George <damien@micropython.org>
2024-05-06 14:47:05 +10:00
Damien George
8a3546b3bd webassembly: Add JavaScript-based asyncio support.
This commit adds a significant portion of the existing MicroPython asyncio
module to the webassembly port, using parts of the existing asyncio code
and some custom JavaScript parts.

The key difference to the standard asyncio is that this version uses the
JavaScript runtime to do the actual scheduling and waiting on events, eg
Promise fulfillment, timeouts, fetching URLs.

This implementation does not include asyncio.run(). Instead one just uses
asyncio.create_task(..) to start tasks and then returns to the JavaScript.
Then JavaScript will run the tasks.

The implementation here tries to reuse as much existing asyncio code as
possible, and gets all the semantics correct for things like cancellation
and asyncio.wait_for.  An alternative approach would reimplement Task,
Event, etc using JavaScript Promise's.  That approach is very difficult to
get right when trying to implement cancellation (because it's not possible
to cancel a JavaScript Promise).

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:24:00 +10:00
Damien George
4c3f5f552b webassembly/objjsproxy: Fix handling of thrown value into JS generator.
Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:07:00 +10:00
Damien George
5114f2c1ea webassembly/proxy_js: Allow a Python proxy of a function to be undone.
This optimises the case where a Python function is, for example, stored to
a JavaScript attribute and then later retrieved from Python.  The Python
function no longer needs to be a proxy with double proxying needed for the
call from Python -> JavaScript -> Python.

Signed-off-by: Damien George <damien@micropython.org>
2024-03-30 13:13:51 +11:00
Damien George
9b090603a0 webassembly: Implement runPythonAsync() for top-level async code.
With this commit, `interpreter.runPythonAsync(code)` can now be used to run
Python code that uses `await` at the top level.  That will yield up to
JavaScript and produce a thenable, which the JavaScript runtime can then
resume.  Also implemented is the ability for Python code to await on
JavaScript promises/thenables.  For example, outer JavaScript code can
await on `runPythonAsync(code)` which then runs Python code that does
`await js.fetch(url)`.  The entire chain of calls will be suspended until
the fetch completes.

Signed-off-by: Damien George <damien@micropython.org>
2024-03-22 13:37:47 +11:00
Damien George
39bd0b8a0a webassembly: Add JavaScript proxying, and js and jsffi modules.
This commit improves the webassembly port by adding:

- Proxying of Python objects to JavaScript with a PyProxy type that lives
  on the JavaScript side.  PyProxy implements JavaScript Proxy traps such
  as has, get, set and ownKeys, to make Python objects have functionality
  on the JavaScript side.

- Proxying of JavaScript objects to Python with a JsProxy type that lives
  on the Python side.  JsProxy passes through calls, attributes,
  subscription and iteration from Python to JavaScript.

- A top-level API on the JavaScript side to construct a MicroPython
  interpreter instance via `loadMicroPython()`.  That function returns an
  object that can be used to execute Python code, access the Python globals
  dict, access the Emscripten filesystem, and other things.  This API is
  based on the API provided by Pyodide (https://pyodide.org/).  As part of
  this, the top-level file is changed from `micropython.js` to
  `micropython.mjs`.

- A Python `js` module which can be used to access all JavaScript-side
  symbols, for example the DOM when run within a browser.

- A Python `jsffi` module with various helper functions like
  `create_proxy()` and `to_js()`.

- A dedenting lexer which automatically dedents Python source code if every
  non-empty line in that source starts with a common whitespace prefix.
  This is very helpful when Python source code is indented within a string
  within HTML or JavaScript for formatting reasons.

Signed-off-by: Damien George <damien@micropython.org>
2024-03-22 13:37:47 +11:00