diff options
author | Rich Hickey <richhickey@gmail.com> | 2011-02-28 17:55:14 -0500 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2011-02-28 17:55:14 -0500 |
commit | 71930b6b6537a796cdf13c4ffa7cf93eb53b6235 (patch) | |
tree | 40ce0541b899e0159af36d78e383da0569aec2b9 | |
parent | 174bd5001264f5c43276922505ba526aae471028 (diff) |
improve startup time via lazy defn loading
-rw-r--r-- | src/clj/clojure/main.clj | 66 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Compiler.java | 37 | ||||
-rw-r--r-- | src/jvm/clojure/lang/FnLoaderThunk.java | 68 | ||||
-rw-r--r-- | src/jvm/clojure/lang/RT.java | 1 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Var.java | 2 |
5 files changed, 170 insertions, 4 deletions
diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj index 957a2b7d..5598daf9 100644 --- a/src/clj/clojure/main.clj +++ b/src/clj/clojure/main.clj @@ -14,10 +14,74 @@ (:refer-clojure :exclude [with-bindings]) (:import (clojure.lang Compiler Compiler$CompilerException LineNumberingPushbackReader RT)) - (:use [clojure.repl :only (demunge root-cause stack-element-str)])) + ;;(:use [clojure.repl :only (demunge root-cause stack-element-str)]) + ) (declare main) +;;;;;;;;;;;;;;;;;;; redundantly copied from clojure.repl to avoid dep ;;;;;;;;;;;;;; +#_(defn root-cause [x] x) +#_(defn stack-element-str + "Returns a (possibly unmunged) string representation of a StackTraceElement" + {:added "1.3"} + [^StackTraceElement el] + (.getClassName el)) + +(def ^:private demunge-map + (into {"$" "/"} (map (fn [[k v]] [v k]) clojure.lang.Compiler/CHAR_MAP))) + +(def ^:private demunge-pattern + (re-pattern (apply str (interpose "|" (map #(str "\\Q" % "\\E") + (keys demunge-map)))))) + +(defn- re-replace [re s f] + (let [m (re-matcher re s) + mseq (take-while identity + (repeatedly #(when (re-find m) + [(re-groups m) (.start m) (.end m)])))] + (apply str + (concat + (mapcat (fn [[_ _ start] [groups end]] + (if end + [(subs s start end) (f groups)] + [(subs s start)])) + (cons [0 0 0] mseq) + (concat mseq [nil])))))) + +(defn demunge + "Given a string representation of a fn class, + as in a stack trace element, returns a readable version." + {:added "1.3"} + [fn-name] + (re-replace demunge-pattern fn-name demunge-map)) + +(defn root-cause + "Returns the initial cause of an exception or error by peeling off all of + its wrappers" + {:added "1.3"} + [^Throwable t] + (loop [cause t] + (if (and (instance? clojure.lang.Compiler$CompilerException cause) + (not= (.source ^clojure.lang.Compiler$CompilerException cause) "NO_SOURCE_FILE")) + cause + (if-let [cause (.getCause cause)] + (recur cause) + cause)))) + +(defn stack-element-str + "Returns a (possibly unmunged) string representation of a StackTraceElement" + {:added "1.3"} + [^StackTraceElement el] + (let [file (.getFileName el) + clojure-fn? (and file (or (.endsWith file ".clj") + (= file "NO_SOURCE_FILE")))] + (str (if clojure-fn? + (demunge (.getClassName el)) + (str (.getClassName el) "." (.getMethodName el))) + " (" (.getFileName el) ":" (.getLineNumber el) ")"))) +;;;;;;;;;;;;;;;;;;; end of redundantly copied from clojure.repl to avoid dep ;;;;;;;;;;;;;; + + (defmacro with-bindings "Executes body in the context of thread-local bindings for several vars that often need to be set!: *ns* *warn-on-reflection* *math-context* diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 1fc3aceb..b64ff3b4 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -411,7 +411,12 @@ static class DefExpr implements Expr{ if(initProvided) { gen.dup(); - init.emit(C.EXPRESSION, objx, gen); + if(init instanceof FnExpr) + { + ((FnExpr)init).emitForDefn(objx, gen); + } + else + init.emit(C.EXPRESSION, objx, gen); gen.invokeVirtual(VAR_TYPE, bindRootMethod); } @@ -3414,6 +3419,7 @@ static public class FnExpr extends ObjExpr{ //if there is a variadic overload (there can only be one) it is stored here FnMethod variadicMethod = null; IPersistentCollection methods; + private boolean hasPrimSigs; // String superName = null; public FnExpr(Object tag){ @@ -3556,6 +3562,7 @@ static public class FnExpr extends ObjExpr{ { Var.popThreadBindings(); } + fn.hasPrimSigs = prims.size() > 0; fn.compile(fn.isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction", (prims.size() == 0)? null @@ -3581,6 +3588,22 @@ static public class FnExpr extends ObjExpr{ public final IPersistentCollection methods(){ return methods; } + + public void emitForDefn(ObjExpr objx, GeneratorAdapter gen){ + if(!hasPrimSigs && closes.count() == 0) + { + Type thunkType = Type.getType(FnLoaderThunk.class); + //presumes var on stack + gen.dup(); + gen.newInstance(thunkType); + gen.dupX1(); + gen.swap(); + gen.push(internalName.replace('/','.')); + gen.invokeConstructor(thunkType,Method.getMethod("void <init>(clojure.lang.Var,String)")); + } + else + emit(C.EXPRESSION,objx,gen); + } } static public class ObjExpr implements Expr{ @@ -6702,6 +6725,13 @@ public static void pushNS(){ Symbol.intern("*ns*")).setDynamic(), null)); } +public static void pushNSandLoader(ClassLoader loader){ + Var.pushThreadBindings(RT.map(Var.intern(Symbol.intern("clojure.core"), + Symbol.intern("*ns*")).setDynamic(), + null, + RT.FN_LOADER_VAR, loader)); +} + public static ILookupThunk getLookupThunk(Object target, Keyword k){ return null; //To change body of created methods use File | Settings | File Templates. } @@ -6857,7 +6887,10 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t for(int n = 0;n<numInits;n++) clinitgen.invokeStatic(objx.objtype, Method.getMethod("void __init" + n + "()")); - clinitgen.invokeStatic(Type.getType(Compiler.class), Method.getMethod("void pushNS()")); + clinitgen.push(objx.internalName.replace('/','.')); + clinitgen.invokeStatic(CLASS_TYPE, Method.getMethod("Class forName(String)")); + clinitgen.invokeVirtual(CLASS_TYPE,Method.getMethod("ClassLoader getClassLoader()")); + clinitgen.invokeStatic(Type.getType(Compiler.class), Method.getMethod("void pushNSandLoader(ClassLoader)")); clinitgen.mark(startTry); clinitgen.invokeStatic(objx.objtype, Method.getMethod("void load()")); clinitgen.mark(endTry); diff --git a/src/jvm/clojure/lang/FnLoaderThunk.java b/src/jvm/clojure/lang/FnLoaderThunk.java new file mode 100644 index 00000000..2a9af7bd --- /dev/null +++ b/src/jvm/clojure/lang/FnLoaderThunk.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) + * which can be found in the file epl-v10.html at the root of this distribution. + * By using this software in any fashion, you are agreeing to be bound by + * the terms of this license. + * You must not remove this notice, or any other, from this software. + **/ + +/* rich 2/28/11 */ + +package clojure.lang; + +public class FnLoaderThunk extends RestFn{ + +final Var v; +final ClassLoader loader; +final String fnClassName; +IFn fn; + +public FnLoaderThunk(Var v, String fnClassName){ + this.v = v; + this.loader = (ClassLoader) RT.FN_LOADER_VAR.get(); + this.fnClassName = fnClassName; + fn = null; +} + +public Object invoke(Object arg1) throws Exception{ + load(); + return fn.invoke(arg1); +} + +public Object invoke(Object arg1, Object arg2) throws Exception{ + load(); + return fn.invoke(arg1,arg2); +} + +public Object invoke(Object arg1, Object arg2, Object arg3) throws Exception{ + load(); + return fn.invoke(arg1,arg2,arg3); +} + +protected Object doInvoke(Object args) throws Exception{ + load(); + return fn.applyTo((ISeq) args); +} + +private void load() throws Exception{ + if(fn == null) + { + fn = (IFn) Class.forName(fnClassName,true,loader).newInstance(); + v.root = fn; + } +} + +public int getRequiredArity(){ + return 0; +} + +public IObj withMeta(IPersistentMap meta){ + return this; +} + +public IPersistentMap meta(){ + return null; +} +} diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index aad678c7..34da51b1 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -209,6 +209,7 @@ final static Var ALLOW_UNRESOLVED_VARS = Var.intern(CLOJURE_NS, Symbol.intern("* final static Var IN_NS_VAR = Var.intern(CLOJURE_NS, Symbol.intern("in-ns"), F); final static Var NS_VAR = Var.intern(CLOJURE_NS, Symbol.intern("ns"), F); +final static Var FN_LOADER_VAR = Var.intern(CLOJURE_NS, Symbol.intern("*fn-loader*"), null).setDynamic(); static final Var PRINT_INITIALIZED = Var.intern(CLOJURE_NS, Symbol.intern("print-initialized")); static final Var PR_ON = Var.intern(CLOJURE_NS, Symbol.intern("pr-on")); //final static Var IMPORTS = Var.intern(CLOJURE_NS, Symbol.intern("*imports*"), DEFAULT_IMPORTS); diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java index aa7e8bfd..ed9d0a59 100644 --- a/src/jvm/clojure/lang/Var.java +++ b/src/jvm/clojure/lang/Var.java @@ -79,7 +79,7 @@ static Keyword nameKey = Keyword.intern(null, "name"); static Keyword nsKey = Keyword.intern(null, "ns"); //static Keyword tagKey = Keyword.intern(null, "tag"); -private volatile Object root; +volatile Object root; volatile boolean dynamic = false; transient final AtomicBoolean threadBound; |