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