summaryrefslogtreecommitdiff
path: root/ports/webassembly/objpyproxy.js
diff options
context:
space:
mode:
Diffstat (limited to 'ports/webassembly/objpyproxy.js')
-rw-r--r--ports/webassembly/objpyproxy.js191
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],
+ );
+ },
+};