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

proc-to-interface conversion clones String based objects #5603

Open
klobuczek opened this Issue Feb 10, 2019 · 6 comments

Comments

Projects
None yet
4 participants
@klobuczek
Copy link

klobuczek commented Feb 10, 2019

Environment

  • jruby 9.2.5.0 (2.5.0) 2018-12-06 6d5a228 Java HotSpot(TM) 64-Bit Server VM 25.191-b12 on 1.8.0_191-b12 +jit [darwin-x86_64]
  • Operating system and platform (e.g. uname -a)
    Darwin berlin 18.2.0 Darwin Kernel Version 18.2.0: Mon Nov 12 20:24:46 PST 2018; root:xnu-4903.231.4~2/RELEASE_X86_64 x86_64

Expected Behavior

Proc-to-interface conversion should preserve the object returned by the ruby block

Actual Behavior

If the ruby block returns a String or any subclass of String the interface proxy returns a copy of the underlying object as String loosing the actual type. The behavior is correct for any other type.

Example:

public interface Interface<T>
{
    T execute();
}

public class TypeDemo {
    public static <T> T demo(Interface<T> i)
    {
        return i.execute();
    }
}

Then the following ruby script

class Abc; end
class AbcSubclass < Abc; end
class StringSubclas < String; end
class StringSubSubclas < StringSubclas; end
class HashSubclass < Hash; end
class ArraySubclass < Array; end

[1, "abc", Abc.new, AbcSubclass.new, StringSubclas.new, StringSubSubclas.new, HashSubclass.new, ArraySubclass.new, Class.new, [1, 2, 3], ["abc"], {a: 1}].each do |before|
  after = Java::TypeDemo.demo { before }
  puts "before.class: #{before.class}, after.class: #{after.class}, same objects: #{before.object_id == after.object_id}"
end

produces the following output:

before.class: Integer, after.class: Integer, same objects: true
before.class: String, after.class: String, same objects: false
before.class: Abc, after.class: Abc, same objects: true
before.class: AbcSubclass, after.class: AbcSubclass, same objects: true
before.class: StringSubclas, after.class: String, same objects: false
before.class: StringSubSubclas, after.class: String, same objects: false
before.class: HashSubclass, after.class: HashSubclass, same objects: true
before.class: ArraySubclass, after.class: ArraySubclass, same objects: true
before.class: Class, after.class: Class, same objects: true
before.class: Array, after.class: Array, same objects: true
before.class: Array, after.class: Array, same objects: true
before.class: Hash, after.class: Hash, same objects: true
@klobuczek

This comment has been minimized.

Copy link
Author

klobuczek commented Feb 12, 2019

Verified. Issue persists in:

jruby 9.2.6.0 (2.5.3) 2019-02-11 15ba00b Java HotSpot(TM) 64-Bit Server VM 25.191-b12 on 1.8.0_191-b12 +jit [darwin-x86_64]```
@lkalwa

This comment has been minimized.

Copy link

lkalwa commented Feb 16, 2019

I recently encountered the same issue.
I can try to find a fix for that, @headius any hint where I should start digging?

@kares

This comment has been minimized.

Copy link
Member

kares commented Feb 18, 2019

believe this is "normal" for bare String -> since JRuby always converts Ruby -> Java by default.
here the target return type is java.lang.Object thus it does RubyString#toJava(Object.class)
however the sub-string path is weird and also that Integers seemingly no longer convert back-and-forth.
will take a look.

@kares

This comment has been minimized.

Copy link
Member

kares commented Feb 18, 2019

yeah, its definitely that - and Integers also get a round-trip going toJava and than back to Ruby land.
one can confirm that by using a "bignum" Integer e.g. 100**20 - you won't get the same object back.
with "fixnum" Integer it seemingly seems to be the case but (except for the fixnum cache) their not the same Java objects ... its the way object_id gets calculated that they appear to be the same.

there isn't any fix for a patch release as it would like break existing scripts using Java Integration.
we could change JI to NOT convert by default in 9.3 ... or at least have an option to turn it off.
you could avoid the (default) conversion by using a JRuby specific return type IRubyObject

@klobuczek

This comment has been minimized.

Copy link
Author

klobuczek commented Mar 20, 2019

@kares found another type that is being converted to Java:
before.class: Time, after.class: Java::JavaUtil::Date, same objects: false
BTW the workaround by using IRubyObject does not work for me as I am implementing a gem for both jruby and MRI so the APIs must be identical.

@headius

This comment has been minimized.

Copy link
Member

headius commented Apr 9, 2019

@klobuczek Going through Java integration there's currently no way to prevent the coercion to a Java type and back to Ruby; on the Java side, if you're calling an API that wants a string, we have to pass it a string.

We have considered adding more knobs and features to Java integration to allow you to control that coercion directly, but there has never been any consensus on what that ought to look like.

An alternative way to do what you want is to implement a small part of your library as a JRuby extension (in Java or another JVM language) and use that to interact with the rest of the JVM world. That will allow you to pass the IRubyObject directly through to the JRuby ext, and from there you can do what you wish.

You say that using IRubyObject did not work for you...do you have an example of what you tried?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.