Skip to content

Commit

Permalink
Showing 21 changed files with 134 additions and 150 deletions.
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/RubyArray.java
Original file line number Diff line number Diff line change
@@ -4969,7 +4969,7 @@ public Object[] toArray(final Object[] arg) {
}

@Override
public Object toJava(Class target) {
public <T> T toJava(Class<T> target) {
if (target.isArray()) {
Class type = target.getComponentType();
Object rawJavaArray = Array.newInstance(type, realLength);
@@ -4978,7 +4978,7 @@ public Object toJava(Class target) {
} catch (ArrayIndexOutOfBoundsException ex) {
throw concurrentModification(getRuntime(), ex);
}
return rawJavaArray;
return target.cast(rawJavaArray);
} else {
return super.toJava(target);
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -856,7 +856,7 @@ public IRubyObject checkArrayType() {
* @see IRubyObject#toJava
*/
@Override
public Object toJava(Class target) {
public <T> T toJava(Class<T> target) {
return defaultToJava(target);
}

27 changes: 16 additions & 11 deletions core/src/main/java/org/jruby/RubyBoolean.java
Original file line number Diff line number Diff line change
@@ -161,6 +161,14 @@ public static IRubyObject false_xor(IRubyObject f, IRubyObject oth) {
public static RubyString false_to_s(IRubyObject f) {
return RubyString.newStringShared(f.getRuntime(), FALSE_BYTES);
}

@Override
public <T> T toJava(Class<T> target) {
if (target.isAssignableFrom(Boolean.class) || target == boolean.class) {
return (T) Boolean.FALSE;
}
return super.toJava(target);
}
}

static final ByteList TRUE_BYTES = new ByteList(new byte[] { 't','r','u','e' }, USASCIIEncoding.INSTANCE);
@@ -192,6 +200,14 @@ public static IRubyObject true_xor(IRubyObject t, IRubyObject oth) {
public static RubyString true_to_s(IRubyObject t) {
return RubyString.newStringShared(t.getRuntime(), TRUE_BYTES);
}

@Override
public <T> T toJava(Class<T> target) {
if (target.isAssignableFrom(Boolean.class) || target == boolean.class) {
return (T) Boolean.TRUE;
}
return super.toJava(target);
}
}

@JRubyMethod(name = "hash")
@@ -221,16 +237,5 @@ public IRubyObject taint(ThreadContext context) {
public void marshalTo(MarshalStream output) throws java.io.IOException {
output.write(isTrue() ? 'T' : 'F');
}

@Override
public Object toJava(Class target) {
if (target.isAssignableFrom(Boolean.class) || target.equals(boolean.class)) {
if (isFalse()) return Boolean.FALSE;

return Boolean.TRUE;
} else {
return super.toJava(target);
}
}
}

6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/RubyClass.java
Original file line number Diff line number Diff line change
@@ -1886,7 +1886,7 @@ public synchronized void addClassAnnotation(Class annotation, Map fields) {
}

@Override
public Object toJava(final Class target) {
public <T> T toJava(Class<T> target) {
if (target == Class.class) {
if (reifiedClass == null) reifyWithAncestors(); // possibly auto-reify
// Class requested; try java_class or else return nearest reified class
@@ -1895,13 +1895,13 @@ public Object toJava(final Class target) {
if ( ! javaClass.isNil() ) return javaClass.toJava(target);

Class reifiedClass = nearestReifiedClass(this);
if ( reifiedClass != null ) return reifiedClass;
if ( reifiedClass != null ) return target.cast(reifiedClass);
// should never fall through, since RubyObject has a reified class
}

if (target.isAssignableFrom(RubyClass.class)) {
// they're asking for something RubyClass extends, give them that
return this;
return target.cast(this);
}

return defaultToJava(target);
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/RubyFile.java
Original file line number Diff line number Diff line change
@@ -2028,10 +2028,10 @@ private static RubyString checkHome(ThreadContext context) {
}

@Override
public Object toJava(Class target) {
if (target == java.io.File.class) {
public <T> T toJava(Class<T> target) {
if (target == File.class) {
final String path = getPath();
return path == null ? null : new java.io.File(path);
return path == null ? null : target.cast(new File(path));
}
return super.toJava(target);
}
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/RubyIO.java
Original file line number Diff line number Diff line change
@@ -4752,14 +4752,14 @@ public boolean getBOM() {
}

@Override
public Object toJava(Class target) {
public <T> T toJava(Class<T> target) {
if (target == java.io.InputStream.class) {
getOpenFile().checkReadable(getRuntime().getCurrentContext());
return getInStream();
return target.cast(getInStream());
}
if (target == java.io.OutputStream.class) {
getOpenFile().checkWritable(getRuntime().getCurrentContext());
return getOutStream();
return target.cast(getOutStream());
}
return super.toJava(target);
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -4718,7 +4718,7 @@ public void setCacheProxy(boolean cacheProxy) {
}

@Override
public Object toJava(final Class target) {
public <T> T toJava(Class<T> target) {
if (target == Class.class) { // try java_class for proxy modules
final ThreadContext context = getRuntime().getCurrentContext();
IRubyObject javaClass = JavaClass.java_class(context, this);
31 changes: 14 additions & 17 deletions core/src/main/java/org/jruby/RubyNil.java
Original file line number Diff line number Diff line change
@@ -249,24 +249,21 @@ public static IRubyObject rationalize(ThreadContext context, IRubyObject recv, I
}

@Override
public Object toJava(Class target) {
public <T> T toJava(Class<T> target) {
if (target.isPrimitive()) {
if (target == Boolean.TYPE) {
return Boolean.FALSE;
} else if (target == Byte.TYPE) {
return (byte)0;
} else if (target == Short.TYPE) {
return (short)0;
} else if (target == Character.TYPE) {
return (char)0;
} else if (target == Integer.TYPE) {
return 0;
} else if (target == Long.TYPE) {
return (long)0;
} else if (target == Float.TYPE) {
return (float)0;
} else if (target == Double.TYPE) {
return (double)0;
if (target == boolean.class) {
return (T) Boolean.FALSE;
} else if (target == char.class) {
return (T) (Character) '\0';
} else {
switch (target.getSimpleName().charAt(0)) {
case 'b': return (T) (Byte) (byte) 0;
case 's': return (T) (Short) (short) 0;
case 'i': return (T) (Integer) 0;
case 'l': return (T) (Long) 0L;
case 'f': return (T) (Float) 0.0F;
case 'd': return (T) (Double) 0.0;
}
}
}
return null;
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyNumeric.java
Original file line number Diff line number Diff line change
@@ -1345,7 +1345,7 @@ public IRubyObject conjugate(ThreadContext context) {
}

@Override
public Object toJava(Class target) {
public <T> T toJava(Class<T> target) {
return JavaUtil.getNumericConverter(target).coerce(this, target);
}

8 changes: 4 additions & 4 deletions core/src/main/java/org/jruby/RubyString.java
Original file line number Diff line number Diff line change
@@ -5624,18 +5624,18 @@ public static ByteList encodeBytelist(CharSequence value, Encoding encoding) {
}

@Override
public Object toJava(Class target) {
public <T> T toJava(Class<T> target) {
if (target.isAssignableFrom(String.class)) {
return decodeString();
return target.cast(decodeString());
}
if (target.isAssignableFrom(ByteList.class)) {
return value;
return target.cast(value);
}
if (target == Character.class || target == Character.TYPE) {
if ( strLength() != 1 ) {
throw getRuntime().newArgumentError("could not coerce string of length " + strLength() + " (!= 1) into a char");
}
return decodeString().charAt(0);
return (T) (Character) decodeString().charAt(0);
}
return super.toJava(target);
}
6 changes: 4 additions & 2 deletions core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
@@ -646,8 +646,10 @@ public static RubySymbol unmarshalFrom(UnmarshalStream input) throws java.io.IOE
}

@Override
public Object toJava(Class target) {
if (target == String.class || target == CharSequence.class) return symbol;
public <T> T toJava(Class<T> target) {
if (target == String.class || target == CharSequence.class) {
return target.cast(symbol);
}

return super.toJava(target);
}
16 changes: 8 additions & 8 deletions core/src/main/java/org/jruby/RubyTime.java
Original file line number Diff line number Diff line change
@@ -1285,27 +1285,27 @@ public static RubyTime load(ThreadContext context, IRubyObject recv, IRubyObject
}

@Override
public Object toJava(Class target) {
public <T> T toJava(Class<T> target) {
if (target == Date.class) {
return getJavaDate();
return target.cast(getJavaDate());
}
if (target == Calendar.class || target == GregorianCalendar.class) {
return dt.toGregorianCalendar();
return target.cast(dt.toGregorianCalendar());
}
if (target == DateTime.class) {
return this.dt;
return target.cast(this.dt);
}
if (target == java.sql.Date.class) {
return new java.sql.Date(dt.getMillis());
return target.cast(new java.sql.Date(dt.getMillis()));
}
if (target == java.sql.Time.class) {
return new java.sql.Time(dt.getMillis());
return target.cast(new java.sql.Time(dt.getMillis()));
}
if (target == java.sql.Timestamp.class) {
return new java.sql.Timestamp(dt.getMillis());
return target.cast(new java.sql.Timestamp(dt.getMillis()));
}
if (target.isAssignableFrom(Date.class)) {
return getJavaDate();
return target.cast(getJavaDate());
}
return super.toJava(target);
}
Original file line number Diff line number Diff line change
@@ -1836,6 +1836,14 @@ public IRubyObject zero_p() {
return getRuntime().newBoolean(isZero());
}

@Override
public <T> T toJava(Class<T> target) {
if (target == BigDecimal.class) {
return (T) value;
}
return super.toJava(target);
}

/**
* Returns the correctly rounded square root of a positive
* BigDecimal. This method performs the fast <i>Square Root by
Original file line number Diff line number Diff line change
@@ -210,7 +210,7 @@ public IRubyObject id() {

@Override
@SuppressWarnings("unchecked")
public Object toJava(final Class type) {
public <T> T toJava(Class<T> type) {
final Object object = getObject();
final Class clazz = object.getClass();

@@ -222,16 +222,16 @@ public Object toJava(final Class type) {
object instanceof Boolean && type == Boolean.TYPE ) {
// FIXME in more permissive call paths, like invokedynamic, this can allow
// precision-loading downcasts to happen silently
return object;
return (T) object;
}
}
else if ( type.isAssignableFrom(clazz) ) {
if ( Java.OBJECT_PROXY_CACHE || metaClass.getCacheProxy() ) {
getRuntime().getJavaSupport().getObjectProxyCache().put(object, this);
}
return object;
return type.cast(object);
}
else if ( type.isAssignableFrom(getClass()) ) return this; // e.g. IRubyObject.class
else if ( type.isAssignableFrom(getClass()) ) return type.cast(this); // e.g. IRubyObject.class

throw getRuntime().newTypeError("failed to coerce " + clazz.getName() + " to " + type.getName());
}
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/java/proxies/JavaProxy.java
Original file line number Diff line number Diff line change
@@ -462,12 +462,12 @@ private RubyMethod getRubyMethod(ThreadContext context, String name, Class... ar

@Override
@SuppressWarnings("unchecked")
public Object toJava(final Class type) {
public <T> T toJava(Class<T> type) {
final Object object = getObject();
final Class<?> clazz = object.getClass();

if ( type.isAssignableFrom(clazz) ) return object;
if ( type.isAssignableFrom(getClass()) ) return this; // e.g. IRubyObject.class
if ( type.isAssignableFrom(clazz) ) return type.cast(object);
if ( type.isAssignableFrom(getClass()) ) return type.cast(this); // e.g. IRubyObject.class

throw getRuntime().newTypeError("failed to coerce " + clazz.getName() + " to " + type.getName());
}
Original file line number Diff line number Diff line change
@@ -169,9 +169,10 @@ public String toString() {
}

@Override
public Object toJava(Class target) {
if ( AccessibleObject.class.isAssignableFrom(target) ) {
return accessibleObject();
public <T> T toJava(Class<T> target) {
AccessibleObject accessibleObject = accessibleObject();
if (target.isAssignableFrom(accessibleObject.getClass())) {
return target.cast(accessibleObject);
}
return super.toJava(target);
}
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/javasupport/JavaObject.java
Original file line number Diff line number Diff line change
@@ -301,12 +301,12 @@ public IRubyObject marshal_load(ThreadContext context, IRubyObject str) {

@Override
@SuppressWarnings("unchecked")
public Object toJava(final Class target) {
public <T> T toJava(Class<T> target) {
final Object value = getValue();
if ( value == null ) return null;

if ( target.isAssignableFrom( value.getClass() ) ) {
return value;
return target.cast(value);
}
return super.toJava(target);
}
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/javasupport/JavaPackage.java
Original file line number Diff line number Diff line change
@@ -295,9 +295,9 @@ public IRubyObject sealed_p(ThreadContext context) {

@Override
@SuppressWarnings("unchecked")
public Object toJava(final Class target) {
public <T> T toJava(Class<T> target) {
if ( target.isAssignableFrom( Package.class ) ) {
return Package.getPackage(packageName);
return target.cast(Package.getPackage(packageName));
}
return super.toJava(target);
}
127 changes: 49 additions & 78 deletions core/src/main/java/org/jruby/javasupport/JavaUtil.java
Original file line number Diff line number Diff line change
@@ -264,7 +264,7 @@ public static <T> T convertProcToInterface(ThreadContext context, RubyBasicObjec
return (T) javaObject.getValue();
}

public static NumericConverter getNumericConverter(Class target) {
public static <T> NumericConverter<T> getNumericConverter(Class<T> target) {
final NumericConverter converter = NUMERIC_CONVERTERS.get(target);
return converter == null ? NUMERIC_TO_OTHER : converter;
}
@@ -587,8 +587,8 @@ public static abstract class JavaConverter {
public String toString() {return type.getName() + " converter";}
}

public interface NumericConverter {
public Object coerce(RubyNumeric numeric, Class target);
public interface NumericConverter<T> {
T coerce(RubyNumeric numeric, Class<T> target);
}

private static IRubyObject trySimpleConversions(Ruby runtime, Object object) {
@@ -910,90 +910,61 @@ public void set(Ruby runtime, Object array, int i, IRubyObject value) {
JAVA_CONVERTERS.put(BigInteger.class, JAVA_BIGINTEGER_CONVERTER);
}

private static final NumericConverter NUMERIC_TO_BYTE = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
final long value = numeric.getLongValue();
if ( isLongByteable(value) ) return (byte) value;
throw numeric.getRuntime().newRangeError("too big for byte: " + numeric);
}
};
private static final NumericConverter NUMERIC_TO_SHORT = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
final long value = numeric.getLongValue();
if ( isLongShortable(value) ) return (short) value;
throw numeric.getRuntime().newRangeError("too big for short: " + numeric);
}
};
private static final NumericConverter NUMERIC_TO_CHARACTER = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
final long value = numeric.getLongValue();
if ( isLongCharable(value) ) return (char) value;
throw numeric.getRuntime().newRangeError("too big for char: " + numeric);
}
private static final NumericConverter<Byte> NUMERIC_TO_BYTE = (numeric, target) -> {
final long value = numeric.getLongValue();
if ( isLongByteable(value) ) return (byte) value;
throw numeric.getRuntime().newRangeError("too big for byte: " + numeric);
};
private static final NumericConverter NUMERIC_TO_INTEGER = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
final long value = numeric.getLongValue();
if ( isLongIntable(value) ) return (int) value;
throw numeric.getRuntime().newRangeError("too big for int: " + numeric);
}
private static final NumericConverter<Short> NUMERIC_TO_SHORT = (numeric, target) -> {
final long value = numeric.getLongValue();
if ( isLongShortable(value) ) return (short) value;
throw numeric.getRuntime().newRangeError("too big for short: " + numeric);
};
private static final NumericConverter NUMERIC_TO_LONG = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
return numeric.getLongValue();
}
private static final NumericConverter<Character> NUMERIC_TO_CHARACTER = (numeric, target) -> {
final long value = numeric.getLongValue();
if ( isLongCharable(value) ) return (char) value;
throw numeric.getRuntime().newRangeError("too big for char: " + numeric);
};
private static final NumericConverter NUMERIC_TO_FLOAT = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
final double value = numeric.getDoubleValue();
// many cases are ok to convert to float; if not one of these, error
if ( isDoubleFloatable(value) ) return (float) value;
throw numeric.getRuntime().newTypeError("too big for float: " + numeric);
}
private static final NumericConverter<Integer> NUMERIC_TO_INTEGER = (numeric, target) -> {
final long value = numeric.getLongValue();
if ( isLongIntable(value) ) return (int) value;
throw numeric.getRuntime().newRangeError("too big for int: " + numeric);
};
private static final NumericConverter NUMERIC_TO_DOUBLE = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
return numeric.getDoubleValue();
}
private static final NumericConverter<Long> NUMERIC_TO_LONG = (numeric, target) -> numeric.getLongValue();
private static final NumericConverter<Float> NUMERIC_TO_FLOAT = (numeric, target) -> {
final double value = numeric.getDoubleValue();
// many cases are ok to convert to float; if not one of these, error
if ( isDoubleFloatable(value) ) return (float) value;
throw numeric.getRuntime().newTypeError("too big for float: " + numeric);
};
private static final NumericConverter NUMERIC_TO_BIGINTEGER = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
return numeric.getBigIntegerValue();
}
};
private static final NumericConverter NUMERIC_TO_OBJECT = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
// for Object, default to natural wrapper type
if (numeric instanceof RubyFixnum) {
long value = numeric.getLongValue();
return Long.valueOf(value);
} else if (numeric instanceof RubyFloat) {
double value = numeric.getDoubleValue();
return Double.valueOf(value);
} else if (numeric instanceof RubyBignum) {
return ((RubyBignum)numeric).getValue();
} else if (numeric instanceof RubyBigDecimal) {
return ((RubyBigDecimal)numeric).getValue();
} else {
return NUMERIC_TO_OTHER.coerce(numeric, target);
}
}
};
private static final NumericConverter NUMERIC_TO_OTHER = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
if (target.isAssignableFrom(numeric.getClass())) {
// just return as-is, since we can't do any coercion
return numeric;
}
// otherwise, error; no conversion available
throw numeric.getRuntime().newTypeError("could not coerce " + numeric.getMetaClass() + " to " + target);
private static final NumericConverter<Double> NUMERIC_TO_DOUBLE = (numeric, target) -> numeric.getDoubleValue();
private static final NumericConverter<BigInteger> NUMERIC_TO_BIGINTEGER = (numeric, target) -> numeric.getBigIntegerValue();

private static final NumericConverter NUMERIC_TO_OTHER = (numeric, target) -> {
if (target.isAssignableFrom(numeric.getClass())) {
// just return as-is, since we can't do any coercion
return numeric;
}
// otherwise, error; no conversion available
throw numeric.getRuntime().newTypeError("could not coerce " + numeric.getMetaClass() + " to " + target);
};
private static final NumericConverter NUMERIC_TO_VOID = new NumericConverter() {
public Object coerce(RubyNumeric numeric, Class target) {
return null;
private static final NumericConverter<Object> NUMERIC_TO_OBJECT = (numeric, target) -> {
// for Object, default to natural wrapper type
if (numeric instanceof RubyFixnum) {
long value = numeric.getLongValue();
return Long.valueOf(value);
} else if (numeric instanceof RubyFloat) {
double value = numeric.getDoubleValue();
return Double.valueOf(value);
} else if (numeric instanceof RubyBignum) {
return ((RubyBignum)numeric).getValue();
} else if (numeric instanceof RubyBigDecimal) {
return ((RubyBigDecimal)numeric).getValue();
} else {
return NUMERIC_TO_OTHER.coerce(numeric, target);
}
};
private static final NumericConverter NUMERIC_TO_VOID = (numeric, target) -> null;
private static boolean isDoubleFloatable(double value) {
return true;
}
Original file line number Diff line number Diff line change
@@ -275,7 +275,7 @@ public interface IRubyObject {
*
* @param type The target type to which the object should be converted.
*/
Object toJava(Class type);
<T> T toJava(Class<T> type);

/**
* RubyMethod dup.
Original file line number Diff line number Diff line change
@@ -596,7 +596,7 @@ public void testParse_3args_2() {
filename = basedir + "/core/src/test/ruby/org/jruby/embed/ruby/next_year.rb";
result = instance.parse(PathType.ABSOLUTE, filename);
IRubyObject ret = result.run();
assertEquals(getNextYear(), ret.toJava(Integer.class));
assertEquals(getNextYear(), (int) ret.toJava(Integer.class));

StringWriter sw = new StringWriter();
instance.setWriter(sw);

0 comments on commit 80b9665

Please sign in to comment.