Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 00e4357a6179
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d45a1c896819
Choose a head ref
  • 6 commits
  • 4 files changed
  • 1 contributor

Commits on Mar 11, 2015

  1. Copy the full SHA
    142fce8 View commit details
  2. tune ArrayProxy - allow indexing by Java Integer wrappers (inspired by

    …#2658)
    
    avoids "ugly-sh" failures such as :
    
    TypeError: wrong argument type Java::JavaLang::Integer (expected Range)
        org/jruby/java/proxies/ArrayJavaProxy.java:81:in `[]'
    kares committed Mar 11, 2015
    Copy the full SHA
    3f4bb5f View commit details
  3. Copy the full SHA
    c4e0a68 View commit details
  4. share ArrayProxy's array index conversion code (between [] and []=) +…

    … review internals
    
    ... also added a test for new `[]` and `[]=` index conversion (inspired by #2658)
    kares committed Mar 11, 2015
    Copy the full SHA
    9cb1fac View commit details
  5. Copy the full SHA
    5779228 View commit details
  6. Copy the full SHA
    d45a1c8 View commit details
84 changes: 43 additions & 41 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -97,7 +97,7 @@
* and internal variables are handled this way, but the implementation
* in RubyObject only returns "this" in {@link #getInstanceVariables()} and
* {@link #getInternalVariables()}.
*
*
* Methods that are implemented here, such as "initialize" should be implemented
* with care; reification of Ruby classes into Java classes can produce
* conflicting method names in rare cases. See JRUBY-5906 for an example.
@@ -107,7 +107,7 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
private static final Logger LOG = LoggerFactory.getLogger("RubyBasicObject");

private static final boolean DEBUG = false;

/** The class of this object */
protected transient RubyClass metaClass;

@@ -116,13 +116,13 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co

/** variable table, lazily allocated as needed (if needed) */
public transient Object[] varTable;

/** locking stamp for Unsafe ops updating the vartable */
public transient volatile int varTableStamp;

/** offset of the varTable field in RubyBasicObject */
public static final long VAR_TABLE_OFFSET = UnsafeHolder.fieldOffset(RubyBasicObject.class, "varTable");

/** offset of the varTableTamp field in RubyBasicObject */
public static final long STAMP_OFFSET = UnsafeHolder.fieldOffset(RubyBasicObject.class, "varTableStamp");

@@ -426,7 +426,7 @@ public boolean isTaint() {
public void setTaint(boolean taint) {
// JRUBY-4113: callers should not call setTaint on immediate objects
if (isImmediate()) return;

if (taint) {
flags |= TAINTED_F;
} else {
@@ -783,26 +783,28 @@ public Object toJava(Class target) {
// for callers that unconditionally pass null retval type (JRUBY-4737)
if (target == void.class) return null;

if (dataGetStruct() instanceof JavaObject) {
final Object innerWrapper = dataGetStruct();
if (innerWrapper instanceof JavaObject) {
// for interface impls

JavaObject innerWrapper = (JavaObject)dataGetStruct();

final Object value = ((JavaObject) innerWrapper).getValue();
// ensure the object is associated with the wrapper we found it in,
// so that if it comes back we don't re-wrap it
if (target.isAssignableFrom(innerWrapper.getValue().getClass())) {
getRuntime().getJavaSupport().getObjectProxyCache().put(innerWrapper.getValue(), this);
if (target.isAssignableFrom(value.getClass())) {
getRuntime().getJavaSupport().getObjectProxyCache().put(value, this);

return innerWrapper.getValue();
return value;
}
} else if (JavaUtil.isDuckTypeConvertable(getClass(), target)) {
}
else if (JavaUtil.isDuckTypeConvertable(getClass(), target)) {
if (!respondsTo("java_object")) {
return JavaUtil.convertProcToInterface(getRuntime().getCurrentContext(), this, target);
}
} else if (target.isAssignableFrom(getClass())) {
}
else if (target.isAssignableFrom(getClass())) {
return this;
}

throw getRuntime().newTypeError("cannot convert instance of " + getClass() + " to " + target);
}

@@ -1047,7 +1049,7 @@ private StringBuilder inspectObj(StringBuilder part) {
for (Map.Entry<String, VariableAccessor> entry : metaClass.getVariableTableManager().getVariableAccessorsForRead().entrySet()) {
Object value = entry.getValue().get(this);
if (value == null || !(value instanceof IRubyObject) || !IdUtil.isInstanceVariable(entry.getKey())) continue;

part.append(sep).append(" ").append(entry.getKey()).append("=");
part.append(invokedynamic(context, (IRubyObject)value, INSPECT));
sep = ",";
@@ -1082,21 +1084,21 @@ public int compareTo(IRubyObject other) {
try {
IRubyObject cmp = invokedynamic(getRuntime().getCurrentContext(),
this, OP_CMP, other);

// if RubyBasicObject#op_cmp is used, the result may be nil
if (!cmp.isNil()) {
return (int) cmp.convertToInteger().getLongValue();
}
} catch (RaiseException ex) {
}

/* We used to raise an error if two IRubyObject were not comparable, but
* in order to support the new ConcurrentHashMapV8 and other libraries
* and containers that arbitrarily call compareTo expecting it to always
* succeed, we have opted to return 0 here. This will allow all
* RubyBasicObject subclasses to be compared, but if the comparison is
* not valid we they will appear the same for sorting purposes.
*
*
* See https://jira.codehaus.org/browse/JRUBY-7013
*/
return 0;
@@ -1181,13 +1183,13 @@ public void removeFinalizers() {
public Object getVariable(int index) {
return VariableAccessor.getVariable(this, index);
}

public void setVariable(int index, Object value) {
ensureInstanceVariablesSettable();
if (index < 0) return;
metaClass.getVariableTableManager().setVariableInternal(this, index, value);
}

public final Object getNativeHandle() {
return metaClass.getVariableTableManager().getNativeHandle(this);
}
@@ -1210,8 +1212,8 @@ public final void setFFIHandle(Object value) {

/**
* Returns true if object has any variables
*
* @see VariableTableManager#hasVariables(org.jruby.RubyBasicObject)
*
* @see VariableTableManager#hasVariables(org.jruby.RubyBasicObject)
*/
public boolean hasVariables() {
return metaClass.getVariableTableManager().hasVariables(this);
@@ -1336,15 +1338,15 @@ public Object removeInternalVariable(String name) {
assert !IdUtil.isRubyVariable(name);
return variableTableRemove(name);
}

/**
* Sync one this object's variables with other's - this is used to make
* rbClone work correctly.
*/
public void syncVariables(IRubyObject other) {
metaClass.getVariableTableManager().syncVariables(this, other);
}

//
// INSTANCE VARIABLE API METHODS
//
@@ -1527,7 +1529,7 @@ public IRubyObject send19(ThreadContext context, IRubyObject[] args, Block block

return getMetaClass().finvoke(context, this, name, newArgs, block);
}

@JRubyMethod(name = "instance_eval", compat = RUBY1_9)
public IRubyObject instance_eval19(ThreadContext context, Block block) {
return specificEval(context, getInstanceEvalClass(), block);
@@ -1807,7 +1809,7 @@ public void finalize() {
}
}
}

private void callFinalizer(IRubyObject finalizer) {
Helpers.invoke(
finalizer.getRuntime().getCurrentContext(),
@@ -2602,7 +2604,7 @@ public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg
}
public IRubyObject send(ThreadContext context, IRubyObject[] args, Block block) {
if (args.length == 0) return send(context, block);

String name = RubySymbol.objectToSymbolString(args[0]);
int newArgsLength = args.length - 1;

@@ -2816,20 +2818,20 @@ protected String validateInstanceVariable(String name) {

throw getRuntime().newNameError("`" + name + "' is not allowable as an instance variable name", name);
}

/**
* Serialization of a Ruby (basic) object involves three steps:
*
*
* <ol>
* <li>Dump the object itself</li>
* <li>Dump a String used to load the appropriate Ruby class</li>
* <li>Dump each variable from varTable in turn</li>
* </ol>
*
*
* The metaClass field is marked transient since Ruby classes generally will
* not be able to serialize (since they hold references to method tables,
* other classes, and potentially thread-, runtime-, or jvm-local state.
*
*
* The varTable field is transient because the layout of the same class may
* differ across runtimes, since it is determined at runtime based on the
* order in which variables get assigned for a given class. We serialize
@@ -2839,36 +2841,36 @@ private void writeObject(ObjectOutputStream oos) throws IOException {
if (metaClass.isSingleton()) {
throw new IOException("can not serialize singleton object");
}

oos.defaultWriteObject();
oos.writeUTF(metaClass.getName());

metaClass.getVariableTableManager().serializeVariables(this, oos);
}

/**
* Deserialization proceeds as follows:
*
*
* <ol>
* <li>Deserialize the object instance. It will have null metaClass and
* varTable fields.</li>
* <li>Deserialize the name of the object's class, and retrieve class from a
* thread-local JRuby instance.</li>
* <li>Retrieve each variable in turn, re-assigning them by name.</li>
* </ol>
*
* @see RubyBasicObject#writeObject(java.io.ObjectOutputStream)
*
* @see RubyBasicObject#writeObject(java.io.ObjectOutputStream)
*/
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
Ruby ruby = Ruby.getThreadLocalRuntime();

if (ruby == null) {
throw new IOException("No thread-local org.jruby.Ruby available; can't deserialize Ruby object. Set with Ruby#setThreadLocalRuntime.");
}

ois.defaultReadObject();
metaClass = (RubyClass)ruby.getClassFromPath(ois.readUTF());

metaClass.getVariableTableManager().deserializeVariables(this, ois);
}

279 changes: 148 additions & 131 deletions core/src/main/java/org/jruby/java/proxies/ArrayJavaProxy.java
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import java.lang.reflect.Array;
import java.util.Arrays;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
import org.jruby.RubyInteger;
@@ -12,7 +13,6 @@
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.java.util.ArrayUtils;
import org.jruby.javasupport.JavaArray;
import org.jruby.javasupport.JavaClass;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
@@ -21,137 +21,139 @@
import org.jruby.runtime.builtin.IRubyObject;

public class ArrayJavaProxy extends JavaProxy {

private final JavaUtil.JavaConverter converter;

public ArrayJavaProxy(Ruby runtime, RubyClass klazz, Object ary) {
this(runtime, klazz, ary, JavaUtil.getJavaConverter(ary.getClass().getComponentType()));
}

public ArrayJavaProxy(Ruby runtime, RubyClass klazz, Object ary, JavaUtil.JavaConverter converter) {
super(runtime, klazz, ary);
this.converter = converter;
}

public static RubyClass createArrayJavaProxy(ThreadContext context) {
Ruby runtime = context.runtime;

RubyClass arrayJavaProxy = runtime.defineClass("ArrayJavaProxy",
runtime.getJavaSupport().getJavaProxyClass(),
ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);

RubyClass singleton = arrayJavaProxy.getSingletonClass();

final DynamicMethod oldNew = singleton.searchMethod("new");

singleton.addMethod("new", new ArrayNewMethod(singleton, Visibility.PUBLIC, oldNew));

singleton.addMethod("new", new ArrayNewMethod(singleton, Visibility.PUBLIC));

arrayJavaProxy.defineAnnotatedMethods(ArrayJavaProxy.class);
arrayJavaProxy.includeModule(runtime.getEnumerable());

return arrayJavaProxy;
}

public JavaArray getJavaArray() {
JavaArray javaArray = (JavaArray)dataGetStruct();
JavaArray javaArray = (JavaArray) dataGetStruct();

if (javaArray == null) {
javaArray = new JavaArray(getRuntime(), getObject());
dataWrapStruct(javaArray);
}

return javaArray;
}
@JRubyMethod(name = {"length","size"})

@JRubyMethod(name = {"length", "size"})
public IRubyObject length(ThreadContext context) {
return context.runtime.newFixnum(Array.getLength(this.getObject()));
return context.runtime.newFixnum( Array.getLength( getObject() ) );
}

@JRubyMethod(name = "empty?")
public IRubyObject empty(ThreadContext context) {
return context.runtime.newBoolean(Array.getLength(this.getObject()) == 0);
return context.runtime.newBoolean( Array.getLength( getObject() ) == 0 );
}

@JRubyMethod(name = "[]")
public IRubyObject op_aref(ThreadContext context, IRubyObject arg) {
if (arg instanceof RubyInteger) {
int index = (int)((RubyInteger)arg).getLongValue();
return ArrayUtils.arefDirect(context.runtime, getObject(), converter, index);
} else {
return getRange(context, arg);
}
if ( arg instanceof RubyRange ) return arrayRange(context, (RubyRange) arg);
final int i = convertArrayIndex(arg);
return ArrayUtils.arefDirect(context.runtime, getObject(), converter, i);
}

@JRubyMethod(name = "[]", required = 1, rest = true)
public IRubyObject op_aref(ThreadContext context, IRubyObject[] args) {
if (args.length == 1 && args[0] instanceof RubyInteger) {
int index = (int)((RubyInteger)args[0]).getLongValue();
return ArrayUtils.arefDirect(context.runtime, getObject(), converter, index);
} else {
return getRange(context, args);
}
if ( args.length == 1 ) return op_aref(context, args[0]);
return getRange(context, args);
}

@JRubyMethod(name = "[]=")
public IRubyObject op_aset(ThreadContext context, IRubyObject index, IRubyObject value) {
ArrayUtils.asetDirect(context.runtime, getObject(), converter, (int)((RubyInteger)index).getLongValue(), value);
return value;
final int i = convertArrayIndex(index);
return ArrayUtils.asetDirect(context.runtime, getObject(), converter, i, value);
}

@JRubyMethod
public IRubyObject at(ThreadContext context, IRubyObject indexObj) {
Ruby runtime = context.runtime;
Object array = getObject();
int length = Array.getLength(array);
long index = indexObj.convertToInteger().getLongValue();

if (index < 0) {
index = index + length;

private static int convertArrayIndex(final IRubyObject index) {
if ( index instanceof RubyInteger ) {
return (int) ((RubyInteger) index).getLongValue();
}

if (index >= 0 && index < length) {
return ArrayUtils.arefDirect(runtime, array, converter, (int)index);
} else {
return context.nil;
if ( index instanceof JavaProxy ) {
return (Integer) index.toJava(Integer.class);
}
return (int) index.convertToInteger().getLongValue();
}

@JRubyMethod
public IRubyObject at(ThreadContext context, IRubyObject index) {
final Ruby runtime = context.runtime;
final Object array = getObject();
final int length = Array.getLength(array);

int i = convertArrayIndex(index);

if ( i < 0 ) i = i + length;

if ( i >= 0 && i < length ) {
return ArrayUtils.arefDirect(runtime, array, converter, i);
}
return context.nil;
}

@JRubyMethod(name = "+")
public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
if (other instanceof ArrayJavaProxy) {
Object otherArray = ((ArrayJavaProxy)other).getObject();

if (getObject().getClass().getComponentType().isAssignableFrom(otherArray.getClass().getComponentType())) {
return ArrayUtils.concatArraysDirect(context, getObject(), otherArray);
final Object array = getObject();
if ( other instanceof ArrayJavaProxy ) {
final Object otherArray = ((ArrayJavaProxy) other).getObject();
final Class<?> componentType = array.getClass().getComponentType();
if ( componentType.isAssignableFrom( otherArray.getClass().getComponentType() ) ) {
return ArrayUtils.concatArraysDirect(context, array, otherArray);
}
}
return ArrayUtils.concatArraysDirect(context, getObject(), other);
return ArrayUtils.concatArraysDirect(context, array, other);
}

@JRubyMethod
public IRubyObject each(ThreadContext context, Block block) {
Ruby runtime = context.runtime;
int length = Array.getLength(getObject());
final Ruby runtime = context.runtime;
final Object array = getObject();
final int length = Array.getLength(array);

for (int i = 0; i < length; i++) {
IRubyObject rubyObj = ArrayUtils.arefDirect(runtime, getObject(), converter, i);
block.yield(context, rubyObj);
for ( int i = 0; i < length; i++ ) {
IRubyObject element = ArrayUtils.arefDirect(runtime, array, converter, i);
block.yield(context, element);
}
return this;
}

@JRubyMethod(name = {"to_a","to_ary"})
public IRubyObject to_a(ThreadContext context) {
return JavaUtil.convertJavaArrayToRubyWithNesting(context, this.getObject());

@JRubyMethod(name = {"to_a", "to_ary"})
public RubyArray to_a(ThreadContext context) {
final Object array = getObject();
return JavaUtil.convertJavaArrayToRubyWithNesting(context, array);
}

@JRubyMethod
public IRubyObject inspect(ThreadContext context) {
StringBuffer buffer = new StringBuffer();
Class componentClass = getObject().getClass().getComponentType();
final StringBuilder buffer = new StringBuilder();
Class<?> componentClass = getObject().getClass().getComponentType();

buffer.append(componentClass.getName());

if (componentClass.isPrimitive()) {
switch (componentClass.getName().charAt(0)) {
case 'b':
@@ -178,90 +180,105 @@ public IRubyObject inspect(ThreadContext context) {
break;
}
} else {
buffer.append(Arrays.toString((Object[])getObject()));
buffer.append(Arrays.toString((Object[]) getObject()));
}
buffer.append('@')
.append(Integer.toHexString(inspectHashCode()));
return context.runtime.newString(buffer.toString());
buffer.append('@').append(Integer.toHexString(inspectHashCode()));
return context.runtime.newString(buffer.toString());
}

public IRubyObject getRange(ThreadContext context, IRubyObject[] args) {
if (args.length == 1) {
return getRange(context, args[0]);
} else if (args.length == 2) {
}
if (args.length == 2) {
return getRange(context, args[0], args[1]);
} else {
throw context.runtime.newArgumentError(args.length, 1);
}
throw context.runtime.newArgumentError(args.length, 1);
}

public IRubyObject getRange(ThreadContext context, IRubyObject arg0) {
int length = Array.getLength(getObject());

if (arg0 instanceof RubyRange) {
RubyRange range = (RubyRange)arg0;
if (range.first() instanceof RubyFixnum && range.last() instanceof RubyFixnum) {
int first = (int)((RubyFixnum)range.first()).getLongValue();
int last = (int)((RubyFixnum)range.last()).getLongValue();

first = first >= 0 ? first : length + first;
last = last >= 0 ? last : length + last;
int newLength = last - first;
if (range.exclude_end_p().isFalse()) newLength += 1;

if (newLength <= 0) {
return ArrayUtils.emptyJavaArrayDirect(context, getObject().getClass().getComponentType());
}

return ArrayUtils.javaArraySubarrayDirect(context, getObject(), first, newLength);
} else {
throw context.runtime.newTypeError("only Fixnum ranges supported");
}
} else {
throw context.runtime.newTypeError(arg0, context.runtime.getRange());
if ( arg0 instanceof RubyRange ) {
return arrayRange(context, (RubyRange) arg0);
}
throw context.runtime.newTypeError(arg0, context.runtime.getRange());
}

public IRubyObject getRange(ThreadContext context, IRubyObject firstObj, IRubyObject lengthObj) {
if (firstObj instanceof RubyFixnum && lengthObj instanceof RubyFixnum) {
int first = (int)((RubyFixnum)firstObj).getLongValue();
int length = (int)((RubyFixnum)lengthObj).getLongValue();

if (length > Array.getLength(getObject())) {
throw context.runtime.newIndexError("length specifed is longer than array");
private IRubyObject arrayRange(final ThreadContext context, final RubyRange range) {
final Object array = getObject();
final int arrayLength = Array.getLength( array );

final IRubyObject rFirst = range.first();
final IRubyObject rLast = range.last();
if ( rFirst instanceof RubyFixnum && rLast instanceof RubyFixnum ) {
int first = (int) ((RubyFixnum) rFirst).getLongValue();
int last = (int) ((RubyFixnum) rLast).getLongValue();

first = first >= 0 ? first : arrayLength + first;
last = last >= 0 ? last : arrayLength + last;

int newLength = last - first;
if ( range.exclude_end_p().isFalse() ) newLength += 1;

if ( newLength <= 0 ) {
return ArrayUtils.emptyJavaArrayDirect(context, array.getClass().getComponentType());
}

first = first >= 0 ? first : Array.getLength(getObject()) + first;
return ArrayUtils.javaArraySubarrayDirect(context, array, first, newLength);
}
throw context.runtime.newTypeError("only Fixnum ranges supported");
}

public IRubyObject getRange(ThreadContext context, IRubyObject first, IRubyObject length) {
return arrayRange(context, first, length);
}

private IRubyObject arrayRange(final ThreadContext context,
final IRubyObject rFirst, final IRubyObject rLength) {
final Object array = getObject();
final int arrayLength = Array.getLength( array );

if ( rFirst instanceof RubyFixnum && rLength instanceof RubyFixnum ) {
int first = (int) ((RubyFixnum) rFirst).getLongValue();
int length = (int) ((RubyFixnum) rLength).getLongValue();

if (length <= 0) {
return ArrayUtils.emptyJavaArrayDirect(context, getObject().getClass().getComponentType());
if ( length > arrayLength ) {
throw context.runtime.newIndexError("length specifed is longer than array");
}
if ( length <= 0 ) {
return ArrayUtils.emptyJavaArrayDirect(context, array.getClass().getComponentType());
}

return ArrayUtils.javaArraySubarrayDirect(context, getObject(), first, length);
} else {
throw context.runtime.newTypeError("only Fixnum ranges supported");
first = first >= 0 ? first : arrayLength + first;
return ArrayUtils.javaArraySubarrayDirect(context, array, first, length);
}
throw context.runtime.newTypeError("only Fixnum ranges supported");
}

public static class ArrayNewMethod extends org.jruby.internal.runtime.methods.JavaMethod.JavaMethodOne {
private DynamicMethod oldNew;


private final DynamicMethod newMethod;

ArrayNewMethod(RubyModule implClass, Visibility visibility) {
this(implClass, visibility, implClass.searchMethod("new"));
}

public ArrayNewMethod(RubyModule implClass, Visibility visibility, DynamicMethod oldNew) {
super(implClass, visibility);
this.oldNew = oldNew;
this.newMethod = oldNew;
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
Ruby runtime = context.runtime;
IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy");

if (arg0 instanceof JavaArray) {
proxy.dataWrapStruct(arg0);
return proxy;
} else {
public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
final Ruby runtime = context.runtime;

if ( ! ( arg0 instanceof JavaArray ) ) {
throw runtime.newTypeError(arg0, runtime.getJavaSupport().getJavaArrayClass());
}

IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy");
proxy.dataWrapStruct(arg0);
return proxy;
}

}
}
41 changes: 19 additions & 22 deletions core/src/main/java/org/jruby/java/util/ArrayUtils.java
Original file line number Diff line number Diff line change
@@ -26,38 +26,35 @@ public static IRubyObject arefDirect(Ruby runtime, Object array, JavaUtil.JavaCo
" for length " + Array.getLength(array) + ")");
}
}

public static IRubyObject concatArraysDirect(ThreadContext context, Object original, Object additional) {
int oldLength = Array.getLength(original);
int addLength = Array.getLength(additional);

ArrayJavaProxy proxy = newProxiedArray(context.runtime, original.getClass().getComponentType(), oldLength + addLength);
Object newArray = proxy.getObject();

System.arraycopy(original, 0, newArray, 0, oldLength);
System.arraycopy(additional, 0, newArray, oldLength, addLength);

return proxy;
}
public static ArrayJavaProxy newProxiedArray(Ruby runtime, Class componentType, int size) {

public static ArrayJavaProxy newProxiedArray(Ruby runtime, Class<?> componentType, int size) {
return newProxiedArray(runtime, componentType, JavaUtil.getJavaConverter(componentType), size);
}

public static ArrayJavaProxy newProxiedArray(Ruby runtime, Class componentType, JavaUtil.JavaConverter converter, int size) {
Object ary = Array.newInstance(componentType, size);
RubyClass newProxyClass = (RubyClass)JavaClass.get(runtime, ary.getClass()).getProxyClass();

ArrayJavaProxy proxy = new ArrayJavaProxy(runtime, newProxyClass, ary, converter);

return proxy;
public static ArrayJavaProxy newProxiedArray(Ruby runtime, Class<?> componentType, JavaUtil.JavaConverter converter, int size) {
final Object array = Array.newInstance(componentType, size);
RubyClass proxyClass = JavaClass.get(runtime, array.getClass()).getProxyClass();
return new ArrayJavaProxy(runtime, proxyClass, array, converter);
}

public static IRubyObject emptyJavaArrayDirect(ThreadContext context, Class componentType) {
Ruby runtime = context.runtime;
return newProxiedArray(runtime, componentType, 0);
}

public static IRubyObject javaArraySubarrayDirect(ThreadContext context, Object fromArray, int index, int size) {
int actualLength = Array.getLength(fromArray);
if (index >= actualLength) {
@@ -66,23 +63,23 @@ public static IRubyObject javaArraySubarrayDirect(ThreadContext context, Object
if (index + size > actualLength) {
size = actualLength - index;
}

ArrayJavaProxy proxy = ArrayUtils.newProxiedArray(context.runtime, fromArray.getClass().getComponentType(), size);
Object newArray = proxy.getObject();
System.arraycopy(fromArray, index, newArray, 0, size);

return proxy;
}
}

public static IRubyObject concatArraysDirect(ThreadContext context, Object original, IRubyObject additional) {
Ruby runtime = context.runtime;
int oldLength = Array.getLength(original);
int addLength = (int)((RubyFixnum) Helpers.invoke(context, additional, "length")).getLongValue();

ArrayJavaProxy proxy = ArrayUtils.newProxiedArray(runtime, original.getClass().getComponentType(), oldLength + addLength);
Object newArray = proxy.getObject();

System.arraycopy(original, 0, newArray, 0, oldLength);

for (int i = 0; i < addLength; i++) {
@@ -111,7 +108,7 @@ public static IRubyObject asetDirect(Ruby runtime, Object array, JavaUtil.JavaCo
}
return value;
}

public static void setWithExceptionHandlingDirect(Ruby runtime, Object ary, int intIndex, Object javaObject) {
try {
Array.set(ary, intIndex, javaObject);
@@ -142,14 +139,14 @@ public static void copyDataToJavaArrayDirect(
Array.set(javaArray, i, rubyArray.entry(i).toJava(targetType));
}
}

public static void copyDataToJavaArray(
ThreadContext context, RubyArray rubyArray, int src, JavaArray javaArray, int dest, int length) {
Class targetType = javaArray.getComponentType();

int destLength = (int)javaArray.length().getLongValue();
int srcLength = rubyArray.getLength();

for (int i = 0; src + i < srcLength && dest + i < destLength && i < length; i++) {
javaArray.setWithExceptionHandling(dest + i, rubyArray.entry(src + i).toJava(targetType));
}
31 changes: 31 additions & 0 deletions test/test_higher_javasupport.rb
Original file line number Diff line number Diff line change
@@ -107,6 +107,37 @@ def test_creating_arrays
assert_equal(17.0, array[2])
end

class IntLike
def initialize(value)
@value = value
end
def to_int; @value end
end

def test_array_with_non_ruby_integer_indexes
size = IntLike.new(2)
array = Java::byte[size].new

array[ 0 ] = 42.to_java(:byte)
assert_equal 42, array[ 0.to_java(:int) ]
# TODO: this should work as well, right?!
#assert_equal 42, array[ 0.to_java(:short) ]

assert_equal 42, array[ IntLike.new(0) ]

array[ 1.to_java('java.lang.Integer') ] = 21
assert_equal 21, array[1]

array[ IntLike.new(1) ] = 41
assert_equal 41, array[1]

assert_nil array.at(3)
assert_equal 41, array.at( 1.0 )
assert_nil array.at( IntLike.new(2) )
assert_equal 42, array.at( IntLike.new(-2) )
assert_equal 41, array.at( -1.to_java(:int) )
end

Pipe = java.nio.channels.Pipe
def test_inner_classes
assert_equal("java.nio.channels.Pipe$SinkChannel",