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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[refactor] make Java embed utils generic #7503

Merged
merged 5 commits into from Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -29,8 +29,10 @@ public enum Methods {
IS_DEFINED_CLASS_VAR, IS_DEFINED_SUPER, IS_DEFINED_METHOD, IS_DEFINED_CALL,
IS_DEFINED_CONSTANT_OR_METHOD, MERGE_KWARGS, IS_HASH_EMPTY, HASH_CHECK, ARRAY_LENGTH;

public static Methods fromOrdinal(int value) {
return value < 0 || value >= values().length ? null : values()[value];
private static final Methods[] VALUES = values();

static Methods fromOrdinal(int value) {
return value < 0 || value >= VALUES.length ? null : VALUES[value];
}
}

Expand Down
39 changes: 23 additions & 16 deletions core/src/main/java/org/jruby/javasupport/JavaEmbedUtils.java
Expand Up @@ -43,20 +43,24 @@
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.UriLikePathHelper;

/**
* Utility functions to help embedders out. These function consolidate logic that is
* used between BSF and JSR 223. People who are embedding JRuby 'raw' should use these
* as well. If at a later date, we discover a flaw or change how we do things, this
* utility class should provide some insulation.
* Utility functions to help embedders out.
* These function consolidate logic that is used between BSF and JSR 223.
* People who are embedding JRuby 'raw' should use these as well.
* If at a later date, we discover a flaw or change how we do things, this utility class should provide some insulation.
*
* <pre>
* Example:
* Ruby runtime = JavaEmbedUtils.initialize(new ArrayList());
* RubyRuntimeAdapter evaler = JavaEmbedUtils.newRuntimeAdapter();
* IRubyObject rubyObject = evaler.parse(runtime, expr.toString(), file, line).run());
* SomeClassOrInterface javaObject = (SomeClassOrInterface) JavaEmbedUtils.rubyToJava(rubyObject);
* runtime.terminate();
*
* Ruby runtime = JavaEmbedUtils.initialize(List.of("a/custom/load/path"));
*
* RubyRuntimeAdapter evaler = JavaEmbedUtils.newRuntimeAdapter();
* IRubyObject rubyObject = evaler.parse(runtime, expr.toString(), file, line).run());
* SomeClassOrInterface javaObject = JavaEmbedUtils.rubyToJava(rubyObject, SomeClassOrInterface.class);
*
* runtime.terminate();
* </pre>
*/
public class JavaEmbedUtils {
/**
Expand Down Expand Up @@ -223,7 +227,7 @@ public static void terminate(Ruby runtime) {
* @return the result of the invocation.
*/
@SuppressWarnings("deprecation")
public static Object invokeMethod(Ruby runtime, Object receiver, String method, Object[] args, Class returnType) {
public static <T> T invokeMethod(Ruby runtime, Object receiver, String method, Object[] args, Class<T> returnType) {
IRubyObject rubyReceiver = receiver != null ? JavaUtil.convertJavaToRuby(runtime, receiver) : runtime.getTopSelf();

IRubyObject[] rubyArgs = JavaUtil.convertJavaArrayToRuby(runtime, args);
Expand All @@ -243,7 +247,12 @@ public static Object invokeMethod(Ruby runtime, Object receiver, String method,
/**
* Convert a Ruby object to a Java object.
*/
public static Object rubyToJava(Ruby runtime, IRubyObject value, Class type) {
public static <T> T rubyToJava(IRubyObject value, Class<T> type) {
return value.toJava(type);
}

// @Deprecated
public static <T> T rubyToJava(Ruby runtime, IRubyObject value, Class<T> type) {
return value.toJava(type);
}

Expand All @@ -252,17 +261,15 @@ public static Object rubyToJava(Ruby runtime, IRubyObject value, Class type) {
* @param value to be converted
* @return the converted object
*/
public static Object rubyToJava(IRubyObject value) {
return value.toJava(Object.class);
public static <T> T rubyToJava(IRubyObject value) {
return (T) value.toJava(Object.class);
}

/**
* Convert a java object to a Ruby object.
*/
@SuppressWarnings("deprecation")
public static IRubyObject javaToRuby(Ruby runtime, Object value) {
if (value instanceof IRubyObject) return (IRubyObject) value;

IRubyObject result = JavaUtil.convertJavaToUsableRubyObject(runtime, value);

return result instanceof JavaObject ? Java.wrap(runtime, result) : result;
Expand Down
94 changes: 93 additions & 1 deletion core/src/test/java/org/jruby/javasupport/JavaEmbedUtilsTest.java
Expand Up @@ -3,14 +3,20 @@
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import org.jruby.java.proxies.ConcreteJavaProxy;
import org.jruby.java.proxies.JavaProxy;
import org.jruby.runtime.builtin.IRubyObject;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
Expand Down Expand Up @@ -65,4 +71,90 @@ public void testAddClassloaderToLoadPathOnNoneTCCL() throws Exception {
String result = runtime.evalScriptlet("require 'test_me';$result").toString();
assertEquals(result, "uri:" + url);
}

@Test
public void testAPIUsageTheNonGenericWay() { // before <T> generic signatures were introduced (JRuby <= 9.4.0)
final Ruby runtime = Ruby.newInstance();
IRubyObject str = runtime.evalScriptlet("'foo'");
Object javaStr = JavaEmbedUtils.rubyToJava(runtime, str, String.class);
assertEquals("foo", javaStr);

str = runtime.evalScriptlet("'bar' * 3");
javaStr = JavaEmbedUtils.rubyToJava(runtime, str, Object.class);
assertEquals("barbarbar", javaStr);

Object val = JavaEmbedUtils.rubyToJava(runtime.newEmptyArray());
assertEquals("org.jruby.RubyArray", val.getClass().getName());
}

@Test
public void testJavaToRubyPrimitive() {
final Ruby runtime = Ruby.newInstance();

IRubyObject v;
v = JavaEmbedUtils.javaToRuby(runtime, -100L);
assertEquals(runtime.newFixnum(-100), v);

v = JavaEmbedUtils.javaToRuby(runtime, 200);
assertEquals(runtime.newFixnum(200), v);

v = JavaEmbedUtils.javaToRuby(runtime, (short) 200);
assertEquals(runtime.newFixnum(200), v);

v = JavaEmbedUtils.javaToRuby(runtime, (byte) 100);
assertEquals(runtime.newFixnum(100), v);

v = JavaEmbedUtils.javaToRuby(runtime, 10.0f);
assertEquals(runtime.newFloat(10.0), v);

v = JavaEmbedUtils.javaToRuby(runtime, 10.0d);
assertEquals(runtime.newFloat(10.0), v);

v = JavaEmbedUtils.javaToRuby(runtime, true);
assertSame(runtime.getTrue(), v);

v = JavaEmbedUtils.javaToRuby(runtime, false);
assertSame(runtime.getFalse(), v);
}

@Test
public void testJavaToRuby() {
final Ruby runtime = Ruby.newInstance();

IRubyObject v;
v = JavaEmbedUtils.javaToRuby(runtime, "");
assertEquals(runtime.newString(), v);

v = JavaEmbedUtils.javaToRuby(runtime, Long.valueOf(42L));
assertEquals(runtime.newFixnum(42), v);

v = JavaEmbedUtils.javaToRuby(runtime, Boolean.TRUE);
assertSame(runtime.getTrue(), v);

v = JavaEmbedUtils.javaToRuby(runtime, new StringBuilder());
assertEquals(ConcreteJavaProxy.class, v.getClass()); // no more JavaObject wrapping!
}

@Test
public void testRubyToJava() {
final Ruby runtime = Ruby.newInstance();

CharSequence sym = JavaEmbedUtils.rubyToJava(runtime.newSymbol("foo"), CharSequence.class);
assertEquals("foo", sym);
}

@Test
public void testJavaToRubyAndRubyToJava() {
final Ruby runtime = Ruby.newInstance();

IRubyObject v = JavaEmbedUtils.javaToRuby(runtime, new ArrayList<>(Arrays.asList("1", '2', 3)));
assertTrue(v instanceof JavaProxy);
List<?> val = JavaEmbedUtils.rubyToJava(v);
assertEquals(3, val.size());

assertEquals(java.util.ArrayList.class, val.getClass());

Collection<Object> coll = JavaEmbedUtils.rubyToJava(v, Collection.class);
assertSame(val, coll);
}
}