Skip to content

Commit

Permalink
Merge branch 'master' into truffle-head
Browse files Browse the repository at this point in the history
Conflicts:
	truffle/src/main/java/org/jruby/truffle/nodes/core/PrimitiveNodes.java
	truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
	truffle/src/main/java/org/jruby/truffle/nodes/core/SymbolNodes.java
	truffle/src/main/java/org/jruby/truffle/nodes/core/TrufflePrimitiveNodes.java
	truffle/src/main/java/org/jruby/truffle/runtime/RubyContext.java
	truffle/src/main/java/org/jruby/truffle/runtime/core/RubyHash.java
	truffle/src/main/java/org/jruby/truffle/runtime/core/RubyString.java
chrisseaton committed Feb 28, 2015
2 parents 658e1b3 + c7e83bd commit 9700ff3
Showing 122 changed files with 4,742 additions and 3,733 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -59,6 +59,7 @@ lib/ruby/stdlib/test*
lib/ruby/stdlib/hoe*
lib/ruby/stdlib/minitest*
lib/ruby/stdlib/power_assert*
lib/ruby/stdlib/psych*
release.properties
share
spaces test
15 changes: 5 additions & 10 deletions BUILDING.md
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@ Building JRuby from Source

Prerequisites:

* A Java 7-compatible (or higher) Java development kit (JDK)
* A Java 7-compatible (or higher) Java development kit (JDK).
MAKE SURE JAVA_HOME IS SET ON OS X!
* Maven 3+
* Apache Ant 1.8+ (see https://github.com/jruby/jruby/issues/2236)
* make and a C++ compiler for installing the jruby-launcher gem
@@ -22,19 +23,13 @@ command to execute is:
mvn
```

Or if you prefer to be more explicit, the default "install" goal can
be specified:

```
mvn install
```

This will do all of the following:
This will run the default "install" goal (```mvn install```) and will do all of the following:

* Compile JRuby
* Compile JRuby-Truffle
* Build `lib/jruby.jar`, needed for running at command line
* It will install the default gems specifications `lib/ruby/gems/shared/specifications/default/` and the ruby files of those gems in `lib/ruby/stdlib/`.
* It will install the default gems specifications `lib/ruby/gems/shared/specifications/default/` and the ruby files of
those gems in `lib/ruby/stdlib/`.

The environment is now suitable for running Ruby applications.

43 changes: 25 additions & 18 deletions core/src/main/java/org/jruby/java/invokers/ConstructorInvoker.java
Original file line number Diff line number Diff line change
@@ -14,43 +14,50 @@
import org.jruby.runtime.builtin.IRubyObject;

public class ConstructorInvoker extends RubyToJavaInvoker {

public ConstructorInvoker(RubyModule host, List<Constructor> ctors) {
super(host, ctors.toArray(new Constructor[ctors.size()]));

trySetAccessible(getAccessibleObjects());
}

@Override
protected JavaCallable createCallable(Ruby ruby, Member member) {
return JavaConstructor.create(ruby, (Constructor)member);
}

@Override
protected JavaCallable[] createCallableArray(JavaCallable callable) {
return new JavaConstructor[] {(JavaConstructor)callable};
}

@Override
protected JavaCallable[] createCallableArray(int size) {
return new JavaConstructor[size];
}

@Override
protected JavaCallable[][] createCallableArrayArray(int size) {
return new JavaConstructor[size][];
}

@Override
protected Class[] getMemberParameterTypes(Member member) {
return ((Constructor)member).getParameterTypes();
return ((Constructor) member).getParameterTypes();
}

@Override
protected boolean isMemberVarArgs(Member member) {
return ((Constructor)member).isVarArgs();
return ((Constructor) member).isVarArgs();
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
JavaProxy proxy = castJavaProxy(self);

int len = args.length;
Object[] convertedArgs = new Object[len];
JavaConstructor constructor = (JavaConstructor)findCallable(self, name, args, len);
final Object[] convertedArgs;
JavaConstructor constructor = (JavaConstructor) findCallable(self, name, args, len);
if (constructor.isVarArgs()) {
len = constructor.getParameterTypes().length - 1;
convertedArgs = new Object[len + 1];
@@ -64,7 +71,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
convertedArgs[i] = convertArg(args[i], constructor, i);
}
}

proxy.setObject(constructor.newInstanceDirect(context, convertedArgs));

return self;
@@ -74,7 +81,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY);
JavaProxy proxy = castJavaProxy(self);
JavaConstructor constructor = (JavaConstructor)findCallableArityZero(self, name);
JavaConstructor constructor = (JavaConstructor) findCallableArityZero(self, name);

proxy.setObject(constructor.newInstanceDirect(context));

@@ -85,7 +92,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0});
JavaProxy proxy = castJavaProxy(self);
JavaConstructor constructor = (JavaConstructor)findCallableArityOne(self, name, arg0);
JavaConstructor constructor = (JavaConstructor) findCallableArityOne(self, name, arg0);
Object cArg0 = convertArg(arg0, constructor, 0);

proxy.setObject(constructor.newInstanceDirect(context, cArg0));
@@ -97,7 +104,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1});
JavaProxy proxy = castJavaProxy(self);
JavaConstructor constructor = (JavaConstructor)findCallableArityTwo(self, name, arg0, arg1);
JavaConstructor constructor = (JavaConstructor) findCallableArityTwo(self, name, arg0, arg1);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(arg1, constructor, 1);

@@ -110,7 +117,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2});
JavaProxy proxy = castJavaProxy(self);
JavaConstructor constructor = (JavaConstructor)findCallableArityThree(self, name, arg0, arg1, arg2);
JavaConstructor constructor = (JavaConstructor) findCallableArityThree(self, name, arg0, arg1, arg2);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(arg1, constructor, 1);
Object cArg2 = convertArg(arg2, constructor, 2);
@@ -120,18 +127,18 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
return self;
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
if (block.isGiven()) {
Ruby runtime = context.runtime;
JavaProxy proxy = castJavaProxy(self);

int len = args.length;
// too much array creation!
Object[] convertedArgs = new Object[len + 1];
IRubyObject[] intermediate = new IRubyObject[len + 1];
System.arraycopy(args, 0, intermediate, 0, len);
intermediate[len] = RubyProc.newProc(runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor)findCallable(self, name, intermediate, len + 1);
intermediate[len] = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor) findCallable(self, name, intermediate, len + 1);
for (int i = 0; i < len + 1; i++) {
convertedArgs[i] = convertArg(intermediate[i], constructor, i);
}
@@ -150,7 +157,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
JavaProxy proxy = castJavaProxy(self);

RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor)findCallableArityOne(self, name, proc);
JavaConstructor constructor = (JavaConstructor) findCallableArityOne(self, name, proc);
Object cArg0 = convertArg(proc, constructor, 0);

proxy.setObject(constructor.newInstanceDirect(context, cArg0));
@@ -167,7 +174,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
JavaProxy proxy = castJavaProxy(self);

RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor)findCallableArityTwo(self, name, arg0, proc);
JavaConstructor constructor = (JavaConstructor) findCallableArityTwo(self, name, arg0, proc);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(proc, constructor, 1);

@@ -185,7 +192,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
JavaProxy proxy = castJavaProxy(self);

RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor)findCallableArityThree(self, name, arg0, arg1, proc);
JavaConstructor constructor = (JavaConstructor) findCallableArityThree(self, name, arg0, arg1, proc);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(arg1, constructor, 1);
Object cArg2 = convertArg(proc, constructor, 2);
@@ -204,7 +211,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
JavaProxy proxy = castJavaProxy(self);

RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor)findCallableArityFour(self, name, arg0, arg1, arg2, proc);
JavaConstructor constructor = (JavaConstructor) findCallableArityFour(self, name, arg0, arg1, arg2, proc);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(arg1, constructor, 1);
Object cArg2 = convertArg(arg2, constructor, 2);
Original file line number Diff line number Diff line change
@@ -7,7 +7,8 @@
import org.jruby.runtime.Visibility;

public abstract class FieldMethodOne extends JavaMethodOne {
Field field;

final Field field;

FieldMethodOne(String name, RubyModule host, Field field) {
super(host, Visibility.PUBLIC);
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@
import org.jruby.runtime.builtin.IRubyObject;

public abstract class FieldMethodZero extends JavaMethodZero {
Field field;

final Field field;

FieldMethodZero(String name, RubyModule host, Field field) {
super(host, Visibility.PUBLIC);
11 changes: 9 additions & 2 deletions core/src/main/java/org/jruby/java/invokers/MethodInvoker.java
Original file line number Diff line number Diff line change
@@ -19,27 +19,34 @@ public abstract class MethodInvoker extends RubyToJavaInvoker {
trySetAccessible(getAccessibleObjects());
}

@Override
protected JavaCallable createCallable(Ruby ruby, Member member) {
return JavaMethod.create(ruby, (Method)member);
}

@Override
protected JavaCallable[] createCallableArray(JavaCallable callable) {
return new JavaMethod[] {(JavaMethod)callable};
}

@Override
protected JavaCallable[] createCallableArray(int size) {
return new JavaMethod[size];
}

@Override
protected JavaCallable[][] createCallableArrayArray(int size) {
return new JavaMethod[size][];
}

@Override
protected Class[] getMemberParameterTypes(Member member) {
return ((Method)member).getParameterTypes();
return ((Method) member).getParameterTypes();
}

@Override
protected boolean isMemberVarArgs(Member member) {
return ((Method)member).isVarArgs();
return ((Method) member).isVarArgs();
}

}
311 changes: 165 additions & 146 deletions core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java

Large diffs are not rendered by default.

296 changes: 139 additions & 157 deletions core/src/main/java/org/jruby/java/proxies/JavaProxy.java

Large diffs are not rendered by default.

1,130 changes: 589 additions & 541 deletions core/src/main/java/org/jruby/javasupport/Java.java

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions core/src/main/java/org/jruby/javasupport/JavaCallable.java
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
* Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -47,6 +47,7 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.cli.Options;
import static org.jruby.javasupport.JavaClass.toRubyArray;

public abstract class JavaCallable extends JavaAccessibleObject implements ParameterTypes {
protected final Class<?>[] parameterTypes;
@@ -83,18 +84,18 @@ public final RubyFixnum arity() {

@JRubyMethod
public final RubyArray argument_types() {
return JavaClass.getRubyArray(getRuntime(), getParameterTypes());
return toRubyArray(getRuntime(), getParameterTypes());
}

// same as argument_types, but matches name in java.lang.reflect.Constructor/Method
@JRubyMethod
public IRubyObject parameter_types() {
return JavaClass.getRubyArray(getRuntime(), getParameterTypes());
public RubyArray parameter_types() {
return toRubyArray(getRuntime(), getParameterTypes());
}

@JRubyMethod
public IRubyObject exception_types() {
return JavaClass.getRubyArray(getRuntime(), getExceptionTypes());
public RubyArray exception_types() {
return toRubyArray(getRuntime(), getExceptionTypes());
}

@JRubyMethod
@@ -111,12 +112,12 @@ public IRubyObject generic_exception_types() {
public IRubyObject parameter_annotations() {
return Java.getInstance(getRuntime(), getParameterAnnotations());
}

@JRubyMethod(name = "varargs?")
public RubyBoolean varargs_p() {
return getRuntime().newBoolean(isVarArgs());
}

@JRubyMethod
public RubyString to_generic_string() {
return getRuntime().newString(toGenericString());
1,637 changes: 224 additions & 1,413 deletions core/src/main/java/org/jruby/javasupport/JavaClass.java

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/javasupport/JavaMethod.java
Original file line number Diff line number Diff line change
@@ -108,7 +108,7 @@ public JavaMethod(Ruby runtime, Method method) {
// Special classes like Collections.EMPTY_LIST are inner classes that are private but
// implement public interfaces. Their methods are all public methods for the public
// interface. Let these public methods execute via setAccessible(true).
if (JavaClass.CAN_SET_ACCESSIBLE) {
if (JavaUtil.CAN_SET_ACCESSIBLE) {
// we should be able to setAccessible ok...
try {
if (methodIsPublic &&
138 changes: 65 additions & 73 deletions core/src/main/java/org/jruby/javasupport/JavaObject.java
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
* Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -40,6 +40,7 @@
import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.jruby.Ruby;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
import org.jruby.RubyModule;
@@ -63,7 +64,8 @@
@JRubyClass(name="Java::JavaObject")
public class JavaObject extends RubyObject {

private static Object NULL_LOCK = new Object();
private static final Object NULL_LOCK = new Object();

private final VariableAccessor objectAccessor;

protected JavaObject(Ruby runtime, RubyClass rubyClass, Object value) {
@@ -86,25 +88,26 @@ protected JavaObject(Ruby runtime, Object value) {
this(runtime, runtime.getJavaSupport().getJavaObjectClass(), value);
}

public static JavaObject wrap(Ruby runtime, Object value) {
if (value != null) {
if (value instanceof Class) {
public static JavaObject wrap(final Ruby runtime, final Object value) {
if ( value != null ) {
if ( value instanceof Class ) {
return JavaClass.get(runtime, (Class<?>) value);
} else if (value.getClass().isArray()) {
}
if ( value.getClass().isArray() ) {
return new JavaArray(runtime, value);
}
}
return new JavaObject(runtime, value);
}

@JRubyMethod(meta = true)
public static IRubyObject wrap(ThreadContext context, IRubyObject self, IRubyObject object) {
Ruby runtime = context.runtime;
Object obj = getWrappedObject(object, NEVER);
public static IRubyObject wrap(final ThreadContext context,
final IRubyObject self, final IRubyObject object) {
final Object objectValue = unwrapObject(object, NEVER);

if (obj == NEVER) return runtime.getNil();
if ( objectValue == NEVER ) return context.nil;

return wrap(runtime, obj);
return wrap(context.runtime, objectValue);
}

@Override
@@ -135,27 +138,25 @@ protected static void registerRubyMethods(Ruby runtime, RubyClass result) {
}

@Override
public boolean equals(Object other) {
Object myValue = getValue();
Object otherValue = other;
if (other instanceof IRubyObject) {
otherValue = getWrappedObject((IRubyObject)other, NEVER);
public boolean equals(final Object other) {
final Object otherValue;
if ( other instanceof IRubyObject ) {
otherValue = unwrapObject((IRubyObject) other, NEVER);
}

if (otherValue == NEVER) {
// not a wrapped object
return false;
else {
otherValue = other;
}
return myValue == otherValue;

if ( otherValue == NEVER ) return false;

return getValue() == otherValue; // TODO seems weird why not equals ?!
}

@Override
public int hashCode() {
Object dataStruct = dataGetStruct();
if (dataStruct != null) {
return dataStruct.hashCode();
}
return 0;
final Object value = dataGetStruct();
if ( value == null ) return 0;
return value.hashCode();
}

@JRubyMethod
@@ -183,61 +184,54 @@ public static IRubyObject to_s(Ruby runtime, Object dataStruct) {
}

@JRubyMethod(name = {"==", "eql?"}, required = 1)
public IRubyObject op_equal(IRubyObject other) {
Object myValue = getValue();
return opEqualShared(myValue, other);
public IRubyObject op_equal(final IRubyObject other) {
return equals(getRuntime(), getValue(), other);
}

public static IRubyObject op_equal(JavaProxy self, IRubyObject other) {
Object myValue = self.getObject();
return opEqualShared(myValue, other);
public static RubyBoolean op_equal(JavaProxy self, IRubyObject other) {
return equals(self.getRuntime(), self.getObject(), other);
}

private static IRubyObject opEqualShared(Object myValue, IRubyObject other) {
Ruby runtime = other.getRuntime();
Object otherValue = getWrappedObject(other, NEVER);
private static RubyBoolean equals(final Ruby runtime,
final Object thisValue, final IRubyObject other) {

final Object otherValue = unwrapObject(other, NEVER);

if (other == NEVER) {
// not a wrapped object
if ( otherValue == NEVER ) { // not a wrapped object
return runtime.getFalse();
}

if (myValue == null && otherValue == null) {
return runtime.getTrue();
if ( thisValue == null ) {
return runtime.newBoolean(otherValue == null);
}

return runtime.newBoolean(myValue.equals(otherValue));
return runtime.newBoolean(thisValue.equals(otherValue));
}

@JRubyMethod(name = "equal?", required = 1)
public IRubyObject same(IRubyObject other) {
Ruby runtime = getRuntime();
Object myValue = getValue();
Object otherValue = getWrappedObject(other, NEVER);
public IRubyObject same(final IRubyObject other) {
final Ruby runtime = getRuntime();
final Object thisValue = getValue();
final Object otherValue = unwrapObject(other, NEVER);

if (other == NEVER) {
// not a wrapped object
if ( otherValue == NEVER ) { // not a wrapped object
return runtime.getFalse();
}

if (myValue == null && otherValue == null) {
return getRuntime().getTrue();
}

if (!(other instanceof JavaObject)) return runtime.getFalse();
if ( ! (other instanceof JavaObject) ) return runtime.getFalse();

boolean isSame = getValue() == ((JavaObject) other).getValue();
return isSame ? getRuntime().getTrue() : getRuntime().getFalse();
return runtime.newBoolean(thisValue == otherValue);
}

private static Object getWrappedObject(IRubyObject other, Object def) {
if (other instanceof JavaObject) {
return ((JavaObject)other).getValue();
} else if (other instanceof JavaProxy) {
return ((JavaProxy)other).getObject();
} else {
return def;
private static Object unwrapObject(
final IRubyObject wrapped, final Object defaultValue) {
if ( wrapped instanceof JavaObject ) {
return ((JavaObject) wrapped).getValue();
}
if ( wrapped instanceof JavaProxy ) {
return ((JavaProxy) wrapped).getObject();
}
return defaultValue;
}

@JRubyMethod
@@ -246,7 +240,7 @@ public RubyString java_type() {
}

@JRubyMethod
public IRubyObject java_class() {
public JavaClass java_class() {
return JavaClass.get(getRuntime(), getJavaClass());
}

@@ -267,13 +261,13 @@ public IRubyObject ruby_synchronized(ThreadContext context, Block block) {
return block.yield(context, null);
}
}

public static IRubyObject ruby_synchronized(ThreadContext context, Object lock, Block block) {
synchronized (lock != null ? lock : NULL_LOCK) {
return block.yield(context, null);
}
}

@JRubyMethod
public IRubyObject marshal_dump() {
if (Serializable.class.isAssignableFrom(getJavaClass())) {
@@ -310,17 +304,15 @@ public IRubyObject marshal_load(ThreadContext context, IRubyObject str) {
}

@Override
public Object toJava(Class cls) {
if (getValue() == null) {
// THIS SHOULD NEVER HAPPEN, but it DOES
return getValue();
}

if (cls.isAssignableFrom(getValue().getClass())) {
return getValue();
@SuppressWarnings("unchecked")
public Object toJava(final Class target) {
final Object value = getValue();
if ( value == null ) return null;

if ( target.isAssignableFrom( value.getClass() ) ) {
return value;
}

return super.toJava(cls);
return super.toJava(target);
}

private static final ObjectAllocator JAVA_OBJECT_ALLOCATOR = new ObjectAllocator() {
9 changes: 9 additions & 0 deletions core/src/main/java/org/jruby/javasupport/JavaSupport.java
Original file line number Diff line number Diff line change
@@ -36,9 +36,12 @@

import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.javasupport.binding.AssignedName;
import org.jruby.javasupport.proxy.JavaProxyClass;
import org.jruby.javasupport.util.ObjectProxyCache;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.collections.*;
import org.jruby.util.collections.ClassValue;

import java.lang.reflect.Member;
import java.util.Map;
@@ -102,4 +105,10 @@ public abstract class JavaSupport {
public abstract RubyClass getJavaConstructorClass();

public abstract Map<Set<?>, JavaProxyClass> getJavaProxyClassCache();

public abstract ClassValue<ThreadLocal<RubyModule>> getUnfinishedProxyClassCache();

public abstract ClassValue<Map<String, AssignedName>> getStaticAssignedNames();

public abstract ClassValue<Map<String, AssignedName>> getInstanceAssignedNames();
}
222 changes: 117 additions & 105 deletions core/src/main/java/org/jruby/javasupport/JavaSupportImpl.java

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions core/src/main/java/org/jruby/javasupport/JavaUtil.java
Original file line number Diff line number Diff line change
@@ -42,8 +42,12 @@
import static java.lang.Character.isUpperCase;
import static java.lang.Character.isDigit;
import static java.lang.Character.toLowerCase;

import java.lang.reflect.ReflectPermission;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -55,6 +59,7 @@
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBasicObject;
import org.jruby.RubyInstanceConfig;
import org.jruby.ext.bigdecimal.RubyBigDecimal;
import org.jruby.RubyBignum;
import org.jruby.RubyBoolean;
@@ -85,8 +90,34 @@
import org.jruby.util.ByteList;
import org.jruby.util.CodegenUtils;
import org.jruby.util.TypeConverter;
import org.jruby.util.cli.Options;

public class JavaUtil {

public static final boolean CAN_SET_ACCESSIBLE;

static {
boolean canSetAccessible = false;

if (RubyInstanceConfig.CAN_SET_ACCESSIBLE) {
try {
AccessController.checkPermission(new ReflectPermission("suppressAccessChecks"));
canSetAccessible = true;
} catch (Throwable t) {
// added this so if things are weird in the future we can debug without
// spinning a new binary
if (Options.JI_LOGCANSETACCESSIBLE.load()) {
t.printStackTrace();
}

// assume any exception means we can't suppress access checks
canSetAccessible = false;
}
}

CAN_SET_ACCESSIBLE = canSetAccessible;
}

public static IRubyObject[] convertJavaArrayToRuby(Ruby runtime, Object[] objects) {
if (objects == null) return IRubyObject.NULL_ARRAY;

@@ -927,6 +958,20 @@ public static Object objectFromJavaProxy(IRubyObject self) {
return ((JavaProxy)self).getObject();
}

public static final Map<String,Class> PRIMITIVE_CLASSES;
static {
Map<String, Class> primitiveClasses = new HashMap<String,Class>();
primitiveClasses.put("boolean", Boolean.TYPE);
primitiveClasses.put("byte", Byte.TYPE);
primitiveClasses.put("char", Character.TYPE);
primitiveClasses.put("short", Short.TYPE);
primitiveClasses.put("int", Integer.TYPE);
primitiveClasses.put("long", Long.TYPE);
primitiveClasses.put("float", Float.TYPE);
primitiveClasses.put("double", Double.TYPE);
PRIMITIVE_CLASSES = Collections.unmodifiableMap(primitiveClasses);
}

@Deprecated
public static Object convertRubyToJava(IRubyObject rubyObject) {
return convertRubyToJava(rubyObject, Object.class);
15 changes: 15 additions & 0 deletions core/src/main/java/org/jruby/javasupport/binding/AssignedName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.jruby.javasupport.binding;

/**
* Created by headius on 2/26/15.
*/
public class AssignedName {
String name;
Priority type;

public AssignedName() {}
public AssignedName(String name, Priority type) {
this.name = name;
this.type = type;
}
}
221 changes: 221 additions & 0 deletions core/src/main/java/org/jruby/javasupport/binding/ClassInitializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package org.jruby.javasupport.binding;

import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.javasupport.Java;
import org.jruby.javasupport.JavaClass;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

/**
* Created by headius on 2/26/15.
*/
public class ClassInitializer extends Initializer {
public ClassInitializer(Ruby runtime, Class<?> javaClass) {
super(runtime, javaClass);
}

public RubyModule initialize(RubyModule proxy) {
RubyClass proxyClass = (RubyClass)proxy;
Class<?> superclass = javaClass.getSuperclass();

final State state = new State(runtime, superclass);

super.initializeBase(proxy);

proxyClass.setReifiedClass(javaClass);

runtime.getJavaSupport().getUnfinishedProxyClassCache().get(javaClass).set(proxyClass);

if ( javaClass.isArray() || javaClass.isPrimitive() ) {
return proxy;
}

setupClassFields(javaClass, state);
setupClassMethods(javaClass, state);
setupClassConstructors(javaClass, state);

runtime.getJavaSupport().getStaticAssignedNames().get(javaClass).putAll(state.staticNames);
runtime.getJavaSupport().getInstanceAssignedNames().get(javaClass).putAll(state.instanceNames);

// flag the class as a Java class proxy.
proxy.setJavaProxy(true);
proxy.getSingletonClass().setJavaProxy(true);

// set parent to either package module or outer class
final RubyModule parent;
final Class<?> enclosingClass = javaClass.getEnclosingClass();
if ( enclosingClass != null ) {
parent = Java.getProxyClass(runtime, enclosingClass);
} else {
parent = Java.getJavaPackageModule(runtime, javaClass.getPackage());
}
proxy.setParent(parent);

// set the Java class name and package
if ( javaClass.isAnonymousClass() ) {
String baseName = ""; // javaClass.getSimpleName() returns "" for anonymous
if ( enclosingClass != null ) {
// instead of an empty name anonymous classes will have a "conforming"
// although not valid (by Ruby semantics) RubyClass name e.g. :
// 'Java::JavaUtilConcurrent::TimeUnit::1' for $1 anonymous enum class
// NOTE: if this turns out suitable shall do the same for method etc.
final String className = javaClass.getName();
final int length = className.length();
final int offset = enclosingClass.getName().length();
if ( length > offset && className.charAt(offset) != '$' ) {
baseName = className.substring( offset );
}
else if ( length > offset + 1 ) { // skip '$'
baseName = className.substring( offset + 1 );
}
}
proxy.setBaseName( baseName );
}
else {
proxy.setBaseName( javaClass.getSimpleName() );
}

installClassFields(proxyClass, state);
installClassInstanceMethods(proxyClass, state);
installClassConstructors(proxyClass, state);
installClassClasses(javaClass, proxyClass);

return proxy;
}

private static void installClassInstanceMethods(final RubyClass proxy, final Initializer.State state) {
installClassStaticMethods(proxy, state);
//assert state.instanceInstallers != null;
for ( Map.Entry<String, NamedInstaller> entry : state.instanceInstallers.entrySet() ) {
entry.getValue().install(proxy);
}
}

private static void setupClassFields(Class<?> javaClass, Initializer.State state) {
Field[] fields = JavaClass.getFields(javaClass);

for (int i = fields.length; --i >= 0;) {
Field field = fields[i];
if (javaClass != field.getDeclaringClass()) continue;

if (ConstantField.isConstant(field)) {
state.constantFields.add(new ConstantField(field));
continue;
}

int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers)) {
addField(state.staticInstallers, state.staticNames, field, Modifier.isFinal(modifiers), true);
} else {
addField(state.instanceInstallers, state.instanceNames, field, Modifier.isFinal(modifiers), false);
}
}
}

private void setupClassMethods(Class<?> javaClass, State state) {
// TODO: protected methods. this is going to require a rework of some of the mechanism.
Method[] methods = getMethods(javaClass);

for (int i = methods.length; --i >= 0;) {
// we need to collect all methods, though we'll only
// install the ones that are named in this class
Method method = methods[i];
String name = method.getName();

if (Modifier.isStatic(method.getModifiers())) {
prepareStaticMethod(javaClass, state, method, name);
} else {
prepareInstanceMethod(javaClass, state, method, name);
}
}

// try to wire up Scala singleton logic if present
handleScalaSingletons(javaClass, state);

// now iterate over all installers and make sure they also have appropriate aliases
assignStaticAliases(state);
assignInstanceAliases(state);
}

private void setupClassConstructors(final Class<?> javaClass, final State state) {
// TODO: protected methods. this is going to require a rework
// of some of the mechanism.
final Constructor[] constructors = JavaClass.getConstructors(javaClass);

// create constructorInstaller; if there are no constructors, it will disable construction
ConstructorInvokerInstaller constructorInstaller = new ConstructorInvokerInstaller("__jcreate!");

for ( int i = constructors.length; --i >= 0; ) {
// we need to collect all methods, though we'll only
// install the ones that are named in this class
constructorInstaller.addConstructor(constructors[i], javaClass);
}

state.constructorInstaller = constructorInstaller;
}

private void prepareInstanceMethod(Class<?> javaClass, State state, Method method, String name) {
AssignedName assignedName = state.instanceNames.get(name);

// For JRUBY-4505, restore __method methods for reserved names
if (INSTANCE_RESERVED_NAMES.containsKey(method.getName())) {
setupInstanceMethods(state.instanceInstallers, javaClass, method, name + METHOD_MANGLE);
return;
}

if (assignedName == null) {
state.instanceNames.put(name, new AssignedName(name, Priority.METHOD));
} else {
if (Priority.METHOD.lessImportantThan(assignedName)) return;
if (!Priority.METHOD.asImportantAs(assignedName)) {
state.instanceInstallers.remove(name);
state.instanceInstallers.remove(name + '=');
state.instanceNames.put(name, new AssignedName(name, Priority.METHOD));
}
}
setupInstanceMethods(state.instanceInstallers, javaClass, method, name);
}

private static void setupInstanceMethods(Map<String, NamedInstaller> methodCallbacks, Class<?> javaClass, Method method, String name) {
MethodInstaller invoker = (MethodInstaller) methodCallbacks.get(name);
if (invoker == null) {
invoker = new InstanceMethodInvokerInstaller(name);
methodCallbacks.put(name, invoker);
}
invoker.addMethod(method, javaClass);
}

private static void assignInstanceAliases(State state) {
for (Map.Entry<String, NamedInstaller> entry : state.instanceInstallers.entrySet()) {
if (entry.getValue().type == NamedInstaller.INSTANCE_METHOD) {
MethodInstaller methodInstaller = (MethodInstaller)entry.getValue();

// no aliases for __method methods
if (entry.getKey().endsWith("__method")) continue;

if (methodInstaller.hasLocalMethod()) {
assignAliases(methodInstaller, state.instanceNames);
}

// JRUBY-6967: Types with java.lang.Comparable were using Ruby Comparable#== instead of dispatching directly to
// java.lang.Object.equals. We force an alias here to ensure we always use equals.
if (entry.getKey().equals("equals")) {
// we only install "local" methods, but need to force this so it will bind
methodInstaller.setLocalMethod(true);
methodInstaller.addAlias("==");
}
}
}
}

private static void installClassConstructors(final RubyModule proxy, final State state) {
if ( state.constructorInstaller != null ) state.constructorInstaller.install(proxy);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyModule;
import org.jruby.javasupport.JavaUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
* Created by headius on 2/26/15.
*/
public class ConstantField {

final Field field;

public ConstantField(Field field) { this.field = field; }

void install(final RubyModule proxy) {
final String name = field.getName();
if ( proxy.getConstantAt(name) == null ) {
try {
final Object value = field.get(null);
proxy.setConstant(name, JavaUtil.convertJavaToUsableRubyObject(proxy.getRuntime(), value));
}
catch (IllegalAccessException iae) {
// if we can't read it, we don't set it
}
}
}

private static final int CONSTANT = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC;

static boolean isConstant(final Field field) {
return (field.getModifiers() & CONSTANT) == CONSTANT &&
Character.isUpperCase( field.getName().charAt(0) );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.jruby.javasupport.binding;

import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.java.invokers.ConstructorInvoker;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;

import static org.jruby.runtime.Visibility.PUBLIC;

/**
* Created by headius on 2/26/15.
*/
public class ConstructorInvokerInstaller extends MethodInstaller {

protected final List<Constructor> constructors = new ArrayList<Constructor>(4);
private boolean localConstructor;

public ConstructorInvokerInstaller(String name) { super(name, STATIC_METHOD); }

// called only by initializing thread; no synchronization required
void addConstructor(final Constructor ctor, final Class<?> clazz) {
if ( ! Ruby.isSecurityRestricted() ) {
try {
ctor.setAccessible(true);
} catch(SecurityException e) {}
}
this.constructors.add(ctor);
localConstructor |= clazz == ctor.getDeclaringClass();
}

@Override void install(final RubyModule proxy) {
if ( localConstructor ) {
proxy.addMethod(name, new ConstructorInvoker(proxy, constructors));
}
else { // if there's no constructor, we must prevent construction
proxy.addMethod(name, new org.jruby.internal.runtime.methods.JavaMethod(proxy, PUBLIC) {
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
throw context.runtime.newTypeError("no public constructors for " + clazz);
}
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.jruby.javasupport.binding;

import java.lang.reflect.Field;

/**
* Created by headius on 2/26/15.
*/
public abstract class FieldInstaller extends NamedInstaller {

final Field field;

public FieldInstaller(String name, int type, Field field) {
super(name,type);
this.field = field;
}
}
535 changes: 535 additions & 0 deletions core/src/main/java/org/jruby/javasupport/binding/Initializer.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyModule;
import org.jruby.java.invokers.InstanceFieldGetter;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
* Created by headius on 2/26/15.
*/
public class InstanceFieldGetterInstaller extends FieldInstaller {

public InstanceFieldGetterInstaller(String name, Field field) {
super(name, INSTANCE_FIELD, field);
}

@Override void install(final RubyModule proxy) {
if ( Modifier.isPublic(field.getModifiers()) ) {
proxy.addMethod(name, new InstanceFieldGetter(name, proxy, field));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyModule;
import org.jruby.java.invokers.InstanceFieldSetter;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
* Created by headius on 2/26/15.
*/
public class InstanceFieldSetterInstaller extends FieldInstaller {

public InstanceFieldSetterInstaller(String name, Field field) {
super(name, INSTANCE_FIELD, field);
}

@Override void install(final RubyModule proxy) {
if ( Modifier.isPublic(field.getModifiers()) ) {
proxy.addMethod(name, new InstanceFieldSetter(name, proxy, field));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyModule;
import org.jruby.java.invokers.InstanceMethodInvoker;

/**
* Created by headius on 2/26/15.
*/
public class InstanceMethodInvokerInstaller extends MethodInstaller {

public InstanceMethodInvokerInstaller(String name) { super(name, INSTANCE_METHOD); }

@Override void install(final RubyModule proxy) {
if ( hasLocalMethod() ) {
proxy.addMethod(name, new InstanceMethodInvoker(proxy, methods));
if ( aliases != null && isPublic() ) {
proxy.defineAliases(aliases, this.name);
//aliases = null;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.jruby.javasupport.binding;

import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.javasupport.JavaClass;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

/**
* Created by headius on 2/26/15.
*/
public class InterfaceInitializer extends Initializer {

public InterfaceInitializer(Ruby runtime, Class<?> javaClass) {
super(runtime, javaClass);
}

public RubyModule initialize(RubyModule proxy) {
final State state = new State(runtime, null);

super.initializeBase(proxy);

runtime.getJavaSupport().getUnfinishedProxyClassCache().get(javaClass).set(proxy);

Field[] fields = JavaClass.getDeclaredFields(javaClass);

for (int i = fields.length; --i >= 0; ) {
final Field field = fields[i];
if ( javaClass != field.getDeclaringClass() ) continue;

if ( ConstantField.isConstant(field) ) {
state.constantFields.add(new ConstantField(field));
}

final int mod = field.getModifiers();
if ( Modifier.isStatic(mod) ) {
addField(state.staticInstallers, state.staticNames, field, Modifier.isFinal(mod), true);
}
}

setupInterfaceMethods(javaClass, state);

// Add in any Scala singleton methods
handleScalaSingletons(javaClass, state);

// Now add all aliases for the static methods (fields) as appropriate
for (Map.Entry<String, NamedInstaller> entry : state.staticInstallers.entrySet()) {
final NamedInstaller installer = entry.getValue();
if (installer.type == NamedInstaller.STATIC_METHOD && installer.hasLocalMethod()) {
assignAliases((MethodInstaller) installer, state.staticNames);
}
}

runtime.getJavaSupport().getStaticAssignedNames().get(javaClass).putAll(state.staticNames);
runtime.getJavaSupport().getInstanceAssignedNames().get(javaClass).clear();

// flag the class as a Java class proxy.
proxy.setJavaProxy(true);
proxy.getSingletonClass().setJavaProxy(true);

installClassFields(proxy, state);
installClassStaticMethods(proxy, state);
installClassClasses(javaClass, proxy);

return proxy;
}

private static void setupInterfaceMethods(Class<?> javaClass, Initializer.State state) {
// TODO: protected methods. this is going to require a rework of some of the mechanism.
Method[] methods = getMethods(javaClass);

for (int i = methods.length; --i >= 0;) {
// Java 8 introduced static methods on interfaces, so we just look for those
Method method = methods[i];
String name = method.getName();

if (!Modifier.isStatic(method.getModifiers())) continue;

prepareStaticMethod(javaClass, state, method, name);
}

// now iterate over all installers and make sure they also have appropriate aliases
assignStaticAliases(state);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.jruby.javasupport.binding;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
* Created by headius on 2/26/15.
*/
public abstract class MethodInstaller extends NamedInstaller {

protected final List<Method> methods = new ArrayList<Method>(4);
protected List<String> aliases;
private boolean localMethod;

public MethodInstaller(String name, int type) { super(name, type); }

// called only by initializing thread; no synchronization required
void addMethod(final Method method, final Class<?> clazz) {
this.methods.add(method);
localMethod |=
clazz == method.getDeclaringClass() ||
method.getDeclaringClass().isInterface();
}

// called only by initializing thread; no synchronization required
void addAlias(final String alias) {
List<String> aliases = this.aliases;
if (aliases == null) {
aliases = this.aliases = new ArrayList<String>(4);
}
if ( ! aliases.contains(alias) ) aliases.add(alias);
}

@Override
boolean hasLocalMethod () { return localMethod; }

void setLocalMethod(boolean flag) { localMethod = flag; }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyModule;
import org.jruby.runtime.Visibility;

/**
* Created by headius on 2/26/15.
*/
public abstract class NamedInstaller {
static final int STATIC_FIELD = 1;
static final int STATIC_METHOD = 2;
static final int INSTANCE_FIELD = 3;
static final int INSTANCE_METHOD = 4;

final String name;
final int type;

Visibility visibility = Visibility.PUBLIC;

public NamedInstaller(String name, int type) {
this.name = name;
this.type = type;
}

abstract void install(RubyModule proxy);

// small hack to save a cast later on
boolean hasLocalMethod() { return true; }

boolean isPublic() { return visibility == Visibility.PUBLIC; }

//boolean isProtected() { return visibility == Visibility.PROTECTED; }

}
31 changes: 31 additions & 0 deletions core/src/main/java/org/jruby/javasupport/binding/Priority.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.jruby.javasupport.binding;

import org.jruby.javasupport.JavaClass;

/**
* Assigned names only override based priority of an assigned type, the type must be less than
* or equal to the assigned type. For example, field name (FIELD) in a subclass will override
* an alias (ALIAS) in a superclass, but not a method (METHOD).
*/
public enum Priority {
RESERVED(0), METHOD(1), FIELD(2), PROTECTED_METHOD(3),
WEAKLY_RESERVED(4), ALIAS(5), PROTECTED_FIELD(6);

private int value;

Priority(int value) {
this.value = value;
}

public boolean asImportantAs(AssignedName other) {
return other != null && other.type.value == value;
}

public boolean lessImportantThan(AssignedName other) {
return other != null && other.type.value < value;
}

public boolean moreImportantThan(AssignedName other) {
return other == null || other.type.value > value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.java.invokers.SingletonMethodInvoker;

/**
* Created by headius on 2/26/15.
*/
public class SingletonMethodInvokerInstaller extends StaticMethodInvokerInstaller {

final Object singleton;

public SingletonMethodInvokerInstaller(String name, Object singleton) {
super(name);
this.singleton = singleton;
}

@Override void install(final RubyModule proxy) {
// we don't check haveLocalMethod() here because it's not local and we know
// that we always want to go ahead and install it
final RubyClass singletonClass = proxy.getSingletonClass();
DynamicMethod method = new SingletonMethodInvoker(this.singleton, singletonClass, methods);
singletonClass.addMethod(name, method);
if ( aliases != null && isPublic() ) {
singletonClass.defineAliases(aliases, name);
//aliases = null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyModule;
import org.jruby.java.invokers.StaticFieldGetter;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
* Created by headius on 2/26/15.
*/
public class StaticFieldGetterInstaller extends FieldInstaller {

public StaticFieldGetterInstaller(String name, Field field) {
super(name, STATIC_FIELD, field);
}

@Override void install(final RubyModule proxy) {
if ( Modifier.isPublic(field.getModifiers()) ) {
proxy.getSingletonClass().addMethod(name, new StaticFieldGetter(name, proxy, field));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyModule;
import org.jruby.java.invokers.StaticFieldSetter;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
* Created by headius on 2/26/15.
*/
public class StaticFieldSetterInstaller extends FieldInstaller {

public StaticFieldSetterInstaller(String name, Field field) {
super(name, STATIC_FIELD, field);
}

@Override void install(final RubyModule proxy) {
if ( Modifier.isPublic(field.getModifiers()) ) {
proxy.getSingletonClass().addMethod(name, new StaticFieldSetter(name, proxy, field));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.jruby.javasupport.binding;

import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.java.invokers.StaticMethodInvoker;

/**
* Created by headius on 2/26/15.
*/
public class StaticMethodInvokerInstaller extends MethodInstaller {

public StaticMethodInvokerInstaller(String name) { super(name, STATIC_METHOD); }

@Override void install(final RubyModule proxy) {
if ( hasLocalMethod() ) {
final RubyClass singletonClass = proxy.getSingletonClass();
DynamicMethod method = new StaticMethodInvoker(singletonClass, methods);
singletonClass.addMethod(name, method);
if ( aliases != null && isPublic() ) {
singletonClass.defineAliases(aliases, name);
//aliases = null;
}
}
}
}
339 changes: 183 additions & 156 deletions core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java

Large diffs are not rendered by default.

465 changes: 221 additions & 244 deletions core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
* rights and limitations under the License.
*
* Copyright (C) 2006 Kresten Krab Thorup <krab@gnu.org>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -30,6 +30,7 @@

import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
import org.jruby.RubyObject;
@@ -38,6 +39,7 @@
import org.jruby.anno.JRubyMethod;
import org.jruby.javasupport.JavaClass;
import org.jruby.javasupport.JavaObject;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

@JRubyClass(name="Java::JavaProxyClass")
@@ -53,54 +55,69 @@ protected static void registerRubyMethods(Ruby runtime, RubyClass result) {
result.getMetaClass().defineAlias("__j_allocate","allocate");
}

@JRubyMethod
public RubyFixnum hash() {
return getRuntime().newFixnum(hashCode());
}

@JRubyMethod
public IRubyObject to_s() {
return getRuntime().newString(toString());
@Deprecated
public IRubyObject op_equal(IRubyObject other) {
return op_eqq(getRuntime().getCurrentContext(), other);
}

@Override
@JRubyMethod(name = {"==", "eql?"})
public IRubyObject op_equal(IRubyObject other) {
if (!(other instanceof JavaProxyReflectionObject)) {
Object otherObj = other.dataGetStruct();
if (!(otherObj instanceof JavaObject)) {
return getRuntime().getFalse();
public RubyBoolean op_eqq(final ThreadContext context, IRubyObject obj) {
if ( ! ( obj instanceof JavaProxyReflectionObject ) ) {
final Object wrappedObj = obj.dataGetStruct();
if ( ! ( wrappedObj instanceof JavaObject ) ) {
return context.runtime.getFalse();
}
other = (IRubyObject)otherObj;
obj = (IRubyObject) wrappedObj;
}

boolean isEqual = equals(other);
return isEqual ? getRuntime().getTrue() : getRuntime().getFalse();
return context.runtime.newBoolean( this.equals(obj) );
}

public int hashCode() {
return getClass().hashCode();

@Deprecated
public IRubyObject same(IRubyObject other) {
return op_equal(getRuntime().getCurrentContext(), other);
}

public String toString() {
return getClass().getName();
@Override
@JRubyMethod(name = "equal?")
public RubyBoolean op_equal(final ThreadContext context, IRubyObject obj) {
if ( this == obj ) return context.runtime.getTrue();

if ( ! ( obj instanceof JavaProxyReflectionObject ) ) {
final Object wrappedObj = obj.dataGetStruct();
if ( ! ( wrappedObj instanceof JavaObject ) ) {
return context.runtime.getFalse();
}
obj = (IRubyObject) wrappedObj;
}
return context.runtime.newBoolean(this == obj);
}

@Override
public boolean equals(Object other) {
return this == other;
}

@JRubyMethod(name = "equal?")
public IRubyObject same(IRubyObject other) {
if (!(other instanceof JavaObject)) {
Object otherObj = other.dataGetStruct();
if (!(otherObj instanceof JavaObject)) {
return getRuntime().getFalse();
}
other = (IRubyObject)otherObj;
}

boolean isSame = this == other;
return isSame ? getRuntime().getTrue() : getRuntime().getFalse();
@Override
@JRubyMethod
public RubyFixnum hash() {
return getRuntime().newFixnum(hashCode());
}

@Override
public int hashCode() {
return 11 * getJavaClass().hashCode();
}

@Override
@JRubyMethod
public IRubyObject to_s() {
return getRuntime().newString(toString());
}

@Override
public String toString() {
return getClass().getName();
}

@JRubyMethod
@@ -109,7 +126,7 @@ public RubyString java_type() {
}

@JRubyMethod
public IRubyObject java_class() {
public JavaClass java_class() {
return JavaClass.get(getRuntime(), getJavaClass());
}

3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/util/ShellLauncher.java
Original file line number Diff line number Diff line change
@@ -782,7 +782,8 @@ private static Process popenShared(Ruby runtime, IRubyObject[] strings, Map env,
}

String[] args = parseCommandLine(runtime.getCurrentContext(), runtime, strings);
boolean useShell = false;
LaunchConfig lc = new LaunchConfig(runtime, strings, false);
boolean useShell = Platform.IS_WINDOWS ? lc.shouldRunInShell() : false;
if (addShell) for (String arg : args) useShell |= shouldUseShell(arg);

// CON: popen is a case where I think we should just always shell out.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -140,7 +140,7 @@ public class Options {
public static final Option<Boolean> TRUFFLE_LOAD_CORE = bool(TRUFFLE, "truffle.load_core", true, "Load the Truffle core library.");

public static final Option<Integer> TRUFFLE_PASSALOT = integer(TRUFFLE, "truffle.passalot", 0, "Probabilty between 0 and 100 to randomly insert Thread.pass at a given line.");
public static final Option<Integer> TRUFFLE_STACK_SERVER_PORT = integer(TRUFFLE, "truffle.stack_server_port", 0, "Port number to run an HTTP server on that returns stack traces");
public static final Option<Integer> TRUFFLE_INSTRUMENTATION_SERVER_PORT = integer(TRUFFLE, "truffle.instrumentation_server_port", 0, "Port number to run an HTTP server on that provides instrumentation services");
public static final Option<String> TRUFFLE_TRANSLATOR_PRINT_AST = string(TRUFFLE, "truffle.translator.print_asts", "", "Comma delimited list of method names to print the AST of after translation.");
public static final Option<String> TRUFFLE_TRANSLATOR_PRINT_FULL_AST = string(TRUFFLE, "truffle.translator.print_full_asts", "", "Comma delimited list of method names to print the full AST of after translation.");
public static final Option<String> TRUFFLE_TRANSLATOR_PRINT_PARSE_TREE = string(TRUFFLE, "truffle.translator.print_parse_trees", "", "Comma delimited list of method names to print the JRuby parse tree of before translation.");
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package org.jruby.util.collections;

import org.jruby.Ruby;

/**
* A proxy cache that uses Java 7's ClassValue.
*/
public class Java7ClassValue<T> extends ClassValue<T> {

public Java7ClassValue(ClassValueCalculator<T> calculator) {
super(calculator);
}

public T get(Class cls) {
return proxy.get(cls);
}

private final java.lang.ClassValue<T> proxy = new java.lang.ClassValue<T>() {
@Override
protected T computeValue(Class<?> type) {
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
package org.jruby.util.collections;

import java.util.concurrent.ConcurrentHashMap;
import org.jruby.Ruby;

/**
* A simple Map-based cache of proxies.
*/
public class MapBasedClassValue<T> extends ClassValue<T> {

public MapBasedClassValue(ClassValueCalculator<T> calculator) {
super(calculator);
}

@Override
public T get(Class cls) {
T obj = cache.get(cls);

if (obj == null) {
T newObj = calculator.computeValue(cls);
obj = cache.putIfAbsent(cls, newObj);
if (obj == null) {
obj = newObj;
}

if (obj != null) return obj;

synchronized (this) {
obj = cache.get(cls);

if (obj != null) return obj;

obj = calculator.computeValue(cls);
cache.put(cls, obj);
}

return obj;
}

// There's not a compelling reason to keep JavaClass instances in a weak map
// (any proxies created are [were] kept in a non-weak map, so in most cases they will
// stick around anyway), and some good reasons not to (JavaClass creation is
14 changes: 7 additions & 7 deletions core/src/main/ruby/jruby/java/java_ext/java.util.rb
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@
module java::util::Collection
include Enumerable
def each
iter = iterator
while iter.hasNext
yield(iter.next)
i = iterator
while i.hasNext
yield i.next
end
end
def <<(a); add(a); self; end
@@ -19,10 +19,10 @@ def -(oth)
nw
end
def length
self.size
size
end
def join(*args)
self.to_a.join(*args)
to_a.join(*args)
end
def to_a
# JRUBY-3910: conversion is faster by going directly to java array
@@ -36,7 +36,7 @@ module java::util::Enumeration
include Enumerable

def each
while (has_more_elements)
while has_more_elements
yield next_element
end
end
@@ -46,7 +46,7 @@ module java::util::Iterator
include Enumerable

def each
while (has_next)
while has_next
yield self.next
end
end
23 changes: 6 additions & 17 deletions core/src/main/ruby/jruby/java/java_module.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
module Java
class << self
def const_missing(sym)
result = JavaUtilities.get_top_level_proxy_or_package(sym)
if const_defined? sym
result
else
const_set(sym, result)
end
end

def method_missing(sym, *args)
class << self
def method_missing(sym, *args)
raise ArgumentError, "Java package `java' does not have a method `#{sym}'" unless args.empty?
JavaUtilities.get_top_level_proxy_or_package sym
end
end

end

JavaUtilities.get_top_level_proxy_or_package sym
end
end
end
4 changes: 2 additions & 2 deletions core/src/main/ruby/jruby/java/java_utilities.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module JavaUtilities
def self.extend_proxy(java_class_name, &block)
java_class = Java::JavaClass.for_name(java_class_name)
java_class.extend_proxy JavaInterfaceExtender.new(java_class_name, &block)
java_class = JavaUtilities.get_proxy_class(java_class_name)
java_class.class_eval &block
end

def self.print_class(java_type, indent="")
99 changes: 66 additions & 33 deletions spec/truffle/junit-frames.xsl
Original file line number Diff line number Diff line change
@@ -198,7 +198,10 @@
font:normal 68% verdana,arial,helvetica;
color:#000000;
}
table tr td, table tr th {
table.details {
width: 95% !important;
}
table.summary tr th, table.summary tr td {
font-size: 68%;
}
table.details tr th{
@@ -332,6 +335,7 @@
<xsl:call-template name="create.stylesheet.link">
<xsl:with-param name="package.name" select="$package.name"/>
</xsl:call-template>
<xsl:call-template name="create.sortable.tables" />
<script type="text/javascript" language="JavaScript">
var TestCases = new Array();
var cur;
@@ -391,7 +395,7 @@
<h2>Tests</h2>
</xsl:otherwise>
</xsl:choose>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<table class="details sortable" border="0" cellpadding="5" cellspacing="2" width="95%">
<xsl:call-template name="testcase.test.header"/>
<!--
test can even not be started at all (failure to load the class)
@@ -583,6 +587,7 @@
<xsl:call-template name="create.stylesheet.link">
<xsl:with-param name="package.name"/>
</xsl:call-template>
<xsl:call-template name="create.sortable.tables" />
</head>
<body>
<xsl:attribute name="onload">open('allclasses-frame.html','classListFrame')</xsl:attribute>
@@ -594,7 +599,7 @@
<xsl:variable name="skippedCount" select="sum(testsuite/@skipped)" />
<xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
<xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount - $skippedCount) div $testCount"/>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<table class="details summary" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th>Tests</th>
<th>Failures</th>
@@ -636,7 +641,7 @@
</table>

<h2>Packages</h2>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<table class="details sortable" border="0" cellpadding="5" cellspacing="2" width="95%">
<xsl:call-template name="testsuite.test.header"/>
<xsl:for-each select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
<xsl:sort select="@package" order="ascending"/>
@@ -660,11 +665,14 @@
<td><xsl:value-of select="sum($insamepackage/@failures)"/></td>
<td><xsl:value-of select="sum($insamepackage/@skipped)" /></td>
<td>
<xsl:if test="sum($insamepackage/@skipped) > 0">
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="sum($insamepackage/@skipped) div sum($insamepackage/@tests)"/>
</xsl:call-template>
</xsl:if>
<xsl:choose>
<xsl:when test="sum($insamepackage/@skipped) > 0">
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="sum($insamepackage/@skipped) div sum($insamepackage/@tests)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>0%</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:call-template name="display-time">
@@ -686,6 +694,7 @@
<xsl:call-template name="create.stylesheet.link">
<xsl:with-param name="package.name" select="$name"/>
</xsl:call-template>
<xsl:call-template name="create.sortable.tables" />
</head>
<body>
<xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
@@ -701,7 +710,7 @@
<xsl:if test="count($insamepackage) &gt; 0">
<h2>Classes</h2>
<p>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<table class="details sortable" border="0" cellpadding="5" cellspacing="2" width="95%">
<xsl:call-template name="testsuite.test.header"/>
<xsl:apply-templates select="$insamepackage" mode="print.test">
<xsl:sort select="@name"/>
@@ -738,6 +747,23 @@
<link rel="stylesheet" type="text/css" title="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></link>
</xsl:template>

<xsl:template name="create.sortable.tables">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.20.1/css/theme.default.min.css
"></link>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.20.1/js/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.20.1/js/jquery.tablesorter.widgets.min.js"></script>

<script type="text/javascript">
jQuery(document).ready(function()
{
var table = jQuery(".sortable");

table.tablesorter( { widgets: ["zebra"] });
table.trigger("applyWidgets");
});
</script>
</xsl:template>

<!-- Page HEADER -->
<xsl:template name="pageHeader">
@@ -753,29 +779,33 @@

<!-- class header -->
<xsl:template name="testsuite.test.header">
<tr valign="top">
<th width="80%">Name</th>
<th>Tests</th>
<th>Errors</th>
<th>Failures</th>
<th>Skipped</th>
<th>Skip %</th>
<th nowrap="nowrap">Time(s)</th>
</tr>
<thead>
<tr valign="top">
<th width="80%">Name</th>
<th>Tests</th>
<th>Errors</th>
<th>Failures</th>
<th>Skipped</th>
<th>Skip %</th>
<th nowrap="nowrap">Time(s)</th>
</tr>
</thead>
</xsl:template>

<!-- method header -->
<xsl:template name="testcase.test.header">
<xsl:param name="show.class" select="''"/>
<tr valign="top">
<xsl:if test="boolean($show.class)">
<th>Class</th>
</xsl:if>
<th>Name</th>
<th>Status</th>
<th width="80%">Type</th>
<th nowrap="nowrap">Time(s)</th>
</tr>
<thead>
<tr valign="top">
<xsl:if test="boolean($show.class)">
<th>Class</th>
</xsl:if>
<th>Name</th>
<th>Status</th>
<th width="80%">Type</th>
<th nowrap="nowrap">Time(s)</th>
</tr>
</thead>
</xsl:template>


@@ -822,11 +852,14 @@
</xsl:choose>
</td>
<td>
<xsl:if test="@skipped > 0">
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="@skipped div @tests"/>
</xsl:call-template>
</xsl:if>
<xsl:choose>
<xsl:when test="@skipped > 0">
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="@skipped div @tests"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>0%</xsl:otherwise>
</xsl:choose>
</td>
<td><xsl:call-template name="display-time">
<xsl:with-param name="value" select="@time"/>
1 change: 0 additions & 1 deletion spec/truffle/tags/core/encoding/aliases_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Encoding.aliases has a 'locale' key and its value equals to the name of the encoding finded by the locale charmap
fails:Encoding.aliases only contains valid aliased encodings
2 changes: 0 additions & 2 deletions spec/truffle/tags/core/encoding/ascii_compatible_tags.txt

This file was deleted.

17 changes: 0 additions & 17 deletions spec/truffle/tags/core/encoding/compatible_tags.txt
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
fails:Encoding.compatible? String, String when the first's Encoding is valid US-ASCII returns US-ASCII if the second String is ASCII-8BIT and ASCII only
fails:Encoding.compatible? String, String when the first's Encoding is valid US-ASCII returns ASCII-8BIT if the second String is ASCII-8BIT but not ASCII only
fails:Encoding.compatible? String, String when the first's Encoding is valid US-ASCII returns US-ASCII if the second String is UTF-8 and ASCII only
fails:Encoding.compatible? String, String when the first's Encoding is valid US-ASCII returns UTF-8 if the second String is UTF-8 but not ASCII only
fails:Encoding.compatible? String, String when the first's Encoding is ASCII compatible and ASCII only returns the first's Encoding if the second is ASCII compatible and ASCII only
fails:Encoding.compatible? String, String when the first's Encoding is ASCII compatible and ASCII only returns the second's Encoding if the second is ASCII compatible but not ASCII only
fails:Encoding.compatible? String, String when the first's Encoding is ASCII compatible but not ASCII only returns the first's Encoding if the second's is valid US-ASCII
fails:Encoding.compatible? String, String when the first's Encoding is ASCII compatible but not ASCII only returns the first's Encoding if the second's is UTF-8 and ASCII only
fails:Encoding.compatible? String, String when the first's Encoding is invalid returns the first's Encoding when the second's Encoding is US-ASCII
fails:Encoding.compatible? String, String when the first's Encoding is invalid returns the first's Encoding when the second String is ASCII only
fails:Encoding.compatible? String, Regexp returns US-ASCII if both are US-ASCII
fails:Encoding.compatible? String, Regexp returns the String's Encoding if it is not US-ASCII but both are ASCII only
fails:Encoding.compatible? String, Regexp returns the String's Encoding if the String is not ASCII only
fails:Encoding.compatible? String, Symbol returns US-ASCII if both are ASCII only
fails:Encoding.compatible? String, Symbol returns the String's Encoding if it is not US-ASCII but both are ASCII only
fails:Encoding.compatible? String, Symbol returns the String's Encoding if the String is not ASCII only
fails:Encoding.compatible? Regexp, String returns US-ASCII if both are US-ASCII
fails:Encoding.compatible? Regexp, Regexp returns US-ASCII if both are US-ASCII
fails:Encoding.compatible? Regexp, Regexp returns the first's Encoding if it is not US-ASCII and not ASCII only
fails:Encoding.compatible? Regexp, Symbol returns US-ASCII if both are US-ASCII
fails:Encoding.compatible? Regexp, Symbol returns the first's Encoding if it is not US-ASCII and not ASCII only
fails:Encoding.compatible? Symbol, String returns US-ASCII if both are ASCII only
fails:Encoding.compatible? Symbol, Regexp returns US-ASCII if both are US-ASCII
fails:Encoding.compatible? Symbol, Regexp returns the Regexp's Encoding if it is not US-ASCII and not ASCII only
fails:Encoding.compatible? Symbol, Symbol returns US-ASCII if both are US-ASCII
fails:Encoding.compatible? Symbol, Symbol returns the first's Encoding if it is not ASCII only
fails:Encoding.compatible? Object, Object returns nil for Object, String
fails:Encoding.compatible? Object, Object returns nil for Object, Regexp
fails:Encoding.compatible? Object, Object returns nil for Object, Symbol
6 changes: 0 additions & 6 deletions spec/truffle/tags/core/encoding/default_external_tags.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,2 @@
fails:Encoding.default_external with command line options is not changed by the -U option
fails:Encoding.default_external with command line options returns the encoding specified by '-E external'
fails:Encoding.default_external with command line options returns the encoding specified by '-E external:'
fails:Encoding.default_external= sets the default external encoding
fails:Encoding.default_external= can accept a name of an encoding as a String
fails:Encoding.default_external= calls #to_s on arguments that are neither Strings nor Encodings
fails:Encoding.default_external= raises a TypeError unless the argument is an Encoding or convertible to a String
fails:Encoding.default_external= raises an ArgumentError if the argument is nil
3 changes: 0 additions & 3 deletions spec/truffle/tags/core/encoding/list_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/encoding/locale_charmap_tags.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
fails:Encoding.locale_charmap returns a String
fails:Encoding.locale_charmap returns a value based on the LC_ALL environment variable
fails:Encoding.locale_charmap is unaffected by assigning to ENV['LC_ALL'] in the same process
1 change: 0 additions & 1 deletion spec/truffle/tags/core/encoding/name_list_tags.txt

This file was deleted.

3 changes: 0 additions & 3 deletions spec/truffle/tags/core/regexp/escape_tags.txt

This file was deleted.

3 changes: 0 additions & 3 deletions spec/truffle/tags/core/regexp/quote_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/core/regexp/union_tags.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
fails:Regexp.union returns a Regexp with US-ASCII encoding if all arguments are ASCII-only
fails:Regexp.union raises ArgumentError if the arguments include conflicting fixed encoding Regexps
fails:Regexp.union returns a Regexp with the encoding of a String containing non-ASCII-compatible characters and another ASCII-only String
4 changes: 0 additions & 4 deletions spec/truffle/tags/core/string/encode_tags.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
fails:String#encode when passed no options transcodes to Encoding.default_internal when set
fails:String#encode when passed no options transcodes a 7-bit String despite no generic converting being available
fails:String#encode when passed no options raises an Encoding::ConverterNotFoundError when no conversion is possible
fails:String#encode when passed to encoding transcodes a 7-bit String despite no generic converting being available
@@ -21,16 +20,13 @@ fails:String#encode when passed to, from, options calls #to_hash to convert the
fails:String#encode given the :xml => :text option replaces all instances of '&' with '&amp;'
fails:String#encode given the :xml => :text option replaces all instances of '<' with '&lt;'
fails:String#encode given the :xml => :text option replaces all instances of '>' with '&gt;'
fails:String#encode given the :xml => :text option does not replace '"'
fails:String#encode given the :xml => :text option replaces undefined characters with their upper-case hexadecimal numeric character references
fails:String#encode given the :xml => :attr option surrounds the encoded text with double-quotes
fails:String#encode given the :xml => :attr option replaces all instances of '&' with '&amp;'
fails:String#encode given the :xml => :attr option replaces all instances of '<' with '&lt;'
fails:String#encode given the :xml => :attr option replaces all instances of '>' with '&gt;'
fails:String#encode given the :xml => :attr option replaces all instances of '"' with '&quot;'
fails:String#encode given the :xml => :attr option replaces undefined characters with their upper-case hexadecimal numeric character references
fails:String#encode when passed no options returns a copy when Encoding.default_internal is nil
fails:String#encode when passed no options returns a copy for a ASCII-only String when Encoding.default_internal is nil
fails:String#encode when passed options returns a copy when Encoding.default_internal is nil
fails:String#encode when passed options normalizes newlines
fails:String#encode when passed to, from returns a copy when both encodings are the same
28 changes: 1 addition & 27 deletions spec/truffle/tags/core/string/slice_tags.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
fails:String#slice calls to_int on the given index
fails:String#slice with index, length always taints resulting strings when self is tainted
fails:String#slice with index, length calls to_int on the given index and the given length
fails:String#slice with index, length returns subclass instances
fails:String#slice with Range returns an empty string if range.begin is inside self and > real end
fails:String#slice with Range always taints resulting strings when self is tainted
@@ -13,69 +11,45 @@ fails:String#slice with Regexp returns subclass instances
fails:String#slice with Regexp, index returns the capture for the given index
fails:String#slice with Regexp, index always taints resulting strings when self or regexp is tainted
fails:String#slice with Regexp, index returns an untrusted string if the regexp is untrusted
fails:String#slice with Regexp, index returns nil if there is no match
fails:String#slice with Regexp, index returns nil if there is no capture for the given index
fails:String#slice with Regexp, index calls to_int on the given index
fails:String#slice with Regexp, index returns subclass instances
fails:String#slice with Regexp, index sets $~ to MatchData when there is a match and nil when there's none
fails:String#slice with Regexp, group returns the capture for the given name
fails:String#slice with Regexp, group returns the last capture for duplicate names
fails:String#slice with Regexp, group returns the innermost capture for nested duplicate names
fails:String#slice with Regexp, group always taints resulting strings when self or regexp is tainted
fails:String#slice with Regexp, group returns nil if there is no match
fails:String#slice with Regexp, group raises an IndexError if there is no capture for the given name
fails:String#slice with Regexp, group raises an IndexError when given the empty String as a group name
fails:String#slice with Regexp, group returns subclass instances
fails:String#slice with Regexp, group sets $~ to MatchData when there is a match and nil when there's none
fails:String#slice with String returns other_str if it occurs in self
fails:String#slice with String taints resulting strings when other is tainted
fails:String#slice with String doesn't set $~
fails:String#slice with String returns nil if there is no match
fails:String#slice with String returns a subclass instance when given a subclass instance
fails:String#slice! with index deletes and return the char at the given position
fails:String#slice! with index returns nil if idx is outside of self
fails:String#slice! with index raises a RuntimeError if self is frozen
fails:String#slice! with index calls to_int on index
fails:String#slice! with index returns the character given by the character index
fails:String#slice! with index, length deletes and returns the substring at idx and the given length
fails:String#slice! with index, length always taints resulting strings when self is tainted
fails:String#slice! with index, length raises a RuntimeError if self is frozen
fails:String#slice! with index, length calls to_int on idx and length
fails:String#slice! with index, length returns subclass instances
fails:String#slice! with index, length returns the substring given by the character offsets
fails:String#slice! Range deletes and return the substring given by the offsets of the range
fails:String#slice! Range returns nil if the given range is out of self
fails:String#slice! Range always taints resulting strings when self is tainted
fails:String#slice! Range returns subclass instances
fails:String#slice! Range calls to_int on range arguments
fails:String#slice! Range works with Range subclasses
fails:String#slice! Range returns the substring given by the character offsets of the range
fails:String#slice! Range raises a RuntimeError on a frozen instance that is modified
fails:String#slice! Range raises a RuntimeError on a frozen instance that would not be modified
fails:String#slice! with Regexp deletes and returns the first match from self
fails:String#slice! with Regexp returns nil if there was no match
fails:String#slice! with Regexp always taints resulting strings when self or regexp is tainted
fails:String#slice! with Regexp doesn't taint self when regexp is tainted
fails:String#slice! with Regexp returns subclass instances
fails:String#slice! with Regexp sets $~ to MatchData when there is a match and nil when there's none
fails:String#slice! with Regexp raises a RuntimeError on a frozen instance that is modified
fails:String#slice! with Regexp raises a RuntimeError on a frozen instance that would not be modified
fails:String#slice! with Regexp, index deletes and returns the capture for idx from self
fails:String#slice! with Regexp, index always taints resulting strings when self or regexp is tainted
fails:String#slice! with Regexp, index doesn't taint self when regexp is tainted
fails:String#slice! with Regexp, index returns nil if there was no match
fails:String#slice! with Regexp, index returns nil if there is no capture for idx
fails:String#slice! with Regexp, index calls to_int on idx
fails:String#slice! with Regexp, index returns subclass instances
fails:String#slice! with Regexp, index returns the encoding aware capture for the given index
fails:String#slice! with Regexp, index sets $~ to MatchData when there is a match and nil when there's none
fails:String#slice! with Regexp, index raises a RuntimeError if self is frozen
fails:String#slice! with String removes and returns the first occurrence of other_str from self
fails:String#slice! with String taints resulting strings when other is tainted
fails:String#slice! with String doesn't set $~
fails:String#slice! with String returns nil if self does not contain other
fails:String#slice! with String doesn't call to_str on its argument
fails:String#slice! with String returns a subclass instance when given a subclass instance
fails:String#slice! with String raises a RuntimeError if self is frozen
fails:String#slice! with Regexp, index accepts a Float for capture index
fails:String#slice! with Regexp, index calls #to_int to convert an Object to capture index

53 changes: 0 additions & 53 deletions spec/truffle/tags/core/string/sub_tags.txt
Original file line number Diff line number Diff line change
@@ -1,72 +1,19 @@
fails:String#sub with pattern, replacement returns a copy of self with all occurrences of pattern replaced with replacement
fails:String#sub with pattern, replacement ignores a block if supplied
fails:String#sub with pattern, replacement supports \G which matches at the beginning of the string
fails:String#sub with pattern, replacement supports /i for ignoring case
fails:String#sub with pattern, replacement doesn't interpret regexp metacharacters if pattern is a string
fails:String#sub with pattern, replacement replaces \1 sequences with the regexp's corresponding capture
fails:String#sub with pattern, replacement treats \1 sequences without corresponding captures as empty strings
fails:String#sub with pattern, replacement replaces \& and \0 with the complete match
fails:String#sub with pattern, replacement replaces \` with everything before the current match
fails:String#sub with pattern, replacement replaces \' with everything after the current match
fails:String#sub with pattern, replacement replaces \\\+ with \\+
fails:String#sub with pattern, replacement replaces \+ with the last paren that actually matched
fails:String#sub with pattern, replacement treats \+ as an empty string if there was no captures
fails:String#sub with pattern, replacement maps \\ in replacement to \
fails:String#sub with pattern, replacement leaves unknown \x escapes in replacement untouched
fails:String#sub with pattern, replacement leaves \ at the end of replacement untouched
fails:String#sub with pattern, replacement taints the result if the original string or replacement is tainted
fails:String#sub with pattern, replacement tries to convert pattern to a string using to_str
fails:String#sub with pattern, replacement raises a TypeError when pattern can't be converted to a string
fails:String#sub with pattern, replacement tries to convert replacement to a string using to_str
fails:String#sub with pattern, replacement raises a TypeError when replacement can't be converted to a string
fails:String#sub with pattern, replacement returns subclass instances when called on a subclass
fails:String#sub with pattern, replacement sets $~ to MatchData of match and nil when there's none
fails:String#sub with pattern, replacement replaces \ with 
fails:String#sub with pattern, replacement replaces \\1 with \1
fails:String#sub with pattern, replacement replaces \\ with \
fails:String#sub with pattern and block returns a copy of self with the first occurrences of pattern replaced with the block's return value
fails:String#sub with pattern and block sets $~ for access from the block
fails:String#sub with pattern and block sets $~ to MatchData of last match and nil when there's none for access from outside
fails:String#sub with pattern and block doesn't raise a RuntimeError if the string is modified while substituting
fails:String#sub with pattern and block doesn't interpolate special sequences like \1 for the block's return value
fails:String#sub with pattern and block converts the block's return value to a string using to_s
fails:String#sub with pattern and block taints the result if the original string or replacement is tainted
fails:String#sub! with pattern, replacement modifies self in place and returns self
fails:String#sub! with pattern, replacement taints self if replacement is tainted
fails:String#sub! with pattern, replacement returns nil if no modifications were made
fails:String#sub! with pattern, replacement raises a RuntimeError when self is frozen
fails:String#sub! with pattern and block modifies self in place and returns self
fails:String#sub! with pattern and block sets $~ for access from the block
fails:String#sub! with pattern and block taints self if block's result is tainted
fails:String#sub! with pattern and block returns nil if no modifications were made
fails:String#sub! with pattern and block raises a RuntimeError if the string is modified while substituting
fails:String#sub! with pattern and block raises a RuntimeError when self is frozen
fails:String#sub with pattern and Hash returns a copy of self with the first occurrence of pattern replaced with the value of the corresponding hash key
fails:String#sub with pattern and Hash removes keys that don't correspond to matches
fails:String#sub with pattern and Hash ignores non-String keys
fails:String#sub with pattern and Hash uses a key's value only a single time
fails:String#sub with pattern and Hash uses the hash's default value for missing keys
fails:String#sub with pattern and Hash coerces the hash values with #to_s
fails:String#sub with pattern and Hash uses the hash's value set from default_proc for missing keys
fails:String#sub with pattern and Hash sets $~ to MatchData of first match and nil when there's none for access from outside
fails:String#sub with pattern and Hash doesn't interpolate special sequences like \1 for the block's return value
fails:String#sub with pattern and Hash untrusts the result if the original string is untrusted
fails:String#sub with pattern and Hash untrusts the result if a hash value is untrusted
fails:String#sub with pattern and Hash taints the result if the original string is tainted
fails:String#sub with pattern and Hash taints the result if a hash value is tainted
fails:String#sub! with pattern and Hash returns self with the first occurrence of pattern replaced with the value of the corresponding hash key
fails:String#sub! with pattern and Hash removes keys that don't correspond to matches
fails:String#sub! with pattern and Hash ignores non-String keys
fails:String#sub! with pattern and Hash uses a key's value only a single time
fails:String#sub! with pattern and Hash uses the hash's default value for missing keys
fails:String#sub! with pattern and Hash coerces the hash values with #to_s
fails:String#sub! with pattern and Hash uses the hash's value set from default_proc for missing keys
fails:String#sub! with pattern and Hash sets $~ to MatchData of first match and nil when there's none for access from outside
fails:String#sub! with pattern and Hash doesn't interpolate special sequences like \1 for the block's return value
fails:String#sub! with pattern and Hash keeps untrusted state
fails:String#sub! with pattern and Hash untrusts self if a hash value is untrusted
fails:String#sub! with pattern and Hash keeps tainted state
fails:String#sub! with pattern and Hash taints self if a hash value is tainted
fails:String#sub with pattern, replacement raises a TypeError when pattern is a Symbol
fails:String#sub with pattern, replacement raises a TypeError when pattern is an Array
fails:String#sub with pattern, replacement replaces \ with 
172 changes: 109 additions & 63 deletions test/jruby/test_higher_javasupport.rb
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ class TestHigherJavasupport < Test::Unit::TestCase
FinalMethodBaseTest = org.jruby.test.FinalMethodBaseTest
Annotation = java.lang.annotation.Annotation
ClassWithPrimitive = org.jruby.test.ClassWithPrimitive

def test_java_int_primitive_assignment
assert_nothing_raised {
cwp = ClassWithPrimitive.new
@@ -212,6 +212,7 @@ def test_make_sure_we_are_not_writing_class_methods_to_the_same_place
end

java_import 'org.jruby.javasupport.test.Color'

def test_lazy_proxy_method_tests_for_alias_and_respond_to
color = Color.new('green')
assert_equal(true, color.respond_to?(:setColor))
@@ -240,6 +241,7 @@ def test_accessor_methods
# No explicit test, but implicitly EMPTY_LIST.each should not blow up interpreter
# Old error was EMPTY_LIST is a private class implementing a public interface with public methods
java_import 'java.util.Collections'

def test_empty_list_each_should_not_blow_up_interpreter
assert_nothing_raised { Collections::EMPTY_LIST.each {|element| } }
end
@@ -252,7 +254,7 @@ def foo
end
assert_equal(true, Foo::ArrayList.new.foo)
end

def test_same_proxy_does_not_raise
# JString already included and it is the same proxy, so do not throw an error
# (e.g. intent of java_import already satisfied)
@@ -271,6 +273,7 @@ class << self
end

java_import 'java.util.Calendar'

def test_date_time_conversion
# Test java.util.Date <=> Time implicit conversion
calendar = Calendar.getInstance
@@ -298,6 +301,7 @@ def test_expected_java_string_methods_exist
end

java_import 'java.math.BigDecimal'

def test_big_decimal_interaction
assert_equal(BigDecimal, BigDecimal.new("1.23").add(BigDecimal.new("2.34")).class)
end
@@ -308,6 +312,7 @@ def test_direct_package_access
end

Properties = Java::java.util.Properties

def test_declare_constant
p = Properties.new
p.setProperty("a", "b")
@@ -387,33 +392,34 @@ def test_that_multiple_threads_including_classes_dont_step_on_each_other
end
=end

unless (java.lang.System.getProperty("java.specification.version") == "1.4")
if javax.xml.namespace.NamespaceContext.instance_of?(Module)
class NSCT
include javax.xml.namespace.NamespaceContext
# JRUBY-66: No super here...make sure we still work.
def initialize(arg)
end
def getNamespaceURI(prefix)
'ape:sex'
end
end
else
class NSCT < javax.xml.namespace.NamespaceContext
# JRUBY-66: No super here...make sure we still work.
def initialize(arg)
end
def getNamespaceURI(prefix)
'ape:sex'
end
if javax.xml.namespace.NamespaceContext.instance_of?(Module)

class NSCT
include javax.xml.namespace.NamespaceContext
# JRUBY-66: No super here...make sure we still work.
def initialize(arg); end
def getNamespaceURI(prefix)
'ape:sex'
end
end
def test_no_need_to_call_super_in_initialize_when_implementing_java_interfaces
# No error is a pass here for JRUBY-66
assert_nothing_raised do
javax.xml.xpath.XPathFactory.newInstance.newXPath.setNamespaceContext(NSCT.new(1))

else

class NSCT < javax.xml.namespace.NamespaceContext
# JRUBY-66: No super here...make sure we still work.
def initialize(arg); end
def getNamespaceURI(prefix)
'ape:sex'
end
end

end

def test_no_need_to_call_super_in_initialize_when_implementing_java_interfaces
# No error is a pass here for JRUBY-66
assert_nothing_raised do
javax.xml.xpath.XPathFactory.newInstance.newXPath.setNamespaceContext(NSCT.new(1))
end
end

def test_can_see_inner_class_constants_with_same_name_as_top_level
@@ -513,11 +519,11 @@ def test_inner_class_proxies
assert !defined?(OuterClass::DefaultInstanceInnerClass)
assert !defined?(OuterClass::PrivateInstanceInnerClass)
end

# Test the new "import" syntax
def test_import
assert_nothing_raised {

assert_nothing_raised {
import java.nio.ByteBuffer
ByteBuffer.allocate(10)
}
@@ -540,7 +546,7 @@ def test_java_method_returns_null

assert_equal("", rn.returnNull.to_s)
end

# test for JRUBY-664
class FinalMethodChildClass < FinalMethodBaseTest
end
@@ -574,59 +580,59 @@ def test_calling_base_class_final_method
# assert(m.updated)
# end
# end

class A < java.lang.Object
include org.jruby.javasupport.test.Interface1

def method1
end
end
A.new

class B < A
include org.jruby.javasupport.test.Interface2

def method2
end
end
B.new

class C < B
end
C.new

def test_interface_methods_seen
ci = org.jruby.javasupport.test.ConsumeInterfaces.new
ci.addInterface1(A.new)
ci.addInterface1(B.new)
ci.addInterface2(B.new)
ci.addInterface1(C.new)
ci.addInterface2(C.new)

end

class LCTestA < java::lang::Object
include org::jruby::javasupport::test::Interface1

def method1
end
end
LCTestA.new

class LCTestB < LCTestA
include org::jruby::javasupport::test::Interface2

def method2
end
end
LCTestB.new

class java::lang::Object
def boo
'boo!'
end
end

def test_lowercase_colon_package_syntax
assert_equal(java::lang::String, java.lang.String)
assert_equal('boo!', java.lang.String.new('xxx').boo)
@@ -638,33 +644,33 @@ def test_lowercase_colon_package_syntax
ci.addInterface1(LCTestB.new)
ci.addInterface2(LCTestB.new)
end

def test_marsal_java_object_fails
assert_raises(TypeError) { Marshal.dump(java::lang::Object.new) }
end

def test_string_from_bytes
assert_equal('foo', String.from_java_bytes('foo'.to_java_bytes))
end

# JRUBY-2088
def test_package_notation_with_arguments
assert_raises(ArgumentError) do
assert_raises(ArgumentError) do
java.lang("ABC").String
end

assert_raises(ArgumentError) do
assert_raises(ArgumentError) do
java.lang.String(123)
end
assert_raises(ArgumentError) do

assert_raises(ArgumentError) do
Java::se("foobar").com.Foobar
end
end

# JRUBY-1545
def test_creating_subclass_to_java_interface_raises_type_error
assert_raises(TypeError) do
def test_creating_subclass_to_java_interface_raises_type_error
assert_raises(TypeError) do
eval(<<CLASSDEF)
class FooXBarBarBar < Java::JavaLang::Runnable
end
@@ -673,11 +679,11 @@ class FooXBarBarBar < Java::JavaLang::Runnable
end

# JRUBY-781
def test_that_classes_beginning_with_small_letter_can_be_referenced
def test_that_classes_beginning_with_small_letter_can_be_referenced
assert_equal Module, org.jruby.test.smallLetterClazz.class
assert_equal Class, org.jruby.test.smallLetterClass.class
end

# JRUBY-1076
def test_package_module_aliased_methods
assert java.lang.respond_to?(:__constants__)
@@ -692,6 +698,7 @@ def test_package_load_doesnt_set_error
$! = nil
undo = javax.swing.undo
assert_nil($!)
assert undo
end

# JRUBY-2106
@@ -704,7 +711,7 @@ def test_top_level_package_load_doesnt_set_error
Java::Boom
assert_nil($!)
end

# JRUBY-2169
def test_java_class_resource_methods
path = 'test/org/jruby/javasupport/test/'
@@ -731,39 +738,39 @@ def test_java_class_resource_methods
str = jc.resource_as_string(file)
assert(/^foo=bar/ =~ str)
end

# JRUBY-2169
def test_ji_extended_methods_for_java_1_5
jc = java.lang.String.java_class
ctor = jc.constructors[0]
meth = jc.java_instance_methods[0]
field = jc.fields[0]

# annotations
assert(Annotation[] === jc.annotations)
assert(Annotation[] === ctor.annotations)
assert(Annotation[] === meth.annotations)
assert(Annotation[] === field.annotations)

# TODO: more extended methods to test


end

# JRUBY-2169
def test_java_class_ruby_class
assert java.lang.Object.java_class.ruby_class == java.lang.Object
assert java.lang.Runnable.java_class.ruby_class == java.lang.Runnable
end

def test_null_toString
assert nil == org.jruby.javasupport.test.NullToString.new.to_s
end

# JRUBY-2277
# kind of a strange place for this test, but the error manifested
# when JI was enabled. the actual bug was a problem in alias_method,
# and not related to JI; see related test in test_methods.rb
# and not related to JI; see related test in test_methods.rb
def test_alias_method_with_JI_enabled_does_not_raise
name = Object.new
def name.to_str
@@ -784,7 +791,7 @@ def test_extend_default_package_class
cls = Class.new(Java::DefaultPackageClass);
assert_nothing_raised { cls.new }
end

# JRUBY-3046
def test_java_methods_have_arity
assert_nothing_raised do
@@ -833,4 +840,43 @@ def test_float_always_coerces_to_java_float
end
end
end

def test_no_warnings_on_concurrent_package_const_initialization
stderr = $stderr; require 'stringio'
begin
$stderr = StringIO.new
threads = (0..10).map do # smt not yet initialized :
Thread.new { Java::JavaTextSpi::CollatorProvider }
end

threads.each { |thread| thread.join }

# expect no already initialized constant warning written e.g.
# file:/.../jruby.jar!/jruby/java/java_module.rb:4 warning: already initialized constant JavaTextSpi
assert ! $stderr.string.index('already initialized constant'), $stderr.string
ensure
$stderr = stderr
end
end

def test_no_warnings_on_concurrent_class_const_initialization
Java.send :remove_const, :DefaultPackageClass if Java.const_defined? :DefaultPackageClass

stderr = $stderr; require 'stringio'
begin
$stderr = StringIO.new
threads = (0..10).map do
Thread.new { Java::DefaultPackageClass }
end

threads.each { |thread| thread.join }

# expect no already initialized constant warning written e.g.
# ... warning: already initialized constant DefaultPackageClass
assert ! $stderr.string.index('already initialized constant'), $stderr.string
ensure
$stderr = stderr
end
end

end
69 changes: 57 additions & 12 deletions test/jruby/test_java_extension.rb
Original file line number Diff line number Diff line change
@@ -2,8 +2,6 @@
require 'test/unit'

class TestJavaExtension < Test::Unit::TestCase
import org.jruby.test.Worker
import org.jruby.test.Abstract

class TestParent < org.jruby.test.Parent
attr_accessor :result
@@ -12,6 +10,8 @@ def run(a)
end
end

java_import org.jruby.test.Worker

def test_overriding_method_in_java_superclass
w = Worker.new
p = TestParent.new
@@ -22,24 +22,24 @@ def test_overriding_method_in_java_superclass
import java.util.HashMap
import java.util.ArrayList
import java.util.HashSet
import java.lang.Short

def test_set
set = HashSet.new
set.add(1)
set.add(2)

newSet = []
set.each {|x| newSet << x }
set.each { |x| newSet << x }

assert newSet.include?(1)
assert newSet.include?(2)
end

def test_comparable
one = Short.new(1)
two = Short.new(2)
three = Short.new(3)
short = Java::JavaLang::Short
one = short.new(1)
two = short.new(2)
three = short.new(3)
list = [three, two, one]
list = list.sort
assert_equal([one, two, three], list)
@@ -147,6 +147,23 @@ def test_java_bean_conventions_in_ruby
assert_equal BLUE, color.color
end

def test_java_class_name
assert_equal 'Java::OrgJrubyJavasupportTest::Color', Color.name

assert_equal 'org.jruby.javasupport.test.Color', Color.java_class.name

assert_equal 'Java::JavaLang::Runnable', java.lang.Runnable.name
assert_equal 'java.lang.Runnable', Java::JavaLang::Runnable.java_class.name

assert_equal 'Java::JavaLang::String', JString.name

assert_equal 'Java::JavaLang::Thread::State', Java::java.lang.Thread::State.name
# an enum class with custom code for each constant :
assert_equal 'Java::JavaUtilConcurrent::TimeUnit::1', java.util.concurrent.TimeUnit::NANOSECONDS.class.name
assert_equal 'Java::JavaUtilConcurrent::TimeUnit::2', java.util.concurrent.TimeUnit::MICROSECONDS.class.name
assert java.util.concurrent.TimeUnit::MICROSECONDS.is_a?(java.util.concurrent.TimeUnit)
end

include_package 'org.jruby.javasupport.test'
include_package 'java.lang'

@@ -192,21 +209,21 @@ def test_synchronized_method_available
result = obj.synchronized { "foo" }
}
assert_equal("foo", result)

begin
obj.wait 1
rescue java.lang.IllegalMonitorStateException
assert true
else
assert false, "java.lang.IllegalMonitorStateException was not thrown"
end

assert_nothing_raised {
obj.synchronized { obj.wait 1 }
}
end


def test_java_interface_impl_with_block
ran = false
SimpleExecutor::WrappedByMethodCall.new.execute(Runnable.impl {ran = true})
@@ -276,14 +293,42 @@ def test_overriding_protected_method
begin
assert_equal "Ruby overrides java!", a.call_protected
rescue Exception => e
flunk "Exception raised: #{$!}"
flunk "Exception raised: #{e}"
end
end

def test_map_interface_to_array
hash = {"one"=>"two","three"=>"four"}
map = java.util.HashMap.new(hash)
assert_equal hash.to_a.sort, map.to_a.sort
end

def test_java_object_wrapper
wrapped1 = Java::JavaObject.wrap object = java.lang.StringBuilder.new
assert wrapped1.is_a? Java::JavaObject
assert_equal object, wrapped1

wrapped2 = Java::JavaObject.wrap java.lang.StringBuilder.new
assert_not_equal object, wrapped2
assert ! wrapped1.equal?(wrapped2)

wrapped3 = Java::JavaObject.wrap object
assert_equal wrapped1, wrapped3
assert wrapped1.equal?(wrapped3)

cal1 = java.util.Calendar.getInstance
cal1.setTime Java::JavaUtil::Date.new 0
cal2 = java.util.Calendar.getInstance
cal2.setTime Java::JavaUtil::Date.new 0

assert ! cal1.equal?(cal2)

wrapped1 = Java::JavaObject.wrap cal1
wrapped2 = Java::JavaObject.wrap cal2
assert wrapped2 == wrapped1
assert wrapped1.eql? wrapped2
assert ! wrapped1.equal?(wrapped2)
end

end

4 changes: 4 additions & 0 deletions tool/truffle-findbugs-exclude.xml
Original file line number Diff line number Diff line change
@@ -70,4 +70,8 @@
<Match>
<Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON" />
</Match>
<Match>
<Class name="org.jruby.truffle.runtime.subsystems.SimpleShell" />
<Bug pattern="DM_EXIT" />
</Match>
</FindBugsFilter>
5 changes: 2 additions & 3 deletions truffle/pom.rb
Original file line number Diff line number Diff line change
@@ -17,9 +17,8 @@

plugin( :compiler,
'encoding' => 'utf-8',
'debug' => 'true',
'verbose' => 'true',
'fork' => 'true',
'debug' => 'false',
'verbose' => 'false',
'showWarnings' => 'true',
'showDeprecation' => 'true',
'source' => [ '${base.java.version}', '1.7' ],
5 changes: 2 additions & 3 deletions truffle/pom.xml
Original file line number Diff line number Diff line change
@@ -68,9 +68,8 @@
</executions>
<configuration>
<encoding>utf-8</encoding>
<debug>true</debug>
<verbose>true</verbose>
<fork>true</fork>
<debug>false</debug>
<verbose>false</verbose>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
<source>${base.java.version}</source>
Original file line number Diff line number Diff line change
@@ -92,14 +92,12 @@ public void init() {
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, SymbolNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, ThreadNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, TrueClassNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, TruffleDebugNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, TrufflePrimitiveNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, PrimitiveNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, EncodingNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, EncodingConverterNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, MethodNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, UnboundMethodNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, ByteArrayNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, TruffleNodesFactory.getFactories());
CoreMethodNodeManager.addCoreMethodNodes(rubyObjectClass, TimeNodesFactory.getFactories());

// Give the core library manager a chance to tweak some of those methods
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ public boolean isCloningAllowed() {

@Override
public Object execute(VirtualFrame frame) {
context.getSafepointManager().poll();
context.getSafepointManager().poll(this);
return body.execute(frame);
}

111 changes: 111 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/nodes/coerce/ToIntNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/


package org.jruby.truffle.nodes.coerce;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.core.FloatNodes;
import org.jruby.truffle.nodes.core.FloatNodesFactory;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyBignum;

@NodeChild(value = "child", type = RubyNode.class)
public abstract class ToIntNode extends RubyNode {

@Child private CallDispatchHeadNode toIntNode;
@Child private FloatNodes.ToINode floatToIntNode;

public ToIntNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public ToIntNode(ToIntNode prev) {
super(prev);
}

@Specialization
public int coerceInt(int value) {
return value;
}

@Specialization
public long coerceLong(long value) {
return value;
}

@Specialization
public RubyBignum coerceRubyBignum(RubyBignum value) {
return value;
}

@Specialization
public Object coerceDouble(double value) {
if (floatToIntNode == null) {
CompilerDirectives.transferToInterpreter();
floatToIntNode = insert(FloatNodesFactory.ToINodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{}));
}

return floatToIntNode.toI(value);
}

@Specialization(guards = "!isRubyBignum(object)")
public Object coerceObject(VirtualFrame frame, Object object) {
notDesignedForCompilation();

if (toIntNode == null) {
CompilerDirectives.transferToInterpreter();
toIntNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

final Object coerced;

try {
coerced = toIntNode.call(frame, object, "to_int", null);
} catch (RaiseException e) {
if (e.getRubyException().getLogicalClass() == getContext().getCoreLibrary().getNoMethodErrorClass()) {
CompilerDirectives.transferToInterpreter();

throw new RaiseException(
getContext().getCoreLibrary().typeErrorNoImplicitConversion(object, "Integer", this));
} else {
throw e;
}
}

if (getContext().getCoreLibrary().getLogicalClass(coerced) == getContext().getCoreLibrary().getFixnumClass()) {
return coerced;
} else {
CompilerDirectives.transferToInterpreter();

throw new RaiseException(
getContext().getCoreLibrary().typeErrorBadCoercion(object, "Integer", "to_int", coerced, this));
}
}

@Override
public abstract int executeIntegerFixnum(VirtualFrame frame);

public abstract int executeIntegerFixnum(VirtualFrame frame, Object object);

@Override
public abstract long executeLongFixnum(VirtualFrame frame);

@Override
public abstract RubyBignum executeBignum(VirtualFrame frame);
}
Loading

0 comments on commit 9700ff3

Please sign in to comment.