ArgumentError: wrong number of arguments calling `eql?` (1 for 0) #1004

Closed
acds opened this Issue Sep 11, 2013 · 9 comments

Projects

None yet

3 participants

@acds
acds commented Sep 11, 2013

I ran into this...

ArgumentError: wrong number of arguments calling `eql?` (1 for 0)
    from org/jruby/RubyHash.java:1118:in `[]'
    from /Users/simon/.rvm/gems/jruby-1.7.4@acds-new/bundler/gems/bindata-7f9bfc438514/lib/bindata/sanitize.rb:132:in `[]'
    from /Users/simon/.rvm/gems/jruby-1.7.4@acds-new/bundler/gems/bindata-7f9bfc438514/lib/bindata/choice.rb:173:in `instantiate_choice'
    from /Users/simon/.rvm/gems/jruby-1.7.4@acds-new/bundler/gems/bindata-7f9bfc438514/lib/bindata/choice.rb:169:in `current_choice'
    from /Users/simon/.rvm/gems/jruby-1.7.4@acds-new/bundler/gems/bindata-7f9bfc438514/lib/bindata/choice.rb:153:in `do_read'
    from /Users/simon/.rvm/gems/jruby-1.7.4@acds-new/bundler/gems/bindata-7f9bfc438514/lib/bindata/trace.rb:84:in `do_read_with_hook'
    from /Users/simon/.rvm/gems/jruby-1.7.4@acds-new/bundler/gems/bindata-7f9bfc438514/lib/bindata/struct.rb:208:in `do_read'
    from org/jruby/RubyArray.java:1617:in `each'
    from /Users/simon/.rvm/gems/jruby-1.7.4@acds-new/bundler/gems/bindata-7f9bfc438514/lib/bindata/struct.rb:208:in `do_read'
    from /Users/simon/.rvm/gems/jruby-1.7.4@acds-new/bundler/gems/bindata-7f9bfc438514/lib/bindata/base.rb:168:in `read'

the library writer confirmed this does not cause issues in MRI dmendel/bindata#11

$ jruby -v
jruby 1.7.4 (2.0.0) 2013-05-16 2390d3b on Java HotSpot(TM) 64-Bit Server VM 1.6.0_51-b11-457-11M4509 [darwin-x86_64]
@headius
Member
headius commented Sep 11, 2013

Well that's weird... what good is an eql? that takes 0 args? investigating.

@headius
Member
headius commented Sep 11, 2013

Do you have an easy way we can reproduce this?

@headius
Member
headius commented Sep 11, 2013

Until we can reproduce this we'll need you to do a few things...

  • If possible, try not running in 2.0.0 mode. There are some known argument-processing issues in our 2.0.0 support.
  • Reproduce the error passing -Xbacktrace.style=raw to JRuby, perhaps also with -X+C to force everything to compile.
  • Try running without JRuby's compiler active using -X-C.
@BanzaiMan
Member

To reproduce, clone https://github.com/dmendel/bindata and run bundle install and jruby test/system_test.rb. There should be two failures, one of which is:

  2) Error:
BinData::Record::with choice field#test_0003_has correct offset:
ArgumentError: wrong number of arguments calling `eql?` (1 for 0)
    org/jruby/RubyHash.java:1072:in `[]'
    /Users/asari/Development/src/bindata/lib/bindata/sanitize.rb:132:in `[]'
    /Users/asari/Development/src/bindata/lib/bindata/choice.rb:173:in `instantiate_choice'
    /Users/asari/Development/src/bindata/lib/bindata/choice.rb:169:in `current_choice'
    /Users/asari/Development/src/bindata/lib/bindata/choice.rb:149:in `method_missing'
    /Users/asari/Development/src/bindata/lib/bindata/choice.rb:149:in `method_missing'
    test/system_test.rb:96:in `(root)'

Notice that method_missing looks like this. https://github.com/dmendel/bindata/blob/7f9bfc4/lib/bindata/choice.rb#L149

    def method_missing(symbol, *args, &block) #:nodoc:
      current_choice.__send__(symbol, *args, &block)
    end

and SanitizedChoices overrides []. https://github.com/dmendel/bindata/blob/7f9bfc4/lib/bindata/sanitize.rb#L131-L133

    def [](key)
      @choices[key]
    end

My guess is that we are somehow mangling the arguments and/or method signature badly in such a way that we end up redefining [] to take zero arguments, leading to the exception.

@headius
Member
headius commented Sep 12, 2013

Raw compiled trace:

ArgumentError: wrong number of arguments calling `eql?` (1 for 0)
    java/lang/Thread.java:1568:in `getStackTrace'
    org/jruby/runtime/backtrace/TraceType.java:175:in `getBacktraceData'
    org/jruby/runtime/backtrace/TraceType.java:39:in `getBacktrace'
    org/jruby/RubyException.java:224:in `prepareBacktrace'
    org/jruby/exceptions/RaiseException.java:212:in `preRaise'
    org/jruby/exceptions/RaiseException.java:193:in `preRaise'
    org/jruby/exceptions/RaiseException.java:110:in `<init>'
    org/jruby/Ruby.java:3750:in `newRaiseException'
    org/jruby/Ruby.java:3294:in `newArgumentError'
    org/jruby/runtime/Arity.java:296:in `raiseArgumentError'
    org/jruby/internal/runtime/methods/JavaMethod.java:246:in `raiseArgumentError'
    org/jruby/internal/runtime/methods/JavaMethod.java:252:in `checkArgumentCount'
    org/jruby/internal/runtime/methods/DynamicMethod.java:210:in `call'
    org/jruby/internal/runtime/methods/CompiledMethod.java:263:in `call'
    org/jruby/runtime/Helpers.java:2741:in `invokedynamic'
    org/jruby/RubyBasicObject.java:1145:in `eql'
    org/jruby/RubyHash.java:556:in `internalKeyExist'
    org/jruby/RubyHash.java:548:in `internalGetEntry'
    org/jruby/RubyHash.java:542:in `internalGet'
    org/jruby/RubyHash.java:1072:in `op_aref'
    /Users/headius/projects/tmp/bindata/lib/bindata/sanitize.rb:132:in `[]'
    Users/headius/projects/tmp/bindata/lib/bindata//Users/headius/projects/tmp/bindata/lib/bindata/sanitize.rb:132:in `[]'
    /Users/headius/projects/tmp/bindata/lib/bindata/choice.rb:173:in `instantiate_choice'
    Users/headius/projects/tmp/bindata/lib/bindata//Users/headius/projects/tmp/bindata/lib/bindata/choice.rb:173:in `instantiate_choice'
    /Users/headius/projects/tmp/bindata/lib/bindata/choice.rb:169:in `current_choice'
    Users/headius/projects/tmp/bindata/lib/bindata//Users/headius/projects/tmp/bindata/lib/bindata/choice.rb:169:in `current_choice'
    /Users/headius/projects/tmp/bindata/lib/bindata/choice.rb:149:in `method_missing'
    Users/headius/projects/tmp/bindata/lib/bindata//Users/headius/projects/tmp/bindata/lib/bindata/choice.rb:149:in `method_missing'
    org/jruby/runtime/Helpers.java:442:in `call'
    org/jruby/runtime/Helpers.java:361:in `callMethodMissing'
    org/jruby/RubyClass.java:546:in `finvoke'
    org/jruby/RubyBasicObject.java:1531:in `send19'
    /Users/headius/projects/tmp/bindata/lib/bindata/choice.rb:149:in `method_missing'
    Users/headius/projects/tmp/bindata/lib/bindata//Users/headius/projects/tmp/bindata/lib/bindata/choice.rb:149:in `method_missing'
    org/jruby/runtime/Helpers.java:442:in `call'
    org/jruby/internal/runtime/methods/DynamicMethod.java:202:in `call'
    org/jruby/runtime/invokedynamic/InvokeDynamicSupport.java:916:in `callMethodMissing'
    org/jruby/runtime/invokedynamic/InvocationLinker.java:132:in `invocationFallback'
    test/system_test.rb:96:in `(root)'
    test/test/system_test.rb:96:in `(root)'
    org/jruby/runtime/CompiledBlock19.java:159:in `yield'
    org/jruby/runtime/CompiledBlock19.java:87:in `call'
    org/jruby/runtime/Block.java:101:in `call'
    org/jruby/RubyProc.java:290:in `call'
    org/jruby/internal/runtime/methods/ProcMethod.java:64:in `call'
    org/jruby/internal/runtime/methods/DynamicMethod.java:202:in `call'
    org/jruby/RubyClass.java:527:in `finvoke'
    org/jruby/RubyBasicObject.java:1504:in `send19'
    org/jruby/RubyKernel.java:2238:in `send19'
@headius
Member
headius commented Sep 12, 2013

I put some logging in Helpers.invokedynamic to see what method was being called, and it appears that when looking up eql? we're getting the hash method in base_primitive.rb instead:

Users$headius$projects$tmp$bindata$lib$bindata$base_primitive$method__14$RUBY$hash@260702ee
Eorg.jruby.RubyModule$INVOKER$i$1$0$op_equal@12fc183e
Users$headius$projects$tmp$bindata$lib$bindata$base_primitive$method__14$RUBY$hash@260702ee
Eorg.jruby.RubyFixnum$INVOKER$i$1$0$op_cmp@25764a11

Unsure why though. Still investigating.

@headius headius added a commit that closed this issue Sep 12, 2013
@headius headius Never use core runtime cache for user classes. Fixes #1004.
The "index" of a RubyModule is only non-zero when it's one of our
known native core classes. All other classes end up with zero.
Since we use this index to offset into the core method cache (in
Ruby.getRuntimeCache() or ThreadContext.runtimeCache) we must
check for index == zero and not use the cache in that case (e.g.
in Helpers.invokedynamic() which calls Helpers.getMethodCached().
77a0453
@headius headius closed this in 77a0453 Sep 12, 2013
@headius
Member
headius commented Sep 12, 2013

I'm surprised we didn't run into problems with this sooner.

We keep a RuntimeCache around for caching common methods on core classes like #hash, #eql?, and so on. To index into the cache, we use the target class's index (from ClassIndex) and the index of the method name (from MethodName) as a sort of matrix of cached methods (basically cache[classIndex * (methodIndex + 1)]).

The problem here was that this logic was not considering classes which have no ClassIndex. Only core JRuby classes have nonzero ClassIndex, so all user classes that passed through this logic were caching their methods in the same place, at index zero (classIndex is zero for non-core classes, so a single slot was shared for all user classes across all methods access via MethodName and the runtime cache). This caused a previous call to #hash to be cached in slot zero, and a subsequent call to #eql? called the same method object, believing it had been properly cached.

This was entirely my fault; for whatever reason I thought that RubyModule.index had different values for user classes rather than zero. I have added doco to help prevent this in the future.

@acds
acds commented Sep 18, 2013

Thanks guys, what do I need to do to pick up this fix ?

@headius
Member
headius commented Sep 19, 2013

JRuby master has it now, so grab jruby-head with whatever ruby installed
you use, a snapshot from http://ci.jruby.org/snapshots, or wait a day or
two for the release :-)
On Sep 18, 2013 5:26 PM, "acds" notifications@github.com wrote:

Thanks guys, what do I need to do to pick up this fix ?


Reply to this email directly or view it on GitHubhttps://github.com/jruby/jruby/issues/1004#issuecomment-24704544
.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment