Skip to content
Permalink

Comparing changes

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

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b2d759a29cb1
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e92030e480f7
Choose a head ref
  • 11 commits
  • 13 files changed
  • 2 contributors

Commits on Aug 17, 2015

  1. Fix some consumers for #1601. Don't leak out if we are swallowing the…

    … raised exception
    enebo authored and kares committed Aug 17, 2015
    Copy the full SHA
    03d90f8 View commit details
  2. Copy the full SHA
    5849a2b View commit details
  3. more of correct $! restore + use get/setErrorInfo for better predicta…

    …bility
    
    semi-picked from 78e968e (#1601)
    kares committed Aug 17, 2015
    Copy the full SHA
    adda855 View commit details
  4. Copy the full SHA
    5445a13 View commit details
  5. Copy the full SHA
    3160c14 View commit details

Commits on Aug 18, 2015

  1. base compareTo should not silence all Ruby raised exceptions (fixes #…

    …2232)
    
    this no longer makese sense - and leads to confusing behavior when Ruby objects are used within Java (delegating to <=> which might be user-defined)
    
    every-one is expected to handle <=> on 1.9 (and return nil if not comparable)
    on 1.8 we still swallow a NoMethodError as Object does not provide <=>
    kares committed Aug 18, 2015
    Copy the full SHA
    771afb0 View commit details
  2. [ji] do not rewrite stack-trace twice for Ruby (raise) exceptions

    ... for cases when Ruby calling Java (integration) ends up calling Ruby
    kares committed Aug 18, 2015
    Copy the full SHA
    cef9ed3 View commit details
  3. delete un-used imports

    kares committed Aug 18, 2015
    Copy the full SHA
    3e57648 View commit details
  4. Copy the full SHA
    6e960d8 View commit details
  5. native RubySymbol#compareTo since we expect to always be able to sort

    ... proc.impl Java part sorts an (symbol) array method-names for binary search
    kares committed Aug 18, 2015
    Copy the full SHA
    6ad6df6 View commit details
  6. Copy the full SHA
    e92030e View commit details
46 changes: 24 additions & 22 deletions core/src/main/java/org/jruby/Main.java
Original file line number Diff line number Diff line change
@@ -75,7 +75,7 @@
*/
public class Main {
private static final Logger LOG = LoggerFactory.getLogger("Main");

public Main(RubyInstanceConfig config) {
this(config, false);
}
@@ -99,16 +99,16 @@ private Main(boolean hardExit) {
this.config = new RubyInstanceConfig();
config.setHardExit(hardExit);
}

private static List<String> getDotfileDirectories() {
ArrayList<String> searchList = new ArrayList<String>();
for (String homeProp : new String[] {"user.dir", "user.home"}) {
String home = SafePropertyAccessor.getProperty(homeProp);
if (home != null) searchList.add(home);
}

// JVM sometimes picks odd location for user.home based on a registry entry
// (see http://bugs.sun.com/view_bug.do?bug_id=4787931). Add extra check in
// (see http://bugs.sun.com/view_bug.do?bug_id=4787931). Add extra check in
// case that entry is wrong. Add before user.home in search list.
if (Platform.IS_WINDOWS) {
String homeDrive = System.getenv("HOMEDRIVE");
@@ -117,10 +117,10 @@ private static List<String> getDotfileDirectories() {
searchList.add(1, (homeDrive + homePath).replace('\\', '/'));
}
}

return searchList;
}

public static void processDotfile() {
for (String home : getDotfileDirectories()) {
File dotfile = new File(home + "/.jrubyrc");
@@ -130,7 +130,7 @@ public static void processDotfile() {

private static void loadJRubyProperties(File dotfile) {
FileInputStream fis = null;

try {
// update system properties with long form jruby properties from .jrubyrc
Properties sysProps = System.getProperties();
@@ -146,7 +146,7 @@ private static void loadJRubyProperties(File dotfile) {
} catch (SecurityException se) {
LOG.debug("exception loading " + dotfile, se);
} finally {
if (fis != null) try {fis.close();} catch (Exception e) {}
if (fis != null) try {fis.close();} catch (Exception e) {}
}
}

@@ -184,15 +184,15 @@ public static class Status {
*/
public static void main(String[] args) {
doGCJCheck();

Main main;

if (DripMain.DRIP_RUNTIME != null) {
main = new Main(DripMain.DRIP_CONFIG, true);
} else {
main = new Main(true);
}

try {
Status status = main.run(args);
if (status.isExit()) {
@@ -245,9 +245,9 @@ private Status internalRun() {

InputStream in = config.getScriptSource();
String filename = config.displayedFileName();

doProcessArguments(in);

Ruby _runtime;

if (DripMain.DRIP_RUNTIME != null) {
@@ -257,10 +257,10 @@ private Status internalRun() {
} else {
_runtime = Ruby.newInstance(config);
}

final Ruby runtime = _runtime;
final AtomicBoolean didTeardown = new AtomicBoolean();

if (config.isHardExit()) {
// we're the command-line JRuby, and should set a shutdown hook for
// teardown.
@@ -354,7 +354,7 @@ private Status handleOutOfMemory(OutOfMemoryError oome) {
}
config.getError().println("Specify -J-Xmx####m to increase it (#### = cap size in MB).");
}

if (config.isVerbose()) {
config.getError().println("Exception trace follows:");
oome.printStackTrace(config.getError());
@@ -402,15 +402,15 @@ private Status doRunFromMain(Ruby runtime, InputStream in, String filename) {
private Status doCheckSyntax(Ruby runtime, InputStream in, String filename) throws RaiseException {
// check primary script
boolean status = checkStreamSyntax(runtime, in, filename);

// check other scripts specified on argv
for (String arg : config.getArgv()) {
status = status && checkFileSyntax(runtime, arg);
}

return new Status(status ? 0 : -1);
}

private boolean checkFileSyntax(Ruby runtime, String filename) {
File file = new File(filename);
if (file.exists()) {
@@ -424,19 +424,21 @@ private boolean checkFileSyntax(Ruby runtime, String filename) {
return false;
}
}

private boolean checkStreamSyntax(Ruby runtime, InputStream in, String filename) {
final ThreadContext context = runtime.getCurrentContext();
final IRubyObject $ex = context.getErrorInfo();
try {
runtime.parseFromMain(in, filename);
config.getOutput().println("Syntax OK");
return true;
} catch (RaiseException re) {
if (re.getException().getMetaClass().getBaseName().equals("SyntaxError")) {
context.setErrorInfo($ex);
config.getError().println("SyntaxError in " + re.getException().message(runtime.getCurrentContext()));
} else {
throw re;
return false;
}
return false;
throw re;
}
}

43 changes: 31 additions & 12 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -1115,18 +1115,17 @@ public IRubyObject op_not_equal(ThreadContext context, IRubyObject other) {
* @return 0 if equal,
* &lt; 0 if this is less than other,
* &gt; 0 if this is greater than other
* @throws IllegalArgumentException if the objects cannot be compared.
*/
public int compareTo(IRubyObject other) {
try {
IRubyObject cmp = invokedynamic(getRuntime().getCurrentContext(),
this, OP_CMP, other);
final Ruby runtime = getRuntime();

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

IRubyObject cmp = invokedynamic(runtime.getCurrentContext(), this, OP_CMP, other);

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

/* We used to raise an error if two IRubyObject were not comparable, but
@@ -1141,6 +1140,27 @@ public int compareTo(IRubyObject other) {
return 0;
}

// on 1.8 nil (Object in general) is not comparable using <=> by default
private int compareTo18(final Ruby runtime, IRubyObject other) {
final ThreadContext context = runtime.getCurrentContext();
final IRubyObject $ex = context.getErrorInfo();
try {
IRubyObject cmp = invokedynamic(context, this, OP_CMP, other);
if ( ! cmp.isNil() ) {
return (int) cmp.convertToInteger().getLongValue();
}
}
catch (RaiseException e) {
RubyException re = e.getException();
if ( re instanceof RubyNoMethodError ) {
// e.g. NoMethodError: undefined method `<=>' for #<Object:0xceb431e>
context.setErrorInfo($ex); return 0;
}
throw e;
}
return 0;
}

public IRubyObject op_equal(ThreadContext context, IRubyObject obj) {
return op_equal_19(context, obj);
}
@@ -1878,11 +1898,10 @@ public IRubyObject eql_p(IRubyObject obj) {
}

public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
Ruby runtime = context.runtime;
if (this == other || invokedynamic(context, this, OP_EQUAL, other).isTrue()){
return RubyFixnum.zero(runtime);
return RubyFixnum.zero(context.runtime);
}
return runtime.getNil();
return context.nil;
}

/** rb_obj_init_copy
197 changes: 99 additions & 98 deletions core/src/main/java/org/jruby/RubyClass.java

Large diffs are not rendered by default.

49 changes: 24 additions & 25 deletions core/src/main/java/org/jruby/RubyComparable.java
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
* Copyright (C) 2005 Charles O Nutter <headius@headius.com>
* Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
* Copyright (C) 2006 Thomas E Enebo <enebo@acm.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"),
@@ -49,35 +49,35 @@ public class RubyComparable {
public static RubyModule createComparable(Ruby runtime) {
RubyModule comparableModule = runtime.defineModule("Comparable");
runtime.setComparable(comparableModule);

comparableModule.defineAnnotatedMethods(RubyComparable.class);

return comparableModule;
}

/* ================
* Utility Methods
* ================
* ================
*/

/** rb_cmpint
*
*
*/
public static int cmpint(ThreadContext context, IRubyObject val, IRubyObject a, IRubyObject b) {
if (val.isNil()) cmperr(a, b);
if (val instanceof RubyFixnum) return RubyNumeric.fix2int((RubyFixnum) val);
if (val instanceof RubyBignum) return ((RubyBignum) val).getValue().signum() == -1 ? -1 : 1;

RubyFixnum zero = RubyFixnum.zero(context.runtime);

if (val.callMethod(context, ">", zero).isTrue()) return 1;
if (val.callMethod(context, "<", zero).isTrue()) return -1;

return 0;
}

/** rb_cmperr
*
*
*/
public static IRubyObject cmperr(IRubyObject recv, IRubyObject other) {
IRubyObject target;
@@ -92,11 +92,11 @@ public static IRubyObject cmperr(IRubyObject recv, IRubyObject other) {

/* ================
* Module Methods
* ================
* ================
*/

/** cmp_equal (cmp_eq inlined here)
*
*
*/
@JRubyMethod(name = "==", required = 1, compat = CompatVersion.RUBY1_8)
public static IRubyObject op_equal(ThreadContext context, IRubyObject recv, IRubyObject other) {
@@ -109,57 +109,56 @@ public static IRubyObject op_equal19(ThreadContext context, IRubyObject recv, IR
}

private static IRubyObject callCmpMethod(ThreadContext context, IRubyObject recv, IRubyObject other, IRubyObject returnValueOnError) {
Ruby runtime = context.runtime;
final Ruby runtime = context.runtime;

if (recv == other) return runtime.getTrue();

final IRubyObject $ex = context.getErrorInfo();
try {
IRubyObject result = invokedynamic(context, recv, OP_CMP, other);

// This is only to prevent throwing exceptions by cmperr - it has poor performance
if (result.isNil()) {
return returnValueOnError;
}
if ( result.isNil() ) return returnValueOnError;

return RubyBoolean.newBoolean(runtime, cmpint(context, result, recv, other) == 0);
} catch (RaiseException e) {
}
catch (RaiseException e) {
if (e.getException().kind_of_p(context, runtime.getStandardError()).isTrue()) {
// clear error info resulting from failure to compare (JRUBY-3292)
context.setErrorInfo(runtime.getNil());
context.setErrorInfo($ex); // restore previous $! error (if any)
return returnValueOnError;
} else {
throw e;
}
throw e;
}
}

/** cmp_gt
*
*
*/
// <=> may return nil in many circumstances, e.g. 3 <=> NaN
// <=> may return nil in many circumstances, e.g. 3 <=> NaN
@JRubyMethod(name = ">", required = 1)
public static RubyBoolean op_gt(ThreadContext context, IRubyObject recv, IRubyObject other) {
IRubyObject result = invokedynamic(context, recv, OP_CMP, other);

if (result.isNil()) cmperr(recv, other);

return RubyBoolean.newBoolean(context.runtime, cmpint(context, result, recv, other) > 0);
}

/** cmp_ge
*
*
*/
@JRubyMethod(name = ">=", required = 1)
public static RubyBoolean op_ge(ThreadContext context, IRubyObject recv, IRubyObject other) {
IRubyObject result = invokedynamic(context, recv, OP_CMP, other);

if (result.isNil()) cmperr(recv, other);

return RubyBoolean.newBoolean(context.runtime, cmpint(context, result, recv, other) >= 0);
}

/** cmp_lt
*
*
*/
@JRubyMethod(name = "<", required = 1)
public static RubyBoolean op_lt(ThreadContext context, IRubyObject recv, IRubyObject other) {
@@ -171,7 +170,7 @@ public static RubyBoolean op_lt(ThreadContext context, IRubyObject recv, IRubyOb
}

/** cmp_le
*
*
*/
@JRubyMethod(name = "<=", required = 1)
public static RubyBoolean op_le(ThreadContext context, IRubyObject recv, IRubyObject other) {
@@ -183,7 +182,7 @@ public static RubyBoolean op_le(ThreadContext context, IRubyObject recv, IRubyOb
}

/** cmp_between
*
*
*/
@JRubyMethod(name = "between?", required = 2)
public static RubyBoolean between_p(ThreadContext context, IRubyObject recv, IRubyObject first, IRubyObject second) {
215 changes: 110 additions & 105 deletions core/src/main/java/org/jruby/RubyNumeric.java

Large diffs are not rendered by default.

172 changes: 92 additions & 80 deletions core/src/main/java/org/jruby/RubySymbol.java

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions core/src/main/java/org/jruby/RubyTime.java
Original file line number Diff line number Diff line change
@@ -1305,17 +1305,23 @@ protected static RubyTime s_mload(IRubyObject recv, RubyTime time, IRubyObject f

int offset = 0;
if (offsetVar != null && offsetVar.respondsTo("to_int")) {
final IRubyObject $ex = runtime.getCurrentContext().getErrorInfo();
try {
offset = ((int) offsetVar.convertToInteger().getLongValue()) * 1000;
} catch (RaiseException typeError) {
}
catch (RaiseException typeError) {
runtime.getCurrentContext().setErrorInfo($ex); // restore $!
}
}

String zone = "";
if (zoneVar != null && zoneVar.respondsTo("to_str")) {
final IRubyObject $ex = runtime.getCurrentContext().getErrorInfo();
try {
zone = zoneVar.convertToString().toString();
} catch (RaiseException typeError) {
}
catch (RaiseException typeError) {
runtime.getCurrentContext().setErrorInfo($ex); // restore $!
}
}

Original file line number Diff line number Diff line change
@@ -376,6 +376,7 @@ public static IRubyObject impl(ThreadContext context, IRubyObject self, IRubyObj
else {
methodNames = args.clone();
Arrays.sort(methodNames); // binarySearch needs a sorted array
// RubySymbol implements a Java compareTo thus will allways work
}

RubyClass implClass = RubyClass.newClass(runtime, runtime.getObject());
@@ -390,7 +391,7 @@ public static IRubyObject impl(ThreadContext context, IRubyObject self, IRubyObj

private static class BlockInterfaceImpl extends org.jruby.internal.runtime.methods.JavaMethod {

private final IRubyObject[] methodNames; // RubySymbol[] (or RubyString[] on 1.8)
private final IRubyObject[] methodNames; // RubySymbol[]
private final Block implBlock;

BlockInterfaceImpl(final RubyClass implClass, final Block implBlock, final IRubyObject[] methodNames) {
6 changes: 0 additions & 6 deletions core/src/main/java/org/jruby/javasupport/Java.java
Original file line number Diff line number Diff line change
@@ -59,12 +59,10 @@
import org.jruby.RubyClass;
import org.jruby.RubyClassPathVariable;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyMethod;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.RubyProc;
import org.jruby.RubyString;
import org.jruby.RubyUnboundMethod;
import org.jruby.javasupport.binding.Initializer;
import org.jruby.javasupport.proxy.JavaProxyClass;
import org.jruby.javasupport.proxy.JavaProxyConstructor;
@@ -86,9 +84,6 @@
import org.jruby.java.addons.StringJavaAddons;
import org.jruby.java.codegen.RealClassGenerator;
import org.jruby.java.dispatch.CallableSelector;
import org.jruby.java.invokers.InstanceMethodInvoker;
import org.jruby.java.invokers.MethodInvoker;
import org.jruby.java.invokers.StaticMethodInvoker;
import org.jruby.java.proxies.ArrayJavaProxy;
import org.jruby.java.proxies.ArrayJavaProxyCreator;
import org.jruby.java.proxies.ConcreteJavaProxy;
@@ -104,7 +99,6 @@
import org.jruby.util.ClassCache.OneShotClassLoader;
import org.jruby.util.ClassDefiningClassLoader;
import org.jruby.util.ClassProvider;
import org.jruby.util.CodegenUtils;
import org.jruby.util.IdUtil;
import org.jruby.util.SafePropertyAccessor;
import org.jruby.util.cli.Options;
7 changes: 7 additions & 0 deletions core/src/main/java/org/jruby/javasupport/JavaCallable.java
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@
import org.jruby.RubyFixnum;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.JumpException;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.builtin.IRubyObject;
@@ -150,6 +151,12 @@ final Object[] convertArguments(final IRubyObject[] args, int offset) {
}

protected final IRubyObject handleThrowable(final Throwable ex, final Member target) {
if ( ex instanceof JumpException ) {
// RaiseException (from the Ruby side) is expected to already
// have its stack-trace rewritten - no need to do it again ...
throw (JumpException) ex;
}

if (REWRITE_JAVA_TRACE) {
Helpers.rewriteStackTrace(getRuntime(), ex);
}
54 changes: 40 additions & 14 deletions spec/java_integration/exceptions/rescue_spec.rb
Original file line number Diff line number Diff line change
@@ -134,7 +134,7 @@ def obj.go
end
end

describe "A native exception wrapped by another" do
describe "A native exception wrapped by another" do
it "gets the first available message from the causes' chain" do
begin
ThrowExceptionInInitializer.new.test
@@ -197,8 +197,9 @@ def obj.go
end
end

describe "A ruby exception raised through java and back to ruby" do
it "its class and message is preserved" do
describe "Ruby exception raised through Java and back to Ruby" do

it "preserves its class and message" do
begin
ExceptionRunner.new.do_it_now do
raise "it comes from ruby"
@@ -208,19 +209,44 @@ def obj.go
e.message.should == "it comes from ruby"
end
end
end

describe "A ruby exception raised through java and back " +
"to ruby via a different thread" do
it "its class and message is preserved" do
begin
ExceptionRunner.new.do_it_threaded do
raise "it comes from ruby"
context "(via a different thread)" do

it "preserves its class and message" do
begin
ExceptionRunner.new.do_it_threaded do
raise "it comes from ruby"
end
fail
rescue RuntimeError => e
e.message.should == "it comes from ruby"
end
fail
rescue RuntimeError => e
e.message.should == "it comes from ruby"
end

end

SampleTask = Struct.new(:time) do
include Comparable

def <=>(that)
raise ArgumentError.new("unexpected #{self.inspect}") unless self.time
raise ArgumentError.new("unexpected #{that.inspect}") unless that.time
self.time <=> that.time
end
end
end

it 'does not swallow Ruby errors on compareTo' do
queue = java.util.PriorityQueue.new(10)
queue.add t2 = SampleTask.new(2)
queue.add t1 = SampleTask.new(1)
begin
queue.add SampleTask.new(nil)
rescue ArgumentError => e
expect( e.message ).to start_with 'unexpected #<struct SampleTask'
else
fail 'compareTo did not raise'
end
expect( queue.first ).to be t1
end

end
10 changes: 10 additions & 0 deletions test/DefaultPackageClass.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
public class DefaultPackageClass {
public String foo() { return "foo"; }

public static int compareTo(org.jruby.RubyObject o1, org.jruby.RubyObject o2) {
if ( o1 == null ) { // JI always converts nil to null
o1 = (org.jruby.RubyNil) o2.getRuntime().getNil();
}
if ( o2 == null ) { // JI always converts nil to null
o2 = (org.jruby.RubyNil) o1.getRuntime().getNil();
}
return ((Comparable) o1).compareTo(o2);
}
}
70 changes: 70 additions & 0 deletions test/test_comparable.rb
Original file line number Diff line number Diff line change
@@ -82,4 +82,74 @@ def test_different
assert(@d != 3)
assert_raises(ArgumentError) { @d.between?(1, 3) }
end

def test_cmp_non_comparables
assert $!.nil?, "$! not nil but: #{$!.inspect}"
# should not raise errors :
assert_equal nil, 0 <=> Time.now
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal nil, nil <=> 42 if RUBY_VERSION > '1.9'
# 1.8.7 NoMethodError: undefined method `<=>' for nil:NilClass
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal nil, '42' <=> nil
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal nil, Object.new <=> nil if RUBY_VERSION > '1.9'
# 1.8.7 NoMethodError: undefined method `<=>' for #<Object:0xceb431e>
assert $!.nil?, "$! not nil but: #{$!.inspect}"

require 'bigdecimal'
big = BigDecimal.new '-10000000000'
assert_equal nil, big <=> Time.now
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal nil, big <=> nil
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal nil, big <=> Object.new
assert $!.nil?, "$! not nil but: #{$!.inspect}"

# Symbol only includes Comparable on 1.9
assert_equal nil, :sym <=> nil if RUBY_VERSION > '1.9'
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal nil, :sym <=> 42 if RUBY_VERSION > '1.9'
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal 0, :sym <=> :sym if RUBY_VERSION > '1.9'
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert (:zym <=> :sym) > 0 if RUBY_VERSION > '1.9'
assert $!.nil?, "$! not nil but: #{$!.inspect}"
end

def test_compareTo_non_comparables
assert $!.nil?, "$! not nil but: #{$!.inspect}"
# should not raise errors :
# TODO: RubyFixnum#compareTo breaks the contract of not-throwing
# with: TypeError: can't convert nil into Integer
#assert_equal 0, invokeCompareTo(0, Time.now)
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal 0, invokeCompareTo(nil, 42)
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal 0, invokeCompareTo(Object.new, nil)
assert $!.nil?, "$! not nil but: #{$!.inspect}"

require 'bigdecimal'
big = BigDecimal.new '-10000000000'
assert_equal 0, invokeCompareTo(big, Time.now)
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal 0, invokeCompareTo(big, Object.new)
assert $!.nil?, "$! not nil but: #{$!.inspect}"

assert_equal 0, invokeCompareTo(:sym, nil)
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal 0, invokeCompareTo(:sym, 42)
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert_equal 0, invokeCompareTo(:sym, :sym)
assert $!.nil?, "$! not nil but: #{$!.inspect}"
assert invokeCompareTo(:zym, :sym) > 0
assert $!.nil?, "$! not nil but: #{$!.inspect}"
end

private

def invokeCompareTo(o1, o2)
Java::DefaultPackageClass.compareTo(o1, o2)
end

end