Skip to content

Commit

Permalink
CLJ-1363 - reflect .- should return field if both field and method exist
Browse files Browse the repository at this point in the history
  • Loading branch information
puredanger authored and stuarthalloway committed Feb 27, 2014
1 parent 3b1a1dd commit 5fda6cb
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 10 deletions.
11 changes: 7 additions & 4 deletions src/jvm/clojure/lang/Compiler.java
Expand Up @@ -943,7 +943,7 @@ else if(instance != null && instance.hasJavaClass() && instance.getJavaClass() !
if(c != null) { if(c != null) {
return new StaticFieldExpr(line, column, c, munge(sym.name), tag); return new StaticFieldExpr(line, column, c, munge(sym.name), tag);
} else } 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 else
{ {
Expand Down Expand Up @@ -1081,18 +1081,20 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{
public final int line; public final int line;
public final int column; public final int column;
public final Symbol tag; 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)"); 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.target = target;
this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null; this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null;
this.field = targetClass != null ? Reflector.getField(targetClass, fieldName, false) : null; this.field = targetClass != null ? Reflector.getField(targetClass, fieldName, false) : null;
this.fieldName = fieldName; this.fieldName = fieldName;
this.line = line; this.line = line;
this.column = column; this.column = column;
this.tag = tag; this.tag = tag;
this.requireField = requireField;
if(field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) if(field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{ {
if(targetClass == null) if(targetClass == null)
Expand All @@ -1111,7 +1113,7 @@ public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Sy
} }


public Object eval() { public Object eval() {
return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName); return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName, requireField);
} }


public boolean canEmitPrimitive(){ public boolean canEmitPrimitive(){
Expand Down Expand Up @@ -1149,6 +1151,7 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
{ {
target.emit(C.EXPRESSION, objx, gen); target.emit(C.EXPRESSION, objx, gen);
gen.push(fieldName); gen.push(fieldName);
gen.push(requireField);
gen.invokeStatic(REFLECTOR_TYPE, invokeNoArgInstanceMember); gen.invokeStatic(REFLECTOR_TYPE, invokeNoArgInstanceMember);
if(context == C.STATEMENT) if(context == C.STATEMENT)
gen.pop(); gen.pop();
Expand Down
28 changes: 22 additions & 6 deletions src/jvm/clojure/lang/Reflector.java
Expand Up @@ -291,13 +291,29 @@ public static Object setInstanceField(Object target, String fieldName, Object va
+ " for " + target.getClass()); + " 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) { public static Object invokeNoArgInstanceMember(Object target, String name) {
//favor method over field return invokeNoArgInstanceMember(target, name, false);
List meths = getMethods(target.getClass(), 0, name, false); }
if(meths.size() > 0)
return invokeMatchingMethod(name, meths, target, RT.EMPTY_ARRAY); public static Object invokeNoArgInstanceMember(Object target, String name, boolean requireField) {
else Class c = target.getClass();
return getInstanceField(target, name);
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) { public static Object invokeInstanceMember(Object target, String name) {
Expand Down
10 changes: 10 additions & 0 deletions test/clojure/test_clojure/java_interop.clj
Expand Up @@ -55,6 +55,16 @@
Integer/MAX_VALUE Integer/MAX_VALUE
(. 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 (deftest test-double-dot
(is (= (.. System (getProperties) (get "os.name")) (is (= (.. System (getProperties) (get "os.name"))
Expand Down

0 comments on commit 5fda6cb

Please sign in to comment.