Avoid calling >= on unknown receivers in ls command#1193
Avoid calling >= on unknown receivers in ls command#1193dejmedus wants to merge 1 commit intoruby:masterfrom
>= on unknown receivers in ls command#1193Conversation
| ancestors = ancestors.reject { |c| c >= Object } if klass < Object | ||
| singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| c >= Class } | ||
| ancestors = ancestors.reject { |c| Object <= c } if klass < Object | ||
| singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| Class <= c } |
There was a problem hiding this comment.
To solve the problem, the change looks good. However, ls foo ls Foo doesn't work when some other methods are missing:
<
constants
class_variables
to_s
ancestors
public_instance_methods
Unlike Object instances that we need to consider BasicObject, I think we can assume Classes doesn't undef/change most of these methods: constants, class_variables, to_s, ancestors, public_instance_methods.
But for <, I guess it might be removed and need to change if klass < Object to if Object > klass in the situation you are assuming.
Can you share what kind of situation :<= is removed? Will :> be removed too?
Do you know an existing gem that creates a class without :<=?
The correct fix depends on the situation what you are assuming, and needs to be commented in test code.
There was a problem hiding this comment.
👋 Yes, sorry, I should have added more details! The situation came up in Rubydex where an undefined method '>=' NoMethodError occurred on c >= Class due to a shadowed singleton_class returning an instance rather than the real class (better details in this issue). I believe the singleton_class is okay due to #1177 and is being fixed in Rubydex, but this raised the idea that flipping the comparison might prevent situations where a foreign object behaved oddly when compared
need to change
if klass < Objecttoif Object > klassin the situation
I've updated my commit with this change:)
There was a problem hiding this comment.
Thank you for providing the background 👍
I've confirmed the bug that described in Shopify/rubydex#685 with IRB 1.17.0
irb(main):001* module Rubydex
irb(main):002* class SingletonClass
irb(main):003* def ancestors; [self] end
irb(main):004* end
irb(main):005* class Namespace
irb(main):006* def singleton_class
irb(main):007* SingletonClass.new
irb(main):008* end
irb(main):009* end
irb(main):010> end
irb(main):011>
irb(main):012>
=> :singleton_class
irb(main):013> ns = Rubydex::Namespace.new
=> #<Rubydex::Namespace:0x000000016221e2c0>
irb(main):014> ls ns
/path/to/.rbenv/versions/4.0.0/lib/ruby/gems/4.0.0/gems/irb-1.17.0/lib/irb/command/ls.rb:74:in 'block in IRB::Command::Ls#dump_methods': undefined method '>=' for an instance of Rubydex::SingletonClass (NoMethodError)
singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| c >= Class }So the error is caused by:
Rubydex::SingletonClass.new >= Class
(irb):13:in '<main>': undefined method '>=' for an instance of Rubydex::SingletonClass (NoMethodError)However, flipping the operator doesn't solve the problem. It still raises error because the receiver was not a Class that doesn't have :>=, but was a normal Object.
Class <= Rubydex::SingletonClass.new
(irb):14:in 'Module#<=': compared with non class/module (TypeError)Previously, when calling comparison methods on receivers we don't control (`c >= Object`), there was a chance it could lack or have overridden the method. This commit flips them to prevent this `Object <= c`
d6cca2f to
b1e82a0
Compare
|
Closing because the original issue was not about Class that removed |
If
Ls#dump_methodsis called with anobjwhose ancestors alter>=, the comparison (c >= Object/c >= Class) may raise an error. For example:To prevent this, we could defensively flip the comparison so that
<=is called on known receivers