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: 98918e40a5ca
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 43e9bb1a5be6
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Oct 7, 2016

  1. Make all 1.8-era convert methods do 1.9 logic.

    The 1.8 methods all blindly call respond_to? which breaks on any
    BasicObject-based targets as in #4210. This patch modifies the
    remaining 1.8 methods in TypeConverter to just call the 1.9 logic,
    since there are no cases where MRI blindly calls respond_to? for
    implicit conversions anymore.
    
    Fixes #4210.
    headius committed Oct 7, 2016

    Verified

    This commit was signed with the committer’s verified signature.
    headius Charles Oliver Nutter
    Copy the full SHA
    d97bf85 View commit details
  2. Copy the full SHA
    43e9bb1 View commit details
Showing with 42 additions and 63 deletions.
  1. +22 −63 core/src/main/java/org/jruby/util/TypeConverter.java
  2. +6 −0 spec/ruby/language/array_spec.rb
  3. +8 −0 spec/ruby/language/hash_spec.rb
  4. +6 −0 spec/ruby/language/method_spec.rb
85 changes: 22 additions & 63 deletions core/src/main/java/org/jruby/util/TypeConverter.java
Original file line number Diff line number Diff line change
@@ -62,37 +62,11 @@ public class TypeConverter {
* @return the converted value
*/
public static IRubyObject convertToType(IRubyObject obj, RubyClass target, String convertMethod, boolean raise) {
if ( ! obj.respondsTo(convertMethod) ) {
switch (convertMethod) {
case "to_int" :
case "to_ary" :
case "to_str" :
case "to_sym" :
case "to_hash" :
case "to_proc" :
case "to_io" : return handleImplicitlyUncoercibleObject(raise, obj, target);
}
return handleUncoercibleObject(raise, obj, target);
}

return obj.callMethod(obj.getRuntime().getCurrentContext(), convertMethod);
return convertToType19(obj, target, convertMethod, raise);
}

public static IRubyObject convertToType(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites, boolean raise) {
if (!sites.respond_to_X.respondsTo(context, obj, obj, true)) {
switch (sites.methodName) {
case "to_int" :
case "to_ary" :
case "to_str" :
case "to_sym" :
case "to_hash" :
case "to_proc" :
case "to_io" : return handleImplicitlyUncoercibleObject(raise, obj, target);
}
return handleUncoercibleObject(raise, obj, target);
}

return sites.site.call(context, obj, obj);
return convertToType19(context, obj, target, sites, raise);
}

/**
@@ -134,10 +108,7 @@ public static IRubyObject convertToType19(ThreadContext context, IRubyObject obj
* @return the converted value
*/
public static IRubyObject convertToType(IRubyObject obj, RubyClass target, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = convertToType(obj, target, convertMethod, true);
if (!target.isInstance(val)) throw obj.getRuntime().newTypeError(obj.getMetaClass() + "#" + convertMethod + " should return " + target.getName());
return val;
return convertToType19(obj, target, convertMethod);
}

/**
@@ -149,10 +120,7 @@ public static IRubyObject convertToType(IRubyObject obj, RubyClass target, Strin
* @return the converted value
*/
public static IRubyObject convertToType(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites) {
if (target.isInstance(obj)) return obj;
IRubyObject val = convertToType(context, obj, target, sites, true);
if (!target.isInstance(val)) throw obj.getRuntime().newTypeError(obj.getMetaClass() + "#" + sites.methodName + " should return " + target.getName());
return val;
return convertToType19(context, obj, target, sites);
}

/**
@@ -265,11 +233,7 @@ public static RubySymbol checkID(Ruby runtime, String name) {
* @return the converted value
*/
public static IRubyObject convertToTypeWithCheck(IRubyObject obj, RubyClass target, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = TypeConverter.convertToType(obj, target, convertMethod, false);
if (val.isNil()) return val;
if (!target.isInstance(val)) throw obj.getRuntime().newTypeError(obj.getMetaClass() + "#" + convertMethod + " should return " + target.getName());
return val;
return convertToTypeWithCheck19(obj, target, convertMethod);
}

/**
@@ -282,10 +246,25 @@ public static IRubyObject convertToTypeWithCheck(IRubyObject obj, RubyClass targ
* @return the converted value
*/
public static IRubyObject convertToTypeWithCheck(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites) {
return convertToTypeWithCheck19(context, obj, target, sites);
}

/**
* Higher level conversion utility similar to convertToType but it can throw an
* additional TypeError during conversion (MRI: rb_check_convert_type).
*
* @param obj the object to convert
* @param target is the type we are trying to convert to
* @param convertMethod is the method to be called to try and convert to targeType
* @return the converted value
*/
public static IRubyObject convertToTypeWithCheck19(IRubyObject obj, RubyClass target, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = TypeConverter.convertToType(context, obj, target, sites, false);
IRubyObject val = TypeConverter.convertToType19(obj, target, convertMethod, false);
if (val.isNil()) return val;
if (!target.isInstance(val)) throw obj.getRuntime().newTypeError(obj.getMetaClass() + "#" + sites.methodName + " should return " + target.getName());
if (!target.isInstance(val)) {
throw newTypeError(obj, target, convertMethod, val);
}
return val;
}

@@ -565,24 +544,4 @@ public static IRubyObject convertToTypeOrRaise(IRubyObject obj, RubyClass target
if (!target.isInstance(val)) throw obj.getRuntime().newTypeError(obj.getMetaClass() + "#" + convertMethod + " should return " + target.getName());
return val;
}

/**
* Higher level conversion utility similar to convertToType but it can throw an
* additional TypeError during conversion (MRI: rb_check_convert_type).
*
* @param obj the object to convert
* @param target is the type we are trying to convert to
* @param convertMethod is the method to be called to try and convert to targeType
* @return the converted value
*/
@Deprecated
public static IRubyObject convertToTypeWithCheck19(IRubyObject obj, RubyClass target, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = TypeConverter.convertToType19(obj, target, convertMethod, false);
if (val.isNil()) return val;
if (!target.isInstance(val)) {
throw newTypeError(obj, target, convertMethod, val);
}
return val;
}
}
6 changes: 6 additions & 0 deletions spec/ruby/language/array_spec.rb
Original file line number Diff line number Diff line change
@@ -132,6 +132,12 @@
[1, *obj].should == [1, obj]
end

it "when applied to a BasicObject coerces it to Array if it respond_to?(:to_a)" do
obj = BasicObject.new
def obj.to_a; [2, 3, 4]; end
[1, *obj].should == [1, 2, 3, 4]
end

it "can be used before other non-splat elements" do
a = [1, 2]
[0, *a, 3].should == [0, 1, 2, 3]
8 changes: 8 additions & 0 deletions spec/ruby/language/hash_spec.rb
Original file line number Diff line number Diff line change
@@ -99,6 +99,14 @@
{a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4}
end

it "expands a BasicObject using ** into the containing Hash literal initialization" do
h = BasicObject.new
def h.to_hash; {:b => 2, :c => 3}; end
{**h, a: 1}.should == {b: 2, c: 3, a: 1}
{a: 1, **h}.should == {a: 1, b: 2, c: 3}
{a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4}
end

ruby_version_is ""..."2.2" do
it "expands an '**{}' element with containing Hash literal keys taking precedence" do
{a: 1, **{a: 2, b: 3, c: 1}, c: 3}.should == {a: 1, b: 3, c: 3}
6 changes: 6 additions & 0 deletions spec/ruby/language/method_spec.rb
Original file line number Diff line number Diff line change
@@ -1021,6 +1021,12 @@ def m(*a, **k) [a, k] end
m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
m({a: 1}, {}).should == [[{a: 1}], {}]
m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]

bo = BasicObject.new
def bo.to_a; [1, 2, 3]; end
def bo.to_hash; {:b => 2, :c => 3}; end

m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
end

evaluate <<-ruby do