Details

    • Type: Bug Bug
    • Status: Open Open
    • Priority: Minor Minor
    • Resolution: Unresolved
    • Affects Version/s: JRuby 1.6RC1
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None
    • Number of attachments :
      0

      Description

      1. Argument error in both MRI and JRuby:
        Proc.new # => tried to create Proc object without a block (ArgumentError)
        
      2. Argument error in both MRI and JRuby:
        class P < Proc; end; p=P.new # => tried to create Proc object without a block (ArgumentError)
        
      3. Returns "hello" in MRI and Rubinius; ArgumentError raised in JRuby.
        class P < Proc; end; def f; P.new; end; x = f {"hello"}; x.call # => "hello"
        

      As for the last case, it just seems wrong to me that it works at all in MRI.

        Activity

        Hide
        Charles Oliver Nutter added a comment -

        The reason this works at all in MRI is accidental; because MRI only updates its global per-call state piece by piece, the Proc.new call inherits the environment from the 'f' method and is able to see and capture its block.

        I would like to disable this feature entirely, but I know some code uses it for Proc.new. The missing behavior is that we don't completely support it for subclasses of Proc, and this is a much harder thing to fix.

        When JRuby compiles, it looks for things like blocks captured (&b), closures in the method body, "eval" calls, "binding" calls, and so on. It uses this information to prepare appropriate state for the call. In the case of:

        def foo
          Proc.new
        end
        

        We additionally use the presence of "Proc.new" in that exact form to know that this magic behavior is in play and the implicit block should not be discarded.

        In the case reported here, because the form is not "Proc.new" exactly, we can't determine that the implicit block is being used for anything, and as an optimization we don't save it anywhere. When the subclass of Proc tries to "new", no block is available and an ArgumentError results.

        The really awful part of this feature is that in order to support it we'd have to defeat a very large optimization that improves performance of Ruby method calls by many times. Given the rarity of a class that subclasses proc and is constructed without passing in a block or block argument, I don't think it's worth it.

        There are a few workarounds possible in JRuby, and I'd be happy to make some of them more "formal" triggers to keep the block around. The first, and simplest, is to just capture the block argument normally (def foo(&block)). There's very little performance penalty for this if you don't pass in a block, which is one of the primary arguments for the magic Proc.new syntax.

        A second way is to include another call that depends on being able to see the implicit block, like block_given?:

        class P < Proc; end
        def foo
          P.new if block_given?
        end
        

        This should always work, since block_given? is a known method that triggers keeping the implicit block around.

        At the moment, I'm going to leave this one unresolved and not attempt to fix it for 1.6, since doing so would require reverting one of the largest optimizations we've made in recent years.

        Show
        Charles Oliver Nutter added a comment - The reason this works at all in MRI is accidental; because MRI only updates its global per-call state piece by piece, the Proc.new call inherits the environment from the 'f' method and is able to see and capture its block. I would like to disable this feature entirely, but I know some code uses it for Proc.new. The missing behavior is that we don't completely support it for subclasses of Proc, and this is a much harder thing to fix. When JRuby compiles, it looks for things like blocks captured (&b), closures in the method body, "eval" calls, "binding" calls, and so on. It uses this information to prepare appropriate state for the call. In the case of: def foo Proc.new end We additionally use the presence of "Proc.new" in that exact form to know that this magic behavior is in play and the implicit block should not be discarded. In the case reported here, because the form is not "Proc.new" exactly, we can't determine that the implicit block is being used for anything, and as an optimization we don't save it anywhere. When the subclass of Proc tries to "new", no block is available and an ArgumentError results. The really awful part of this feature is that in order to support it we'd have to defeat a very large optimization that improves performance of Ruby method calls by many times. Given the rarity of a class that subclasses proc and is constructed without passing in a block or block argument, I don't think it's worth it. There are a few workarounds possible in JRuby, and I'd be happy to make some of them more "formal" triggers to keep the block around. The first, and simplest, is to just capture the block argument normally (def foo(&block)). There's very little performance penalty for this if you don't pass in a block, which is one of the primary arguments for the magic Proc.new syntax. A second way is to include another call that depends on being able to see the implicit block, like block_given?: class P < Proc; end def foo P.new if block_given? end This should always work, since block_given? is a known method that triggers keeping the implicit block around. At the moment, I'm going to leave this one unresolved and not attempt to fix it for 1.6, since doing so would require reverting one of the largest optimizations we've made in recent years.

          People

          • Assignee:
            Thomas E Enebo
            Reporter:
            Hiro Asari
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated: