diff options
author | Rich Hickey <richhickey@gmail.com> | 2009-11-21 10:52:15 -0500 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2009-11-21 10:52:15 -0500 |
commit | d923bb2443b15753bccfa753e9677504ffb90188 (patch) | |
tree | f97a466f29ca32bd4ae688535a342f57c67fbf2c | |
parent | 75cd05080f7260c54007d7728fb280ae53b56f63 (diff) |
direct calls through to on interface methods
-rw-r--r-- | src/clj/clojure/core_deftype.clj | 27 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Compiler.java | 113 |
2 files changed, 126 insertions, 14 deletions
diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj index 85f3b422..e68f5874 100644 --- a/src/clj/clojure/core_deftype.clj +++ b/src/clj/clojure/core_deftype.clj @@ -203,6 +203,8 @@ (cons c (super-chain (.getSuperclass c))))) (defn find-protocol-impl [protocol x] + (if (and (:on protocol) (instance? (:on protocol) x)) + x (let [t (type x) c (class x) impl #(get (:impls protocol) %)] @@ -210,7 +212,7 @@ (impl c) (and c (or (first (remove nil? (map impl (butlast (super-chain c))))) (first (remove nil? (map impl (disj (supers c) Object)))) - (impl Object)))))) + (impl Object))))))) (defn find-protocol-method [protocol methodk x] (get (find-protocol-impl protocol x) methodk)) @@ -228,7 +230,9 @@ (defn satisfies? "Returns true if x satisfies the protocol" [protocol x] - (when (find-protocol-impl protocol x) + (when + (or (and (:on protocol) (instance? (:on protocol) x)) + (find-protocol-impl protocol x)) true)) (defn -cache-protocol-fn [#^clojure.lang.AFunction pf x] @@ -241,7 +245,7 @@ (set! (.__methodImplCache pf) (expand-method-impl-cache cache (class x) f)) f)) -(defn- emit-method-builder [method arglists] +(defn- emit-method-builder [on-interface method on-method arglists] (let [methodk (keyword method) gthis (with-meta (gensym) {:tag 'clojure.lang.AFunction})] `(fn [cache#] @@ -252,13 +256,17 @@ (let [gargs (map #(gensym (str "g__" % "__")) args) target (first gargs)] `([~@gargs] + (~@(if on-interface + `(if (instance? ~on-interface ~target) + (. ~(with-meta target {:tag on-interface}) ~(or on-method method) ~@(rest gargs))) + `(do)) (let [cache# (.__methodImplCache ~gthis)] (if (clojure.lang.Util/identical (clojure.lang.Util/classOf ~target) (.lastClass cache#)) ((.lastImpl cache#) ~@gargs) (let [f# (or (.fnFor cache# (clojure.lang.Util/classOf ~target)) (-cache-protocol-fn ~gthis ~target))] - (f# ~@gargs))))))) + (f# ~@gargs)))))))) arglists))] (set! (.__methodImplCache f#) cache#) f#)))) @@ -281,7 +289,7 @@ (defn- emit-protocol [name opts+sigs] (let [[opts sigs] - (loop [opts {} sigs opts+sigs] + (loop [opts {:on nil} sigs opts+sigs] (condp #(%1 %2) (first sigs) string? (recur (assoc opts :doc (first sigs)) (next sigs)) keyword? (recur (assoc opts (first sigs) (second sigs)) (nnext sigs)) @@ -308,12 +316,19 @@ (assoc ~opts :sigs '~sigs :var (var ~name) + :method-map + ~(and (:on opts) + (apply hash-map + (mapcat + (fn [s] + [(keyword (:name s)) (keyword (or (:on s) (:name s)))]) + (vals sigs)))) :method-builders ~(apply hash-map (mapcat (fn [s] [`(intern *ns* (with-meta '~(:name s) {:protocol (var ~name)})) - (emit-method-builder (:name s) (:arglists s))]) + (emit-method-builder (:on opts) (:name s) (:on s) (:arglists s))]) (vals sigs))))) (-reset-methods ~name) '~name))) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index d9eeb7f1..03b592df 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -217,6 +217,9 @@ static final public Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol. static final public Var COMPILE_FILES = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), Symbol.create("*compile-files*"), Boolean.FALSE); +static final public Var INSTANCE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), + Symbol.create("instance?")); + //Integer static final public Var LINE = Var.create(0); @@ -2682,6 +2685,48 @@ static class KeywordInvokeExpr implements Expr{ // } // //} + +public static class InstanceOfExpr implements Expr, MaybePrimitiveExpr{ + Expr expr; + Class c; + + public InstanceOfExpr(Class c, Expr expr){ + this.expr = expr; + this.c = c; + } + + public Object eval() throws Exception{ + if(c.isInstance(expr.eval())) + return RT.T; + return RT.F; + } + + public boolean canEmitPrimitive(){ + return true; + } + + public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){ + expr.emit(context,objx,gen); + gen.instanceOf(Type.getType(c)); + } + + public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ + emitUnboxed(context,objx,gen); + HostExpr.emitBoxReturn(objx,gen,Boolean.TYPE); + if(context == C.STATEMENT) + gen.pop(); + } + + public boolean hasJavaClass() throws Exception{ + return true; + } + + public Class getJavaClass() throws Exception{ + return Boolean.TYPE; + } + +} + static class InvokeExpr implements Expr{ public final Expr fexpr; public final Object tag; @@ -2690,19 +2735,37 @@ static class InvokeExpr implements Expr{ public final String source; public boolean isProtocol = false; public int siteIndex = 0; + public Class protocolOn; + public java.lang.reflect.Method onMethod; + static Keyword onKey = Keyword.intern("on"); + static Keyword methodMapKey = Keyword.intern("method-map"); - public InvokeExpr(String source, int line, Symbol tag, Expr fexpr, IPersistentVector args){ + public InvokeExpr(String source, int line, Symbol tag, Expr fexpr, IPersistentVector args) throws Exception{ this.source = source; this.fexpr = fexpr; this.args = args; this.line = line; if(fexpr instanceof VarExpr) { - Var pvar = (Var)RT.get(((VarExpr)fexpr).var.meta(), protocolKey); + Var fvar = ((VarExpr)fexpr).var; + Var pvar = (Var)RT.get(fvar.meta(), protocolKey); if(pvar != null) { this.isProtocol = true; this.siteIndex = registerProtocolCallsite(((VarExpr)fexpr).var); + Object pon = RT.get(pvar.get(), onKey); + this.protocolOn = HostExpr.maybeClass(pon,false); + if(this.protocolOn != null) + { + IPersistentMap mmap = (IPersistentMap) RT.get(pvar.get(), methodMapKey); + String mname = ((Keyword) mmap.valAt(Keyword.intern(fvar.sym))).sym.toString(); + List methods = Reflector.getMethods(protocolOn, args.count() - 1, mname, false); + if(methods.size() != 1) + throw new IllegalArgumentException( + "No single method: " + mname + " of interface: " + protocolOn.getName() + + " found for function: " + fvar.sym + " of protocol: " + pvar.sym); + this.onMethod = (java.lang.reflect.Method) methods.get(0); + } } } this.tag = tag != null ? tag : (fexpr instanceof VarExpr ? ((VarExpr) fexpr).tag : null); @@ -2731,7 +2794,6 @@ static class InvokeExpr implements Expr{ if(isProtocol) { emitProto(context,objx,gen); - emitArgsAndCall(1, context,objx,gen); } else { @@ -2739,6 +2801,8 @@ static class InvokeExpr implements Expr{ gen.checkCast(IFN_TYPE); emitArgsAndCall(0, context,objx,gen); } + if(context == C.STATEMENT) + gen.pop(); } public void emitProto(C context, ObjExpr objx, GeneratorAdapter gen){ @@ -2746,13 +2810,21 @@ static class InvokeExpr implements Expr{ Label notSameClassLabel = gen.newLabel(); Label faultLabel = gen.newLabel(); Label callLabel = gen.newLabel(); + Label onLabel = gen.newLabel(); + Label endLabel = gen.newLabel(); + Var v = ((VarExpr)fexpr).var; Expr e = (Expr) args.nth(0); e.emit(C.EXPRESSION, objx, gen); gen.dup(); //target, target + if(protocolOn != null) + { + gen.instanceOf(Type.getType(protocolOn)); + gen.ifZCmp(GeneratorAdapter.NE, onLabel); + gen.dup(); + } gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class -// gen.invokeVirtual(OBJECT_TYPE,Method.getMethod("Class getClass()")); //target,class gen.dup(); //target,class,class gen.loadThis(); gen.getField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,class,cached-class @@ -2800,16 +2872,33 @@ static class InvokeExpr implements Expr{ gen.swap(); //impl,target gen.goTo(callLabel); - //not in fn table, null out cached fn and use proto-fn itself (whcih should seed table for next time) + //not in fn table, null out cached fn and use proto-fn itself (which should seed table for next time) gen.mark(faultLabel); //target,protofn,null gen.pop(); //target, protofn gen.swap(); //protofn, target gen.loadThis(); gen.visitInsn(Opcodes.ACONST_NULL); gen.putField(objx.objtype, objx.cachedProtoFnName(siteIndex), AFUNCTION_TYPE); //target, class, proto-fn - + gen.goTo(callLabel); gen.mark(callLabel); //impl, target + emitArgsAndCall(1, context,objx,gen); + gen.goTo(endLabel); + + gen.mark(onLabel); //target + if(protocolOn != null) + { + MethodExpr.emitTypedArgs(objx, gen, onMethod.getParameterTypes(), RT.subvec(args,1,args.count())); + if(context == C.RETURN) + { + ObjMethod method = (ObjMethod) METHOD.deref(); + method.emitClearLocals(gen); + } + Method m = new Method(onMethod.getName(), Type.getReturnType(onMethod), Type.getArgumentTypes(onMethod)); + gen.invokeInterface(Type.getType(protocolOn), m); + HostExpr.emitBoxReturn(objx, gen, onMethod.getReturnType()); + } + gen.mark(endLabel); } @@ -2837,8 +2926,6 @@ static class InvokeExpr implements Expr{ gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[Math.min(MAX_POSITIONAL_ARITY + 1, args.count())])); - if(context == C.STATEMENT) - gen.pop(); } public boolean hasJavaClass() throws Exception{ @@ -2853,6 +2940,16 @@ static class InvokeExpr implements Expr{ if(context != C.EVAL) context = C.EXPRESSION; Expr fexpr = analyze(context, form.first()); + if(fexpr instanceof VarExpr && ((VarExpr)fexpr).var.equals(INSTANCE)) + { + if(RT.second(form) instanceof Symbol) + { + Class c = HostExpr.maybeClass(RT.second(form),false); + if(c != null) + return new InstanceOfExpr(c, analyze(context, RT.third(form))); + } + } + if(fexpr instanceof KeywordExpr && RT.count(form) == 2) { // fexpr = new ConstantExpr(new KeywordCallSite(((KeywordExpr)fexpr).k)); |