ballerina-platform / ballerina-lang

The Ballerina Programming Language
https://ballerina.io/
Apache License 2.0
3.68k stars 751 forks source link

[Bug]: Inconsistent behaviors in specifying parameter types for interop functions #42456

Open gabilang opened 7 months ago

gabilang commented 7 months ago

Description

Following interop case with the parameter type specified gives the parameter count mismatch error which may cause confusions.

public isolated client class Client {
    isolated function getResourceMethod(service object {} serviceObject, string[] path) returns any = @java:Method {
         'class: "myapp.app.App",
         paramTypes: ["io.ballerina.runtime.api.values.BObject", "io.ballerina.runtime.api.values.BArray"]
     } external;
}

Interop java method:

public static Object getResourceMethod(BObject service, BArray path) {
    return 1000;
}

error:

{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH 'Parameter count does not match with Java method 'getResourceMethod' found in class 'myapp.app.App''

Anyway it works fine if the parameterType field is specified as

paramTypes: ["io.ballerina.runtime.api.values.BArray"]

Steps to Reproduce

No response

Affected Version(s)

No response

OS, DB, other environment details and versions

No response

Related area

-> Runtime

Related issue(s) (optional)

No response

Suggested label(s) (optional)

No response

Suggested assignee(s) (optional)

No response

gabilang commented 6 months ago

It also works as expected when we specify the parameter types as

paramTypes: ["io.ballerina.runtime.api.values.BObject", "io.ballerina.runtime.api.values.BArray"]

and the matching Java method as

public static Object getResourceMethod(BObject receiver, BObject service, BArray path) {
    return 1000;
}

But in this scenario, the parameter receiver may not be used.

gabilang commented 5 months ago

Defining the Ballerina interop functions as follows (without specifying parameter types)

public isolated client class Client {
    isolated function getResourceMethod(service object {} serviceObject, string[] path) returns any = @java:Method {
         'class: "myapp.app.App"
     } external;
}

is working to call the Java method

public static Object getResourceMethod(BObject service, BArray path) {
    return 1000;
}

Here the direct parameter count, parameter types and return type are matching and the Java method is resolved as the matching method. Adding the receiver type parameter to the Java method also is working with the same Ballerina external function. But if the receiver type parameter is not used in the Java method, adding such is unnecessary and may be redundant.

We can fix this inconsistent behavior in two ways,

  1. Mandate the receiver type parameter for every Java method which maps external functions from Ballerina objects.

    The matching Java method for the above Ballerina function will be

    public static Object getResourceMethod(BObject receiver, BObject service, BArray path) {
        return 1000;
    }

    This will be a breaking change.

  2. Allow the following parameter type definition in the Ballerina external function to match the exact Java method,

    public isolated client class Client {
        isolated function getResourceMethod(service object {} serviceObject, string[] path) returns any = @java:Method {
             'class: "myapp.app.App",
             paramTypes: ["io.ballerina.runtime.api.values.BObject", "io.ballerina.runtime.api.values.BArray"]
         } external;
    }

Currently specifying parameter types as paramTypes: ["io.ballerina.runtime.api.values.BArray"] is working but it should not be allowed here since in the original issue the first parameter of the Java method is not a receiver type parameter.

Please add your thoughts @warunalakshitha @hasithaa