Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overriding "respond_to?" in java fails to be recognized #7154

Closed
HoneyryderChuck opened this issue Mar 24, 2022 · 4 comments
Closed

Overriding "respond_to?" in java fails to be recognized #7154

HoneyryderChuck opened this issue Mar 24, 2022 · 4 comments
Milestone

Comments

@HoneyryderChuck
Copy link

Coming these issues in the protobuf project (environment details inside).

Is this something that the jruby team can help unblock?

@JasonLunn
Copy link
Contributor

Moving description of the issue originally captured in protocolbuffers/protobuf#9668 here.

JRuby Version: 9.3.3.0

Given a JRuby class defined in Java e.g.

import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.RubyObject;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.load.BasicLibraryService;

public class RespondToJavaService implements BasicLibraryService  {

  @Override
  public boolean basicLoad(Ruby ruby) throws IOException {
    ruby.defineModule("Foo").defineClassUnder("Bar", ruby.getObject(), new ObjectAllocator() {
      @Override
      public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
        return new RespondTo(runtime, klazz);
      }
    }).defineAnnotatedMethods(RespondTo.class);
    return true;
  }

  @JRubyClass(name = "RespondTo")
  public static class RespondTo extends RubyObject {

    public RespondTo(Ruby runtime, RubyClass klazz) {
      super(runtime, klazz);
    }

    @JRubyMethod(name = "respond_to?", required = 1, optional = 1)
    public IRubyObject respondTo(ThreadContext context, IRubyObject[] args) {
      System.err.println("Custom repond_to? got called!");
      return Helpers.invokeSuper(context, this, metaClass, "respond_to?", args, Block.NULL_BLOCK);
    }
  }
}

The same behavior is observed when binding a static method:

import java.io.IOException;

import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.RubyObject;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.load.BasicLibraryService;

public class RespondToJavaService implements BasicLibraryService  {

  @Override
  public boolean basicLoad(Ruby ruby) throws IOException {
    ruby.defineModule("Foo").defineClassUnder("Bar", ruby.getObject(), new ObjectAllocator() {
      @Override
      public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
        return new RespondTo(runtime, klazz);
      }
    }).defineAnnotatedMethods(RespondTo.class);
    return true;
  }

  @JRubyClass(name = "RespondTo")
  public static class RespondTo extends RubyObject {

    public RespondTo(Ruby runtime, RubyClass klazz) {
      super(runtime, klazz);
    }

    @JRubyMethod(name = "respond_to?", required = 1, optional = 1)
    public static IRubyObject respondTo(ThreadContext context, IRubyObject self, IRubyObject[] args) {
      System.err.println("Custom repond_to? got called!");
      String methodName = args[0].asJavaString();
      return self.getMetaClass().respondsToMethod(methodName, true) ? context.runtime.getTrue() : context.runtime.getFalse();
    }
  }
}

*** What did you expect to see
jruby -r ./respond_to_java.jar -e 'Foo::Bar.new.respond_to? :baz' should emit Custom repond_to? got called!

*** What did you see instead?
jruby -r ./respond_to_java.jar -e 'Foo::Bar.new.respond_to? :baz' emits nothing on the first example, but works on the second example, except if jruby is invoked with -X-C.
jruby -r ./respond_to_java.jar -e 'Foo::Bar.new.respond_to? :baz, :beep' does emit Custom repond_to? got called!
Invocations with more than 2 or fewer than 1 arguments raise ArgumentErrors as expected.

@JasonLunn
Copy link
Contributor

@headius suggested the following workaround that explicitly sets isBuiltin to false on the method, and for now that works for me regardless of whether -X-C is passed to the JRuby CLI:

import java.io.IOException;

import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.RubyObject;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.load.BasicLibraryService;

public class RespondToJavaService implements BasicLibraryService  {

  @Override
  public boolean basicLoad(Ruby ruby) throws IOException {
    RubyClass bar = ruby.defineModule("Foo").defineClassUnder("Bar", ruby.getObject(), new ObjectAllocator() {
      @Override
      public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
        return new RespondTo(runtime, klazz);
      }
    });
    bar.defineAnnotatedMethods(RespondTo.class);
    bar.searchMethod("respond_to?").setIsBuiltin(false);

    return true;
  }

  @JRubyClass(name = "RespondTo")
  public static class RespondTo extends RubyObject {

    public RespondTo(Ruby runtime, RubyClass klazz) {
      super(runtime, klazz);
    }

    @JRubyMethod(name = "respond_to?", required = 1, optional = 1)
    public IRubyObject respondTo(ThreadContext context, IRubyObject[] args) {
      System.err.println("Custom repond_to? got called!");
      return Helpers.invokeSuper(context, this, metaClass, "respond_to?", args, Block.NULL_BLOCK);
    }
  }
}

headius added a commit to headius/jruby that referenced this issue Mar 25, 2022
The original purpose of isBuiltin was to allow us to detect when
a dynamically-acquired method is actually the implementation we
shipped in a core JRuby class, to enable us to customize related
logic specific to how we know the JRuby implementation works. This
PR codifies that relationship, only setting methods to be
isBuiltin if they are constructed and bound during the core boot
process of JRuby itself.

In the future we will entertain more descriptive metadata for such
methods, but for now this narrows isBuiltin just enough to avoid
third-party extensions running into issues.

Fixes jruby#7154
@headius headius added this to the JRuby 9.3.5.0 milestone Mar 25, 2022
@headius headius changed the title Overriding "respond_to?" in java (from protobuf gem) Overriding "respond_to?" in java fails to be recognized Mar 25, 2022
headius added a commit to headius/jruby that referenced this issue Mar 29, 2022
The original purpose of isBuiltin was to allow us to detect when
a dynamically-acquired method is actually the implementation we
shipped in a core JRuby class, to enable us to customize related
logic specific to how we know the JRuby implementation works. This
PR codifies that relationship, only setting methods to be
isBuiltin if they are constructed and bound during the core boot
process of JRuby itself.

In the future we will entertain more descriptive metadata for such
methods, but for now this narrows isBuiltin just enough to avoid
third-party extensions running into issues.

Fixes jruby#7154
@headius
Copy link
Member

headius commented Mar 31, 2022

This should be fixed by #7156 for JRuby 9.3.5 and future versions.

@headius headius closed this as completed Mar 31, 2022
@headius
Copy link
Member

headius commented Mar 31, 2022

Perhaps someone could contribute a test case for this and method_missing and perhaps other Object/Kernel methods that could be overridden in Java? Help prevent us breaking user-custom versions of these methods in the future!

@headius headius modified the milestones: JRuby 9.3.5.0, JRuby 9.3.6.0 Jun 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants