Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/sdks
diff options
context:
space:
mode:
authorKenneth Pouncey <kjpou@pt.lu>2018-08-07 12:50:16 +0300
committerAlexander Köplinger <alex.koeplinger@outlook.com>2018-08-07 12:50:16 +0300
commit00f64d2998c36660cbc4dd848fe25d1a7343630a (patch)
tree5d8da98e860a563813badcedc4e7cff31597e105 /sdks
parent371de430b67c1bbda41884c3d28ef3b9d3154182 (diff)
Wasm Bindings enhancements (#9865)
* Fix Tasks and Promises in bindings. - Tasks were not being marshalled correctly in some circumstances. The only place that I could reproduce this was in the bindings tests. Running from outside the test harness would not reproduce this error. - Promises would intermittently throw Exceptions: NullReferenceException, ArrayIndex out of bounds. - When using Promises from JS to CS there were random Memory access exceptions. Looks like memory was being trampled on. Adding an integer variable to the create_task_completion_source call fixed these problems as the CreateTaskSource is defined as receiving an int variable. This also fixed the NullReferenceExceptions and ArrayIndex out of bounds exceptions. * Make JSObject disposable - Add the ability to dispose of a JSObject. - The dispose will remove the object from the tracked object list and will allow the reference counter to be recycled later. - By keeping a recycled list of references and reusing those will keep the tracked objects array from growing out of hand. - Add test to the Test Harness. - Modifiy the way the bindings init works by passing in of the Assembly and Class `[asm]namespace.class`. Modfiy test.js to use the new format. * Add support for Get and Set of javascript properties on a JSObject. * The namespace problem was fixed in https://github.com/mono/mono/pull/9836 * Cleanup unused and duplicate definitions
Diffstat (limited to 'sdks')
-rw-r--r--sdks/README.md10
-rw-r--r--sdks/wasm/binding_support.js191
-rw-r--r--sdks/wasm/bindings-test.cs184
-rw-r--r--sdks/wasm/bindings.cs100
-rw-r--r--sdks/wasm/driver.c20
-rw-r--r--sdks/wasm/test.js2
6 files changed, 472 insertions, 35 deletions
diff --git a/sdks/README.md b/sdks/README.md
index 16621e5b379..b7da0d0dccb 100644
--- a/sdks/README.md
+++ b/sdks/README.md
@@ -44,11 +44,11 @@ make build-bcl package-bcl
Go to the `wasm` directory for building and testing WebAssembly. Right now the following targets are available:
- build: Build the test runner and test suites
-- run-mini: Run mini test suite
-- run-corlib: Run corlib test suite
-- run-system: Run System test suite
-- run-system-core: Run System.Core test suite
-
+- run-all-mini: Run mini test suite
+- run-all-corlib: Run corlib test suite
+- run-all-system: Run System test suite
+- run-all-system-core: Run System.Core test suite
+- run-all-binding: Run bindings test suite
For bcl or runtime changes, you must manually run the corresponding build/package steps in `builds`.
For test suite changes, it's enough to just rerun the local target.
diff --git a/sdks/wasm/binding_support.js b/sdks/wasm/binding_support.js
index 8d898709a62..7e5ca206d4c 100644
--- a/sdks/wasm/binding_support.js
+++ b/sdks/wasm/binding_support.js
@@ -2,9 +2,10 @@
var BindingSupportLib = {
$BINDING__postset: 'BINDING.export_functions (Module);',
$BINDING: {
- BINDING_ASM: "binding_tests",
- js_objects_table: [],
-
+ BINDING_ASM: "[binding_tests]WebAssembly.Runtime",
+ mono_wasm_object_registry: [],
+ mono_wasm_ref_counter: 0,
+ mono_wasm_free_list: [],
mono_bindings_init: function (binding_asm) {
this.BINDING_ASM = binding_asm;
},
@@ -36,19 +37,38 @@ var BindingSupportLib = {
this.mono_obj_array_new = Module.cwrap ('mono_wasm_obj_array_new', 'number', ['number']);
this.mono_obj_array_set = Module.cwrap ('mono_wasm_obj_array_set', 'void', ['number', 'number', 'number']);
- this.binding_module = this.assembly_load (this.BINDING_ASM);
- var wasm_runtime_class = this.find_class (this.binding_module, "WebAssembly", "Runtime")
+ var binding_fqn_asm = this.BINDING_ASM.substring(this.BINDING_ASM.indexOf ("[") + 1, this.BINDING_ASM.indexOf ("]")).trim();
+ var binding_fqn_class = this.BINDING_ASM.substring (this.BINDING_ASM.indexOf ("]") + 1).trim();
+
+ this.binding_module = this.assembly_load (binding_fqn_asm);
+ if (!this.binding_module)
+ throw "Can't find bindings module assembly: " + binding_fqn_asm;
+
+ if (binding_fqn_class !== null && typeof binding_fqn_class !== "undefined")
+ {
+ var namespace = "WebAssembly";
+ var classname = binding_fqn_class.length > 0 ? binding_fqn_class : "Runtime";
+ if (binding_fqn_class.indexOf(".") != -1) {
+ var idx = binding_fqn_class.lastIndexOf(".");
+ namespace = binding_fqn_class.substring (0, idx);
+ classname = binding_fqn_class.substring (idx + 1);
+ }
+ }
+
+ var wasm_runtime_class = this.find_class (this.binding_module, namespace, classname)
if (!wasm_runtime_class)
- throw "Can't find WebAssembly.Runtime class";
+ throw "Can't find " + binding_fqn_class + " class";
var get_method = function(method_name) {
var res = BINDING.find_method (wasm_runtime_class, method_name, -1)
if (!res)
- throw "Can't find method WebAssembly.Runtime:" + method_name;
+ throw "Can't find method " + namespace + "." + classname + ":" + method_name;
return res;
}
this.bind_js_obj = get_method ("BindJSObject");
this.bind_existing_obj = get_method ("BindExistingObject");
+ this.unbind_js_obj = get_method ("UnBindJSObject");
+ this.unbind_js_obj_and_fee = get_method ("UnBindJSObjectAndFree");
this.get_js_id = get_method ("GetJSObjectId");
this.get_raw_mono_obj = get_method ("GetMonoObject");
@@ -68,7 +88,7 @@ var BindingSupportLib = {
get_js_obj: function (js_handle) {
if (js_handle > 0)
- return this.js_objects_table [js_handle - 1];
+ return this.mono_wasm_require_handle(js_handle);
return null;
},
@@ -125,6 +145,10 @@ var BindingSupportLib = {
};
}
case 6: {// Task
+
+ if (typeof Promise === "undefined" || typeof Promise.resolve === "undefined")
+ throw new Error ("Promises are not supported thus C# Tasks can not work in this context.");
+
var obj = this.extract_js_obj (mono_obj);
var cont_obj = null;
var promise = new Promise (function (resolve, reject) {
@@ -150,7 +174,7 @@ var BindingSupportLib = {
},
create_task_completion_source: function () {
- return this.call_method (this.create_tcs, null, "", []);
+ return this.call_method (this.create_tcs, null, "i", [ -1 ]);
},
set_task_result: function (tcs, result) {
@@ -205,6 +229,16 @@ var BindingSupportLib = {
return this.call_method (this.bind_existing_obj, null, "mi", [mono_obj, js_id]);
},
+ wasm_unbind_js_obj: function (js_obj_id)
+ {
+ return this.call_method (this.unbind_js_obj, null, "i", [js_obj_id]);
+ },
+
+ wasm_unbind_js_obj_and_free: function (js_obj_id)
+ {
+ return this.call_method (this.unbind_js_obj_and_fee, null, "i", [js_obj_id]);
+ },
+
wasm_get_js_id: function (mono_obj)
{
return this.call_method (this.get_js_id, null, "m", [mono_obj]);
@@ -216,7 +250,7 @@ var BindingSupportLib = {
},
try_extract_mono_obj:function (js_obj) {
- if (js_obj == null || js_obj == undefined || !js_obj.__mono_gchandle__)
+ if (js_obj === null || typeof js_obj === "undefined" || typeof js_obj.__mono_gchandle__ === "undefined")
return 0;
return this.wasm_get_raw_obj (js_obj.__mono_gchandle__);
},
@@ -228,20 +262,20 @@ var BindingSupportLib = {
},
get_task_and_bind: function (tcs, js_obj) {
- var task_gchandle = this.call_method (this.tcs_get_task_and_bind, null, "oi", [ tcs, this.js_objects_table.length + 1 ]);
+ var gc_handle = this.mono_wasm_free_list.length ? this.mono_wasm_free_list.pop() : this.mono_wasm_ref_counter++;
+ var task_gchandle = this.call_method (this.tcs_get_task_and_bind, null, "oi", [ tcs, gc_handle + 1 ]);
js_obj.__mono_gchandle__ = task_gchandle;
- this.js_objects_table.push (js_obj);
+ this.mono_wasm_object_registry[gc_handle] = js_obj;
return this.wasm_get_raw_obj (js_obj.__mono_gchandle__);
},
extract_mono_obj: function (js_obj) {
- //halp JS ppl, is this enough?
- if (js_obj == null || js_obj == undefined)
+ //help JS ppl, is this enough?
+ if (js_obj === null || typeof js_obj === "undefined")
return 0;
if (!js_obj.__mono_gchandle__) {
- js_obj.__mono_gchandle__ = this.wasm_binding_obj_new(this.js_objects_table.length + 1);
- this.js_objects_table.push(js_obj);
+ this.mono_wasm_register_obj(js_obj);
}
return this.wasm_get_raw_obj (js_obj.__mono_gchandle__);
@@ -253,15 +287,15 @@ var BindingSupportLib = {
var js_id = this.wasm_get_js_id (mono_obj);
if (js_id > 0)
- return this.js_objects_table [js_id - 1];
+ return this.mono_wasm_require_handle(js_id);
+ var gcHandle = this.mono_wasm_free_list.length ? this.mono_wasm_free_list.pop() : this.mono_wasm_ref_counter++;
var js_obj = {
- __mono_gchandle__: this.wasm_bind_existing(mono_obj, this.js_objects_table.length + 1),
+ __mono_gchandle__: this.wasm_bind_existing(mono_obj, gcHandle + 1),
is_mono_bridged_obj: true
};
- this.js_objects_table.push(js_obj);
-
+ this.mono_wasm_object_registry[gcHandle] = js_obj;
return js_obj;
},
@@ -401,7 +435,46 @@ var BindingSupportLib = {
return function() {
return BINDING.call_method (method, null, signature, arguments);
};
- }
+ },
+ // Object wrapping helper functions to handle reference handles that will
+ // be used in managed code.
+ mono_wasm_register_obj: function(obj) {
+
+ var gc_handle = undefined;
+ if (obj !== null && typeof obj !== "undefined") {
+ gc_handle = obj.__mono_gchandle__;
+ if (typeof gc_handle === "undefined") {
+ var handle = this.mono_wasm_free_list.length ?
+ this.mono_wasm_free_list.pop() : this.mono_wasm_ref_counter++;
+ gc_handle = handle + 1;
+ obj.__mono_gchandle__ = this.wasm_binding_obj_new(gc_handle);
+
+ }
+ this.mono_wasm_object_registry[handle] = obj;
+ }
+ return gc_handle;
+ },
+ mono_wasm_require_handle: function(handle) {
+ if (handle > 0)
+ return this.mono_wasm_object_registry[handle - 1];
+ return null;
+ },
+ mono_wasm_unregister_obj: function(js_id) {
+ var obj = this.mono_wasm_object_registry[js_id - 1]
+ if (typeof obj !== "undefined" && obj !== null) {
+ var gc_handle = obj.__mono_gchandle__;
+ if (typeof gc_handle !== "undefined") {
+ this.wasm_unbind_js_obj_and_free(js_id);
+ delete obj.__mono_gchandle__;
+ this.mono_wasm_free_list.push(js_id - 1);
+ return obj;
+ }
+ }
+ return null;
+ },
+ mono_wasm_free_handle: function(handle) {
+ this.mono_wasm_unregister_obj(handle);
+ },
},
mono_wasm_invoke_js_with_args: function(js_handle, method_name, args, is_exception) {
@@ -434,6 +507,82 @@ var BindingSupportLib = {
return BINDING.js_string_to_mono_string (res);
}
},
+ mono_wasm_get_object_property: function(js_handle, property_name, is_exception) {
+ BINDING.bindings_lazy_init ();
+
+ var obj = BINDING.mono_wasm_require_handle (js_handle);
+ if (!obj) {
+ setValue (is_exception, 1, "i32");
+ return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'");
+ }
+
+ var js_name = BINDING.conv_string (property_name);
+ if (!js_name) {
+ setValue (is_exception, 1, "i32");
+ return BINDING.js_string_to_mono_string ("Invalid property name object '" + js_name + "'");
+ }
+
+ var res;
+ try {
+ var m = obj [js_name];
+ if (m === Object(m) && obj.__is_mono_proxied__)
+ m.__is_mono_proxied__ = true;
+
+ return BINDING.js_to_mono_obj (m);
+ } catch (e) {
+ var res = e.toString ();
+ setValue (is_exception, 1, "i32");
+ if (res === null || typeof res === "undefined")
+ res = "unknown exception";
+ return BINDING.js_string_to_mono_string (res);
+ }
+ },
+ mono_wasm_set_object_property: function (js_handle, property_name, value, createIfNotExist, hasOwnProperty, is_exception) {
+
+ BINDING.bindings_lazy_init ();
+
+ var requireObject = BINDING.mono_wasm_require_handle (js_handle);
+ if (!requireObject) {
+ setValue (is_exception, 1, "i32");
+ return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'");
+ }
+
+ var property = BINDING.conv_string (property_name);
+ if (!property) {
+ setValue (is_exception, 1, "i32");
+ return BINDING.js_string_to_mono_string ("Invalid property name object '" + property_name + "'");
+ }
+
+ var result = false;
+
+ var js_value = BINDING.unbox_mono_obj(value);
+
+ if (createIfNotExist) {
+ requireObject[property] = js_value;
+ result = true;
+ }
+ else {
+ result = false;
+ if (!createIfNotExist)
+ {
+ if (!requireObject.hasOwnProperty(property))
+ return false;
+ }
+ if (hasOwnProperty === true) {
+ if (requireObject.hasOwnProperty(property)) {
+ requireObject[property] = js_value;
+ result = true;
+ }
+ }
+ else {
+ requireObject[property] = js_value;
+ result = true;
+ }
+
+ }
+ return BINDING.call_method (BINDING.box_js_bool, null, "im", [ result ]);
+ },
+
};
autoAddDeps(BindingSupportLib, '$BINDING')
diff --git a/sdks/wasm/bindings-test.cs b/sdks/wasm/bindings-test.cs
index c3774240bf9..120d5bce10e 100644
--- a/sdks/wasm/bindings-test.cs
+++ b/sdks/wasm/bindings-test.cs
@@ -100,6 +100,21 @@ public class TestClass {
return task;
}
+ public static TaskCompletionSource<object> tcs3;
+ public static Task task3;
+ public static object MkTaskNull () {
+ tcs3 = new TaskCompletionSource<object> ();
+ task3 = tcs3.Task;
+ return task3;
+ }
+
+ public static Task<object> taskString;
+ public static object MkTaskString () {
+ tcs3 = new TaskCompletionSource<object> ();
+ taskString = tcs3.Task;
+ return taskString;
+ }
+
public static Task<object> the_promise;
public static void InvokePromise (object obj) {
the_promise = (Task<object>)obj;
@@ -107,6 +122,31 @@ public class TestClass {
Console.WriteLine ("Promise result is {0}", t.Result);
}, null, TaskContinuationOptions.ExecuteSynchronously); //must be sync cuz the mainloop pump is gone
}
+
+ public static List<JSObject> js_objs_to_dispose = new List<JSObject>();
+ public static void DisposeObject(JSObject obj)
+ {
+ js_objs_to_dispose.Add(obj);
+ obj.Dispose();
+ }
+
+ public static object[] js_props;
+ public static void RetrieveObjectProperties (JSObject obj) {
+ js_props = new object[4];
+ js_props [0] = obj.GetObjectProperty ("myInt");
+ js_props [1] = obj.GetObjectProperty ("myDouble");
+ js_props [2] = obj.GetObjectProperty ("myString");
+ js_props [3] = obj.GetObjectProperty ("myBoolean");
+ }
+
+ public static void PopulateObjectProperties (JSObject obj, bool createIfNotExist) {
+ js_props = new object[4];
+ obj.SetObjectProperty ("myInt", 100, createIfNotExist);
+ obj.SetObjectProperty ("myDouble", 4.5, createIfNotExist);
+ obj.SetObjectProperty ("myString", "qwerty", createIfNotExist);
+ obj.SetObjectProperty ("myBoolean", true, createIfNotExist);
+ }
+
}
[TestFixture]
@@ -248,14 +288,14 @@ public class BindingTests {
Runtime.InvokeJS (@"
var tsk = call_test_method (""MkTask"", """", [ ]);
tsk.then (function (value) {
- print ('PassTaskToJS cont with value ' + value);
+ Module.print ('PassTaskToJS cont with value ' + value);
});
");
Assert.AreEqual (0, TestClass.int_val);
TestClass.tcs.SetResult (99);
//FIXME our test harness doesn't suppport async tests.
// So manually verify it for now by checking stdout for `PassTaskToJS cont with value 99`
- // Assert.AreEqual (99, TestClass.int_val);
+ //Assert.AreEqual (99, TestClass.int_val);
}
@@ -266,7 +306,7 @@ public class BindingTests {
var tsk = call_test_method (""MkTask"", """", [ ]);
tsk.then (function (value) {},
function (reason) {
- print ('PassTaskToJS2 cont failed due to ' + reason);
+ Module.print ('PassTaskToJS2 cont failed due to ' + reason);
});
");
Assert.AreEqual (0, TestClass.int_val);
@@ -276,6 +316,65 @@ public class BindingTests {
// Assert.AreEqual (99, TestClass.int_val);
}
+ [Test]
+ public static void PassTaskToJS3 () {
+ TestClass.int_val = 0;
+ Runtime.InvokeJS (@"
+ var tsk = call_test_method (""MkTaskNull"", """", [ ]);
+ tsk.then( () => {
+ Module.print('PassTaskToJS3 cont without value '); // Success!
+ }, reason => {
+ Module.print('PassTaskToJS3 cont failed due to ' + reason); // Error!
+ } );
+ ");
+ Assert.AreEqual (0, TestClass.int_val);
+ TestClass.tcs3.SetResult (null);
+ }
+
+ [Test]
+ public static void PassTaskToJS4 () {
+ TestClass.int_val = 0;
+ Runtime.InvokeJS (@"
+ var tsk = call_test_method (""MkTaskNull"", """", [ ]);
+ tsk.then( value => {
+ Module.print(value); // Success!
+ }, reason => {
+ Module.print('PassTaskToJS4 cont failed due to ' + reason); // Error!
+ } );
+ ");
+ Assert.AreEqual (0, TestClass.int_val);
+ TestClass.tcs3.SetException (new Exception ("it failed"));
+ }
+
+ [Test]
+ public static void PassTaskToJS5 () {
+ TestClass.int_val = 0;
+ Runtime.InvokeJS (@"
+ var tsk = call_test_method (""MkTaskString"", """", [ ]);
+ tsk.then( success => {
+ Module.print('PassTaskToJS5 cont with value ' + success); // Success!
+ }, reason => {
+ Module.print('PassTaskToJS5 cont failed due to ' + reason); // Error!
+ } );
+ ");
+ Assert.AreEqual (0, TestClass.int_val);
+ TestClass.tcs3.SetResult ("Success");
+ }
+
+ [Test]
+ public static void PassTaskToJS6 () {
+ TestClass.int_val = 0;
+ Runtime.InvokeJS (@"
+ var tsk = call_test_method (""MkTaskString"", """", [ ]);
+ tsk.then( success => {
+ Module.print('PassTaskToJS6 cont with value ' + success); // Success!
+ }, reason => {
+ Module.print('PassTaskToJS6 cont failed due to ' + reason); // Error!
+ } );
+ ");
+ Assert.AreEqual (0, TestClass.int_val);
+ TestClass.tcs3.SetException (new Exception ("it failed"));
+ }
[Test]
public static void PassPromiseToCS () {
@@ -324,4 +423,83 @@ public class BindingTests {
Assert.AreNotEqual (0, TestClass.int_val);
}
+
+ [Test]
+ public static void DisposeObject () {
+ Runtime.InvokeJS (@"
+ var obj1 = {
+ };
+ var obj2 = {
+ };
+ var obj3 = {
+ };
+ call_test_method (""DisposeObject"", ""o"", [ obj3 ]);
+ call_test_method (""DisposeObject"", ""o"", [ obj2 ]);
+ call_test_method (""DisposeObject"", ""o"", [ obj1 ]);
+ ");
+
+ Assert.AreEqual (-1, TestClass.js_objs_to_dispose [0].JSHandle);
+ Assert.AreEqual (-1, TestClass.js_objs_to_dispose [1].JSHandle);
+ Assert.AreEqual (-1, TestClass.js_objs_to_dispose [2].JSHandle);
+ }
+
+ [Test]
+ public static void GetObjectProperties () {
+ Runtime.InvokeJS (@"
+ var obj = {myInt: 100, myDouble: 4.5, myString: ""qwerty"", myBoolean: true};
+ call_test_method (""RetrieveObjectProperties"", ""o"", [ obj ]);
+ ");
+
+ Assert.AreEqual (100, TestClass.js_props [0]);
+ Assert.AreEqual (4.5, TestClass.js_props [1]);
+ Assert.AreEqual ("qwerty", TestClass.js_props [2]);
+ Assert.AreEqual (true, TestClass.js_props [3]);
+ }
+
+ [Test]
+ public static void SetObjectProperties () {
+ Runtime.InvokeJS (@"
+ var obj = {myInt: 200, myDouble: 0, myString: ""foo"", myBoolean: false};
+ call_test_method (""PopulateObjectProperties"", ""oi"", [ obj, false ]);
+ call_test_method (""RetrieveObjectProperties"", ""o"", [ obj ]);
+ ");
+
+ Assert.AreEqual (100, TestClass.js_props [0]);
+ Assert.AreEqual (4.5, TestClass.js_props [1]);
+ Assert.AreEqual ("qwerty", TestClass.js_props [2]);
+ Assert.AreEqual (true, TestClass.js_props [3]);
+ }
+
+ [Test]
+ public static void SetObjectPropertiesIfNotExistsFalse () {
+ // This test will not create the properties if they do not already exist
+ Runtime.InvokeJS (@"
+ var obj = {myInt: 200};
+ call_test_method (""PopulateObjectProperties"", ""oi"", [ obj, false ]);
+ call_test_method (""RetrieveObjectProperties"", ""o"", [ obj ]);
+ ");
+
+ Assert.AreEqual (100, TestClass.js_props [0]);
+ Assert.AreEqual (null, TestClass.js_props [1]);
+ Assert.AreEqual (null, TestClass.js_props [2]);
+ Assert.AreEqual (null, TestClass.js_props [3]);
+ }
+
+ [Test]
+ public static void SetObjectPropertiesIfNotExistsTrue () {
+ // This test will set the value of the property if it exists and will create and
+ // set the value if it does not exists
+ Runtime.InvokeJS (@"
+ var obj = {myInt: 200};
+ call_test_method (""PopulateObjectProperties"", ""oi"", [ obj, true ]);
+ call_test_method (""RetrieveObjectProperties"", ""o"", [ obj ]);
+ ");
+
+ Assert.AreEqual (100, TestClass.js_props [0]);
+ Assert.AreEqual (4.5, TestClass.js_props [1]);
+ Assert.AreEqual ("qwerty", TestClass.js_props [2]);
+ Assert.AreEqual (true, TestClass.js_props [3]);
+ }
+
+
}
diff --git a/sdks/wasm/bindings.cs b/sdks/wasm/bindings.cs
index 333a0dadfbc..9306fbfcb2a 100644
--- a/sdks/wasm/bindings.cs
+++ b/sdks/wasm/bindings.cs
@@ -23,6 +23,10 @@ namespace WebAssembly {
[MethodImplAttribute (MethodImplOptions.InternalCall)]
internal static extern object InvokeJSWithArgs (int js_obj_handle, string method, object[] _params, out int exceptional_result);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object GetObjectProperty(int js_obj_handle, string propertyName, out int exceptional_result);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object SetObjectProperty(int js_obj_handle, string propertyName, object value, bool createIfNotExists, bool hasOwnProperty, out int exceptional_result);
public static string InvokeJS (string str)
{
@@ -46,6 +50,36 @@ namespace WebAssembly {
return (int)(IntPtr)obj.Handle;
}
+ static int UnBindJSObject(int js_id)
+ {
+ if (bound_objects.ContainsKey(js_id))
+ {
+ var obj = bound_objects[js_id];
+ bound_objects.Remove(js_id);
+ return (int)(IntPtr)obj.Handle;
+ }
+
+ return 0;
+
+ }
+
+ static int UnBindJSObjectAndFree(int js_id)
+ {
+ if (bound_objects.ContainsKey(js_id))
+ {
+ var obj = bound_objects[js_id];
+ bound_objects.Remove(js_id);
+ var gCHandle = obj.Handle;
+ gCHandle.Free();
+ obj.JSHandle = -1;
+ return (int)(IntPtr)gCHandle;
+ }
+
+ return 0;
+
+ }
+
+
static object CreateTaskSource (int js_id) {
return new TaskCompletionSource<object> ();
}
@@ -132,6 +166,7 @@ namespace WebAssembly {
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
+ case TypeCode.Boolean:
res += "i";
break;
case TypeCode.Int64:
@@ -191,11 +226,14 @@ namespace WebAssembly {
public JSException (string msg) : base (msg) {}
}
- public class JSObject {
+ public class JSObject : IDisposable {
internal int JSHandle;
internal GCHandle Handle;
internal object RawObject;
+ // Flag: Has Dispose already been called?
+ bool disposed = false;
+
internal JSObject (int js_handle) {
this.JSHandle = js_handle;
this.Handle = GCHandle.Alloc (this);
@@ -215,8 +253,68 @@ namespace WebAssembly {
return res;
}
+
+ public object GetObjectProperty(string expr)
+ {
+
+ int exception = 0;
+ var propertyValue = Runtime.GetObjectProperty(JSHandle, expr, out exception);
+
+ if (exception != 0)
+ throw new JSException((string)propertyValue);
+
+ return propertyValue;
+
+ }
+
+ public void SetObjectProperty(string expr, object value, bool createIfNotExists = true, bool hasOwnProperty = false)
+ {
+
+ int exception = 0;
+ var setPropResult = Runtime.SetObjectProperty(JSHandle, expr, value, createIfNotExists, hasOwnProperty, out exception);
+ if (exception != 0)
+ throw new JSException($"Error setting {expr} on (js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})");
+
+ }
+
+
public override string ToString () {
return $"(js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})";
}
+
+
+ protected void FreeHandle()
+ {
+
+ Runtime.InvokeJS("BINDING.mono_wasm_free_handle(" + JSHandle + ");");
+ }
+
+ public void Dispose()
+ {
+ // Dispose of unmanaged resources.
+ Dispose(true);
+ // Suppress finalization.
+ GC.SuppressFinalize(this);
+ }
+
+ // Protected implementation of Dispose pattern.
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (disposing)
+ {
+
+ // Free any other managed objects here.
+ //
+ }
+
+ // Free any unmanaged objects here.
+ //
+ FreeHandle();
+
+ disposed = true;
+ }
}
}
diff --git a/sdks/wasm/driver.c b/sdks/wasm/driver.c
index 41b8bf835ff..ee339bae7fa 100644
--- a/sdks/wasm/driver.c
+++ b/sdks/wasm/driver.c
@@ -87,6 +87,8 @@ typedef struct _MonoAssemblyName MonoAssemblyName;
//JS funcs
extern MonoObject* mono_wasm_invoke_js_with_args (int js_handle, MonoString *method, MonoArray *args, int *is_exception);
+extern MonoObject* mono_wasm_get_object_property (int js_handle, MonoString *method, int *is_exception);
+extern MonoObject* mono_wasm_set_object_property (int js_handle, MonoString *method, MonoObject *value, int createIfNotExist, int hasOwnProperty, int *is_exception);
// Blazor specific custom routines - see dotnet_support.js for backing code
extern void* mono_wasm_invoke_js_marshalled (MonoString **exceptionMessage, void *asyncHandleLongPtr, MonoString *funcName, MonoString *argsJson);
@@ -119,7 +121,6 @@ MonoAssembly* mono_assembly_load (MonoAssemblyName *aname, const char *basedir,
MonoAssemblyName* mono_assembly_name_new (const char *name);
void mono_assembly_name_free (MonoAssemblyName *aname);
const char* mono_image_get_name (MonoImage *image);
-const char* mono_class_get_name (MonoClass *klass);
MonoString* mono_string_new (MonoDomain *domain, const char *text);
void mono_add_internal_call (const char *name, const void* method);
MonoString * mono_string_from_utf16 (char *data);
@@ -131,8 +132,6 @@ int mono_class_is_delegate (MonoClass* klass);
const char* mono_class_get_name (MonoClass *klass);
const char* mono_class_get_namespace (MonoClass *klass);
-
-
#define mono_array_get(array,type,index) ( *(type*)mono_array_addr ((array), type, (index)) )
#define mono_array_addr(array,type,index) ((type*)(void*) mono_array_addr_with_size (array, sizeof (type), index))
#define mono_array_setref(array,index,value) \
@@ -210,6 +209,8 @@ mono_wasm_load_runtime (const char *managed_path, int enable_debugging)
mono_add_internal_call ("WebAssembly.Runtime::InvokeJS", mono_wasm_invoke_js);
mono_add_internal_call ("WebAssembly.Runtime::InvokeJSWithArgs", mono_wasm_invoke_js_with_args);
+ mono_add_internal_call ("WebAssembly.Runtime::GetObjectProperty", mono_wasm_get_object_property);
+ mono_add_internal_call ("WebAssembly.Runtime::SetObjectProperty", mono_wasm_set_object_property);
// Blazor specific custom routines - see dotnet_support.js for backing code
mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJSMarshalled", mono_wasm_invoke_js_marshalled);
@@ -276,6 +277,16 @@ mono_wasm_string_from_js (const char *str)
}
+static int
+class_is_task (MonoClass *klass)
+{
+ if (!strcmp ("System.Threading.Tasks", mono_class_get_namespace (klass)) &&
+ (!strcmp ("Task", mono_class_get_name (klass)) || !strcmp ("Task`1", mono_class_get_name (klass))))
+ return 1;
+
+ return 0;
+}
+
#define MARSHAL_TYPE_INT 1
#define MARSHAL_TYPE_FP 2
#define MARSHAL_TYPE_STRING 3
@@ -316,8 +327,9 @@ mono_wasm_get_obj_type (MonoObject *obj)
return MARSHAL_TYPE_VT;
if (mono_class_is_delegate (klass))
return MARSHAL_TYPE_DELEGATE;
- if (!strcmp ("System.Threading.Tasks", mono_class_get_namespace (klass)) && (!strcmp ("Task", mono_class_get_name (klass)) || !strcmp ("Task`1", mono_class_get_name (klass))))
+ if (class_is_task(klass))
return MARSHAL_TYPE_TASK;
+
return MARSHAL_TYPE_OBJECT;
}
}
diff --git a/sdks/wasm/test.js b/sdks/wasm/test.js
index aab5570b96b..32eadfb59f8 100644
--- a/sdks/wasm/test.js
+++ b/sdks/wasm/test.js
@@ -123,7 +123,7 @@ if (!send_message)
//Ok, this is temporary
//this is a super big hack (move it to a decently named assembly, at the very least)
var binding_test_module = assembly_load ("binding_tests");
-Module.mono_bindings_init("binding_tests");
+Module.mono_bindings_init("[binding_tests]WebAssembly.Runtime");
//binding test suite support code
var binding_test_class = find_class (binding_test_module, "", "TestClass");