2

Working on a legacy JRuby app (1.6.8) running under Java 11, I traced a peculiar error a test case: "require 'java'; puts java::util::regex::Pattern.class" errors ArgumentError: wrong number of arguments (0 for 1). For other built-in JRE classes, this seems to work fine (see below).

This causes JRuby itself to fail sometimes, at this line in https://github.com/jruby/jruby/blob/1.6.8/lib/ruby/site_ruby/shared/builtin/javasupport/core_ext/object.rb#L10 :

if self.class.superclass.instance_method(:method_added) != method(:java_package_method_added)

To get the app to work, I had to comment out that line in the JRuby source.

I'm perplexed as to the cause of this, or how to fix it appropriately. Google shows several instances of people getting that error when trying to load particular apps, but no one figuring out why (all the solutions said "try different versions of the app).

Why can JRuby not execute .class ? What argument is it expecting?

Complete trace below:

$ java -version
java version "11.0.1" 2018-10-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)

$ ./jruby -v
jruby 1.6.8 (ruby-1.8.7-p357) (2012-09-18 1772b40) (Java HotSpot(TM) 64-Bit Server VM 11.0.1) [linux-amd64-java]

$ ./jruby -e "require 'java'; puts java::util::regex::Matcher.class"
WARNING: An illegal reflective access operation has occurred
...
Class

$ ./jruby -e "require 'java'; puts java::lang::String.class"
WARNING: An illegal reflective access operation has occurred
...
Class

$ ./jruby -e "require 'java'; puts java::util::regex::Pattern.class"
WARNING: An illegal reflective access operation has occurred
....
ArgumentError: wrong number of arguments (0 for 1)
  (root) at -e:1

Java behavior

Java itself seems to have no problem:

jshell> int.class
$6 ==> int

jshell> String.class
$7 ==> class java.lang.String

jshell> Pattern.class
$8 ==> class java.util.regex.Pattern

jshell> Pattern.class.getClass()
$12 ==> class java.lang.Class

jshell> String.class.getClass()
$13 ==> class java.lang.Class

Partial Solution?

I can fix this error in JRuby by commenting out the if statement referenced above. However, on this legacy app, JRuby is in a compiled jar, and so I can't edit its source. Is there a way to add a monkey patch to JRuby, without changing the binary jar, to fix the failing if statement?

Getting JRuby

JRuby 1.6.8 is available at http://central.maven.org/maven2/org/jruby/jruby-dist/1.6.8/

SRobertJames
  • 8,210
  • 14
  • 60
  • 107
  • I have no experiences with JRuby, but as far as I can recognize, it is not just accessing the class but *importing* it, so I suppose, it will read the class file’s bytecode at one point. The bytecode format has evolved and trying to read these classes with software from 2012 may work with some class files but fail with others. Just a guess… – Holger Jan 08 '19 at 08:24
  • @Holger, I don't believe JRuby directly access the underlying .class file, beyond loading it in the JVM, like any other app does. It's not trying to independently parse the bytes in the bytecode. – SRobertJames Jan 08 '19 at 13:44
  • But there’s nothing magical in the `Pattern` class, especially not compared to the `Matcher` class within the same package. Also, there are no fundamental changes made in Java 11 (apart from the bytecode format). – Holger Jan 08 '19 at 13:54

1 Answers1

4

I think that jruby is trying to read one of members of Pattern class:

private static int getClass(int c) {
    return sun.text.Normalizer.getCombiningClass(c);
}

as it looks a bit like getter but have that extra argument.

You can confirm that by creating own class like that:

class MyClass {
    private static int getClass(int c) {
        return 4;
    }
}

And then try to get class from it in JRuby.

It works with java 8 as this method was non-static, and like Holger said jruby seems to prefer to use existing static methods even if they do not match signature, and check for special ".class" property is done at the end. (As in java Something.class is not a property, but just a keyword)

Best option would be to have higher priority for methods that are matching invocation (0 needed arguments), and maybe for public methods. Also getters should probably only be matched by name of property only if they are really getters - so they do not have any arguments.

Download for this version of jruby does not work for me, so I can't test this: https://www.jruby.org/files/downloads/1.6.8/index.html

GotoFinal
  • 3,585
  • 2
  • 18
  • 33
  • Thanks. I edited with a link to download JRuby 1.6.8. – SRobertJames Jan 10 '19 at 01:44
  • I understand you to be saying that when JRuby invokes `.class` on a Java object, it will first try `.getClass()` (if it exists, even if its private and requires args), and then, if it doesn't exist, try `.class`. Is that correct? Where in JRuby's source is that? – SRobertJames Jan 10 '19 at 01:51
  • 1
    @SRobertJames every Java class has a `getClass()` method. It’s even `public`. But its an instance method and hence not applicable when you use “*type name* `.class`”. The interesting point about the `getClass(int)` method in the `Pattern` class is that it does already exist in Java 8, but has made `static` in Java 11. So it seems indeed that JRuby prefers `static` methods when you use “*type name . identifier*” if there is one, even if its private and has a different argument count, and only checks whether the identifier is `class` if that method lookup failed. – Holger Jan 10 '19 at 08:50