From 69135c4b048dbdf968198e24de8956e3e0c11720 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 18:55:31 -0700 Subject: [PATCH 01/19] Refactor repeated code for evaling arg-exprs into separate method. --- src/jvm/clojure/lang/Compiler.java | 19 ++++++++++--------- 1 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index d037186..1193d35 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1346,9 +1346,7 @@ static class InstanceMethodExpr extends MethodExpr{ try { Object targetval = target.eval(); - Object[] argvals = new Object[args.count()]; - for(int i = 0; i < args.count(); i++) - argvals[i] = ((Expr) args.nth(i)).eval(); + Object[] argvals = argexprVals(args); if(method != null) { LinkedList ms = new LinkedList(); @@ -1494,9 +1492,7 @@ static class StaticMethodExpr extends MethodExpr{ public Object eval() { try { - Object[] argvals = new Object[args.count()]; - for(int i = 0; i < args.count(); i++) - argvals[i] = ((Expr) args.nth(i)).eval(); + Object[] argvals = argexprVals(args); if(method != null) { LinkedList ms = new LinkedList(); @@ -2297,9 +2293,7 @@ public static class NewExpr implements Expr{ } public Object eval() { - Object[] argvals = new Object[args.count()]; - for(int i = 0; i < args.count(); i++) - argvals[i] = ((Expr) args.nth(i)).eval(); + Object[] argvals = argexprVals(args); if(this.ctor != null) { try @@ -2545,6 +2539,13 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{ } } +private static Object[] argexprVals(IPersistentVector argexprs){ + Object[] argvals = new Object[argexprs.count()]; + for(int i = 0; i < argexprs.count(); i++) + argvals[i] = ((Expr) argexprs.nth(i)).eval(); + return argvals; +} + static final public IPersistentMap CHAR_MAP = PersistentHashMap.create('-', "_", // '.', "_DOT_", -- 1.7.3.5 From 843a282b6913f898b0a983f70f23f2c75ecd9ab0 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 18:57:19 -0700 Subject: [PATCH 02/19] Refactor Compiler.getMatchingParams to not be coupled to Exprs (in preparation to move it to Reflector) --- src/jvm/clojure/lang/Compiler.java | 29 +++++++++++++++++++---------- 1 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 1193d35..49c5a3a 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1319,7 +1319,7 @@ static class InstanceMethodExpr extends MethodExpr{ params.add(m.getParameterTypes()); rets.add(m.getReturnType()); } - methodidx = getMatchingParams(methodName, params, args, rets); + methodidx = getMatchingParams(methodName, params, argexprTypes(args), rets); } java.lang.reflect.Method m = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); @@ -1478,7 +1478,7 @@ static class StaticMethodExpr extends MethodExpr{ params.add(m.getParameterTypes()); rets.add(m.getReturnType()); } - methodidx = getMatchingParams(methodName, params, args, rets); + methodidx = getMatchingParams(methodName, params, argexprTypes(args), rets); } method = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) @@ -2192,7 +2192,7 @@ static public boolean subsumes(Class[] c1, Class[] c2){ return better; } -static int getMatchingParams(String methodName, ArrayList paramlists, IPersistentVector argexprs, +static int getMatchingParams(String methodName, ArrayList paramlists, Class[] argTypes, List rets) { //presumes matching lengths @@ -2202,19 +2202,17 @@ static int getMatchingParams(String methodName, ArrayList paramlists, I for(int i = 0; i < paramlists.size(); i++) { boolean match = true; - ISeq aseq = argexprs.seq(); int exact = 0; - for(int p = 0; match && p < argexprs.count() && aseq != null; ++p, aseq = aseq.next()) + for(int p = 0; match && p < argTypes.length; ++p) { - Expr arg = (Expr) aseq.first(); - Class aclass = arg.hasJavaClass() ? arg.getJavaClass() : Object.class; + Class aclass = argTypes[p]; Class pclass = paramlists.get(i)[p]; - if(arg.hasJavaClass() && aclass == pclass) + if(aclass == pclass) exact++; else match = Reflector.paramArgTypeMatch(pclass, aclass); } - if(exact == argexprs.count()) + if(exact == argTypes.length) { if(!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i))) matchIdx = i; @@ -2280,7 +2278,7 @@ public static class NewExpr implements Expr{ int ctoridx = 0; if(ctors.size() > 1) { - ctoridx = getMatchingParams(c.getName(), params, args, rets); + ctoridx = getMatchingParams(c.getName(), params, argexprTypes(args), rets); } this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; @@ -2539,6 +2537,17 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{ } } +static Class[] argexprTypes(IPersistentVector argexprs){ + Class[] argTypes = new Class[argexprs.count()]; + ISeq aseq = argexprs.seq(); + for(int i = 0; i < argexprs.count() && aseq != null; i++, aseq = aseq.next()) + { + Expr arg = (Expr) aseq.first(); + argTypes[i] = arg.hasJavaClass() ? arg.getJavaClass() : Object.class; + } + return argTypes; +} + private static Object[] argexprVals(IPersistentVector argexprs){ Object[] argvals = new Object[argexprs.count()]; for(int i = 0; i < argexprs.count(); i++) -- 1.7.3.5 From 4e6c51266e8e19eadab672e98c27aba3e07aecca Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 19:04:03 -0700 Subject: [PATCH 03/19] move getMatchingParams method from Compiler to Reflector --- src/jvm/clojure/lang/Compiler.java | 78 +--------------------------------- src/jvm/clojure/lang/Reflector.java | 74 ++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 49c5a3a..535d2b8 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1319,7 +1319,7 @@ static class InstanceMethodExpr extends MethodExpr{ params.add(m.getParameterTypes()); rets.add(m.getReturnType()); } - methodidx = getMatchingParams(methodName, params, argexprTypes(args), rets); + methodidx = Reflector.getMatchingParams(methodName, params, argexprTypes(args), rets); } java.lang.reflect.Method m = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); @@ -1478,7 +1478,7 @@ static class StaticMethodExpr extends MethodExpr{ params.add(m.getParameterTypes()); rets.add(m.getReturnType()); } - methodidx = getMatchingParams(methodName, params, argexprTypes(args), rets); + methodidx = Reflector.getMatchingParams(methodName, params, argexprTypes(args), rets); } method = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) @@ -2173,78 +2173,6 @@ static class ThrowExpr extends UntypedExpr{ } -static public boolean subsumes(Class[] c1, Class[] c2){ - //presumes matching lengths - Boolean better = false; - for(int i = 0; i < c1.length; i++) - { - if(c1[i] != c2[i])// || c2[i].isPrimitive() && c1[i] == Object.class)) - { - if(!c1[i].isPrimitive() && c2[i].isPrimitive() - //|| Number.class.isAssignableFrom(c1[i]) && c2[i].isPrimitive() - || - c2[i].isAssignableFrom(c1[i])) - better = true; - else - return false; - } - } - return better; -} - -static int getMatchingParams(String methodName, ArrayList paramlists, Class[] argTypes, - List rets) - { - //presumes matching lengths - int matchIdx = -1; - boolean tied = false; - boolean foundExact = false; - for(int i = 0; i < paramlists.size(); i++) - { - boolean match = true; - int exact = 0; - for(int p = 0; match && p < argTypes.length; ++p) - { - Class aclass = argTypes[p]; - Class pclass = paramlists.get(i)[p]; - if(aclass == pclass) - exact++; - else - match = Reflector.paramArgTypeMatch(pclass, aclass); - } - if(exact == argTypes.length) - { - if(!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i))) - matchIdx = i; - foundExact = true; - } - else if(match && !foundExact) - { - if(matchIdx == -1) - matchIdx = i; - else - { - if(subsumes(paramlists.get(i), paramlists.get(matchIdx))) - { - matchIdx = i; - tied = false; - } - else if(Arrays.equals(paramlists.get(matchIdx), paramlists.get(i))) - { - if(rets.get(matchIdx).isAssignableFrom(rets.get(i))) - matchIdx = i; - } - else if(!(subsumes(paramlists.get(matchIdx), paramlists.get(i)))) - tied = true; - } - } - } - if(tied) - throw new IllegalArgumentException("More than one matching method found: " + methodName); - - return matchIdx; -} - public static class NewExpr implements Expr{ public final IPersistentVector args; public final Constructor ctor; @@ -2278,7 +2206,7 @@ public static class NewExpr implements Expr{ int ctoridx = 0; if(ctors.size() > 1) { - ctoridx = getMatchingParams(c.getName(), params, argexprTypes(args), rets); + ctoridx = Reflector.getMatchingParams(c.getName(), params, argexprTypes(args), rets); } this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index d13bf61..8609190 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -64,7 +64,7 @@ static Object invokeMatchingMethod(String methodName, List methods, Object targe Class[] params = m.getParameterTypes(); if(isCongruent(params, args)) { - if(foundm == null || Compiler.subsumes(params, foundm.getParameterTypes())) + if(foundm == null || subsumes(params, foundm.getParameterTypes())) { foundm = m; boxedArgs = boxArgs(params, args); @@ -348,6 +348,78 @@ static public Field getField(Class c, String name, boolean getStatics){ return null; } +static public boolean subsumes(Class[] c1, Class[] c2){ + //presumes matching lengths + Boolean better = false; + for(int i = 0; i < c1.length; i++) + { + if(c1[i] != c2[i])// || c2[i].isPrimitive() && c1[i] == Object.class)) + { + if(!c1[i].isPrimitive() && c2[i].isPrimitive() + //|| Number.class.isAssignableFrom(c1[i]) && c2[i].isPrimitive() + || + c2[i].isAssignableFrom(c1[i])) + better = true; + else + return false; + } + } + return better; +} + +static int getMatchingParams(String methodName, ArrayList paramlists, Class[] argTypes, + List rets) + { + //presumes matching lengths + int matchIdx = -1; + boolean tied = false; + boolean foundExact = false; + for(int i = 0; i < paramlists.size(); i++) + { + boolean match = true; + int exact = 0; + for(int p = 0; match && p < argTypes.length; ++p) + { + Class aclass = argTypes[p]; + Class pclass = paramlists.get(i)[p]; + if(aclass == pclass) + exact++; + else + match = Reflector.paramArgTypeMatch(pclass, aclass); + } + if(exact == argTypes.length) + { + if(!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i))) + matchIdx = i; + foundExact = true; + } + else if(match && !foundExact) + { + if(matchIdx == -1) + matchIdx = i; + else + { + if(subsumes(paramlists.get(i), paramlists.get(matchIdx))) + { + matchIdx = i; + tied = false; + } + else if(Arrays.equals(paramlists.get(matchIdx), paramlists.get(i))) + { + if(rets.get(matchIdx).isAssignableFrom(rets.get(i))) + matchIdx = i; + } + else if(!(subsumes(paramlists.get(matchIdx), paramlists.get(i)))) + tied = true; + } + } + } + if(tied) + throw new IllegalArgumentException("More than one matching method found: " + methodName); + + return matchIdx; +} + static public List getMethods(Class c, int arity, String name, boolean getStatics){ Method[] allmethods = c.getMethods(); ArrayList methods = new ArrayList(); -- 1.7.3.5 From bb6d362f7c5aed6e8377382510cf626ea96f5e21 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 19:13:52 -0700 Subject: [PATCH 04/19] Moved instance-method-finding code from Compiler to Reflector --- src/jvm/clojure/lang/Compiler.java | 29 +---------------------------- src/jvm/clojure/lang/Reflector.java | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 535d2b8..64042f2 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1302,34 +1302,7 @@ static class InstanceMethodExpr extends MethodExpr{ this.tag = tag; if(target.hasJavaClass() && target.getJavaClass() != null) { - List methods = Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false); - if(methods.isEmpty()) - method = null; - //throw new IllegalArgumentException("No matching method found"); - else - { - int methodidx = 0; - if(methods.size() > 1) - { - ArrayList params = new ArrayList(); - ArrayList rets = new ArrayList(); - for(int i = 0; i < methods.size(); i++) - { - java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); - params.add(m.getParameterTypes()); - rets.add(m.getReturnType()); - } - methodidx = Reflector.getMatchingParams(methodName, params, argexprTypes(args), rets); - } - java.lang.reflect.Method m = - (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); - if(m != null && !Modifier.isPublic(m.getDeclaringClass().getModifiers())) - { - //public method of non-public class, try to find it in hierarchy - m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m); - } - method = m; - } + method = Reflector.getMatchingInstanceMethod(target.getJavaClass(), methodName, argexprTypes(args)); } else method = null; diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 8609190..f6d7e5f 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -472,6 +472,36 @@ static public List getMethods(Class c, int arity, String name, boolean getStatic return methods; } +public static Method getMatchingInstanceMethod(Class c, String methodName, Class[] argTypes){ + List methods = getMethods(c, argTypes.length, methodName, false); + if(methods.isEmpty()) + return null; + //throw new IllegalArgumentException("No matching method found"); + else + { + int methodidx = 0; + if(methods.size() > 1) + { + ArrayList params = new ArrayList(); + ArrayList rets = new ArrayList(); + for(int i = 0; i < methods.size(); i++) + { + java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); + params.add(m.getParameterTypes()); + rets.add(m.getReturnType()); + } + methodidx = getMatchingParams(methodName, params, argTypes, rets); + } + java.lang.reflect.Method m = + (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); + if(m != null && !Modifier.isPublic(m.getDeclaringClass().getModifiers())) + { + //public method of non-public class, try to find it in hierarchy + m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m); + } + return m; + } +} static Object boxArg(Class paramType, Object arg){ if(!paramType.isPrimitive()) -- 1.7.3.5 From 31bc334467b582ecc01acf3f7ad4d7b9b6d8589b Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 19:19:11 -0700 Subject: [PATCH 05/19] Moved static-method-finding code from Compiler to Reflector --- src/jvm/clojure/lang/Compiler.java | 19 +------------------ src/jvm/clojure/lang/Reflector.java | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 64042f2..b8bc5d6 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1436,24 +1436,7 @@ static class StaticMethodExpr extends MethodExpr{ this.line = line; this.tag = tag; - List methods = Reflector.getMethods(c, args.count(), methodName, true); - if(methods.isEmpty()) - throw new IllegalArgumentException("No matching method: " + methodName); - - int methodidx = 0; - if(methods.size() > 1) - { - ArrayList params = new ArrayList(); - ArrayList rets = new ArrayList(); - for(int i = 0; i < methods.size(); i++) - { - java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); - params.add(m.getParameterTypes()); - rets.add(m.getReturnType()); - } - methodidx = Reflector.getMatchingParams(methodName, params, argexprTypes(args), rets); - } - method = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); + method = Reflector.getMatchingStaticMethod(c, methodName, argexprTypes(args)); if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { RT.errPrintWriter() diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index f6d7e5f..30d79f3 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -503,6 +503,27 @@ public static Method getMatchingInstanceMethod(Class c, String methodName, Class } } +public static Method getMatchingStaticMethod(Class c, String methodName, Class[] argTypes){ + List methods = getMethods(c, argTypes.length, methodName, true); + if(methods.isEmpty()) + throw new IllegalArgumentException("No matching method: " + methodName); + + int methodidx = 0; + if(methods.size() > 1) + { + ArrayList params = new ArrayList(); + ArrayList rets = new ArrayList(); + for(int i = 0; i < methods.size(); i++) + { + java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); + params.add(m.getParameterTypes()); + rets.add(m.getReturnType()); + } + methodidx = getMatchingParams(methodName, params, argTypes, rets); + } + return (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); +} + static Object boxArg(Class paramType, Object arg){ if(!paramType.isPrimitive()) return paramType.cast(arg); -- 1.7.3.5 From 9c41ebf4046fe89f809c5fbd3dc2a8f29baf4cb0 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 19:24:31 -0700 Subject: [PATCH 06/19] Moved constructor-finding code from Compiler to Reflector --- src/jvm/clojure/lang/Compiler.java | 25 +------------------------ src/jvm/clojure/lang/Reflector.java | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index b8bc5d6..699eed2 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -2142,30 +2142,7 @@ public static class NewExpr implements Expr{ public NewExpr(Class c, IPersistentVector args, int line) { this.args = args; this.c = c; - Constructor[] allctors = c.getConstructors(); - ArrayList ctors = new ArrayList(); - ArrayList params = new ArrayList(); - ArrayList rets = new ArrayList(); - for(int i = 0; i < allctors.length; i++) - { - Constructor ctor = allctors[i]; - if(ctor.getParameterTypes().length == args.count()) - { - ctors.add(ctor); - params.add(ctor.getParameterTypes()); - rets.add(c); - } - } - if(ctors.isEmpty()) - throw new IllegalArgumentException("No matching ctor found for " + c); - - int ctoridx = 0; - if(ctors.size() > 1) - { - ctoridx = Reflector.getMatchingParams(c.getName(), params, argexprTypes(args), rets); - } - - this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; + ctor = Reflector.getMatchingConstructor(c, argexprTypes(args)); if(ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { RT.errPrintWriter() diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 30d79f3..104f080 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -420,6 +420,33 @@ static int getMatchingParams(String methodName, ArrayList paramlists, C return matchIdx; } +public static Constructor getMatchingConstructor(Class c, Class[] argTypes){ + Constructor[] allctors = c.getConstructors(); + ArrayList ctors = new ArrayList(); + ArrayList params = new ArrayList(); + ArrayList rets = new ArrayList(); + for(int i = 0; i < allctors.length; i++) + { + Constructor ctor = allctors[i]; + if(ctor.getParameterTypes().length == argTypes.length) + { + ctors.add(ctor); + params.add(ctor.getParameterTypes()); + rets.add(c); + } + } + if(ctors.isEmpty()) + throw new IllegalArgumentException("No matching ctor found for " + c); + + int ctoridx = 0; + if(ctors.size() > 1) + { + ctoridx = getMatchingParams(c.getName(), params, argTypes, rets); + } + + return ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; +} + static public List getMethods(Class c, int arity, String name, boolean getStatics){ Method[] allmethods = c.getMethods(); ArrayList methods = new ArrayList(); -- 1.7.3.5 From 426fc8758310b182eeb97fe13a29936915d560f5 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 22:35:29 -0700 Subject: [PATCH 07/19] Removing external references to low-level, arity-based Reflector.getMethods method --- src/jvm/clojure/lang/Compiler.java | 13 +++++++------ src/jvm/clojure/lang/Reflector.java | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 699eed2..346e99b 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -854,7 +854,9 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{ } } - static class Parser implements IParser{ + private static final Class[] EMPTY_TYPES = new Class[0]; + + static class Parser implements IParser{ public Expr parse(C context, Object frm) { ISeq form = (ISeq) frm; //(. x fieldname-sym) or @@ -879,9 +881,9 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{ { Symbol sym = (Symbol) RT.third(form); if(c != null) - maybeField = Reflector.getMethods(c, 0, munge(sym.name), true).size() == 0; + maybeField = Reflector.getMatchingStaticMethod(c, munge(sym.name), EMPTY_TYPES) == null; else if(instance != null && instance.hasJavaClass() && instance.getJavaClass() != null) - maybeField = Reflector.getMethods(instance.getJavaClass(), 0, munge(sym.name), false).size() == 0; + maybeField = Reflector.getMatchingInstanceMethod(instance.getJavaClass(), munge(sym.name), EMPTY_TYPES) == null; } if(maybeField) //field { @@ -3098,12 +3100,11 @@ static class InvokeExpr implements Expr{ " (The protocol method may have been defined before and removed.)"); } String mname = munge(mmapVal.sym.toString()); - List methods = Reflector.getMethods(protocolOn, args.count() - 1, mname, false); - if(methods.size() != 1) + this.onMethod = Reflector.getMatchingInstanceMethod(protocolOn, mname, argexprTypes(RT.subvec(args, 1, args.count()))); + if(this.onMethod == null) 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); } } } diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 104f080..3395a99 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -447,7 +447,7 @@ public static Constructor getMatchingConstructor(Class c, Class[] argTypes){ return ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; } -static public List getMethods(Class c, int arity, String name, boolean getStatics){ +static private List getMethods(Class c, int arity, String name, boolean getStatics){ Method[] allmethods = c.getMethods(); ArrayList methods = new ArrayList(); ArrayList bridgeMethods = new ArrayList(); @@ -533,7 +533,7 @@ public static Method getMatchingInstanceMethod(Class c, String methodName, Class public static Method getMatchingStaticMethod(Class c, String methodName, Class[] argTypes){ List methods = getMethods(c, argTypes.length, methodName, true); if(methods.isEmpty()) - throw new IllegalArgumentException("No matching method: " + methodName); + return null; int methodidx = 0; if(methods.size() > 1) -- 1.7.3.5 From 0b45d0484a9b2f4b227c0c9d5d270be1d9c40909 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 21:47:02 -0700 Subject: [PATCH 08/19] Move code around invoking a Constructor instance from Compiler into Reflector.newInstance --- src/jvm/clojure/lang/Compiler.java | 9 +-------- src/jvm/clojure/lang/Reflector.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 346e99b..bc121a7 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -2157,14 +2157,7 @@ public static class NewExpr implements Expr{ Object[] argvals = argexprVals(args); if(this.ctor != null) { - try - { - return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), argvals)); - } - catch(Exception e) - { - throw Util.runtimeException(e); - } + return Reflector.newInstance(this.ctor, argvals); } return Reflector.invokeConstructor(c, argvals); } diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 3395a99..cf8e708 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -127,6 +127,17 @@ public static Method getAsMethodOfPublicBase(Class c, Method m){ return getAsMethodOfPublicBase(sc, m); } +public static Object newInstance(Constructor ctor, Object[] args){ + try + { + return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), args)); + } + catch(Exception e) + { + throw Util.runtimeException(e); + } +} + public static Object invokeConstructor(Class c, Object[] args) { try { -- 1.7.3.5 From d2c82bdc18199877e2c2fed38e659b25dd535491 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Mon, 25 Apr 2011 22:26:25 -0700 Subject: [PATCH 09/19] Move code around invoking a Method instance from Compiler into Reflector.invokeMethod --- src/jvm/clojure/lang/Compiler.java | 8 ++------ src/jvm/clojure/lang/Reflector.java | 7 ++++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index bc121a7..f697a5a 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1324,9 +1324,7 @@ static class InstanceMethodExpr extends MethodExpr{ Object[] argvals = argexprVals(args); if(method != null) { - LinkedList ms = new LinkedList(); - ms.add(method); - return Reflector.invokeMatchingMethod(methodName, ms, targetval, argvals); + return Reflector.invokeMethod(targetval, method, argvals); } return Reflector.invokeInstanceMethod(targetval, methodName, argvals); } @@ -1453,9 +1451,7 @@ static class StaticMethodExpr extends MethodExpr{ Object[] argvals = argexprVals(args); if(method != null) { - LinkedList ms = new LinkedList(); - ms.add(method); - return Reflector.invokeMatchingMethod(methodName, ms, null, argvals); + return Reflector.invokeMethod(c, method, argvals); } return Reflector.invokeStaticMethod(c, methodName, argvals); } diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index cf8e708..bfe99eb 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -41,7 +41,12 @@ private static String noMethodReport(String methodName, Object target){ return "No matching method found: " + methodName + (target==null?"":" for " + target.getClass()); } -static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args) + +public static Object invokeMethod(Object target, Method method, Object[] args){ + return invokeMatchingMethod(method.getName(), Arrays.asList(method), target, args); +} + +private static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args) { Method m = null; Object[] boxedArgs = null; -- 1.7.3.5 From 03c19f7c1e6fb23a458b5df80fdc2d63271bcaea Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Tue, 26 Apr 2011 00:11:47 -0700 Subject: [PATCH 10/19] Remove unused Reflector methods --- src/jvm/clojure/lang/Reflector.java | 47 ----------------------------------- 1 files changed, 0 insertions(+), 47 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index bfe99eb..4eb14ad 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -190,11 +190,6 @@ public static Object invokeConstructor(Class c, Object[] args) { } } -public static Object invokeStaticMethodVariadic(String className, String methodName, Object... args) { - return invokeStaticMethod(className, methodName, args); - -} - public static Object invokeStaticMethod(String className, String methodName, Object[] args) { Class c = RT.classForName(className); try @@ -311,48 +306,6 @@ public static Object invokeNoArgInstanceMember(Object target, String name) { return getInstanceField(target, name); } -public static Object invokeInstanceMember(Object target, String name) { - //check for field first - Class c = target.getClass(); - Field f = getField(c, name, false); - if(f != null) //field get - { - try - { - return prepRet(f.getType(), f.get(target)); - } - catch(IllegalAccessException e) - { - throw Util.runtimeException(e); - } - } - return invokeInstanceMethod(target, name, RT.EMPTY_ARRAY); -} - -public static Object invokeInstanceMember(String name, Object target, Object arg1) { - //check for field first - Class c = target.getClass(); - Field f = getField(c, name, false); - if(f != null) //field set - { - try - { - f.set(target, boxArg(f.getType(), arg1)); - } - catch(IllegalAccessException e) - { - throw Util.runtimeException(e); - } - return arg1; - } - return invokeInstanceMethod(target, name, new Object[]{arg1}); -} - -public static Object invokeInstanceMember(String name, Object target, Object... args) { - return invokeInstanceMethod(target, name, args); -} - - static public Field getField(Class c, String name, boolean getStatics){ Field[] allfields = c.getFields(); for(int i = 0; i < allfields.length; i++) -- 1.7.3.5 From 0d748f70823a5fc1d30fa041cfe42ef32c67b715 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Tue, 26 Apr 2011 00:12:23 -0700 Subject: [PATCH 11/19] Privatize reflector helper methods --- src/jvm/clojure/lang/Reflector.java | 14 +++++++------- 1 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 4eb14ad..781b8d3 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -105,7 +105,7 @@ private static Object invokeMatchingMethod(String methodName, List methods, Obje } -public static Method getAsMethodOfPublicBase(Class c, Method m){ +private static Method getAsMethodOfPublicBase(Class c, Method m){ for(Class iface : c.getInterfaces()) { for(Method im : iface.getMethods()) @@ -317,7 +317,7 @@ static public Field getField(Class c, String name, boolean getStatics){ return null; } -static public boolean subsumes(Class[] c1, Class[] c2){ +private static boolean subsumes(Class[] c1, Class[] c2){ //presumes matching lengths Boolean better = false; for(int i = 0; i < c1.length; i++) @@ -336,7 +336,7 @@ static public boolean subsumes(Class[] c1, Class[] c2){ return better; } -static int getMatchingParams(String methodName, ArrayList paramlists, Class[] argTypes, +private static int getMatchingParams(String methodName, ArrayList paramlists, Class[] argTypes, List rets) { //presumes matching lengths @@ -520,7 +520,7 @@ public static Method getMatchingStaticMethod(Class c, String methodName, Class[] return (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); } -static Object boxArg(Class paramType, Object arg){ +private static Object boxArg(Class paramType, Object arg){ if(!paramType.isPrimitive()) return paramType.cast(arg); else if(paramType == boolean.class) @@ -547,7 +547,7 @@ static Object boxArg(Class paramType, Object arg){ ", given: " + arg.getClass().getName()); } -static Object[] boxArgs(Class[] params, Object[] args){ +private static Object[] boxArgs(Class[] params, Object[] args){ if(params.length == 0) return null; Object[] ret = new Object[params.length]; @@ -560,7 +560,7 @@ static Object[] boxArgs(Class[] params, Object[] args){ return ret; } -static public boolean paramArgTypeMatch(Class paramType, Class argType){ +private static boolean paramArgTypeMatch(Class paramType, Class argType){ if(argType == null) return !paramType.isPrimitive(); if(paramType == argType || paramType.isAssignableFrom(argType)) @@ -589,7 +589,7 @@ static public boolean paramArgTypeMatch(Class paramType, Class argType){ return false; } -static boolean isCongruent(Class[] params, Object[] args){ +private static boolean isCongruent(Class[] params, Object[] args){ boolean ret = false; if(args == null) return params.length == 0; -- 1.7.3.5 From 6b6c14c5832dc83e3ddc1168df094194ea611ea2 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Wed, 27 Apr 2011 13:07:48 -0700 Subject: [PATCH 12/19] Reflector DRY clean-up: .invokeConstructor and .getMatchingConstructor now both use the same code to find a Constructor instance, and .invokeConstructor uses .newInstance to create a new instance from a Constructor. --- src/jvm/clojure/lang/Reflector.java | 110 +++++++++++++++++++---------------- 1 files changed, 59 insertions(+), 51 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 781b8d3..dcd52f1 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -12,14 +12,53 @@ package clojure.lang; -import java.lang.reflect.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; -import java.util.Arrays; public class Reflector{ +private static Class getClass(Object o){ + return o == null ? null : o.getClass(); +} + +private static String getName(Class c){ + return c == null ? "null" : c.getName(); +} + +private static String toString(Class[] cs){ + StringBuffer sb = new StringBuffer(cs[0].getName()); + for (int i = 1; i < cs.length; i++) + { + sb.append(","); + sb.append(getName(cs[i])); + } + return sb.toString(); +} + +private static Class[] argTypes(Object[] args){ + Class[] cs = new Class[args.length]; + for (int i = 0; i < args.length; i++) + cs[i] = getClass(args[i]); + return cs; +} + +/* Enums to mitigate bugs from multiple boolean flags + */ + +private enum Invoking{ + T(true), F(false); + public final boolean b; + private Invoking(boolean b){ + this.b = b; + } +} + public static Object invokeInstanceMethod(Object target, String methodName, Object[] args) { try { @@ -144,50 +183,9 @@ public static Object newInstance(Constructor ctor, Object[] args){ } public static Object invokeConstructor(Class c, Object[] args) { - try - { - Constructor[] allctors = c.getConstructors(); - ArrayList ctors = new ArrayList(); - for(int i = 0; i < allctors.length; i++) - { - Constructor ctor = allctors[i]; - if(ctor.getParameterTypes().length == args.length) - ctors.add(ctor); - } - if(ctors.isEmpty()) - { - throw new IllegalArgumentException("No matching ctor found" - + " for " + c); - } - else if(ctors.size() == 1) - { - Constructor ctor = (Constructor) ctors.get(0); - return ctor.newInstance(boxArgs(ctor.getParameterTypes(), args)); - } - else //overloaded w/same arity - { - for(Iterator iterator = ctors.iterator(); iterator.hasNext();) - { - Constructor ctor = (Constructor) iterator.next(); - Class[] params = ctor.getParameterTypes(); - if(isCongruent(params, args)) - { - Object[] boxedArgs = boxArgs(params, args); - return ctor.newInstance(boxedArgs); - } - } - throw new IllegalArgumentException("No matching ctor found" - + " for " + c); - } - } - catch(Exception e) - { - if(e.getCause() instanceof Exception) - throw Util.runtimeException(e.getCause()); - else if(e.getCause() instanceof Error) - throw (Error) e.getCause(); - throw Util.runtimeException(e); - } + Class[] argTypes = argTypes(args); + Constructor ctor = getMatchingConstructor(c, argTypes, Invoking.T); + return newInstance(ctor, args); } public static Object invokeStaticMethod(String className, String methodName, Object[] args) { @@ -389,7 +387,7 @@ private static int getMatchingParams(String methodName, ArrayList param return matchIdx; } -public static Constructor getMatchingConstructor(Class c, Class[] argTypes){ +private static Constructor getMatchingConstructor(Class c, Class[] argTypes, Invoking invoking){ Constructor[] allctors = c.getConstructors(); ArrayList ctors = new ArrayList(); ArrayList params = new ArrayList(); @@ -404,16 +402,26 @@ public static Constructor getMatchingConstructor(Class c, Class[] argTypes){ rets.add(c); } } - if(ctors.isEmpty()) - throw new IllegalArgumentException("No matching ctor found for " + c); - int ctoridx = 0; + int ctoridx = -1; + if (ctors.size() == 1) + { + ctoridx = 0; + } if(ctors.size() > 1) { ctoridx = getMatchingParams(c.getName(), params, argTypes, rets); } - return ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; + Constructor ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; + + if (ctor == null && invoking.b) + throw new IllegalArgumentException("No matching constructor found in "+c.getName() + " for argtypes: " + toString(argTypes)); + return ctor; +} + +public static Constructor getMatchingConstructor(Class c, Class[] argTypes){ + return getMatchingConstructor(c, argTypes, Invoking.F); } static private List getMethods(Class c, int arity, String name, boolean getStatics){ -- 1.7.3.5 From dd8c2abdcbe1d922341e2474fa4d4d7d9f8035dd Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Wed, 27 Apr 2011 14:16:30 -0700 Subject: [PATCH 13/19] Reflector DRY clean-up: .getMatchingStaticMethod and .getMatchingInstanceMethod now both use the same code to find a Method instance. --- src/jvm/clojure/lang/Reflector.java | 34 ++++++++++++++-------------------- 1 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index dcd52f1..d067f20 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -58,6 +58,13 @@ private enum Invoking{ this.b = b; } } +private enum Statics{ + T(true), F(false); + public final boolean b; + private Statics(boolean b){ + this.b = b; + } +} public static Object invokeInstanceMethod(Object target, String methodName, Object[] args) { try @@ -476,8 +483,8 @@ static private List getMethods(Class c, int arity, String name, boolean getStati return methods; } -public static Method getMatchingInstanceMethod(Class c, String methodName, Class[] argTypes){ - List methods = getMethods(c, argTypes.length, methodName, false); +private static Method getMatchingMethod(Class c, String methodName, Class[] argTypes, Statics statics){ + List methods = getMethods(c, argTypes.length, methodName, statics.b); if(methods.isEmpty()) return null; //throw new IllegalArgumentException("No matching method found"); @@ -507,25 +514,12 @@ public static Method getMatchingInstanceMethod(Class c, String methodName, Class } } -public static Method getMatchingStaticMethod(Class c, String methodName, Class[] argTypes){ - List methods = getMethods(c, argTypes.length, methodName, true); - if(methods.isEmpty()) - return null; +public static Method getMatchingInstanceMethod(Class c, String methodName, Class[] argTypes){ + return getMatchingMethod(c, methodName, argTypes, Statics.F); +} - int methodidx = 0; - if(methods.size() > 1) - { - ArrayList params = new ArrayList(); - ArrayList rets = new ArrayList(); - for(int i = 0; i < methods.size(); i++) - { - java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); - params.add(m.getParameterTypes()); - rets.add(m.getReturnType()); - } - methodidx = getMatchingParams(methodName, params, argTypes, rets); - } - return (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); +public static Method getMatchingStaticMethod(Class c, String methodName, Class[] argTypes){ + return getMatchingMethod(c, methodName, argTypes, Statics.T); } private static Object boxArg(Class paramType, Object arg){ -- 1.7.3.5 From 7d1cd88a1953d5b09f867461afeb139b12468f1a Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Wed, 27 Apr 2011 14:20:39 -0700 Subject: [PATCH 14/19] Reflector DRY clean-up: invert relationship between .invokeMatchingMethod and .invokeMethod --- src/jvm/clojure/lang/Reflector.java | 37 ++++++++++++++++++++++------------ 1 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index d067f20..e8905d1 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -14,6 +14,7 @@ package clojure.lang; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -89,7 +90,21 @@ private static String noMethodReport(String methodName, Object target){ } public static Object invokeMethod(Object target, Method method, Object[] args){ - return invokeMatchingMethod(method.getName(), Arrays.asList(method), target, args); + try + { + return prepRet(method.getReturnType(), method.invoke(target, boxArgs(method.getParameterTypes(), args))); + } + catch(InvocationTargetException e) + { + if (e.getCause() instanceof RuntimeException) + throw (RuntimeException)e.getCause(); // often this is a ClassCastException + else + throw Util.runtimeException(e); + } + catch(Exception e) + { + throw Util.runtimeException(e); + } } private static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args) @@ -136,19 +151,8 @@ private static Object invokeMatchingMethod(String methodName, List methods, Obje throw new IllegalArgumentException("Can't call public method of non-public class: " + oldm.toString()); } - try - { - return prepRet(m.getReturnType(), m.invoke(target, boxedArgs)); - } - catch(Exception e) - { - if(e.getCause() instanceof Exception) - throw Util.runtimeException(e.getCause()); - else if(e.getCause() instanceof Error) - throw (Error) e.getCause(); - throw Util.runtimeException(e); - } + return invokeMethod(target, m, args); } private static Method getAsMethodOfPublicBase(Class c, Method m){ @@ -183,6 +187,13 @@ public static Object newInstance(Constructor ctor, Object[] args){ { return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), args)); } + catch(InvocationTargetException e) + { + if (e.getCause() instanceof RuntimeException) + throw (RuntimeException)e.getCause(); // often this is a ClassCastException + else + throw Util.runtimeException(e); + } catch(Exception e) { throw Util.runtimeException(e); -- 1.7.3.5 From 7e65a8ee8bbcd1d6e7388a960dbcf41c47277896 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Wed, 27 Apr 2011 14:42:22 -0700 Subject: [PATCH 15/19] Reflector DRY clean-up: .invokeStaticMethod and .invokeInstanceMethod now both use the same code to find a Method instance as getMatchingStaticMethod and .getMatchingInstanceMethod --- src/jvm/clojure/lang/Reflector.java | 84 +++++++---------------------------- 1 files changed, 16 insertions(+), 68 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index e8905d1..0a9042f 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -68,20 +68,7 @@ private enum Statics{ } public static Object invokeInstanceMethod(Object target, String methodName, Object[] args) { - try - { - Class c = target.getClass(); - List methods = getMethods(c, args.length, methodName, false); - return invokeMatchingMethod(methodName, methods, target, args); - } - catch(Exception e) - { - if(e.getCause() instanceof Exception) - throw Util.runtimeException(e.getCause()); - else if(e.getCause() instanceof Error) - throw (Error) e.getCause(); - throw Util.runtimeException(e); - } + return invokeMatchingMethod(target.getClass(), methodName, target, args, Statics.F); } private static String noMethodReport(String methodName, Object target){ @@ -107,52 +94,10 @@ public static Object invokeMethod(Object target, Method method, Object[] args){ } } -private static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args) - { - Method m = null; - Object[] boxedArgs = null; - if(methods.isEmpty()) - { - throw new IllegalArgumentException(noMethodReport(methodName,target)); - } - else if(methods.size() == 1) - { - m = (Method) methods.get(0); - boxedArgs = boxArgs(m.getParameterTypes(), args); - } - else //overloaded w/same arity - { - Method foundm = null; - for(Iterator i = methods.iterator(); i.hasNext();) - { - m = (Method) i.next(); - - Class[] params = m.getParameterTypes(); - if(isCongruent(params, args)) - { - if(foundm == null || subsumes(params, foundm.getParameterTypes())) - { - foundm = m; - boxedArgs = boxArgs(params, args); - } - } - } - m = foundm; - } - if(m == null) - throw new IllegalArgumentException(noMethodReport(methodName,target)); - - if(!Modifier.isPublic(m.getDeclaringClass().getModifiers())) - { - //public method of non-public class, try to find it in hierarchy - Method oldm = m; - m = getAsMethodOfPublicBase(m.getDeclaringClass(), m); - if(m == null) - throw new IllegalArgumentException("Can't call public method of non-public class: " + - oldm.toString()); - } - - return invokeMethod(target, m, args); +private static Object invokeMatchingMethod(Class c, String methodName, Object target, Object[] args, Statics statics){ + Class[] argTypes = argTypes(args); + Method method = getMatchingMethod(c, methodName, argTypes, statics, Invoking.T); + return invokeMethod(target, method, args); } private static Method getAsMethodOfPublicBase(Class c, Method m){ @@ -225,8 +170,7 @@ public static Object invokeStaticMethod(String className, String methodName, Obj public static Object invokeStaticMethod(Class c, String methodName, Object[] args) { if(methodName.equals("new")) return invokeConstructor(c, args); - List methods = getMethods(c, args.length, methodName, true); - return invokeMatchingMethod(methodName, methods, null, args); + return invokeMatchingMethod(c, methodName, c, args, Statics.T); } public static Object getStaticField(String className, String fieldName) { @@ -313,11 +257,13 @@ public static Object setInstanceField(Object target, String fieldName, Object va + " for " + target.getClass()); } +private static final Class[] EMPTY_TYPES = new Class[0]; + public static Object invokeNoArgInstanceMember(Object target, String name) { //favor method over field - List meths = getMethods(target.getClass(), 0, name, false); - if(meths.size() > 0) - return invokeMatchingMethod(name, meths, target, RT.EMPTY_ARRAY); + Method m = getMatchingInstanceMethod(target.getClass(), name, EMPTY_TYPES); + if(m != null) + return invokeMethod(target, m, EMPTY_TYPES); else return getInstanceField(target, name); } @@ -494,7 +440,7 @@ static private List getMethods(Class c, int arity, String name, boolean getStati return methods; } -private static Method getMatchingMethod(Class c, String methodName, Class[] argTypes, Statics statics){ +private static Method getMatchingMethod(Class c, String methodName, Class[] argTypes, Statics statics, Invoking invoking){ List methods = getMethods(c, argTypes.length, methodName, statics.b); if(methods.isEmpty()) return null; @@ -521,16 +467,18 @@ private static Method getMatchingMethod(Class c, String methodName, Class[] argT //public method of non-public class, try to find it in hierarchy m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m); } + if (m == null && invoking.b) + throw new IllegalArgumentException("No matching "+methodName+" method found in "+c.getName() + " for argtypes: " + toString(argTypes)); return m; } } public static Method getMatchingInstanceMethod(Class c, String methodName, Class[] argTypes){ - return getMatchingMethod(c, methodName, argTypes, Statics.F); + return getMatchingMethod(c, methodName, argTypes, Statics.F, Invoking.F); } public static Method getMatchingStaticMethod(Class c, String methodName, Class[] argTypes){ - return getMatchingMethod(c, methodName, argTypes, Statics.T); + return getMatchingMethod(c, methodName, argTypes, Statics.T, Invoking.F); } private static Object boxArg(Class paramType, Object arg){ -- 1.7.3.5 From b32aa7d63d795ad7a2bacb2e50f743319fa68ed4 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Wed, 27 Apr 2011 15:58:47 -0700 Subject: [PATCH 16/19] Reflector DRY clean-up: Field-related methods now all use the same code to find Fields and get/set their values. --- src/jvm/clojure/lang/Reflector.java | 114 +++++++++++++--------------------- 1 files changed, 44 insertions(+), 70 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 0a9042f..185152c 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -173,28 +173,38 @@ public static Object invokeStaticMethod(Class c, String methodName, Object[] arg return invokeMatchingMethod(c, methodName, c, args, Statics.T); } +private static Object getFieldValue(Class c, String fieldName, Object target, Statics statics){ + Field f = getField(c, fieldName, statics, Invoking.T); + try + { + return prepRet(f.getType(), f.get(target)); + } + catch(Exception e) + { + throw Util.runtimeException(e); + } +} + +private static Object setFieldValue(Class c, String fieldName, Object target, Object val, Statics statics){ + Field f = getField(c, fieldName, statics, Invoking.T); + try + { + f.set(target, boxArg(f.getType(), val)); + return val; + } + catch(Exception e) + { + throw Util.runtimeException(e); + } +} + public static Object getStaticField(String className, String fieldName) { Class c = RT.classForName(className); return getStaticField(c, fieldName); } public static Object getStaticField(Class c, String fieldName) { -// if(fieldName.equals("class")) -// return c; - Field f = getField(c, fieldName, true); - if(f != null) - { - try - { - return prepRet(f.getType(), f.get(null)); - } - catch(IllegalAccessException e) - { - throw Util.runtimeException(e); - } - } - throw new IllegalArgumentException("No matching field found: " + fieldName - + " for " + c); + return getFieldValue(c, fieldName, c, Statics.T); } public static Object setStaticField(String className, String fieldName, Object val) { @@ -203,58 +213,15 @@ public static Object setStaticField(String className, String fieldName, Object v } public static Object setStaticField(Class c, String fieldName, Object val) { - Field f = getField(c, fieldName, true); - if(f != null) - { - try - { - f.set(null, boxArg(f.getType(), val)); - } - catch(IllegalAccessException e) - { - throw Util.runtimeException(e); - } - return val; - } - throw new IllegalArgumentException("No matching field found: " + fieldName - + " for " + c); + return setFieldValue(c, fieldName, c, val, Statics.T); } public static Object getInstanceField(Object target, String fieldName) { - Class c = target.getClass(); - Field f = getField(c, fieldName, false); - if(f != null) - { - try - { - return prepRet(f.getType(), f.get(target)); - } - catch(IllegalAccessException e) - { - throw Util.runtimeException(e); - } - } - throw new IllegalArgumentException("No matching field found: " + fieldName - + " for " + target.getClass()); + return getFieldValue(target.getClass(), fieldName, target, Statics.F); } public static Object setInstanceField(Object target, String fieldName, Object val) { - Class c = target.getClass(); - Field f = getField(c, fieldName, false); - if(f != null) - { - try - { - f.set(target, boxArg(f.getType(), val)); - } - catch(IllegalAccessException e) - { - throw Util.runtimeException(e); - } - return val; - } - throw new IllegalArgumentException("No matching field found: " + fieldName - + " for " + target.getClass()); + return setFieldValue(target.getClass(), fieldName, target, val, Statics.F); } private static final Class[] EMPTY_TYPES = new Class[0]; @@ -268,15 +235,22 @@ public static Object invokeNoArgInstanceMember(Object target, String name) { return getInstanceField(target, name); } +private static Field getField(Class c, String fieldName, Statics statics, Invoking invoking){ + for(Field f : c.getFields()) + { + if(fieldName.equals(f.getName()) + && Modifier.isStatic(f.getModifiers()) == statics.b) + return f; + } + + if (invoking.b) + throw new IllegalArgumentException("No "+fieldName+" field found in "+c.getName()); + + return null; +} + static public Field getField(Class c, String name, boolean getStatics){ - Field[] allfields = c.getFields(); - for(int i = 0; i < allfields.length; i++) - { - if(name.equals(allfields[i].getName()) - && Modifier.isStatic(allfields[i].getModifiers()) == getStatics) - return allfields[i]; - } - return null; + return getField(c, name, getStatics ? Statics.T : Statics.F, Invoking.F); } private static boolean subsumes(Class[] c1, Class[] c2){ -- 1.7.3.5 From 92c4b0d168f548a685faf008ba7aa5bddcb4fb34 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Wed, 27 Apr 2011 16:47:24 -0700 Subject: [PATCH 17/19] Reflector DRY clean-up: combine access checks --- src/jvm/clojure/lang/Reflector.java | 67 +++++++++++++++++++--------------- 1 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 185152c..1512975 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -100,31 +100,41 @@ private static Object invokeMatchingMethod(Class c, String methodName, Object ta return invokeMethod(target, method, args); } -private static Method getAsMethodOfPublicBase(Class c, Method m){ - for(Class iface : c.getInterfaces()) - { - for(Method im : iface.getMethods()) - { - if(im.getName().equals(m.getName()) - && Arrays.equals(m.getParameterTypes(), im.getParameterTypes())) - { - return im; - } - } - } - Class sc = c.getSuperclass(); - if(sc == null) - return null; - for(Method scm : sc.getMethods()) - { - if(scm.getName().equals(m.getName()) - && Arrays.equals(m.getParameterTypes(), scm.getParameterTypes()) - && Modifier.isPublic(scm.getDeclaringClass().getModifiers())) - { - return scm; - } - } - return getAsMethodOfPublicBase(sc, m); +private static Method ensureMethodOfPublicBase(Class c, Method m){ + if (Modifier.isPublic(m.getDeclaringClass().getModifiers())) + return m; + else + { + for(Class iface : c.getInterfaces()) + { + for(Method im : iface.getMethods()) + { + if(im.getName().equals(m.getName()) + && Arrays.equals(m.getParameterTypes(), im.getParameterTypes())) + { + return im; + } + } + } + Class sc = c.getSuperclass(); + if(sc == null) + return null; + for(Method scm : sc.getMethods()) + { + if(scm.getName().equals(m.getName()) + && Arrays.equals(m.getParameterTypes(), scm.getParameterTypes()) + && Modifier.isPublic(scm.getDeclaringClass().getModifiers())) + { + return scm; + } + } + Method n = ensureMethodOfPublicBase(sc, m); // look in parent + if (n == null) + throw new IllegalArgumentException("Can't call public method of non-public class: " + + m.toString()); + else + return n; + } } public static Object newInstance(Constructor ctor, Object[] args){ @@ -436,11 +446,8 @@ private static Method getMatchingMethod(Class c, String methodName, Class[] argT } java.lang.reflect.Method m = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); - if(m != null && !Modifier.isPublic(m.getDeclaringClass().getModifiers())) - { - //public method of non-public class, try to find it in hierarchy - m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m); - } + if(m != null) + m = ensureMethodOfPublicBase(c, m); if (m == null && invoking.b) throw new IllegalArgumentException("No matching "+methodName+" method found in "+c.getName() + " for argtypes: " + toString(argTypes)); return m; -- 1.7.3.5 From 35e1e770930f3678c58c74cc4794e36e7af9fd01 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Wed, 27 Apr 2011 16:55:39 -0700 Subject: [PATCH 18/19] Reflector DRY clean-up: .getMatchingMethod and .getMatchingConstructor now use the same code to find member instances. --- src/jvm/clojure/lang/Reflector.java | 115 ++++++++++++++++++++--------------- 1 files changed, 65 insertions(+), 50 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index 1512975..aff6919 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -49,6 +49,24 @@ private static Class[] argTypes(Object[] args){ return cs; } +/* + * Functions to allow common code to work with both Method and Constructor instances + */ + +private static Class[] getParameterTypes(Object member){ + if (member instanceof Method) + return ((Method)member).getParameterTypes(); + else + return ((Constructor)member).getParameterTypes(); +} + +private static Class getReturnType(Object member){ + if (member instanceof Method) + return ((Method)member).getReturnType(); + else + return ((Constructor)member).getDeclaringClass(); +} + /* Enums to mitigate bugs from multiple boolean flags */ @@ -282,7 +300,7 @@ private static boolean subsumes(Class[] c1, Class[] c2){ return better; } -private static int getMatchingParams(String methodName, ArrayList paramlists, Class[] argTypes, +private static int getMatchingParams(T member, ArrayList paramlists, Class[] argTypes, List rets) { //presumes matching lengths @@ -330,38 +348,56 @@ private static int getMatchingParams(String methodName, ArrayList param } } if(tied) - throw new IllegalArgumentException("More than one matching method found: " + methodName); + { + if (member instanceof Method) + { + Method m = ((Method)member); + Class c = m.getDeclaringClass(); + throw new IllegalArgumentException("Found multiple "+m.getName()+" methods in "+c.getName()+" for argtypes: "+toString(argTypes)); + } + else + { + Constructor ctor = ((Constructor)member); + Class c = ctor.getDeclaringClass(); + throw new IllegalArgumentException("Found multiple constructors in "+c.getName()+" for argtypes: "+toString(argTypes)); + } + } return matchIdx; } -private static Constructor getMatchingConstructor(Class c, Class[] argTypes, Invoking invoking){ - Constructor[] allctors = c.getConstructors(); - ArrayList ctors = new ArrayList(); +private static T getMatchingMember(List members, Class[] argTypes, Invoking invoking){ + ArrayList matches = new ArrayList(); ArrayList params = new ArrayList(); ArrayList rets = new ArrayList(); - for(int i = 0; i < allctors.length; i++) + for(T member : members) { - Constructor ctor = allctors[i]; - if(ctor.getParameterTypes().length == argTypes.length) + if(getParameterTypes(member).length == argTypes.length) { - ctors.add(ctor); - params.add(ctor.getParameterTypes()); - rets.add(c); + matches.add(member); + params.add(getParameterTypes(member)); + rets.add(getReturnType(member)); } } - int ctoridx = -1; - if (ctors.size() == 1) + int matchidx = -1; + if (matches.size() == 1) { - ctoridx = 0; + matchidx = 0; } - if(ctors.size() > 1) + if(matches.size() > 1) { - ctoridx = getMatchingParams(c.getName(), params, argTypes, rets); + matchidx = getMatchingParams(matches.get(0), params, argTypes, rets); } - Constructor ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null; + T match = matchidx >= 0 ? matches.get(matchidx) : null; + + return match; +} + +private static Constructor getMatchingConstructor(Class c, Class[] argTypes, Invoking invoking){ + List ctors = Arrays.asList(c.getConstructors()); + Constructor ctor = getMatchingMember(ctors, argTypes, invoking); if (ctor == null && invoking.b) throw new IllegalArgumentException("No matching constructor found in "+c.getName() + " for argtypes: " + toString(argTypes)); @@ -372,7 +408,7 @@ public static Constructor getMatchingConstructor(Class c, Class[] argTypes){ return getMatchingConstructor(c, argTypes, Invoking.F); } -static private List getMethods(Class c, int arity, String name, boolean getStatics){ +private static List getMethodsForName(Class c, String name, Statics statics){ Method[] allmethods = c.getMethods(); ArrayList methods = new ArrayList(); ArrayList bridgeMethods = new ArrayList(); @@ -380,8 +416,7 @@ static private List getMethods(Class c, int arity, String name, boolean getStati { Method method = allmethods[i]; if(name.equals(method.getName()) - && Modifier.isStatic(method.getModifiers()) == getStatics - && method.getParameterTypes().length == arity) + && Modifier.isStatic(method.getModifiers()) == statics.b) { try { @@ -408,14 +443,13 @@ static private List getMethods(Class c, int arity, String name, boolean getStati if(methods.isEmpty()) methods.addAll(bridgeMethods); - if(!getStatics && c.isInterface()) + if(!statics.b && c.isInterface()) { allmethods = Object.class.getMethods(); for(int i = 0; i < allmethods.length; i++) { if(name.equals(allmethods[i].getName()) - && Modifier.isStatic(allmethods[i].getModifiers()) == getStatics - && allmethods[i].getParameterTypes().length == arity) + && Modifier.isStatic(allmethods[i].getModifiers()) == statics.b) { methods.add(allmethods[i]); } @@ -425,33 +459,14 @@ static private List getMethods(Class c, int arity, String name, boolean getStati } private static Method getMatchingMethod(Class c, String methodName, Class[] argTypes, Statics statics, Invoking invoking){ - List methods = getMethods(c, argTypes.length, methodName, statics.b); - if(methods.isEmpty()) - return null; - //throw new IllegalArgumentException("No matching method found"); - else - { - int methodidx = 0; - if(methods.size() > 1) - { - ArrayList params = new ArrayList(); - ArrayList rets = new ArrayList(); - for(int i = 0; i < methods.size(); i++) - { - java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i); - params.add(m.getParameterTypes()); - rets.add(m.getReturnType()); - } - methodidx = getMatchingParams(methodName, params, argTypes, rets); - } - java.lang.reflect.Method m = - (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null); - if(m != null) - m = ensureMethodOfPublicBase(c, m); - if (m == null && invoking.b) - throw new IllegalArgumentException("No matching "+methodName+" method found in "+c.getName() + " for argtypes: " + toString(argTypes)); - return m; - } + List methods = getMethodsForName(c, methodName, statics); + Method m = getMatchingMember(methods, argTypes, invoking); + + if(m != null) + m = ensureMethodOfPublicBase(c, m); + if (m == null && invoking.b) + throw new IllegalArgumentException("No matching "+methodName+" method found in "+c.getName() + " for argtypes: " + toString(argTypes)); + return m; } public static Method getMatchingInstanceMethod(Class c, String methodName, Class[] argTypes){ -- 1.7.3.5 From 7ba3761857becbaa54656aa13f300050a0fbf947 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Wed, 27 Apr 2011 17:01:35 -0700 Subject: [PATCH 19/19] Remove cruft from Reflector. --- src/jvm/clojure/lang/Reflector.java | 50 +++-------------------------------- 1 files changed, 4 insertions(+), 46 deletions(-) diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index aff6919..ab72248 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -19,7 +19,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; public class Reflector{ @@ -33,7 +32,7 @@ private static String getName(Class c){ } private static String toString(Class[] cs){ - StringBuffer sb = new StringBuffer(cs[0].getName()); + StringBuffer sb = new StringBuffer(getName(cs[0])); for (int i = 1; i < cs.length; i++) { sb.append(","); @@ -89,11 +88,6 @@ public static Object invokeInstanceMethod(Object target, String methodName, Obje return invokeMatchingMethod(target.getClass(), methodName, target, args, Statics.F); } -private static String noMethodReport(String methodName, Object target){ - return "No matching method found: " + methodName - + (target==null?"":" for " + target.getClass()); -} - public static Object invokeMethod(Object target, Method method, Object[] args){ try { @@ -158,7 +152,7 @@ private static Method ensureMethodOfPublicBase(Class c, Method m){ public static Object newInstance(Constructor ctor, Object[] args){ try { - return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), args)); + return ctor.newInstance(boxArgs(ctor.getParameterTypes(), args)); } catch(InvocationTargetException e) { @@ -181,18 +175,7 @@ public static Object invokeConstructor(Class c, Object[] args) { public static Object invokeStaticMethod(String className, String methodName, Object[] args) { Class c = RT.classForName(className); - try - { - return invokeStaticMethod(c, methodName, args); - } - catch(Exception e) - { - if(e.getCause() instanceof Exception) - throw Util.runtimeException(e.getCause()); - else if(e.getCause() instanceof Error) - throw (Error) e.getCause(); - throw Util.runtimeException(e); - } + return invokeStaticMethod(c, methodName, args); } public static Object invokeStaticMethod(Class c, String methodName, Object[] args) { @@ -318,7 +301,7 @@ private static int getMatchingParams(T member, ArrayList getMethodsForName(Class c, String name, Statics stat { } } -// && (!method.isBridge() -// || (c == StringBuilder.class && -// c.getMethod(method.getName(), method.getParameterTypes()) -// .equals(method)))) -// { -// methods.add(allmethods[i]); -// } } if(methods.isEmpty()) @@ -546,24 +522,6 @@ private static boolean paramArgTypeMatch(Class paramType, Class argType){ return false; } -private static boolean isCongruent(Class[] params, Object[] args){ - boolean ret = false; - if(args == null) - return params.length == 0; - if(params.length == args.length) - { - ret = true; - for(int i = 0; ret && i < params.length; i++) - { - Object arg = args[i]; - Class argType = (arg == null) ? null : arg.getClass(); - Class paramType = params[i]; - ret = paramArgTypeMatch(paramType, argType); - } - } - return ret; -} - public static Object prepRet(Class c, Object x){ if (!(c.isPrimitive() || c == Boolean.class)) return x; -- 1.7.3.5