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: 1da1c321d807
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 3e4dd4e96155
Choose a head ref
  • 9 commits
  • 9 files changed
  • 1 contributor

Commits on Apr 17, 2016

  1. Copy the full SHA
    a7cdec8 View commit details
  2. Copy the full SHA
    381e179 View commit details
  3. [ji] use (new) include(module) + avoid double checking module type in…

    … iface template
    
    ... and cleanup some comments
    kares committed Apr 17, 2016
    Copy the full SHA
    4067cf5 View commit details
  4. [ji] remove re-def of === method on Ruby class implementing an iface

    ... can't seem to find a useful case that wouldn't work without it
     found a lot of weirdness this has been causing - diff comparing against arbitrary Java object's interfaces
    kares committed Apr 17, 2016
    Copy the full SHA
    1f21e82 View commit details
  5. Copy the full SHA
    c1620a8 View commit details
  6. Copy the full SHA
    78bceb1 View commit details
  7. Copy the full SHA
    8ec0bf4 View commit details
  8. Copy the full SHA
    3ca3143 View commit details
  9. Copy the full SHA
    3e4dd4e View commit details
21 changes: 16 additions & 5 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -2493,19 +2493,30 @@ public RubyModule include(IRubyObject[] modules) {
ThreadContext context = getRuntime().getCurrentContext();
// MRI checks all types first:
for (int i = modules.length; --i >= 0; ) {
IRubyObject obj = modules[i];
if (!obj.isModule()) {
throw context.runtime.newTypeError(obj, context.runtime.getModule());
IRubyObject module = modules[i];
if ( ! module.isModule() ) {
throw context.runtime.newTypeError(module, context.runtime.getModule());
}
}
for (int i = modules.length - 1; i >= 0; i--) {
modules[i].callMethod(context, "append_features", this);
modules[i].callMethod(context, "included", this);
IRubyObject module = modules[i];
module.callMethod(context, "append_features", this);
module.callMethod(context, "included", this);
}

return this;
}

@JRubyMethod(name = "include", required = 1) // most common path: include Enumerable
public RubyModule include(ThreadContext context, IRubyObject module) {
if ( ! module.isModule() ) {
throw context.runtime.newTypeError(module, context.runtime.getModule());
}
module.callMethod(context, "append_features", this);
module.callMethod(context, "included", this);
return this;
}

@JRubyMethod(name = "included", required = 1, visibility = PRIVATE)
public IRubyObject included(ThreadContext context, IRubyObject other) {
return context.runtime.getNil();
Original file line number Diff line number Diff line change
@@ -42,6 +42,7 @@ public static RubyModule createJavaInterfaceTemplateModule(ThreadContext context
return JavaInterfaceTemplate;
}

@Deprecated // not used - should go away in >= 9.2
// not intended to be called directly by users (private)
// OLD TODO from Ruby code:
// This should be implemented in JavaClass.java, where we can
@@ -182,37 +183,14 @@ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {

// If we hold a Java object, we need a java_class accessor
clazz.addMethod("java_class", new JavaClassAccessor(clazz));

// Because we implement Java interfaces now, we need a new === that's
// aware of those additional "virtual" supertypes
if (!clazz.searchMethod("===").isUndefined()) {
clazz.defineAlias("old_eqq", "===");
clazz.addMethod("===", new JavaMethodOne(clazz, Visibility.PUBLIC) {

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg) {
// TODO: WRONG - get interfaces from class
if (arg.respondsTo("java_object")) {
IRubyObject interfaces = self.getMetaClass().getInstanceVariables().getInstanceVariable("@java_interfaces");
assert interfaces instanceof RubyArray : "interface list was not an array";

return context.runtime.newBoolean(((RubyArray) interfaces).op_diff(
((JavaObject) arg.dataGetStruct()).java_class().interfaces()).equals(RubyArray.newArray(context.runtime)));
} else {
return Helpers.invoke(context, self, "old_eqq", arg);
}
}
});
}
}

// Now we add an "implement" and "implement_all" methods to the class
if ( ! clazz.isMethodBound("implement", false) ) {
final RubyClass singleton = clazz.getSingletonClass();

// implement is called to force this class to create stubs for all
// methods in the given interface, so they'll show up in the list
// of methods and be invocable without passing through method_missing
// implement is called to force this class to create stubs for all methods in the given interface,
// so they'll show up in the list of methods and be invocable without passing through method_missing
singleton.addMethod("implement", new JavaMethodOne(clazz, Visibility.PRIVATE) {

@Override
@@ -225,8 +203,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
}
});

// implement all forces implementation of all interfaces we intend
// for this class to implement
// implement all forces implementation of all interfaces we intend for this class to implement
singleton.addMethod("implement_all", new JavaMethodOne(clazz, Visibility.PRIVATE) {

@Override
@@ -243,12 +220,17 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
}
}

private static class InterfaceProxyFactory extends JavaMethodN { // __jcreate! and __jcreate_meta!
private static final class InterfaceProxyFactory extends JavaMethodN { // __jcreate! and __jcreate_meta!

InterfaceProxyFactory(final RubyClass clazz) { super(clazz, Visibility.PRIVATE); }

@Override // will be called with zero args
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
return newInterfaceProxy(self);
}

@Override
public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
return newInterfaceProxy(self);
}

@@ -295,8 +277,8 @@ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
private static IRubyObject newInterfaceProxy(final IRubyObject self) {
final RubyClass current = self.getMetaClass();
// construct the new interface impl and set it into the object
IRubyObject newObject = Java.newInterfaceImpl(self, Java.getInterfacesFromRubyClass(current));
JavaUtilities.set_java_object(self, self, newObject);
JavaObject newObject = Java.newInterfaceImpl(self, Java.getInterfacesFromRubyClass(current));
JavaUtilities.set_java_object(self, self, newObject); // self.dataWrapStruct(newObject);
return newObject;
}

@@ -305,9 +287,9 @@ private static void appendFeaturesToModule(ThreadContext context, final IRubyObj
// included together. make it so.
final Ruby runtime = context.runtime;

final IRubyObject java_class = module.getInstanceVariables().getInstanceVariable("@java_class");
// not allowed for existing Java interface modules
if (module.getInstanceVariables().hasInstanceVariable("@java_class") &&
module.getInstanceVariables().getInstanceVariable("@java_class").isTrue()) {
if (java_class != null && java_class.isTrue()) {
throw runtime.newTypeError("can not add Java interface to existing Java interface");
}

@@ -341,7 +323,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
}

final RubyModule target = (RubyModule) arg;
target.include( getInterfaceModules(self).toJavaArray() );
target.include( getInterfaceModules(self).toJavaArrayMaybeUnsafe() );

return Helpers.invokeAs(context, clazz.getSuperClass(), self, name, arg, block);
}
@@ -350,11 +332,8 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz

@JRubyMethod
public static IRubyObject extended(ThreadContext context, IRubyObject self, IRubyObject object) {
if ( ! (self instanceof RubyModule) ) {
throw context.runtime.newTypeError(self, context.runtime.getModule());
}
RubyClass singleton = object.getSingletonClass();
singleton.include(new IRubyObject[] { self });
singleton.include(context, self);
return singleton;
}

@@ -376,11 +355,11 @@ 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
// RubySymbol implements a Java compareTo thus will always work
}

RubyClass implClass = RubyClass.newClass(runtime, runtime.getObject());
implClass.include(new IRubyObject[] { self });
implClass.include(context, self);

final IRubyObject implObject = implClass.callMethod(context, "new");

@@ -389,7 +368,7 @@ public static IRubyObject impl(ThreadContext context, IRubyObject self, IRubyObj
return implObject;
}

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

private final IRubyObject[] methodNames; // RubySymbol[]
private final Block implBlock;
@@ -456,8 +435,8 @@ private static JavaClass getJavaClassForInterface(final IRubyObject module) {
return (JavaClass) module.getInstanceVariables().getInstanceVariable("@java_class");
}

private static RubyArray getJavaInterfaces(final IRubyObject module) {
return (RubyArray) module.getInstanceVariables().getInstanceVariable("@java_interfaces");
private static RubyArray getJavaInterfaces(final IRubyObject clazz) {
return (RubyArray) clazz.getInstanceVariables().getInstanceVariable("@java_interfaces");
}

private static RubyArray getInterfaceModules(final IRubyObject module) {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/javasupport/Java.java
Original file line number Diff line number Diff line change
@@ -1143,7 +1143,7 @@ public static IRubyObject new_proxy_instance2(IRubyObject recv, final IRubyObjec
return newInterfaceImpl(wrapper, interfaces);
}

public static IRubyObject newInterfaceImpl(final IRubyObject wrapper, Class[] interfaces) {
public static JavaObject newInterfaceImpl(final IRubyObject wrapper, Class[] interfaces) {
final Ruby runtime = wrapper.getRuntime();

final int length = interfaces.length;
36 changes: 18 additions & 18 deletions core/src/main/java/org/jruby/javasupport/JavaObject.java
Original file line number Diff line number Diff line change
@@ -111,12 +111,12 @@ public static IRubyObject wrap(final ThreadContext context,
}

@Override
public Class<?> getJavaClass() {
public final Class<?> getJavaClass() {
Object dataStruct = dataGetStruct();
return dataStruct != null ? dataStruct.getClass() : Void.TYPE;
}

public Object getValue() {
public final Object getValue() {
return dataGetStruct();
}

@@ -253,50 +253,50 @@ public IRubyObject is_java_proxy() {

@JRubyMethod(name = "synchronized")
public final IRubyObject ruby_synchronized(ThreadContext context, Block block) {
Object lock = getValue();
final Object lock = getValue();
synchronized (lock != null ? lock : NULL_LOCK) {
return block.yield(context, null);
}
}

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

@JRubyMethod
public IRubyObject marshal_dump() {
public IRubyObject marshal_dump(ThreadContext context) {
if (Serializable.class.isAssignableFrom(getJavaClass())) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);

oos.writeObject(getValue());
new ObjectOutputStream(baos).writeObject(getValue());

return getRuntime().newString(new ByteList(baos.toByteArray()));
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
return context.runtime.newString(new ByteList(baos.toByteArray(), false));
}
catch (IOException ex) {
throw context.runtime.newIOErrorFromException(ex);
}
} else {
throw getRuntime().newTypeError("no marshal_dump is defined for class " + getJavaClass());
}
throw context.runtime.newTypeError("no marshal_dump is defined for class " + getJavaClass());
}

@JRubyMethod
public IRubyObject marshal_load(ThreadContext context, IRubyObject str) {
try {
ByteList byteList = str.convertToString().getByteList();
ByteArrayInputStream bais = new ByteArrayInputStream(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());
ObjectInputStream ois = new JRubyObjectInputStream(context.runtime, bais);

dataWrapStruct(ois.readObject());
dataWrapStruct(new JRubyObjectInputStream(context.runtime, bais).readObject());

return this;
} catch (IOException ioe) {
throw context.runtime.newIOErrorFromException(ioe);
} catch (ClassNotFoundException cnfe) {
throw context.runtime.newTypeError("Class not found unmarshaling Java type: " + cnfe.getLocalizedMessage());
}
catch (IOException ex) {
throw context.runtime.newIOErrorFromException(ex);
}
catch (ClassNotFoundException ex) {
throw context.runtime.newTypeError("Class not found unmarshaling Java type: " + ex.getLocalizedMessage());
}
}

66 changes: 66 additions & 0 deletions spec/java_integration/interfaces/comparison_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require File.dirname(__FILE__) + "/../spec_helper"

java_import 'java_integration.fixtures.SingleMethodInterface'
java_import 'java_integration.fixtures.BeanLikeInterface'

describe 'interface comparison' do

it 'compares against interface like with an included module' do
value_holder1 = Class.new do
include SingleMethodInterface
def initialize(val)
@value = val
end
def callIt
@value
end
end

expect( value_holder1 < SingleMethodInterface ).to be true
expect( value_holder1 === SingleMethodInterface ).to be false
expect( SingleMethodInterface === value_holder1 ).to be false

val = value_holder1.new(111)
expect( val.class < SingleMethodInterface ).to be true
expect( SingleMethodInterface === val.class ).to be false
expect( SingleMethodInterface === val ).to be true
expect( val === SingleMethodInterface ).to be false
end

it 'compares with interface like with an included module' do
mod = Module.new { include Enumerable }
val = Class.new { include mod }.new

expect( val.class < Enumerable ).to be true
expect( Enumerable === val.class ).to be false
expect( val.class === Enumerable ).to be false
expect( Enumerable === val ).to be true
expect( val === Enumerable ).to be false
expect( val === val.class.new ).to be false
expect( val === mod ).to be false

mod = Module.new do
include BeanLikeInterface
end
sup = Class.new do
include mod, java.lang.Runnable, java.lang.Iterable # includes Enumerable
def each; yield nil end
end
child = Class.new(sup) { include java.lang.Cloneable; def getValue; 1; end }
obj = child.new

expect( child < BeanLikeInterface ).to be true
expect( obj.class < java.lang.Runnable ).to be true
expect( java.lang.Cloneable === obj ).to be true
expect( java.lang.Iterable === obj ).to be true
expect( BeanLikeInterface === obj ).to be true
expect( Enumerable === obj ).to be true
expect( obj === BeanLikeInterface ).to be false

expect( obj === obj ).to be true
expect( obj === sup.new ).to be false
expect( obj === java.lang.Runnable.impl {} ).to be false # was true in < 9.1 !
expect( obj === java.util.HashSet.new ).to be false # was true in < 9.1 !
end

end
5 changes: 3 additions & 2 deletions spec/java_integration/interfaces/implementation_spec.rb
Original file line number Diff line number Diff line change
@@ -43,8 +43,9 @@ def callIt
end

it "should be kind_of? the interface" do
expect(@value_holder1.new(1)).to be_kind_of(SingleMethodInterface)
expect(SingleMethodInterface === @value_holder1.new(1)).to be true
expect( @value_holder1.new(1) ).to be_kind_of SingleMethodInterface
expect( @value_holder1.new(1) ).to be_a SingleMethodInterface
expect( SingleMethodInterface === @value_holder1.new(1) ).to be true
end

it "should be implemented with 'include InterfaceClass'" do
84 changes: 84 additions & 0 deletions test/jruby/test_include.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
require 'test/unit'

class TestInclude < Test::Unit::TestCase
module X ; end

class Q
def foo arg = []
arg << :Q
end
end

class Y < Q ; include X end

module A
def foo arg = []
arg << :A ; super
end
end

module X ; include A end

module Z
def foo arg = []
arg << :Z ; super
end
end

class Y ; include Z end

class Y ; include X end

def test_include_order
pend '[:A, :Z, :Q] on JRuby GH-1938' if defined? JRUBY_VERSION
assert_equal [:Z, :A, :Q], Y.new.foo
end

module M1 ; V = 123 end
module M2 ; V = 456 end

class Foo
include M1

def self.get; return V end
end

def test_including_module_busts_constant_caches
assert_equal 123, Foo.get
Foo.send(:include, M2)
assert_equal 456, Foo.get
end

class Bar
include M1, M2
end

def test_multi_include
assert_equal 123, Bar::V
assert_equal Bar, Bar.send(:include)
assert_equal Bar, Bar.send(:include, Comparable, Enumerable)

assert_equal Object, Object.include
assert_equal Object, Object.include(*[])
end

# JRUBY-3036
def test_included_does_not_hit_each_class
ObjectSpace.each_object(Class) do |cls|
if cls < Top
assert cls.top
end
end
end

end

module IncludedTestCaseModule; end

class Top
def self.top; true; end
end

class Next < Top
include IncludedTestCaseModule
end
22 changes: 0 additions & 22 deletions test/jruby/test_included_in_object_space.rb

This file was deleted.

25 changes: 0 additions & 25 deletions test/jruby/test_including_module_busts_constant_caches.rb

This file was deleted.