Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Method missing patch for clojure compiler
From d918ba819dc67d9977c3a13c1d3ac16e46e02dc4 Mon Sep 17 00:00:00 2001
From: dennis zhuang <killme2008@gmail.com>
Date: Tue, 28 Jun 2016 00:30:16 +0800
Subject: [PATCH] Patch Compiler#resolveIn to introduce method_missing
---
src/jvm/clojure/lang/Compiler.java | 23 ++++++++++++++++++++---
src/jvm/clojure/lang/Var.java | 9 +++++++++
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 6066825..9fa1005 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -3556,31 +3556,31 @@ static class StaticInvokeExpr implements Expr, MaybePrimitiveExpr{
Type target = Type.getType(c);
PersistentVector argv = PersistentVector.EMPTY;
for(ISeq s = RT.seq(args); s != null; s = s.next())
argv = argv.cons(analyze(C.EXPRESSION, s.first()));
return new StaticInvokeExpr(target,retClass,paramClasses, paramTypes,variadic, argv, tag);
}
}
static class InvokeExpr implements Expr{
public final Expr fexpr;
public final Object tag;
- public final IPersistentVector args;
+ public IPersistentVector args;
public final int line;
public final int column;
public final String source;
public boolean isProtocol = false;
public boolean isDirect = false;
public int siteIndex = -1;
public Class protocolOn;
public java.lang.reflect.Method onMethod;
static Keyword onKey = Keyword.intern("on");
static Keyword methodMapKey = Keyword.intern("method-map");
static Object sigTag(int argcount, Var v){
Object arglists = RT.get(RT.meta(v), arglistsKey);
Object sigTag = null;
for(ISeq s = RT.seq(arglists); s != null; s = s.next())
@@ -3622,43 +3622,56 @@ static class InvokeExpr implements Expr{
String mname = munge(mmapVal.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);
}
}
}
if (tag != null) {
this.tag = tag;
} else if (fexpr instanceof VarExpr) {
Var v = ((VarExpr) fexpr).var;
+ if(v.rawSym!=null){
+ PersistentVector argvs = PersistentVector.EMPTY;
+ argvs = argvs.cons(new StringExpr(v.rawSym.toString()));
+ for(int i = 0; i < args.count(); i++)
+ argvs = argvs.cons(args.nth(i));
+ this.args = argvs;
+ }
Object arglists = RT.get(RT.meta(v), arglistsKey);
Object sigTag = sigTag(args.count(),v);
this.tag = sigTag == null ? ((VarExpr) fexpr).tag : sigTag;
} else {
this.tag = null;
}
}
public Object eval() {
try
{
IFn fn = (IFn) fexpr.eval();
PersistentVector argvs = PersistentVector.EMPTY;
+ if (fexpr instanceof VarExpr){
+ Var v = ((VarExpr) fexpr).var;
+ if(v.rawSym!=null){
+ argvs = argvs.cons(new StringExpr(v.rawSym.toString()));
+ }
+ }
for(int i = 0; i < args.count(); i++)
argvs = argvs.cons(((Expr) args.nth(i)).eval());
return fn.applyTo(RT.seq( Util.ret1(argvs, argvs = null) ));
}
catch(Throwable e)
{
if(!(e instanceof CompilerException))
throw new CompilerException(source, line, column, e);
else
throw (CompilerException) e;
}
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
if(isProtocol)
@@ -7122,32 +7135,36 @@ static Namespace namespaceFor(Namespace inns, Symbol sym){
// ...otherwise check the Namespaces map.
ns = Namespace.find(nsSym);
}
return ns;
}
static public Object resolveIn(Namespace n, Symbol sym, boolean allowPrivate) {
//note - ns-qualified vars must already exist
if(sym.ns != null)
{
Namespace ns = namespaceFor(n, sym);
if(ns == null)
throw Util.runtimeException("No such namespace: " + sym.ns);
Var v = ns.findInternedVar(Symbol.intern(sym.name));
- if(v == null)
- throw Util.runtimeException("No such var: " + sym);
+ if(v == null){
+ v = ns.findInternedVar(Symbol.intern("-method-missing"));
+ if(v == null)
+ throw Util.runtimeException("No such var: " + sym);
+ v.rawSym = Symbol.intern(sym.name);
+ }
else if(v.ns != currentNS() && !v.isPublic() && !allowPrivate)
throw new IllegalStateException("var: " + sym + " is not public");
return v;
}
else if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
{
return RT.classForName(sym.name);
}
else if(sym.equals(NS))
return RT.NS_VAR;
else if(sym.equals(IN_NS))
return RT.IN_NS_VAR;
else
{
if(Util.equals(sym, COMPILE_STUB_SYM.get()))
diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java
index 9e79aac..78f0a1c 100644
--- a/src/jvm/clojure/lang/Var.java
+++ b/src/jvm/clojure/lang/Var.java
@@ -5,30 +5,32 @@
* 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 Jul 31, 2007 */
package clojure.lang;
import java.util.concurrent.atomic.AtomicBoolean;
public final class Var extends ARef implements IFn, IRef, Settable{
+Symbol rawSym;
+
static class TBox{
volatile Object val;
final Thread thread;
public TBox(Thread t, Object val){
this.thread = t;
this.val = val;
}
}
static public class Unbound extends AFn{
final public Var v;
public Unbound(Var v){
@@ -183,30 +185,37 @@ Var(Namespace ns, Symbol sym, Object root){
public boolean isBound(){
return hasRoot() || (threadBound.get() && dvals.get().bindings.containsKey(this));
}
final public Object get(){
if(!threadBound.get())
return root;
return deref();
}
final public Object deref(){
TBox b = getThreadBinding();
if(b != null)
return b.val;
+
+ if(this.rawSym!=null){
+ Var v = this.ns.findInternedVar(rawSym);
+ if(v!=null){
+ return v.deref();
+ }
+ }
return root;
}
public void setValidator(IFn vf){
if(hasRoot())
validate(vf, root);
validator = vf;
}
public Object alter(IFn fn, ISeq args) {
set(fn.applyTo(RT.cons(deref(), args)));
return this;
}
public Object set(Object val){
--
2.7.4 (Apple Git-66)
@killme2008
Copy link
Author

killme2008 commented Jun 27, 2016

Useage:

  • apply this patch to clojure project code.
  • Run mvn package to build clojure jar
  • Run below code with the newly built clojure jar:
(ns missing-test)

;;Define method missing, return arguments
(defn -method-missing [ & args] args)


;;in another namespace to invoke not exists functions
(ns missing-example)
(require '[missing-test :as t])

(t/hello 3)  ;; => ("hello" 3)

(t/world (range 1 10))  ;; => ("world" (1 2 3 4 5 6 7 8 9))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment