Skip to content

org.jruby.javasupport.JavaMethod invocation does not wrap returned JavaObject #2857

@vietj

Description

@vietj

The Vert.x 3 Ruby lang integration uses java_class.declared_method for dispatching the Ruby Vert.x API to the Java Vert.x API.

The JavaMethod object does not wrap the returned objects instance of JavaObject, so such objects are not usable from JRuby as they are and must be rewrapped with their corresponding JRuby wrappers.

The current implementation extends the returned JavaMethod object to avoid such issues:

public class ConvertingJavaMethod extends JavaMethod {

  private final Class boxedReturnType;
  private final JavaUtil.JavaConverter returnConverter;

  public ConvertingJavaMethod(Ruby runtime, Method method) {
    super(runtime, method);
    method.setAccessible(true);
    if (method.getReturnType().isPrimitive() && method.getReturnType() != void.class) {
      this.boxedReturnType = CodegenUtils.getBoxType(method.getReturnType());
    } else {
      this.boxedReturnType = method.getReturnType();
    }
    returnConverter = JavaUtil.getJavaConverter(method.getReturnType());
  }

  @Override
  public IRubyObject invoke(IRubyObject[] args) {
    IRubyObject ret = super.invoke(args);
    if (ret instanceof JavaObject) {
      ret = convertReturn(((JavaObject) ret).getValue());
    }
    return ret;
  }

  private IRubyObject convertReturn(Object result) {
    if (result != null && result.getClass() != boxedReturnType) {
      // actual type does not exactly match method return type, re-get converter
      // FIXME: when the only autoconversions are primitives, this won't be needed
      return JavaUtil.convertJavaToUsableRubyObject(getRuntime(), result);
    }
    return JavaUtil.convertJavaToUsableRubyObjectWithConverter(getRuntime(), result, returnConverter);
  }
}

Such code is used in such way:

(Java::IoVertxLangJruby::Helper.fixJavaMethod(@j_del.java_class.declared_method(:runOnContext,Java::IoVertxCore::Handler.java_class))).invoke(@j_del,Proc.new { yield })

(the helper transforms the JavaMethod returned by declared_method to ConvertingJavaMethod)

The current code needs to use method.setAccessible(true) otherwise some Vert.x methods cannot be invoked leading to a runtime error like:

org.jruby.exceptions.RaiseException: (TypeError) illegal access on 'complete': Class org.jruby.javasupport.JavaMethod can not access a member of class io.vertx.core.impl.FutureImpl with modifiers "public"
    at org.jruby.javasupport.JavaMethod.invoke(org/jruby/javasupport/JavaMethod.java:257)
    at RUBY.complete(/Users/julien/java/vertx-lang-ruby/target/classes/vertx/future.rb:72)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions