JRuby (please use github issues at http://bugs.jruby.org)
  1. JRuby (please use github issues at http://bugs.jruby.org)
  2. JRUBY-4871

[1.9] Attempt to invoke any method on Delegator leads to ClassCastException

    Details

    • Number of attachments :
      0

      Description

      require 'delegate'
      
      class MyDelegator < Delegator
          attr_reader :__getobj__
          
          def __setobj__(o)
            @__getobj__ = o
          end
      end
      
      d = MyDelegator.new(10)
      d.freeze ## fails HERE
      

      An attempt to execute the example above leads to:

      jruby --1.9 deleg.rb
      org/jruby/RubyObject$i_method_0_0$RUBYINVOKER$freeze.gen:65535:in `call': java.lang.ClassCastException: org.jruby.RubyBasicObject
      cannot be cast to org.jruby.RubyObject
              from JavaMethod.java:836:in `call'
              from JavaMethod.java:642:in `call'
              from SuperCallSite.java:286:in `cacheAndCall'
              from SuperCallSite.java:70:in `callBlock'
              from SuperCallSite.java:75:in `call'
              from ZSuperNode.java:100:in `interpret'
              from NewlineNode.java:104:in `interpret'
              from BlockNode.java:71:in `interpret'
              from InterpretedMethod.java:139:in `call'
              from CachingCallSite.java:293:in `cacheAndCall'
              from CachingCallSite.java:112:in `call'
              from CallNoArgNode.java:61:in `interpret'
              from NewlineNode.java:104:in `interpret'
              from BlockNode.java:71:in `interpret'
              from RootNode.java:129:in `interpret'
              from Ruby.java:707:in `runInterpreter'
              from Ruby.java:568:in `runNormally'
              from Ruby.java:410:in `runFromMain'
              from Main.java:286:in `run'
              from Main.java:128:in `run'
              from Main.java:97:in `main'
      

      This also causes LOTS of new rubyspec failures for Delegate library.

        Issue Links

          Activity

          Hide
          David Calavera added a comment -

          I can't find a good solution for this, perhaps you can bump into something. The problem is that freeze and other methods are declared into RubyObject but they are also public methods of BasicObject. So we generate this java source for the Object class:

          javaMethod = new org.jruby.RubyObject$i_method_0_0$RUBYINVOKER$freeze(cls, Visibility.PUBLIC);
          populateMethod(javaMethod, 0, "freeze", false, CallConfiguration.FrameNoneScopeNone);
          javaMethod.setNativeCall(org.jruby.RubyObject.class, "freeze", org.jruby.runtime.builtin.IRubyObject.class, new Class[] {org.jruby.runtime.ThreadContext.class}, false);
          cls.addMethodAtBootTimeOnly("freeze", javaMethod);
          

          But the method 'freeze' can be called from BasicObject:

          bin/jruby --1.9 -e 'p BasicObject.public_methods'
          [:singleton_method_undefined, :singleton_method_removed, :singleton_method_added, :method_missing, :__subclasses__, :new, :allocate, :superclass, :const_get, :public_instance_methods, :autoload?, :freeze, :const_missing, :included_modules, :==, :ancestors, :public_class_method, :instance_method, :<, :protected_method_defined?, :>, :===, :private_instance_methods, :hash, :class_variables, :<=, :method_defined?, :instance_methods, :class_variable_defined?, :name, :private_method_defined?, :const_set, :autoload, :include?, :protected_instance_methods, :module_eval, :<=>, :private_class_method, :constants, :to_s, :class_eval, :public_method_defined?, :>=, :const_defined?, :methods, :untrust, :extend, :=~, :nil?, :object_id, :protected_methods, :trust, :inspect, :method, :tainted?, :__id__, :is_a?, :instance_variable_defined?, :instance_variable_get, :tap, :frozen?, :singleton_class, :respond_to?, :instance_variable_set, :untaint, :respond_to_missing?, :clone, :display, :send, :to_enum, :private_methods, :enum_for, :singleton_methods, :untrusted?, :eql?, :type, :public_send, :dup, :kind_of?, :instance_of?, :id, :taint, :class, :instance_variables, :!~, :public_methods, :instance_exec, :__send__, :instance_eval, :equal?, :!, :!=]
          

          But we can't move the freeze methods and the others to RubyBasicObject because BasicObject just includes those instance methods:

          bin/jruby --1.9 -e 'p BasicObject.instance_methods'
          [:instance_exec, :__send__, :instance_eval, :==, :equal?, :!, :!=]
          

          So, how we can generate something like this and don't include the methods into the BasicObject instance?

          javaMethod = new org.jruby.RubyBasicObject$i_method_0_0$RUBYINVOKER$freeze(cls, Visibility.PUBLIC);
          populateMethod(javaMethod, 0, "freeze", false, CallConfiguration.FrameNoneScopeNone);
          javaMethod.setNativeCall(org.jruby.RubyObject.class, "freeze", org.jruby.runtime.builtin.IRubyObject.class, new Class[] {org.jruby.runtime.ThreadContext.class}, false);
          cls.addMethodAtBootTimeOnly("freeze", javaMethod);
          
          Show
          David Calavera added a comment - I can't find a good solution for this, perhaps you can bump into something. The problem is that freeze and other methods are declared into RubyObject but they are also public methods of BasicObject. So we generate this java source for the Object class: javaMethod = new org.jruby.RubyObject$i_method_0_0$RUBYINVOKER$freeze(cls, Visibility.PUBLIC); populateMethod(javaMethod, 0, "freeze", false, CallConfiguration.FrameNoneScopeNone); javaMethod.setNativeCall(org.jruby.RubyObject.class, "freeze", org.jruby.runtime.builtin.IRubyObject.class, new Class[] {org.jruby.runtime.ThreadContext.class}, false); cls.addMethodAtBootTimeOnly("freeze", javaMethod); But the method 'freeze' can be called from BasicObject: bin/jruby --1.9 -e 'p BasicObject.public_methods' [:singleton_method_undefined, :singleton_method_removed, :singleton_method_added, :method_missing, :__subclasses__, :new, :allocate, :superclass, :const_get, :public_instance_methods, :autoload?, :freeze, :const_missing, :included_modules, :==, :ancestors, :public_class_method, :instance_method, :<, :protected_method_defined?, :>, :===, :private_instance_methods, :hash, :class_variables, :<=, :method_defined?, :instance_methods, :class_variable_defined?, :name, :private_method_defined?, :const_set, :autoload, :include?, :protected_instance_methods, :module_eval, :<=>, :private_class_method, :constants, :to_s, :class_eval, :public_method_defined?, :>=, :const_defined?, :methods, :untrust, :extend, :=~, :nil?, :object_id, :protected_methods, :trust, :inspect, :method, :tainted?, :__id__, :is_a?, :instance_variable_defined?, :instance_variable_get, :tap, :frozen?, :singleton_class, :respond_to?, :instance_variable_set, :untaint, :respond_to_missing?, :clone, :display, :send, :to_enum, :private_methods, :enum_for, :singleton_methods, :untrusted?, :eql?, :type, :public_send, :dup, :kind_of?, :instance_of?, :id, :taint, :class, :instance_variables, :!~, :public_methods, :instance_exec, :__send__, :instance_eval, :equal?, :!, :!=] But we can't move the freeze methods and the others to RubyBasicObject because BasicObject just includes those instance methods: bin/jruby --1.9 -e 'p BasicObject.instance_methods' [:instance_exec, :__send__, :instance_eval, :==, :equal?, :!, :!=] So, how we can generate something like this and don't include the methods into the BasicObject instance? javaMethod = new org.jruby.RubyBasicObject$i_method_0_0$RUBYINVOKER$freeze(cls, Visibility.PUBLIC); populateMethod(javaMethod, 0, "freeze", false, CallConfiguration.FrameNoneScopeNone); javaMethod.setNativeCall(org.jruby.RubyObject.class, "freeze", org.jruby.runtime.builtin.IRubyObject.class, new Class[] {org.jruby.runtime.ThreadContext.class}, false); cls.addMethodAtBootTimeOnly("freeze", javaMethod);
          Hide
          Charles Oliver Nutter added a comment -

          Ahh, I see the problem.

          The methods in question are actually defined on Kernel but sourced from RubyObject. They do exist on RubyBasicObject, but the invokers we generate for them try to cast to RubyObject and fail if it's a RubyBasicObject.

          I believe the fix is to move the annotations for methods that Kernel defines to RubyBasicObject and modify RubyBasicObject to bind its set of methods separately. I'll look into this.

          Show
          Charles Oliver Nutter added a comment - Ahh, I see the problem. The methods in question are actually defined on Kernel but sourced from RubyObject. They do exist on RubyBasicObject, but the invokers we generate for them try to cast to RubyObject and fail if it's a RubyBasicObject. I believe the fix is to move the annotations for methods that Kernel defines to RubyBasicObject and modify RubyBasicObject to bind its set of methods separately. I'll look into this.
          Hide
          Charles Oliver Nutter added a comment -

          I have a fix in progress. It's a big patch, since it required me to move all the Object methods up into RubyBasicObject and add stubs to RubyKernel to bind them. But it's running tests now, and should be landing soon.

          Show
          Charles Oliver Nutter added a comment - I have a fix in progress. It's a big patch, since it required me to move all the Object methods up into RubyBasicObject and add stubs to RubyKernel to bind them. But it's running tests now, and should be landing soon.
          Hide
          Charles Oliver Nutter added a comment -

          It's amazing this didn't bite us sooner.

          I've fixed it as described above and in my commit below.

          commit 6eb8df5bd511405c8f7e465ec7e24d1223880508
          Author: Charles Oliver Nutter <headius@headius.com>
          Date: Thu Jan 20 14:24:01 2011 -0600

          Fix JRUBY-4871: [1.9] Attempt to invoke any method on Delegator leads to ClassCastException

          • Moved all methods defined by Kernel into pass-through methods in RubyKernel and impls in RubyBasicObject
          • RubyKernel, RubyBasicObject, and RubyObject now define their methods only from their own class
          • Arity-split 1.9 default initialize
          • Deleted RubyObject methods that only called super version
          • Also fixes JRUBY-5366: jruby 1.6 RC1 ruby 1.9: org.jruby.RubyBasicObject cannot be cast to org.jruby.RubyObject
          Show
          Charles Oliver Nutter added a comment - It's amazing this didn't bite us sooner. I've fixed it as described above and in my commit below. commit 6eb8df5bd511405c8f7e465ec7e24d1223880508 Author: Charles Oliver Nutter <headius@headius.com> Date: Thu Jan 20 14:24:01 2011 -0600 Fix JRUBY-4871 : [1.9] Attempt to invoke any method on Delegator leads to ClassCastException Moved all methods defined by Kernel into pass-through methods in RubyKernel and impls in RubyBasicObject RubyKernel, RubyBasicObject, and RubyObject now define their methods only from their own class Arity-split 1.9 default initialize Deleted RubyObject methods that only called super version Also fixes JRUBY-5366 : jruby 1.6 RC1 ruby 1.9: org.jruby.RubyBasicObject cannot be cast to org.jruby.RubyObject

            People

            • Assignee:
              Charles Oliver Nutter
              Reporter:
              Vladimir Sizikov
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: