Hi,

Since my last mail about how to handle blocks and compiling I've been 
spending some time thinking about the correct way to handle closures. I 
think I've found a way to that'll probably work fine.
Let's take my last example one more time, compiling it as if each block 
was a closure (we wouldn't need to do this everywhere in reality of 
course): the file to be compiled, called foobar.rb:

class String
   def testOne(param1)
    @abc = "tttoo"
    @@baz = :arg

    index = 0
    each_byte { |b|
      index+=1
      0.upto(index) {|i| puts index,i}
    }
   end
end

"str".testOne(:fux)

this would compile into:

class String
   def testOne(param1)
    @abc = "tttoo"
    @@baz = :arg

    index = 0
    each_byte { |b|
      index+=1
      0.upto(index) {|i| puts index,i}
    }
   end
end

"str".testOne(:fux)


And this could get compiled more or less into a class looking somewhat
like this:

public class FoobarRubyFile {
   public IRubyObject testOne(IRuby runtime, IRubyObject self,
IRubyObject param1) {
    IRubyObject[] frame = new IRubyObject[2];
    runtime.pushFrame(frame);
    frame[0] = self;
    frame[0].instanceVariableSet("abc",runtime.newString("tttoo"));
    frame[0].singletonVariableSet("baz",runtime.newSymbol("arg"));
    frame[1] = RubyFixnum.zero(runtime);
    IRubyObject retval = self.each_byte(runtime,new Block1(runtime));
    runtime.popFrame();
    return retval;
   }

   public static class Block1 implements Block {
    private IRuby runtime; private FrameStack closure;
    public Block1(IRuby runtime) {
     this.runtime = runtime;
     this.closure = runtime.copyFrames(1);
    }
    public IRubyObject invoke(IRubyObject[] args) {
     runtime.pushFrame(args);
     runtime.getFrame(1)[1].invokeMethod("+=",RubyFixnum.one(runtime));
     IRubyObject retval =
RubyFixnum.zero(runtime).invokeMethod("upto",runtime.getFrame(1)[1],new
Block2(runtime));
     runtime.popFrame();
     return retval;
    }
   }
   public static class Block2 implements Block {
    private IRuby runtime; private FrameStack closure;
    public Block2(IRuby runtime) {
     this.runtime = runtime;
     this.closure = runtime.copyFrames(2);
    }
    public IRubyObject invoke(IRubyObject[] args) {
     runtime.pushFrame(args);
     IRubyObject retval = runtime.invokeMethod("puts",new
IRubyObject[]{runtime.getFrame(2)[1], args[0]})
     runtime.popFrame();
     return retval;
    }
   }

   public IRubyObject jruby_run(final IRuby runtime) {
    IRubyObject[] frame = new IRubyObject[3];
    runtime.pushFrame(frame);
    frame[0] = runtime.currentScopeSelf();
    RubyClass clsString = runtime.getOrDefineClass("String");

clsString.defineMethod("testOne",RubyArity.ONE,getClass().getMethod("testOne",null));

    frame[1] = runtime.newString("str");
    frame[2] = runtime.newSymbol("fux");
    IRubyObject retval = testOne(runtime,frame[1],frame[2]);
    runtime.popFrame();
    return retval;
   }
}

Actually, the only really big difference here, is that the constructor 
for the blocks doesn't take a reference to enclosing_self anymore. What 
they instead do is copy as many frames as compilation knows they need, 
thus keeping the reference to these objects even after they've died in 
the enclosing context. Since I seem to remember that instance variables 
are closed over too, we need to keep a reference to self somewhere. This 
will be kept at index 0 in every frame instantiated directly inside a 
class. The compiler knows if it will have to use 
instance_variable_get/set instead of dereferencing directly. What's nice 
by this approach is that closure more or less gets there automatically. 
If we do the classical closure example of a bank account with two blocks 
that closes over a balance binding, they will reference the same binding 
through the frame without actually doing anything more than copy the frame.

The big issue with this solution is of course that many more variables 
will be closed over than necessary sometimes. The problem is, I can't 
come up with a good way of closing only over used variables without the 
rest of the system taking a hit, either by having more dereferencing 
when using local vars, or some other not-so-good options.

Anyway, just some thoughts. Right now it's quite theoritacal. Yet, 
anyway. I'm thinking about seeing how hard it would be to do a complete 
compiler that spits out pseudocode like this.

-- 
  Ola Bini (http://ola-bini.blogspot.com)
  JvYAML, RbYAML, JRuby and Jatha contributor
  System Developer, Karolinska Institutet (http://www.ki.se)
  OLogix Consulting (http://www.ologix.com)

  "Yields falsehood when quined" yields falsehood when quined.



-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Jruby-devel mailing list
Jruby-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jruby-devel

Reply via email to