Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Workaround JRuby incompatibilities #345

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 45 additions & 23 deletions lib/rspec/support/method_signature_verifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ def valid_non_kw_args?(positional_arg_count, optional_max_arg_count=positional_a
optional_max_arg_count <= max_non_kw_args
end

def classify_arity([email protected])
if arity < 0
# `~` inverts the one's complement and gives us the
# number of required args
@min_non_kw_args = ~arity
@max_non_kw_args = INFINITY
else
@min_non_kw_args = arity
@max_non_kw_args = arity
end
end

if RubyFeatures.optional_and_splat_args_supported?
def description
@description ||= begin
Expand Down Expand Up @@ -137,36 +149,46 @@ def unlimited_args?
false
end

def classify_parameters
arity = @method.arity
if arity < 0
# `~` inverts the one's complement and gives us the
# number of required args
@min_non_kw_args = ~arity
@max_non_kw_args = INFINITY
else
@min_non_kw_args = arity
@max_non_kw_args = arity
end
end
alias_method :classify_parameters, :classify_arity
end

INFINITY = 1 / 0.0
end

# Some versions of JRuby have a nasty bug we have to work around :(.
# https://github.com/jruby/jruby/issues/2816
if RSpec::Support::Ruby.jruby? &&
RubyFeatures.optional_and_splat_args_supported? &&
Class.new { attr_writer :foo }.instance_method(:foo=).parameters == []
if RSpec::Support::Ruby.jruby?
# JRuby has only partial support for UnboundMethod#parameters, so we fall back on using #arity
# https://github.com/jruby/jruby/issues/2816 and https://github.com/jruby/jruby/issues/2817
if RubyFeatures.optional_and_splat_args_supported? &&
Java::JavaLang::String.instance_method(:char_at).parameters == []

class MethodSignature < remove_const(:MethodSignature)
private
class MethodSignature < remove_const(:MethodSignature)
private

def classify_parameters
super
return unless @method.parameters == [] && @method.arity == 1
@max_non_kw_args = @min_non_kw_args = 1
def classify_parameters
super
if (arity = @method.arity) != 0 && @method.parameters.empty?
classify_arity(arity)
end
end
end
end

# JRuby used to always report -1 arity for Java proxy methods
if Java::JavaLang::String.instance_method(:char_at).arity == -1
class MethodSignature < remove_const(:MethodSignature)
private

def classify_parameters
super
return unless @method.arity == -1 && @method.owner.respond_to?(:java_class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is breaking the build for rspec/rspec-expectations, see https://travis-ci.org/rspec/rspec-expectations/jobs/351862257

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now that the method is supposed to work for Proc as well as Method, and #owner isn't available for Proc. The easiest way to handle this would simply to add @method.respond_to?(:owner) to the referenced early return. This will effectively disable the workaround for Proc, while retaining it for Method.

While the same problem exists for Proc, and you could observe it using the following construction,

Java::JavaLang::String.new.method(:char_at).to_proc.arity # => -1

I can't think of a workaround that would address this case, as I'm not aware of any JRuby API that allows you to figure out if a Proc is a direct wrapper around a Java method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add the proc fix for now, and add a pending spec demonstrating the problem for the scenario described, this will document our knowledge on this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened a PR with the proposed fix in #347

java_instance_methods = @method.owner.java_class.java_instance_methods
compatible_overloads = java_instance_methods.select do |java_method|
@method == @method.owner.instance_method(java_method.name)
end
if compatible_overloads.size == 1
classify_arity(compatible_overloads.first.arity)
end
end
end
end
end
Expand Down
18 changes: 18 additions & 0 deletions spec/rspec/support/method_signature_verifier_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,24 @@ def arity_block(_, &block); end
expect(valid_non_kw_args?(2)).to eq false
end
end

if Ruby.jruby?
describe 'a single-argument Java method' do
let(:test_method) { Java::JavaLang::String.instance_method(:char_at) }

it 'validates against a single argument' do
expect(valid_non_kw_args?(1)).to eq true
end

it 'fails validation against 0 arguments' do
expect(valid_non_kw_args?(0)).to eq false
end

it 'fails validation against 2 arguments' do
expect(valid_non_kw_args?(2)).to eq false
end
end
end
end

let(:fake_matcher) { Object.new }
Expand Down