Skip to content

Commit

Permalink
[ji] when a Ruby Class is being coerced to a Java class - auto-reify …
Browse files Browse the repository at this point in the history
…(closing #3454)

... much less confusing than for user classes to see the nearest reified class in the inheritance hierarchy (without explicitly doing `become_java!`)
kares committed Apr 6, 2016
1 parent 591c78f commit 27d8098
Showing 4 changed files with 34 additions and 16 deletions.
8 changes: 6 additions & 2 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -825,6 +825,10 @@ public IRubyObject checkArrayType() {
*/
@Override
public Object toJava(Class target) {
return defaultToJava(target);
}

final <T> T defaultToJava(Class<T> target) {
// for callers that unconditionally pass null retval type (JRUBY-4737)
if (target == void.class) return null;

@@ -838,7 +842,7 @@ public Object toJava(Class target) {
if (target.isAssignableFrom(value.getClass())) {
getRuntime().getJavaSupport().getObjectProxyCache().put(value, this);

return value;
return (T) value;
}
}
else if (JavaUtil.isDuckTypeConvertable(getClass(), target)) {
@@ -847,7 +851,7 @@ else if (JavaUtil.isDuckTypeConvertable(getClass(), target)) {
}
}
else if (target.isAssignableFrom(getClass())) {
return this;
return (T) this;
}

throw getRuntime().newTypeError("cannot convert instance of " + getClass() + " to " + target);
17 changes: 7 additions & 10 deletions core/src/main/java/org/jruby/RubyClass.java
Original file line number Diff line number Diff line change
@@ -1215,11 +1215,10 @@ public Object unmarshalFrom(Ruby runtime, RubyClass type,
* @return true if the class can be reified, false otherwise
*/
public boolean isReifiable() {
RubyClass realSuper = null;

// already reified is not reifiable
if (reifiedClass != null) return false;

final RubyClass realSuper;
// root classes are not reifiable
if (superClass == null || (realSuper = superClass.getRealClass()) == null) return false;

@@ -1750,17 +1749,15 @@ public synchronized void addClassAnnotation(Class annotation, Map fields) {

@Override
public Object toJava(Class klass) {
Class returnClass = null;

if (klass == Class.class) {
if (reifiedClass == null) reifyWithAncestors(); // possibly auto-reify
// Class requested; try java_class or else return nearest reified class
if (respondsTo("java_class")) {
return callMethod("java_class").toJava(klass);
} else {
for (RubyClass current = this; current != null; current = current.getSuperClass()) {
returnClass = current.getReifiedClass();
if (returnClass != null) return returnClass;
}
}
for (RubyClass current = this; current != null; current = current.getSuperClass()) {
Class reifiedClazz = current.getReifiedClass();
if ( reifiedClazz != null ) return reifiedClazz;
}
// should never fall through, since RubyObject has a reified class
}
@@ -1770,7 +1767,7 @@ public Object toJava(Class klass) {
return this;
}

return super.toJava(klass);
return defaultToJava(klass);
}

/**
10 changes: 6 additions & 4 deletions spec/java_integration/types/coercion_spec.rb
Original file line number Diff line number Diff line change
@@ -728,15 +728,17 @@ def receive_primitive_box(obj)
end
end

it "provides nearest reified class for unreified user classes" do
rubycls = Class.new
expect(rubycls.to_java(cls)).to eq(cls.forName('org.jruby.RubyObject'));
class UserKlass < Object; end

it "reifies user class on-demand" do
expect(klass = UserKlass.to_java(cls)).to be_a java.lang.Class
expect( klass.getSuperclass ).to be cls.forName('org.jruby.RubyObject')
end

it "returns reified class for reified used classes" do
rubycls = Class.new; require 'jruby/core_ext'
rubycls.become_java!
expect(rubycls.to_java(cls)).to eq(JRuby.reference(rubycls).reified_class)
expect(rubycls.to_java(cls)).to be JRuby.reference(rubycls).getReifiedClass
end

it "converts Java proxy classes to their JavaClass/java.lang.Class equivalent" do
15 changes: 15 additions & 0 deletions test/jruby/test_higher_javasupport.rb
Original file line number Diff line number Diff line change
@@ -39,6 +39,21 @@ def test_reified_class_in_jruby_class_loader
end
end

class Klass1 < Object
def method1(arg); arg end
end
class Klass2 < Klass1
def self.method2; end
end

def test_passing_a_java_class_auto_reifies
assert_nil Klass2.to_java.getReifiedClass
# previously TestHelper.getClassName(Klass2) returned 'org.jruby.RubyObject'
assert_equal 'rubyobj.TestHigherJavasupport.Klass2', TestHelper.getClassName(Klass2)
assert_not_nil Klass2.to_java.getReifiedClass
assert_not_nil Klass1.to_java.getReifiedClass
end

def test_java_passing_class
assert_equal("java.util.ArrayList", TestHelper.getClassName(ArrayList))
end

0 comments on commit 27d8098

Please sign in to comment.