From 5fda6cb262d1807566ecadd3af9aaafb58ee5544 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 27 Feb 2014 09:23:20 -0500 Subject: [PATCH] CLJ-1363 - reflect .- should return field if both field and method exist --- src/jvm/clojure/lang/Compiler.java | 11 +++++---- src/jvm/clojure/lang/Reflector.java | 28 +++++++++++++++++----- test/clojure/test_clojure/java_interop.clj | 10 ++++++++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index c9177d4795..e1a7b9245b 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -943,7 +943,7 @@ else if(instance != null && instance.hasJavaClass() && instance.getJavaClass() ! if(c != null) { return new StaticFieldExpr(line, column, c, munge(sym.name), tag); } else - return new InstanceFieldExpr(line, column, instance, munge(sym.name), tag); + return new InstanceFieldExpr(line, column, instance, munge(sym.name), tag, (((Symbol)RT.third(form)).name.charAt(0) == '-')); } else { @@ -1081,11 +1081,12 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{ public final int line; public final int column; public final Symbol tag; - final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String)"); + public final boolean requireField; + final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String,boolean)"); final static Method setInstanceFieldMethod = Method.getMethod("Object setInstanceField(Object,String,Object)"); - public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Symbol tag) { + public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Symbol tag, boolean requireField) { this.target = target; this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null; this.field = targetClass != null ? Reflector.getField(targetClass, fieldName, false) : null; @@ -1093,6 +1094,7 @@ public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Sy this.line = line; this.column = column; this.tag = tag; + this.requireField = requireField; if(field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { if(targetClass == null) @@ -1111,7 +1113,7 @@ public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Sy } public Object eval() { - return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName); + return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName, requireField); } public boolean canEmitPrimitive(){ @@ -1149,6 +1151,7 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ { target.emit(C.EXPRESSION, objx, gen); gen.push(fieldName); + gen.push(requireField); gen.invokeStatic(REFLECTOR_TYPE, invokeNoArgInstanceMember); if(context == C.STATEMENT) gen.pop(); diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index a28104bcff..117a56bef2 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -291,13 +291,29 @@ public static Object setInstanceField(Object target, String fieldName, Object va + " for " + target.getClass()); } +// not used as of Clojure 1.6, but left for runtime compatibility with +// compiled bytecode from older versions 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); - else - return getInstanceField(target, name); + return invokeNoArgInstanceMember(target, name, false); +} + +public static Object invokeNoArgInstanceMember(Object target, String name, boolean requireField) { + Class c = target.getClass(); + + if(requireField) { + Field f = getField(c, name, false); + if(f != null) + return getInstanceField(target, name); + else + throw new IllegalArgumentException("No matching field found: " + name + + " for " + target.getClass()); + } else { + List meths = getMethods(c, 0, name, false); + if(meths.size() > 0) + return invokeMatchingMethod(name, meths, target, RT.EMPTY_ARRAY); + else + return getInstanceField(target, name); + } } public static Object invokeInstanceMember(Object target, String name) { diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj index f8171c6ab1..3febeab66e 100644 --- a/test/clojure/test_clojure/java_interop.clj +++ b/test/clojure/test_clojure/java_interop.clj @@ -55,6 +55,16 @@ Integer/MAX_VALUE (. Integer MAX_VALUE) )) +(definterface I (a [])) +(deftype T [a] I (a [_] "method")) + +(deftest test-reflective-field-name-ambiguous + (let [t (->T "field")] + (is (= "method" (. ^T t a))) + (is (= "field" (. ^T t -a))) + (is (= "method" (. t a))) + (is (= "field" (. t -a))) + (is (thrown? IllegalArgumentException (. t -BOGUS))))) (deftest test-double-dot (is (= (.. System (getProperties) (get "os.name"))