diff options
Diffstat (limited to 'ports/webassembly/objpyproxy.js')
| -rw-r--r-- | ports/webassembly/objpyproxy.js | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/ports/webassembly/objpyproxy.js b/ports/webassembly/objpyproxy.js new file mode 100644 index 000000000..52670b66e --- /dev/null +++ b/ports/webassembly/objpyproxy.js @@ -0,0 +1,191 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023-2024 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +class PyProxy { + constructor(ref) { + this._ref = ref; + } + + // Convert js_obj -- which is possibly a PyProxy -- to a JavaScript object. + static toJs(js_obj) { + if (!(js_obj instanceof PyProxy)) { + return js_obj; + } + + const type = Module.ccall( + "proxy_c_to_js_get_type", + "number", + ["number"], + [js_obj._ref], + ); + + if (type === 1 || type === 2) { + // List or tuple. + const array_ref = Module._malloc(2 * 4); + const item = Module._malloc(3 * 4); + Module.ccall( + "proxy_c_to_js_get_array", + "null", + ["number", "pointer"], + [js_obj._ref, array_ref], + ); + const len = Module.getValue(array_ref, "i32"); + const items_ptr = Module.getValue(array_ref + 4, "i32"); + const js_array = []; + for (let i = 0; i < len; ++i) { + Module.ccall( + "proxy_convert_mp_to_js_obj_cside", + "null", + ["pointer", "pointer"], + [Module.getValue(items_ptr + i * 4, "i32"), item], + ); + const js_item = proxy_convert_mp_to_js_obj_jsside(item); + js_array.push(PyProxy.toJs(js_item)); + } + Module._free(array_ref); + Module._free(item); + return js_array; + } + + if (type === 3) { + // Dict. + const map_ref = Module._malloc(2 * 4); + const item = Module._malloc(3 * 4); + Module.ccall( + "proxy_c_to_js_get_dict", + "null", + ["number", "pointer"], + [js_obj._ref, map_ref], + ); + const alloc = Module.getValue(map_ref, "i32"); + const table_ptr = Module.getValue(map_ref + 4, "i32"); + const js_dict = {}; + for (let i = 0; i < alloc; ++i) { + const mp_key = Module.getValue(table_ptr + i * 8, "i32"); + if (mp_key > 8) { + // Convert key to JS object. + Module.ccall( + "proxy_convert_mp_to_js_obj_cside", + "null", + ["pointer", "pointer"], + [mp_key, item], + ); + const js_key = proxy_convert_mp_to_js_obj_jsside(item); + + // Convert value to JS object. + const mp_value = Module.getValue( + table_ptr + i * 8 + 4, + "i32", + ); + Module.ccall( + "proxy_convert_mp_to_js_obj_cside", + "null", + ["pointer", "pointer"], + [mp_value, item], + ); + const js_value = proxy_convert_mp_to_js_obj_jsside(item); + + // Populate JS dict. + js_dict[js_key] = PyProxy.toJs(js_value); + } + } + Module._free(map_ref); + Module._free(item); + return js_dict; + } + + // Cannot convert to JS, leave as a PyProxy. + return js_obj; + } +} + +// This handler's goal is to allow minimal introspection +// of Python references from the JS world/utilities. +const py_proxy_handler = { + isExtensible() { + return true; + }, + ownKeys(target) { + const value = Module._malloc(3 * 4); + Module.ccall( + "proxy_c_to_js_dir", + "null", + ["number", "pointer"], + [target._ref, value], + ); + const dir = proxy_convert_mp_to_js_obj_jsside_with_free(value); + return PyProxy.toJs(dir).filter((attr) => !attr.startsWith("__")); + }, + getOwnPropertyDescriptor(target, prop) { + return { + value: target[prop], + enumerable: true, + writable: true, + configurable: true, + }; + }, + has(target, prop) { + return Module.ccall( + "proxy_c_to_js_has_attr", + "number", + ["number", "string"], + [target._ref, prop], + ); + }, + get(target, prop) { + if (prop === "_ref") { + return target._ref; + } + const value = Module._malloc(3 * 4); + Module.ccall( + "proxy_c_to_js_lookup_attr", + "number", + ["number", "string", "pointer"], + [target._ref, prop, value], + ); + return proxy_convert_mp_to_js_obj_jsside_with_free(value); + }, + set(target, prop, value) { + const value_conv = Module._malloc(3 * 4); + proxy_convert_js_to_mp_obj_jsside(value, value_conv); + const ret = Module.ccall( + "proxy_c_to_js_store_attr", + "number", + ["number", "string", "number"], + [target._ref, prop, value_conv], + ); + Module._free(value_conv); + return ret; + }, + deleteProperty(target, prop) { + return Module.ccall( + "proxy_c_to_js_delete_attr", + "number", + ["number", "string"], + [target._ref, prop], + ); + }, +}; |
