-
Notifications
You must be signed in to change notification settings - Fork 5
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
Autoload results in "Java package 'org.ruboto.jruby9k_poc' does not have a method `const_missing' with 1 argument" #7
Comments
A couple of findings: Referring to Using autoloading in a regular Ruby class gives a different error than using autoloading in an activity class backed by a Java class:
|
In our production app, I added two patches to work around the issue. Firstly, I added this patch to if qualified_name.start_with?('Java::')
parent = Module
end This prevents calling Secondly, I patched module DirGlobPatch
def [](*patterns, **opts)
puts "patterns: #{patterns.inspect}"
super(*patterns.map{|pattern| pattern.sub('uri:classloader:', APK_PATH)}, **opts)
end
end
Dir.singleton_class.prepend DirGlobPatch With these two patches, I am able to start the application, log in, and list orders, so a lot is working now including communication with the server by TCP socket and HTTP. The issue with the |
I am tracing the call down to @headius I'll hang around Gitter this evening (utc) if you have time to help me? |
I can reproduce in command line in a smallish example: import org.jruby.embed.LocalContextScope;
import org.jruby.embed.LocalVariableBehavior;
import org.jruby.embed.ScriptingContainer;
public class Main {
private static ScriptingContainer ruby;
public static void main(String[] args) {
ruby = new ScriptingContainer(LocalContextScope.SINGLETON, LocalVariableBehavior.TRANSIENT);
SuperClass component = new SuperClass();
ruby.runScriptlet("Java::" + ((Object) component).getClass().getName() + ".__persistent__ = true");
ruby.put("SuperClass", runRubyMethod(component, "singleton_class"));
if (ruby.getCompatVersion().is1_9()) {
ruby.runScriptlet(
"Gem.paths = ENV.merge('GEM_HOME' => File.expand_path('~/.gem/jruby/1.9.3'))\n"
);
}
ruby.runScriptlet("class SuperClass\n" +
" def ruby_method\n" +
" require 'active_support/dependencies'\n" +
" ActiveSupport::Dependencies.autoload_paths << 'uri:classloader:/'\n" +
" AutoloadedClass.new.perform\n" +
" puts 'Autoload OK'\n" +
" rescue => e\n" +
" puts \"Exception testing autload: #{e}\"\n" +
" end\n" +
"end\n");
component.testAutoload();
}
public static Object runRubyMethod(Object receiver, String methodName) {
return ruby.runRubyMethod(Object.class, receiver, methodName);
}
} public class SuperClass {
public void testAutoload() {
Main.runRubyMethod(this, "ruby_method");
}
} autoloaded_class.rb: class AutoloadedClass
def perform
puts 'AutoloadedClass#perform'
end
end |
The example above works with JRuby 1.7.x and fails with JRuby 9.2.x.x. |
Here is an even smaller example without ActiveSupport. It works with JRuby 1.7.x and fails with JRuby 9.2.8.0. If the instance is a regular Ruby object, not a Java object, the example works on JRuby 1.7.x, JRuby 9.2.8.0, MRI 2.6.3, and TruffleRuby 19.1.0. instance = java.lang.String.new
instance.class.__persistent__ = true if instance.class.respond_to?('__persistent__=')
MyClass = instance.singleton_class
class MyClass
def my_method
MissingConstant
raise 'Expected NameError'
rescue NameError
puts "Success!"
end
end
instance.my_method |
This issue is in code written by @kares for jruby/jruby#3333, which allows accessing non-public inner classes lazily using a const_missing hook. The issue seems to be that a I've tried to patch this with simpler logic but it doesn't pass the tests that @kares wrote: diff --git a/core/src/main/java/org/jruby/java/proxies/JavaProxy.java b/core/src/main/java/org/jruby/java/proxies/JavaProxy.java
index ab32c909c9..5ab6d28acf 100644
--- a/core/src/main/java/org/jruby/java/proxies/JavaProxy.java
+++ b/core/src/main/java/org/jruby/java/proxies/JavaProxy.java
@@ -557,7 +557,16 @@ public class JavaProxy extends RubyObject {
// handling non-public inner classes retrieval ... like private constants
@JRubyMethod(name = "const_missing", required = 1, meta = true, visibility = Visibility.PRIVATE, frame = true)
public static IRubyObject const_missing(ThreadContext context, IRubyObject self, IRubyObject name) {
- return Java.get_inner_class(context, (RubyModule) self, name);
+ Class<?> javaClass = JavaClass.getJavaClass(context, (RubyModule) self);
+
+ for (Class<?> declaredClass : javaClass.getDeclaredClasses()) {
+ if (declaredClass.getSimpleName().equals(name.toString())) {
+ return ((RubyModule) self).setConstant(name.toString(), Java.getProxyClass(context.runtime, declaredClass));
+ }
+ }
+
+ // else dispatch to super const_missing
+ return Helpers.invokeSuper(context, self, name, Block.NULL_BLOCK);
}
@JRubyMethod(meta = true) |
I tested with the patched jruby-complete-9.2.9.0-SNAPSHOT from your branch, and it solves the ClassCastException ! Woohoo! The original error for this issue still remains:
I will try to make a smaller reproduction than the POC. |
Aha, I have a theory. I suspect that ActiveSupport is basically doing its own lookup+const_missing rather than just going after the constant directly. We have a const_missing under the covers in our package modules, but the Ruby side does not expose a const_missing method (since there could be a "const_missing" package). If I'm right, this is a simple enough reproduction:
|
Yes, I think you are right. |
The error from ruboto/JRuby9K_POC#7 was caused (most likely) by ActiveSupport attempting to call `const_missing` directly on the package object, but finding it marked private and refusing to make the call. This change removes the private visibility from both const_missing and method_missing.
I believe this is fixed on master. I also made method_missing visible. @kares Do you know why these were private? |
do not recall any particular reason, fix should be fine |
Tried the jruby-complete-20190822.050313-17.jar without the dependencies.rb patch, and it looks good! |
The error from ruboto/JRuby9K_POC#7 was caused (most likely) by ActiveSupport attempting to call `const_missing` directly on the package object, but finding it marked private and refusing to make the call. This change removes the private visibility from both const_missing and method_missing.
Example added to the app:
The text was updated successfully, but these errors were encountered: