tfpt review "/shelveset:error;REDMOND\sborde" Microsoft.Scripting: Change in DefaultBinder is to make the binder exception processing logic be extensible so that languages can customize the type of exception thrown. IronRuby: Improves error reporting in IronRuby by checking the results of the bind failure. This allowed many Math tests to be enabled. Also, added a DefaultProtocol for to_f. This allowed more Math tests to be enabled as you can now pass the string "1.0" to a function expecting a "Float". I have added test cases for the Float conversion rules in core\math\acos_spec.rb. I have not copied this to the other specs as that would be hard to maintain. We will look at factoring such conversion checks into a utility library so that they will be much easier to use. Fixes some String bugs - String.#inspect on string subtype should return a string, and passing wrong argument types to Strings#[]= should cause TypeError instead of ArgumentError. This is where I started and it put me in the direction of the fixes above. Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext? -------------- next part -------------- A non-text attachment was scrubbed... Name: error.diff Type: application/octet-stream Size: 76269 bytes Desc: error.diff URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20090123/fa79af49/attachment-0001.obj>
it "coerces string argument with Float() without calling singleton
to_f" do
    s = MathSpecs.string_with_singleton_to_f("0.5", 0.5)
    Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  
  it "coerces string argument with Float() without calling
String#to_f" do
    MathSpecs.hijack_String_to_f
    begin
      Math.acos("0.5").should be_close(Math.acos(0.5), TOLERANCE)
    ensure
      MathSpecs.reset_String_to_f
    end
  end
These can be rewritten with mocks instead of specialized classes. 
it "coerces string argument with Float() without calling singleton
to_f" do
  s = "0.5"
  s.should_not_receive(:to_f)
  Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
End
The second spec is really testing method dispatch, and otherwise is the same
thing as this one. Same with:
  it "raises an ArgumentError if the singleton string cannot be directly
coerced with Float()" do
    s = MathSpecs.string_with_singleton_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
   
  it "raises an ArgumentError if the string subclass cannot be directly
coerced with Float()" do
    s = MathSpecs.string_subclass_with_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  End
JD
-----Original Message-----
From: Shri Borde 
Sent: Friday, January 23, 2009 2:04 PM
To: IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: Code Review: Better error messages
  tfpt review "/shelveset:error;REDMOND\sborde"
Microsoft.Scripting:
  Change in DefaultBinder is to make the binder exception processing logic be
extensible so that languages can customize the type of exception thrown.
IronRuby:
  
  Improves error reporting in IronRuby by checking the results of the bind
failure. This allowed many Math tests to be enabled.
  
  Also, added a DefaultProtocol for to_f. This allowed more Math tests to be
enabled as you can now pass the string "1.0" to a function expecting a
"Float". I have added test cases for the Float conversion rules in
core\math\acos_spec.rb. I have not copied this to the other specs as that would
be hard to maintain. We will look at factoring such conversion checks into a
utility library so that they will be much easier to use.
  
  Fixes some String bugs - String.#inspect on string subtype should return a
string, and passing wrong argument types to Strings#[]= should cause TypeError
instead of ArgumentError. This is where I started and it put me in the direction
of the fixes above.
  
  Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext?
Jim, using should_not_receive sounds like a good idea. Will do the change.
But we still need all those specs including the one using hijack_String_to_f. It
is not testing method dispatch. Ruby method dispatch only controls which method
body will be called. It does not do argument conversions. That is handled by the
target method which is free to use to_f to convert the argument to a float, but
is not required to. The specs below will ensure that the library method does
follow the right convention/protocol for converting the argument to a float.
Does that make sense?
Also, I had this comment:
/// <summary>
/// An overload with a block should get precedence. However,
Microsoft.Scripting.Action.Calls.MethodBinder
/// gives precedence to overloads with a matching number of arguments as the
actual arguments. Consider Object#eval
/// which effectively has the following two overloads in the Ruby sense.
///     obj.instance_eval(string [, filename [, lineno]] ) => obj
///     obj.instance_eval {| | block } => obj
/// Consider this call:
///     obj.instance_eval(2, 3) { }
/// This should match with the second Ruby overload, and report an ArgumentError
because of the mismatch in the number of arguments.
/// However, the MethodBinder will prefer the first Ruby overload as it has a
matching number of arguments, and will report
/// a type-mismatch instead.
/// Filterting down the overload list upfront yields better errors.
/// </summary>
Tomas pointed out that this filtering was incorrect. Tomas, it did cause test
failures (My last test pass had this filtering done in error-handling after the
MethodBinder did its stuff).
However, overloads do get precedence in some cases like the instance_eval
example in the comment above, and also with Array#fill as shown below. There is
a RubySpec test for Array#fill, but we have a tag because we get it wrong. I
agree with Tomas that this will need to be handled more carefully, and we might
need to have an attribute on the library methods indicating whether the block
gets precedence, or whether the argument count gets precedence in selecting the
method overload. I will undo my change for now.
http://ruby-doc.org/core/classes/Array.html
array.fill(obj) ? array
array.fill(obj, start [, length]) ? array
array.fill(obj, range ) ? array
array.fill {|index| block } ? array
array.fill(start [, length] ) {|index| block } ? array
array.fill(range) {|index| block } ? array
MRI
irb(main):005:0> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
ArgumentError: wrong number of arguments (3 for 2)
IronRuby>>> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
=> [1, 1, 1]
Thanks,
Shri
-----Original Message-----
From: Jim Deville
Sent: Friday, January 23, 2009 3:05 PM
To: Shri Borde; IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
it "coerces string argument with Float() without calling singleton
to_f" do
    s = MathSpecs.string_with_singleton_to_f("0.5", 0.5)
    Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  it "coerces string argument with Float() without calling
String#to_f" do
    MathSpecs.hijack_String_to_f
    begin
      Math.acos("0.5").should be_close(Math.acos(0.5), TOLERANCE)
    ensure
      MathSpecs.reset_String_to_f
    end
  end
These can be rewritten with mocks instead of specialized classes.
it "coerces string argument with Float() without calling singleton
to_f" do
  s = "0.5"
  s.should_not_receive(:to_f)
  Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
End
The second spec is really testing method dispatch, and otherwise is the same
thing as this one. Same with:
  it "raises an ArgumentError if the singleton string cannot be directly
coerced with Float()" do
    s = MathSpecs.string_with_singleton_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  it "raises an ArgumentError if the string subclass cannot be directly
coerced with Float()" do
    s = MathSpecs.string_subclass_with_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  End
JD
-----Original Message-----
From: Shri Borde
Sent: Friday, January 23, 2009 2:04 PM
To: IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: Code Review: Better error messages
  tfpt review "/shelveset:error;REDMOND\sborde"
Microsoft.Scripting:
  Change in DefaultBinder is to make the binder exception processing logic be
extensible so that languages can customize the type of exception thrown.
IronRuby:
  Improves error reporting in IronRuby by checking the results of the bind
failure. This allowed many Math tests to be enabled.
  Also, added a DefaultProtocol for to_f. This allowed more Math tests to be
enabled as you can now pass the string "1.0" to a function expecting a
"Float". I have added test cases for the Float conversion rules in
core\math\acos_spec.rb. I have not copied this to the other specs as that would
be hard to maintain. We will look at factoring such conversion checks into a
utility library so that they will be much easier to use.
  Fixes some String bugs - String.#inspect on string subtype should return a
string, and passing wrong argument types to Strings#[]= should cause TypeError
instead of ArgumentError. This is where I started and it put me in the direction
of the fixes above.
  Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext?
The question is whether we need to copy the exact error reporting for argument
mismatch, or whether best effort approach isn''t good enough. The
advantage of the current behavior is a simpler binder. We can certainly add
attributes that will prioritize some overloads but it would make the binder more
complicated. I don''t see a scenario where it makes sense for an app to
catch ArgumentErrors and depend on it. Array#fill behavior might also be
considered a bug in MRI.
Tomas
-----Original Message-----
http://ruby-doc.org/core/classes/Array.html
array.fill(obj) ? array
array.fill(obj, start [, length]) ? array
array.fill(obj, range ) ? array
array.fill {|index| block } ? array
array.fill(start [, length] ) {|index| block } ? array
array.fill(range) {|index| block } ? array
MRI
irb(main):005:0> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
ArgumentError: wrong number of arguments (3 for 2)
IronRuby>>> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
=> [1, 1, 1]
Thanks,
Shri
-----Original Message-----
From: Jim Deville
Sent: Friday, January 23, 2009 3:05 PM
To: Shri Borde; IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
it "coerces string argument with Float() without calling singleton
to_f" do
    s = MathSpecs.string_with_singleton_to_f("0.5", 0.5)
    Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  it "coerces string argument with Float() without calling
String#to_f" do
    MathSpecs.hijack_String_to_f
    begin
      Math.acos("0.5").should be_close(Math.acos(0.5), TOLERANCE)
    ensure
      MathSpecs.reset_String_to_f
    end
  end
These can be rewritten with mocks instead of specialized classes.
it "coerces string argument with Float() without calling singleton
to_f" do
  s = "0.5"
  s.should_not_receive(:to_f)
  Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
End
The second spec is really testing method dispatch, and otherwise is the same
thing as this one. Same with:
  it "raises an ArgumentError if the singleton string cannot be directly
coerced with Float()" do
    s = MathSpecs.string_with_singleton_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  it "raises an ArgumentError if the string subclass cannot be directly
coerced with Float()" do
    s = MathSpecs.string_subclass_with_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  End
JD
-----Original Message-----
From: Shri Borde
Sent: Friday, January 23, 2009 2:04 PM
To: IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: Code Review: Better error messages
  tfpt review "/shelveset:error;REDMOND\sborde"
Microsoft.Scripting:
  Change in DefaultBinder is to make the binder exception processing logic be
extensible so that languages can customize the type of exception thrown.
IronRuby:
  Improves error reporting in IronRuby by checking the results of the bind
failure. This allowed many Math tests to be enabled.
  Also, added a DefaultProtocol for to_f. This allowed more Math tests to be
enabled as you can now pass the string "1.0" to a function expecting a
"Float". I have added test cases for the Float conversion rules in
core\math\acos_spec.rb. I have not copied this to the other specs as that would
be hard to maintain. We will look at factoring such conversion checks into a
utility library so that they will be much easier to use.
  Fixes some String bugs - String.#inspect on string subtype should return a
string, and passing wrong argument types to Strings#[]= should cause TypeError
instead of ArgumentError. This is where I started and it put me in the direction
of the fixes above.
  Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext?
Agreed, this is a negative scenario and exact error reporting is not a priority
in such contrived scenarios.
-----Original Message-----
From: Tomas Matousek 
Sent: Saturday, January 24, 2009 3:03 PM
To: Shri Borde; Jim Deville; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
The question is whether we need to copy the exact error reporting for argument
mismatch, or whether best effort approach isn''t good enough. The
advantage of the current behavior is a simpler binder. We can certainly add
attributes that will prioritize some overloads but it would make the binder more
complicated. I don''t see a scenario where it makes sense for an app to
catch ArgumentErrors and depend on it. Array#fill behavior might also be
considered a bug in MRI.
Tomas
-----Original Message-----
http://ruby-doc.org/core/classes/Array.html
array.fill(obj) ? array
array.fill(obj, start [, length]) ? array
array.fill(obj, range ) ? array
array.fill {|index| block } ? array
array.fill(start [, length] ) {|index| block } ? array
array.fill(range) {|index| block } ? array
MRI
irb(main):005:0> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
ArgumentError: wrong number of arguments (3 for 2)
IronRuby>>> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
=> [1, 1, 1]
Thanks,
Shri
-----Original Message-----
From: Jim Deville 
Sent: Friday, January 23, 2009 3:05 PM
To: Shri Borde; IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
it "coerces string argument with Float() without calling singleton
to_f" do
    s = MathSpecs.string_with_singleton_to_f("0.5", 0.5)
    Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  
  it "coerces string argument with Float() without calling
String#to_f" do
    MathSpecs.hijack_String_to_f
    begin
      Math.acos("0.5").should be_close(Math.acos(0.5), TOLERANCE)
    ensure
      MathSpecs.reset_String_to_f
    end
  end
These can be rewritten with mocks instead of specialized classes. 
it "coerces string argument with Float() without calling singleton
to_f" do
  s = "0.5"
  s.should_not_receive(:to_f)
  Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
End
The second spec is really testing method dispatch, and otherwise is the same
thing as this one. Same with:
  it "raises an ArgumentError if the singleton string cannot be directly
coerced with Float()" do
    s = MathSpecs.string_with_singleton_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
   
  it "raises an ArgumentError if the string subclass cannot be directly
coerced with Float()" do
    s = MathSpecs.string_subclass_with_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  End
JD
-----Original Message-----
From: Shri Borde 
Sent: Friday, January 23, 2009 2:04 PM
To: IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: Code Review: Better error messages
  tfpt review "/shelveset:error;REDMOND\sborde"
Microsoft.Scripting:
  Change in DefaultBinder is to make the binder exception processing logic be
extensible so that languages can customize the type of exception thrown.
IronRuby:
  
  Improves error reporting in IronRuby by checking the results of the bind
failure. This allowed many Math tests to be enabled.
  
  Also, added a DefaultProtocol for to_f. This allowed more Math tests to be
enabled as you can now pass the string "1.0" to a function expecting a
"Float". I have added test cases for the Float conversion rules in
core\math\acos_spec.rb. I have not copied this to the other specs as that would
be hard to maintain. We will look at factoring such conversion checks into a
utility library so that they will be much easier to use.
  
  Fixes some String bugs - String.#inspect on string subtype should return a
string, and passing wrong argument types to Strings#[]= should cause TypeError
instead of ArgumentError. This is where I started and it put me in the direction
of the fixes above.
  
  Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext?
Tomas, correct me if I''m wrong, but Ruby uses a message based method
dispatch. When to_f is called, the message to_f gets sent to the object, and
then normal method resolution occurs. There is no way to bypass that resolution.
You can''t call String#to_f instead of the singleton class''s
to_f. You call to_f on the object, and it sends the message. That is why I argue
those tests are the same. If we have the ability, or the possibility of
something different, then something is wrong with our method dispatch an method
resolution. That should be exposed in the method tests in Language specs. This
spec doesn''t need both the case of a classes instance method and a
singleton instance method.
JD
-----Original Message-----
From: Shri Borde
Sent: Saturday, January 24, 2009 2:36 PM
To: Jim Deville; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
Jim, using should_not_receive sounds like a good idea. Will do the change.
But we still need all those specs including the one using hijack_String_to_f. It
is not testing method dispatch. Ruby method dispatch only controls which method
body will be called. It does not do argument conversions. That is handled by the
target method which is free to use to_f to convert the argument to a float, but
is not required to. The specs below will ensure that the library method does
follow the right convention/protocol for converting the argument to a float.
Does that make sense?
Also, I had this comment:
/// <summary>
/// An overload with a block should get precedence. However,
Microsoft.Scripting.Action.Calls.MethodBinder
/// gives precedence to overloads with a matching number of arguments as the
actual arguments. Consider Object#eval
/// which effectively has the following two overloads in the Ruby sense.
///     obj.instance_eval(string [, filename [, lineno]] ) => obj
///     obj.instance_eval {| | block } => obj
/// Consider this call:
///     obj.instance_eval(2, 3) { }
/// This should match with the second Ruby overload, and report an ArgumentError
because of the mismatch in the number of arguments.
/// However, the MethodBinder will prefer the first Ruby overload as it has a
matching number of arguments, and will report
/// a type-mismatch instead.
/// Filterting down the overload list upfront yields better errors.
/// </summary>
Tomas pointed out that this filtering was incorrect. Tomas, it did cause test
failures (My last test pass had this filtering done in error-handling after the
MethodBinder did its stuff).
However, overloads do get precedence in some cases like the instance_eval
example in the comment above, and also with Array#fill as shown below. There is
a RubySpec test for Array#fill, but we have a tag because we get it wrong. I
agree with Tomas that this will need to be handled more carefully, and we might
need to have an attribute on the library methods indicating whether the block
gets precedence, or whether the argument count gets precedence in selecting the
method overload. I will undo my change for now.
http://ruby-doc.org/core/classes/Array.html
array.fill(obj) ? array
array.fill(obj, start [, length]) ? array
array.fill(obj, range ) ? array
array.fill {|index| block } ? array
array.fill(start [, length] ) {|index| block } ? array
array.fill(range) {|index| block } ? array
MRI
irb(main):005:0> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
ArgumentError: wrong number of arguments (3 for 2)
IronRuby>>> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
=> [1, 1, 1]
Thanks,
Shri
-----Original Message-----
From: Jim Deville
Sent: Friday, January 23, 2009 3:05 PM
To: Shri Borde; IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
it "coerces string argument with Float() without calling singleton
to_f" do
    s = MathSpecs.string_with_singleton_to_f("0.5", 0.5)
    Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  it "coerces string argument with Float() without calling
String#to_f" do
    MathSpecs.hijack_String_to_f
    begin
      Math.acos("0.5").should be_close(Math.acos(0.5), TOLERANCE)
    ensure
      MathSpecs.reset_String_to_f
    end
  end
These can be rewritten with mocks instead of specialized classes.
it "coerces string argument with Float() without calling singleton
to_f" do
  s = "0.5"
  s.should_not_receive(:to_f)
  Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
End
The second spec is really testing method dispatch, and otherwise is the same
thing as this one. Same with:
  it "raises an ArgumentError if the singleton string cannot be directly
coerced with Float()" do
    s = MathSpecs.string_with_singleton_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  it "raises an ArgumentError if the string subclass cannot be directly
coerced with Float()" do
    s = MathSpecs.string_subclass_with_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  End
JD
-----Original Message-----
From: Shri Borde
Sent: Friday, January 23, 2009 2:04 PM
To: IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: Code Review: Better error messages
  tfpt review "/shelveset:error;REDMOND\sborde"
Microsoft.Scripting:
  Change in DefaultBinder is to make the binder exception processing logic be
extensible so that languages can customize the type of exception thrown.
IronRuby:
  Improves error reporting in IronRuby by checking the results of the bind
failure. This allowed many Math tests to be enabled.
  Also, added a DefaultProtocol for to_f. This allowed more Math tests to be
enabled as you can now pass the string "1.0" to a function expecting a
"Float". I have added test cases for the Float conversion rules in
core\math\acos_spec.rb. I have not copied this to the other specs as that would
be hard to maintain. We will look at factoring such conversion checks into a
utility library so that they will be much easier to use.
  Fixes some String bugs - String.#inspect on string subtype should return a
string, and passing wrong argument types to Strings#[]= should cause TypeError
instead of ArgumentError. This is where I started and it put me in the direction
of the fixes above.
  Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext?
I believe Jim is right. Library methods performing conversions (or other
operations) do so either by calling internal C methods or using regular Ruby
method dispatch. The former can''t be detected from Ruby except for
using a tracing proc (set_trace_func). Method dispatch goes to the singleton of
the object first and then to its class. So the use of should_not_receive ensures
that to_f is not invoked on the given object.
Tomas
-----Original Message-----
From: Jim Deville 
Sent: Saturday, January 24, 2009 4:26 PM
To: Shri Borde; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
Tomas, correct me if I''m wrong, but Ruby uses a message based method
dispatch. When to_f is called, the message to_f gets sent to the object, and
then normal method resolution occurs. There is no way to bypass that resolution.
You can''t call String#to_f instead of the singleton class''s
to_f. You call to_f on the object, and it sends the message. That is why I argue
those tests are the same. If we have the ability, or the possibility of
something different, then something is wrong with our method dispatch an method
resolution. That should be exposed in the method tests in Language specs. This
spec doesn''t need both the case of a classes instance method and a
singleton instance method.
JD
-----Original Message-----
From: Shri Borde 
Sent: Saturday, January 24, 2009 2:36 PM
To: Jim Deville; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
Jim, using should_not_receive sounds like a good idea. Will do the change.
But we still need all those specs including the one using hijack_String_to_f. It
is not testing method dispatch. Ruby method dispatch only controls which method
body will be called. It does not do argument conversions. That is handled by the
target method which is free to use to_f to convert the argument to a float, but
is not required to. The specs below will ensure that the library method does
follow the right convention/protocol for converting the argument to a float.
Does that make sense?
Also, I had this comment:
/// <summary>
/// An overload with a block should get precedence. However,
Microsoft.Scripting.Action.Calls.MethodBinder
/// gives precedence to overloads with a matching number of arguments as the
actual arguments. Consider Object#eval
/// which effectively has the following two overloads in the Ruby sense.
///     obj.instance_eval(string [, filename [, lineno]] ) => obj
///     obj.instance_eval {| | block } => obj
/// Consider this call:
///     obj.instance_eval(2, 3) { }
/// This should match with the second Ruby overload, and report an ArgumentError
because of the mismatch in the number of arguments.
/// However, the MethodBinder will prefer the first Ruby overload as it has a
matching number of arguments, and will report
/// a type-mismatch instead. 
/// Filterting down the overload list upfront yields better errors.
/// </summary> 
Tomas pointed out that this filtering was incorrect. Tomas, it did cause test
failures (My last test pass had this filtering done in error-handling after the
MethodBinder did its stuff).
However, overloads do get precedence in some cases like the instance_eval
example in the comment above, and also with Array#fill as shown below. There is
a RubySpec test for Array#fill, but we have a tag because we get it wrong. I
agree with Tomas that this will need to be handled more carefully, and we might
need to have an attribute on the library methods indicating whether the block
gets precedence, or whether the argument count gets precedence in selecting the
method overload. I will undo my change for now.
http://ruby-doc.org/core/classes/Array.html
array.fill(obj) ? array
array.fill(obj, start [, length]) ? array
array.fill(obj, range ) ? array
array.fill {|index| block } ? array
array.fill(start [, length] ) {|index| block } ? array
array.fill(range) {|index| block } ? array
MRI
irb(main):005:0> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
ArgumentError: wrong number of arguments (3 for 2)
IronRuby>>> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
=> [1, 1, 1]
Thanks,
Shri
-----Original Message-----
From: Jim Deville 
Sent: Friday, January 23, 2009 3:05 PM
To: Shri Borde; IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
it "coerces string argument with Float() without calling singleton
to_f" do
    s = MathSpecs.string_with_singleton_to_f("0.5", 0.5)
    Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  
  it "coerces string argument with Float() without calling
String#to_f" do
    MathSpecs.hijack_String_to_f
    begin
      Math.acos("0.5").should be_close(Math.acos(0.5), TOLERANCE)
    ensure
      MathSpecs.reset_String_to_f
    end
  end
These can be rewritten with mocks instead of specialized classes. 
it "coerces string argument with Float() without calling singleton
to_f" do
  s = "0.5"
  s.should_not_receive(:to_f)
  Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
End
The second spec is really testing method dispatch, and otherwise is the same
thing as this one. Same with:
  it "raises an ArgumentError if the singleton string cannot be directly
coerced with Float()" do
    s = MathSpecs.string_with_singleton_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
   
  it "raises an ArgumentError if the string subclass cannot be directly
coerced with Float()" do
    s = MathSpecs.string_subclass_with_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  End
JD
-----Original Message-----
From: Shri Borde 
Sent: Friday, January 23, 2009 2:04 PM
To: IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: Code Review: Better error messages
  tfpt review "/shelveset:error;REDMOND\sborde"
Microsoft.Scripting:
  Change in DefaultBinder is to make the binder exception processing logic be
extensible so that languages can customize the type of exception thrown.
IronRuby:
  
  Improves error reporting in IronRuby by checking the results of the bind
failure. This allowed many Math tests to be enabled.
  
  Also, added a DefaultProtocol for to_f. This allowed more Math tests to be
enabled as you can now pass the string "1.0" to a function expecting a
"Float". I have added test cases for the Float conversion rules in
core\math\acos_spec.rb. I have not copied this to the other specs as that would
be hard to maintain. We will look at factoring such conversion checks into a
utility library so that they will be much easier to use.
  
  Fixes some String bugs - String.#inspect on string subtype should return a
string, and passing wrong argument types to Strings#[]= should cause TypeError
instead of ArgumentError. This is where I started and it put me in the direction
of the fixes above.
  
  Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext?
The test below actually shows that the singleton to_f is not called. Similarly,
a string like "1.0" gets converted to a Float even after undef-ing
String#to_f. So the library methods are *not* calling to_f on strings at all,
but are directly converting the string contents to a Float value.
The question is not whether should_not_receive ensures that to_f is not invoked
(I agree that we should use this API). The question is whether we need the
testing of all these cases for the default protocol for converting Float
arguments, or if its redundant with language tests testing method dispatch. The
language tests will cover the conversions done in the libraries.
-----Original Message-----
From: Tomas Matousek 
Sent: Saturday, January 24, 2009 10:58 PM
To: Jim Deville; Shri Borde; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
I believe Jim is right. Library methods performing conversions (or other
operations) do so either by calling internal C methods or using regular Ruby
method dispatch. The former can''t be detected from Ruby except for
using a tracing proc (set_trace_func). Method dispatch goes to the singleton of
the object first and then to its class. So the use of should_not_receive ensures
that to_f is not invoked on the given object.
Tomas
-----Original Message-----
From: Jim Deville 
Sent: Saturday, January 24, 2009 4:26 PM
To: Shri Borde; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
Tomas, correct me if I''m wrong, but Ruby uses a message based method
dispatch. When to_f is called, the message to_f gets sent to the object, and
then normal method resolution occurs. There is no way to bypass that resolution.
You can''t call String#to_f instead of the singleton class''s
to_f. You call to_f on the object, and it sends the message. That is why I argue
those tests are the same. If we have the ability, or the possibility of
something different, then something is wrong with our method dispatch an method
resolution. That should be exposed in the method tests in Language specs. This
spec doesn''t need both the case of a classes instance method and a
singleton instance method.
JD
-----Original Message-----
From: Shri Borde 
Sent: Saturday, January 24, 2009 2:36 PM
To: Jim Deville; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
Jim, using should_not_receive sounds like a good idea. Will do the change.
But we still need all those specs including the one using hijack_String_to_f. It
is not testing method dispatch. Ruby method dispatch only controls which method
body will be called. It does not do argument conversions. That is handled by the
target method which is free to use to_f to convert the argument to a float, but
is not required to. The specs below will ensure that the library method does
follow the right convention/protocol for converting the argument to a float.
Does that make sense?
Also, I had this comment:
/// <summary>
/// An overload with a block should get precedence. However,
Microsoft.Scripting.Action.Calls.MethodBinder
/// gives precedence to overloads with a matching number of arguments as the
actual arguments. Consider Object#eval
/// which effectively has the following two overloads in the Ruby sense.
///     obj.instance_eval(string [, filename [, lineno]] ) => obj
///     obj.instance_eval {| | block } => obj
/// Consider this call:
///     obj.instance_eval(2, 3) { }
/// This should match with the second Ruby overload, and report an ArgumentError
because of the mismatch in the number of arguments.
/// However, the MethodBinder will prefer the first Ruby overload as it has a
matching number of arguments, and will report
/// a type-mismatch instead. 
/// Filterting down the overload list upfront yields better errors.
/// </summary> 
Tomas pointed out that this filtering was incorrect. Tomas, it did cause test
failures (My last test pass had this filtering done in error-handling after the
MethodBinder did its stuff).
However, overloads do get precedence in some cases like the instance_eval
example in the comment above, and also with Array#fill as shown below. There is
a RubySpec test for Array#fill, but we have a tag because we get it wrong. I
agree with Tomas that this will need to be handled more carefully, and we might
need to have an attribute on the library methods indicating whether the block
gets precedence, or whether the argument count gets precedence in selecting the
method overload. I will undo my change for now.
http://ruby-doc.org/core/classes/Array.html
array.fill(obj) ? array
array.fill(obj, start [, length]) ? array
array.fill(obj, range ) ? array
array.fill {|index| block } ? array
array.fill(start [, length] ) {|index| block } ? array
array.fill(range) {|index| block } ? array
MRI
irb(main):005:0> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
ArgumentError: wrong number of arguments (3 for 2)
IronRuby>>> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
=> [1, 1, 1]
Thanks,
Shri
-----Original Message-----
From: Jim Deville 
Sent: Friday, January 23, 2009 3:05 PM
To: Shri Borde; IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
it "coerces string argument with Float() without calling singleton
to_f" do
    s = MathSpecs.string_with_singleton_to_f("0.5", 0.5)
    Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  
  it "coerces string argument with Float() without calling
String#to_f" do
    MathSpecs.hijack_String_to_f
    begin
      Math.acos("0.5").should be_close(Math.acos(0.5), TOLERANCE)
    ensure
      MathSpecs.reset_String_to_f
    end
  end
These can be rewritten with mocks instead of specialized classes. 
it "coerces string argument with Float() without calling singleton
to_f" do
  s = "0.5"
  s.should_not_receive(:to_f)
  Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
End
The second spec is really testing method dispatch, and otherwise is the same
thing as this one. Same with:
  it "raises an ArgumentError if the singleton string cannot be directly
coerced with Float()" do
    s = MathSpecs.string_with_singleton_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
   
  it "raises an ArgumentError if the string subclass cannot be directly
coerced with Float()" do
    s = MathSpecs.string_subclass_with_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  End
JD
-----Original Message-----
From: Shri Borde 
Sent: Friday, January 23, 2009 2:04 PM
To: IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: Code Review: Better error messages
  tfpt review "/shelveset:error;REDMOND\sborde"
Microsoft.Scripting:
  Change in DefaultBinder is to make the binder exception processing logic be
extensible so that languages can customize the type of exception thrown.
IronRuby:
  
  Improves error reporting in IronRuby by checking the results of the bind
failure. This allowed many Math tests to be enabled.
  
  Also, added a DefaultProtocol for to_f. This allowed more Math tests to be
enabled as you can now pass the string "1.0" to a function expecting a
"Float". I have added test cases for the Float conversion rules in
core\math\acos_spec.rb. I have not copied this to the other specs as that would
be hard to maintain. We will look at factoring such conversion checks into a
utility library so that they will be much easier to use.
  
  Fixes some String bugs - String.#inspect on string subtype should return a
string, and passing wrong argument types to Strings#[]= should cause TypeError
instead of ArgumentError. This is where I started and it put me in the direction
of the fixes above.
  
  Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext?
I feel that it is redundant.
JD
-----Original Message-----
From: Shri Borde
Sent: Saturday, January 24, 2009 11:21 PM
To: Tomas Matousek; Jim Deville; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
The test below actually shows that the singleton to_f is not called. Similarly,
a string like "1.0" gets converted to a Float even after undef-ing
String#to_f. So the library methods are *not* calling to_f on strings at all,
but are directly converting the string contents to a Float value.
The question is not whether should_not_receive ensures that to_f is not invoked
(I agree that we should use this API). The question is whether we need the
testing of all these cases for the default protocol for converting Float
arguments, or if its redundant with language tests testing method dispatch. The
language tests will cover the conversions done in the libraries.
-----Original Message-----
From: Tomas Matousek
Sent: Saturday, January 24, 2009 10:58 PM
To: Jim Deville; Shri Borde; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
I believe Jim is right. Library methods performing conversions (or other
operations) do so either by calling internal C methods or using regular Ruby
method dispatch. The former can''t be detected from Ruby except for
using a tracing proc (set_trace_func). Method dispatch goes to the singleton of
the object first and then to its class. So the use of should_not_receive ensures
that to_f is not invoked on the given object.
Tomas
-----Original Message-----
From: Jim Deville
Sent: Saturday, January 24, 2009 4:26 PM
To: Shri Borde; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
Tomas, correct me if I''m wrong, but Ruby uses a message based method
dispatch. When to_f is called, the message to_f gets sent to the object, and
then normal method resolution occurs. There is no way to bypass that resolution.
You can''t call String#to_f instead of the singleton class''s
to_f. You call to_f on the object, and it sends the message. That is why I argue
those tests are the same. If we have the ability, or the possibility of
something different, then something is wrong with our method dispatch an method
resolution. That should be exposed in the method tests in Language specs. This
spec doesn''t need both the case of a classes instance method and a
singleton instance method.
JD
-----Original Message-----
From: Shri Borde
Sent: Saturday, January 24, 2009 2:36 PM
To: Jim Deville; IronRuby External Code Reviewers
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
Jim, using should_not_receive sounds like a good idea. Will do the change.
But we still need all those specs including the one using hijack_String_to_f. It
is not testing method dispatch. Ruby method dispatch only controls which method
body will be called. It does not do argument conversions. That is handled by the
target method which is free to use to_f to convert the argument to a float, but
is not required to. The specs below will ensure that the library method does
follow the right convention/protocol for converting the argument to a float.
Does that make sense?
Also, I had this comment:
/// <summary>
/// An overload with a block should get precedence. However,
Microsoft.Scripting.Action.Calls.MethodBinder
/// gives precedence to overloads with a matching number of arguments as the
actual arguments. Consider Object#eval
/// which effectively has the following two overloads in the Ruby sense.
///     obj.instance_eval(string [, filename [, lineno]] ) => obj
///     obj.instance_eval {| | block } => obj
/// Consider this call:
///     obj.instance_eval(2, 3) { }
/// This should match with the second Ruby overload, and report an ArgumentError
because of the mismatch in the number of arguments.
/// However, the MethodBinder will prefer the first Ruby overload as it has a
matching number of arguments, and will report
/// a type-mismatch instead.
/// Filterting down the overload list upfront yields better errors.
/// </summary>
Tomas pointed out that this filtering was incorrect. Tomas, it did cause test
failures (My last test pass had this filtering done in error-handling after the
MethodBinder did its stuff).
However, overloads do get precedence in some cases like the instance_eval
example in the comment above, and also with Array#fill as shown below. There is
a RubySpec test for Array#fill, but we have a tag because we get it wrong. I
agree with Tomas that this will need to be handled more carefully, and we might
need to have an attribute on the library methods indicating whether the block
gets precedence, or whether the argument count gets precedence in selecting the
method overload. I will undo my change for now.
http://ruby-doc.org/core/classes/Array.html
array.fill(obj) ? array
array.fill(obj, start [, length]) ? array
array.fill(obj, range ) ? array
array.fill {|index| block } ? array
array.fill(start [, length] ) {|index| block } ? array
array.fill(range) {|index| block } ? array
MRI
irb(main):005:0> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
ArgumentError: wrong number of arguments (3 for 2)
IronRuby>>> [1,2].fill(1,1,2) {|i| puts "index=#{i}" }
=> [1, 1, 1]
Thanks,
Shri
-----Original Message-----
From: Jim Deville
Sent: Friday, January 23, 2009 3:05 PM
To: Shri Borde; IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: RE: Code Review: Better error messages
it "coerces string argument with Float() without calling singleton
to_f" do
    s = MathSpecs.string_with_singleton_to_f("0.5", 0.5)
    Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  it "coerces string argument with Float() without calling
String#to_f" do
    MathSpecs.hijack_String_to_f
    begin
      Math.acos("0.5").should be_close(Math.acos(0.5), TOLERANCE)
    ensure
      MathSpecs.reset_String_to_f
    end
  end
These can be rewritten with mocks instead of specialized classes.
it "coerces string argument with Float() without calling singleton
to_f" do
  s = "0.5"
  s.should_not_receive(:to_f)
  Math.acos(s).should be_close(Math.acos(0.5), TOLERANCE)
End
The second spec is really testing method dispatch, and otherwise is the same
thing as this one. Same with:
  it "raises an ArgumentError if the singleton string cannot be directly
coerced with Float()" do
    s = MathSpecs.string_with_singleton_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  end
  it "raises an ArgumentError if the string subclass cannot be directly
coerced with Float()" do
    s = MathSpecs.string_subclass_with_to_f("test", 0.5)
    lambda { Math.acos(s) }.should raise_error(ArgumentError)
    ScratchPad.recorded.should == nil # to_f should not be called
  End
JD
-----Original Message-----
From: Shri Borde
Sent: Friday, January 23, 2009 2:04 PM
To: IronRuby External Code Reviewers; DLR Code Reviews
Cc: ironruby-core at rubyforge.org
Subject: Code Review: Better error messages
  tfpt review "/shelveset:error;REDMOND\sborde"
Microsoft.Scripting:
  Change in DefaultBinder is to make the binder exception processing logic be
extensible so that languages can customize the type of exception thrown.
IronRuby:
  Improves error reporting in IronRuby by checking the results of the bind
failure. This allowed many Math tests to be enabled.
  Also, added a DefaultProtocol for to_f. This allowed more Math tests to be
enabled as you can now pass the string "1.0" to a function expecting a
"Float". I have added test cases for the Float conversion rules in
core\math\acos_spec.rb. I have not copied this to the other specs as that would
be hard to maintain. We will look at factoring such conversion checks into a
utility library so that they will be much easier to use.
  Fixes some String bugs - String.#inspect on string subtype should return a
string, and passing wrong argument types to Strings#[]= should cause TypeError
instead of ArgumentError. This is where I started and it put me in the direction
of the fixes above.
  Tomas: In RubyBinder.GetTypeName, is there a better way to get to RubyContext?