This could be really big. Patch attached.

## script
require 'benchmark'

c = 0
script = 'a = 1; b = 2; c = c + a + b'
bnd = binding

puts "implicit binding"
puts Benchmark.realtime { 1000000.times { eval script } }
puts "explicit binding"
puts Benchmark.realtime { 1000000.times { eval script, bnd } }

## before
~/NetBeansProjects/jruby $ jruby bench_eval.rb
implicit binding
85.61300015449524
explicit binding
81.47699999809265
~/NetBeansProjects/jruby $ jruby -J-server -O bench_eval.rb
implicit binding
69.26199984550476
explicit binding
67.39000010490417

## after
~/NetBeansProjects/jruby $ jruby bench_eval.rb
implicit binding
8.778000116348267
explicit binding
4.713000059127808
~/NetBeansProjects/jruby $ jruby -J-server -O bench_eval.rb
implicit binding
4.736999988555908
explicit binding
3.310999870300293

- Charlie
Index: src/org/jruby/runtime/DynamicScope.java
===================================================================
--- src/org/jruby/runtime/DynamicScope.java     (revision 4422)
+++ src/org/jruby/runtime/DynamicScope.java     (working copy)
@@ -219,6 +219,10 @@
         return staticScope;
     }
     
+    public void setStaticScope(StaticScope staticScope) {
+        this.staticScope = staticScope;
+    }
+    
     public String toString() {
         return toString(new StringBuffer(), "");
     }
Index: src/org/jruby/runtime/ThreadContext.java
===================================================================
--- src/org/jruby/runtime/ThreadContext.java    (revision 4422)
+++ src/org/jruby/runtime/ThreadContext.java    (working copy)
@@ -771,8 +771,8 @@
         pushRubyClass(block.getKlass());
     }
     
-    public void postEvalWithBinding(Block block) {
-        block.getFrame().setIsBindingFrame(false);
+    public void postEvalWithBinding() {
+        getCurrentFrame().setIsBindingFrame(false);
         popFrameReal();
         popRubyClass();
     }
Index: src/org/jruby/javasupport/util/CompilerHelpers.java
===================================================================
--- src/org/jruby/javasupport/util/CompilerHelpers.java (revision 4422)
+++ src/org/jruby/javasupport/util/CompilerHelpers.java (working copy)
@@ -679,6 +679,21 @@
         context.postScopedBody();
     }
     
+    public static void preEval(ThreadContext context, String[] varNames, Block 
binding) {
+        context.preEvalWithBinding(binding);
+        
+        BlockStaticScope staticScope = 
(BlockStaticScope)context.getCurrentScope().getStaticScope();
+        for (int i = 0; i < varNames.length; i++) {
+            staticScope.addVariable(varNames[i]);
+        }
+        
+        context.getCurrentScope().growIfNeeded();
+    }
+    
+    public static void postEval(ThreadContext context) {
+        context.postEvalWithBinding();
+    }
+    
     public static void registerEndBlock(CompiledSharedScopeBlock block, Ruby 
runtime) {
         runtime.pushExitBlock(runtime.newProc(true, block));
     }
Index: src/org/jruby/RubyObject.java
===================================================================
--- src/org/jruby/RubyObject.java       (revision 4422)
+++ src/org/jruby/RubyObject.java       (working copy)
@@ -37,6 +37,7 @@
  ***** END LICENSE BLOCK *****/
 package org.jruby;
 
+import java.io.StringReader;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.jruby.evaluator.EvaluationState;
 import org.jruby.exceptions.JumpException;
@@ -58,7 +59,9 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.WeakHashMap;
 import org.jruby.ast.Node;
+import org.jruby.ast.executable.Script;
 import org.jruby.runtime.ClassIndex;
 import org.jruby.runtime.MethodIndex;
 
@@ -862,6 +865,8 @@
             }
         }, args, block);
     }
+    
+    private static final WeakHashMap<Integer, Script> EVAL_SCRIPTS = new 
WeakHashMap<Integer, Script>();
 
     /* (non-Javadoc)
      * @see 
org.jruby.runtime.builtin.IRubyObject#evalWithBinding(org.jruby.runtime.builtin.IRubyObject,
 org.jruby.runtime.builtin.IRubyObject, java.lang.String)
@@ -888,22 +893,49 @@
         blockOfBinding.getDynamicScope().getStaticScope().determineModule();
 
         try {
-            // Binding provided for scope, use it
-            context.preEvalWithBinding(blockOfBinding);
-            IRubyObject newSelf = context.getFrameSelf();
-            Node node = 
-                getRuntime().parseEval(src.toString(), file, 
blockOfBinding.getDynamicScope(), lineNumber);
+            Integer key = src.hashCode() + 
blockOfBinding.getDynamicScope().getStaticScope().getEnclosingScope().hashCode();
+            if (EVAL_SCRIPTS.containsKey(key)) {
+                Script script = EVAL_SCRIPTS.get(key);
+                
+                if (script == null) {
+                    Node node = getRuntime().parseEval(src.toString(), file, 
blockOfBinding.getDynamicScope(), lineNumber);
+                    script = getRuntime().tryCompile(node);
+                    EVAL_SCRIPTS.put(key, script);
+                }
+                
+                try {
+                    context.pushScope(blockOfBinding.getDynamicScope());
+                    
+                    return script.eval(context, this, NULL_ARRAY, 
blockOfBinding);
+                } finally {
+                    context.popScope();
+                }
+            } else {
+                // Binding provided for scope, use it
+                context.preEvalWithBinding(blockOfBinding);
+                IRubyObject newSelf = context.getFrameSelf();
+                Node node = 
+                    getRuntime().parseEval(src.toString(), file, 
blockOfBinding.getDynamicScope(), lineNumber);
+                
+                // store eval'ed script in the weak map, in case we end up 
seeing it again
+                EVAL_SCRIPTS.put(key, null);
 
-            return EvaluationState.eval(getRuntime(), context, node, newSelf, 
blockOfBinding);
+                try {
+                    return EvaluationState.eval(getRuntime(), context, node, 
newSelf, blockOfBinding);
+                } finally {
+                    context.postEvalWithBinding();
+
+                    // restore position
+                    context.setPosition(savedPosition);
+                }
+            }
         } catch (JumpException.BreakJump bj) {
             throw getRuntime().newLocalJumpError("break", 
(IRubyObject)bj.getValue(), "unexpected break");
         } catch (JumpException.RedoJump rj) {
             throw getRuntime().newLocalJumpError("redo", 
(IRubyObject)rj.getValue(), "unexpected redo");
-        } finally {
-            context.postEvalWithBinding(blockOfBinding);
-
-            // restore position
-            context.setPosition(savedPosition);
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw new RuntimeException(t);
         }
     }
 
Index: src/org/jruby/Ruby.java
===================================================================
--- src/org/jruby/Ruby.java     (revision 4422)
+++ src/org/jruby/Ruby.java     (working copy)
@@ -479,19 +479,22 @@
         }
     }
     
-    private Script tryCompile(Node node) {
+    public Script tryCompile(Node node) {
         Script script = null;
         try {
             String filename = node.getPosition().getFile();
             String classname;
             if (filename.equals("-e")) {
                 classname = "__dash_e__";
+            } else if (filename.equals("(eval)")) {
+                classname = "__eval__" + node.hashCode();
             } else {
                 classname = filename.replace('\\', '/').replaceAll(".rb", "");
             }
 
             ASTInspector inspector = new ASTInspector();
             inspector.inspect(node);
+            inspector.disable();
 
             StandardASMCompiler compiler = new StandardASMCompiler(classname, 
filename);
             NodeCompilerFactory.compileRoot(node, compiler, inspector);
Index: src/org/jruby/compiler/impl/StandardASMCompiler.java
===================================================================
--- src/org/jruby/compiler/impl/StandardASMCompiler.java        (revision 4422)
+++ src/org/jruby/compiler/impl/StandardASMCompiler.java        (working copy)
@@ -265,7 +265,41 @@
         method.trycatch(tryBegin, tryFinally, tryFinally, null);
         
         method.end();
+        
+        // the eval method is used for evaluating under a binding
+        method = new 
SkinnyMethodAdapter(getClassVisitor().visitMethod(ACC_PUBLIC, "eval", 
METHOD_SIGNATURE, null, null));
+        method.start();
 
+        // invoke __file__ with threadcontext, self, args (null), and block 
(null)
+        tryBegin = new Label();
+        tryFinally = new Label();
+        
+        method.label(tryBegin);
+        method.aload(THREADCONTEXT_INDEX);
+        buildStaticScopeNames(method, topLevelScope);
+        method.aload(CLOSURE_INDEX);
+        method.invokestatic(cg.p(CompilerHelpers.class), "preEval", 
cg.sig(void.class, ThreadContext.class, String[].class, Block.class));
+        
+        method.aload(THIS);
+        method.aload(THREADCONTEXT_INDEX);
+        method.aload(SELF_INDEX);
+        method.aload(ARGS_INDEX);
+        method.aload(CLOSURE_INDEX);
+
+        method.invokevirtual(classname, methodName, METHOD_SIGNATURE);
+        method.aload(THREADCONTEXT_INDEX);
+        method.invokestatic(cg.p(CompilerHelpers.class), "postEval", 
cg.sig(void.class, ThreadContext.class));
+        method.areturn();
+        
+        method.label(tryFinally);
+        method.aload(THREADCONTEXT_INDEX);
+        method.invokestatic(cg.p(CompilerHelpers.class), "postEval", 
cg.sig(void.class, ThreadContext.class));
+        method.athrow();
+        
+        method.trycatch(tryBegin, tryFinally, tryFinally, null);
+        
+        method.end();
+
         // add main impl, used for detached or command-line execution of this 
script with a new runtime
         // root method of a script is always in stub0, method0
         method = new 
SkinnyMethodAdapter(getClassVisitor().visitMethod(ACC_PUBLIC | ACC_STATIC, 
"main", cg.sig(Void.TYPE, cg.params(String[].class)), null, null));
Index: src/org/jruby/ast/executable/Script.java
===================================================================
--- src/org/jruby/ast/executable/Script.java    (revision 4422)
+++ src/org/jruby/ast/executable/Script.java    (working copy)
@@ -7,4 +7,5 @@
 public interface Script {
     public IRubyObject run(ThreadContext context, IRubyObject self, 
IRubyObject[] args, Block block);
     public IRubyObject load(ThreadContext context, IRubyObject self, 
IRubyObject[] args, Block block);
+    public IRubyObject eval(ThreadContext context, IRubyObject self, 
IRubyObject[] args, Block binding);
 }

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply via email to