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