How to pass parameter to a foreign method

Hi all,

I am trying to pass a parameter from a NativeModule to foreign code without success. Every try ends up with an error like this:

Database/Database.uno(17.3): E3128: Call to 'Database.addPerson(Fuse.Scripting.Context,object[])' has some invalid arguments (string)
<PATH>/Database/Database.uno(17,4,17,13): Error E3128: Call to 'Database.addPerson(Fuse.Scripting.Context,object[])' has some invalid arguments (string)

My NativeModule looks like

using Uno;
using Uno.Collections;
using Fuse;
using Fuse.Scripting;

using Uno.Compiler.ExportTargetInterop;

public class Database : NativeModule
{
	public Database()
	{
		AddMember(new NativeFunction("addPerson", (NativeCallback)addPerson));
	}

	object addPerson(Context c, object[] args)
	{
		addPerson("Bob");
		addPerson(args[0]);

		return null;
	}

	[Foreign(Language.ObjC)]
	static extern(iOS) void addPerson(string name)
	@{
             NSLog(name);
	@}
}

I tried different variants but none worked. I also seek the documentation without success :frowning: Does anybody knows what is wrong with my code.

For starters, marking a method as extern means it will only be included in the generated code if the build target is iOS, yet you are using your static extern(iOS) addPerson freely in a method that is NOT extern(iOS). In this scenario Uno cannot guarantee that your method does in fact exist, and thus attempts recursion as the only method it can resolve to is the instance method.

Either wrap your calls to your static addPerson method (btw I would personally recommend more distinct method naming and avoiding overloading as a general style guide) in if defined(iOS) { ... } or add implementations for Android and !mobile as well:

static extern(Android) void addPerson(string name)
{
  throw new Uno.Exception("Not implemented for Android");
}
static extern(!mobile) void addPerson(string name)
{
  throw new Uno.Exception("Not implemented for desktop");
}```

You'll find these things become a prime concern when working with foreign code, needing to be careful about plugging the cross-platform holes.

Hello Andreas,

thank you for your reply.
The posted code is just an example to deal with parameters from javascript to native module and vise versa. I will use more distinct method naming in future :-).

Within this test I am not able to return an array of strings back to javascript.

NativeModule:

ublic class Database : NativeModule
{
	public Database()
	{
		AddMember(new NativeFunction("addPerson", (NativeCallback)addPerson));
		AddMember(new NativeFunction("allPersons", (NativeCallback)allPersons));
	}

	object addPerson(Context c, object[] args)
	{
		debug_log("UNO - addPerson: " + args[0]);
		addPerson((string)args[0]);

		return null;
	}

	object allPersons(Context c, object[] args)
	{
		string[] persons = (string[]) allPersons();

		debug_log("UNO - allPersons.Length: " + persons.Length);

		return persons;
	}


	[Foreign(Language.ObjC)]
	static extern(iOS) void addPerson(string name)
	@{
		NSLog(@"iOS - addPerson %@", name);
	@}

	[Foreign(Language.Java)]
	static extern(!iOS) void addPerson(string name)
	{
		debug_log("Java - addPerson: " + name);
	}

	[Foreign(Language.ObjC)]
	static extern(iOS) object allPersons()
	@{
		return ["foo1", "foo2"];
	@}

	[Foreign(Language.Java)]
	static extern(!iOS) object allPersons()
	{
		String[] persons = new String [2];
		persons[0] = "foo1";
		persons[1] = "foo2";

		debug_log("JAVA - allPersons.Length: " + persons.Length);

		return persons;
	}
}

JavaScript

Database.addPerson("foo"); // --> works fine
var persons = Database.allPersons(); // persons always null

for (var i = 0; i < persons.length; i++) {

    console.log(element);
}

Does anyone know whats wrong with the code?

I am getting the following error:

System.Exception: Unhandled type in V8 marshaller: System.String[]:System.String[]
  at Fuse.Scripting.V8.Context.ThrowPendingExceptions () <0x26b1afa0 + 0x0003f> in <filename unknown>:0 
  at Fuse.Scripting.V8.Object.CallMethod (System.String name, System.Object[] args) <0x26b30458 + 0x0041f> in <filename unknown>:0 
  at Fuse.Scripting.ClassInstance.CallMethod (Fuse.Scripting.Function method, System.Object[] args) <0x26b303e0 + 0x00061> in <filename unknown>:0 
  at Fuse.Reactive.RootableScriptModule.CallModuleFunc (Fuse.Scripting.Function moduleFunc, System.Object[] args) <0x26b303a8 + 0x00023> in <filename unknown>:0 
  at Fuse.Scripting.ScriptModule.Evaluate (Fuse.Scripting.Context c, Fuse.Scripting.ModuleResult result) <0x26b1f360 + 0x00232> in <filename unknown>:0 
  at Fuse.Reactive.RootableScriptModule.Evaluate (Fuse.Scripting.Context c, Fuse.Scripting.ModuleResult result) <0x26b2ba88 + 0x0002b> in <filename unknown>:0 
  at Fuse.Scripting.Module.Evaluate (Fuse.Scripting.Context c, System.String id) <0x26b1dc60 + 0x00172> in <filename unknown>:0 
  at Fuse.Reactive.JavaScript.EvaluateModule () <0x26b2b140 + 0x00083> in <filename unknown>:0 
  at Fuse.Reactive.JavaScript.EvaluateExports () <0x26b2ada8 + 0x00013> in <filename unknown>:0 
  at Fuse.Reactive.JavaScript+EvaluateDataContext.Evaluate () <0x26b2acb8 + 0x00027> in <filename unknown>:0 
  at Fuse.Reactive.ThreadWorker.RunInner () <0x26b174c0 + 0x00482> in <filename unknown>:0 

I think we’ve solved this on Slack, but for future reference, the problem is that the JavaScript engine doesn’t know about all Uno types.

In this case the V8 JavaScript context is complaining that it doesn’t know what to do with the string[] type. To work around it, use a Scripting.Array (which is an Uno handle to an actual JS array) and fill it with your strings. There’s a helper method on the JavaScript Context to do this: NewArray.