Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Splat with method_missing inconsistent #2410

Closed
endofunky opened this issue Jan 2, 2015 · 10 comments
Closed

Splat with method_missing inconsistent #2410

endofunky opened this issue Jan 2, 2015 · 10 comments

Comments

@endofunky
Copy link

The behavior when using the splat operator together with method_missing appears to be different from MRI.

Example:

class A
  attr_reader :obj

  def initialize(*args)
    @obj = args
  end

  def method_missing(method, *args, &block)
    if obj.respond_to?(method)
      obj.__send__(method, *args, &block)
    else
      super
    end
  end
end

On MRI:

a = A.new(*A.new([1,2,3]), *A.new([3,4,5]))

puts a.inspect
# => #<A:0x007fcba58d07c0 @obj=[[1, 2, 3], [3, 4, 5]]>

puts a.obj.first.class.name
# => Array

On JRuby 9.0.0 SNAPSHOT:

a = A.new(*A.new([1,2,3]), *A.new([3,4,5]))

puts a.inspect
# => #<A:0x6e38921c @obj=[#<A:0x27c6e487 @obj=[[1, 2, 3]]>, #<A:0x38364841 @obj=[[3, 4, 5]]>]>

puts a.obj.first.class.name
# => A
@enebo enebo added this to the JRuby 9.0.0.0-pre1 milestone Jan 4, 2015
@endofunky endofunky changed the title Split with method_missing inconsistent Splat with method_missing inconsistent Jan 6, 2015
@headius
Copy link
Member

headius commented Jan 7, 2015

Confirmed in both interpreter and JIT. High priority.

Seems like it's not coercing to array, so perhaps it's checking if to_ary is defined before making the call when it should just go for it?

@headius
Copy link
Member

headius commented Jan 7, 2015

Yeah it seems to be an IR problem. If I log the method name in method_missing, I see it called twice for MRI and zero times for JRuby 9k. So splatting is not making the call it should in this place.

@subbuss You did work on splatting recently...perhaps take a look into this?

@subbuss
Copy link
Contributor

subbuss commented Jan 7, 2015

looking..

@subbuss
Copy link
Contributor

subbuss commented Jan 7, 2015

Seems like a bug in Helpers.arrayValue(..) else part of line 1862 .. it is unconditionally returning a new array instead of calling method_missing .. but since these helpers have been around for ever, I don't want to just fix it. But, fixing it there would fix it .. or maybe IRRuntimeHelpers:irSplat is not calling the right helper in Helpers.java. Is this good enough for you or @enebo to take this over?

@subbuss subbuss closed this as completed in fda9948 Jan 7, 2015
@subbuss
Copy link
Contributor

subbuss commented Jan 7, 2015

Please review my fix and close if it looks good. Will require new tests since this hasn't been covered by any suite so far?

@subbuss subbuss reopened this Jan 7, 2015
@subbuss
Copy link
Contributor

subbuss commented Jan 7, 2015

fwiw, I tested with the above snippet and also by making method_missing return a Fixnum and both MRI and 9k now raise an error.

@endofunky
Copy link
Author

I just saw the ``to_a' did not return Array` error you're raising and had a look what MRI does in that case.

class A
  attr_reader :obj

  def initialize(*args)
    @obj = args
  end

  def method_missing(method, *args, &block)
    if obj.respond_to?(method)
      obj.__send__(method, *args, &block)
    else
      super
    end
  end
end

class B < Array
  def to_a
    nil
  end
end

b = B.new
b.push 3
b.push 4
b.push 5

a = A.new(*A.new([1,2,3]), *A.new(b))

puts b.to_a.inspect
puts b.class.name
puts a.inspect

MRI output:

nil
B
#<A:0x007fd49809d4a8 @obj=[[1, 2, 3], [3, 4, 5]]>

I didn't expect this to work. May be this is actually undefined behavior? Or am I missing something here?

@endofunky
Copy link
Author

Actually have a one more. Given to_a returns nil and B isn't a subclass of Array:

class A
  attr_reader :obj

  def initialize(*args)
    @obj = args
  end

  def method_missing(method, *args, &block)
    puts "called"
    if obj.respond_to?(method)
      obj.__send__(method, *args, &block)
    else
      super
    end
  end
end

class B
  def to_a
    nil
  end
end

b = B.new

puts b.to_a.inspect
puts b.class.name

a = A.new(*A.new([1,2,3]), *A.new(b))

puts a.inspect

MRI output:

nil
B
called
called
#<A:0x007fed27215a40 @obj=[[1, 2, 3], #<B:0x007fed27215db0>]>

I added in a puts to make sure method_missing is even getting called. The above result is the same even if B#to_a returns any other type, such as Fixnum, or if the method isn't defined at all.

@subbuss
Copy link
Contributor

subbuss commented Jan 7, 2015

jruby current master returns identical output as MRI in both cases.

[subbu@earth api] jruby -X-C /tmp/bug.rb
nil
B
#<A:0x4da8846f @obj=[[1, 2, 3], [3, 4, 5]]>

and

[subbu@earth api] jruby -X-C /tmp/bug.rb
nil
B
called
called
#<A:0x126f5e8d @obj=[[1, 2, 3], #<B:0x78a6dc8c>]>

When to_a returns nil, the default behavior is to return an empty array. See the jruby commit that fixes this bug to see the code.

@headius
Copy link
Member

headius commented Jan 14, 2015

It sounds like this one is fixed. @tobiassvn If you are seeing the original reported behavior failing, please reopen and demonstrate. If you are seeing related but different behaviors failing, file a new issue.

@headius headius closed this as completed Jan 14, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants