summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2011-02-28 17:55:14 -0500
committerRich Hickey <richhickey@gmail.com>2011-02-28 17:55:14 -0500
commit71930b6b6537a796cdf13c4ffa7cf93eb53b6235 (patch)
tree40ce0541b899e0159af36d78e383da0569aec2b9
parent174bd5001264f5c43276922505ba526aae471028 (diff)
improve startup time via lazy defn loading
-rw-r--r--src/clj/clojure/main.clj66
-rw-r--r--src/jvm/clojure/lang/Compiler.java37
-rw-r--r--src/jvm/clojure/lang/FnLoaderThunk.java68
-rw-r--r--src/jvm/clojure/lang/RT.java1
-rw-r--r--src/jvm/clojure/lang/Var.java2
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;