Skip to content

Add a spec for Enumerator#size of zero - #1394

Open
edgibbs wants to merge 1 commit into
ruby:masterfrom
edgibbs:edgibbs/add-enumerator-size-zero-specs
Open

Add a spec for Enumerator#size of zero#1394
edgibbs wants to merge 1 commit into
ruby:masterfrom
edgibbs:edgibbs/add-enumerator-size-zero-specs

Conversation

@edgibbs

@edgibbs edgibbs commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

👋🏾 Found this when doing some work on garnetjs. After doing some digging verified it was a potential issue and impacted Opal in the same way. Enumerator#size returns the size of the enumerator, or nil when the size cannot be determined without iterating. 'core/enumerator/size_spec.rb' pins an Integer size (100), nil, and a Proc, but never zero.

The case cannot fail on CRuby, where 0 is truthy and INT2FIX(0) is a nonzero VALUE distinct from Qnil. It can fail on implementations hosted in a language where 0 is falsy: Opal returns nil for Enumerator.new(0) {}.size, because opal/corelib/enumerator.rb assigns the size with arguments[0] || nil and JS || collapses a size of 0 to nil. The neighbouring Enumerator.new(100) example passes there, so nothing in the suite currently detects this.

Matchers follow the existing examples in the file.

Reproduction steps

Reproducing the Opal failure (requires Node, since Opal compiles to JS):

$ gem install opal          # tested with 1.8.3

$ opal --no-exit -e 'p Enumerator.new(100) {}.size; p Enumerator.new(0) {}.size' 
100
nil

$ ruby --disable-gems -e 'p Enumerator.new(100) {}.size; p Enumerator.new(0) {}.size'
100
0

The existing Enumerator.new(100) example passes on Opal. Only the new case will fail.

Cause — opal/corelib/enumerator.rb:33, inside Enumerator#initialize:

@size = `arguments[0] || nil`

That's inline JavaScript. 0 || nil evaluates to nil because 0 is falsy in JS, so a known size of zero becomes "size unknown." Nothing downstream can recover it — #size (line 56) just returns @size:

def size
  @size.respond_to?(:call) ? @size.call(*@args) : @size
end

`Enumerator#size` returns the size of the enumerator, or nil when the size
cannot be determined without iterating. 'core/enumerator/size_spec.rb' pins an
Integer size (100), nil, and a Proc, but never zero.

The case cannot fail on CRuby, where 0 is truthy and INT2FIX(0) is a nonzero
VALUE distinct from Qnil. It can fail on implementations hosted in a language
where 0 is falsy: Opal returns nil for Enumerator.new(0) {}.size, because
`opal/corelib/enumerator.rb` assigns the size with `arguments[0] || nil` and JS
|| collapses a size of 0 to nil. The neighbouring Enumerator.new(100) example
passes there, so nothing in the suite currently detects this.

Matchers follow the existing examples in the file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant