From 1be8888fe4add8fd005468cb820e50a0d383b088 Mon Sep 17 00:00:00 2001 From: Ben Smith-Mannschott Date: Tue, 18 Oct 2011 21:51:13 +0200 Subject: [PATCH] CLJ-850: Be sure invokePrim returning subclass of of Object properly implements Fn$XX Given: - P is some primitive type - O is type Object - R some subclass of Object: When Clojure generates a `R invokePrim(P x)`, it also generates a `O invoke(O x)` , which delegates to `R invokePrim(P x)`. `R invokePrim(P x)` overloads, but does not override the method of the corresponding Fn$PO interface. (Java, the virtual machine, supports overloading on return type, though Java the language does not.) Clojure needs to generate an additional `O invokePrim(P x)` method to satisfy the interface. This also delegates to `R invokePrim(P x)`. --- src/jvm/clojure/lang/Compiler.java | 22 +++++++++++++++++++--- test/clojure/test_clojure/metadata.clj | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index a88b914..2084f98 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -5208,8 +5208,22 @@ public static class FnMethod extends ObjMethod{ } + private boolean isSubclassOfObject(Type t){ + final String d = t.getDescriptor(); + return d.startsWith("L") && !d.equals("Ljava/lang/Object;"); + } + public void doEmitPrim(ObjExpr fn, ClassVisitor cv){ - Method ms = new Method("invokePrim", getReturnType(), argtypes); + Type returnType = getReturnType(); + if (isSubclassOfObject(returnType)) + doEmitPrim(fn, cv, OBJECT_TYPE); + doEmitPrim(fn, cv, returnType); + doEmitDelegatingToPrim(fn, cv, returnType); + } + + private void doEmitPrim(ObjExpr fn, ClassVisitor cv, Type returnType){ + + Method ms = new Method("invokePrim", returnType, argtypes); GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_FINAL, ms, @@ -5246,10 +5260,12 @@ public static class FnMethod extends ObjMethod{ gen.returnValue(); //gen.visitMaxs(1, 1); gen.endMethod(); + } - //generate the regular invoke, calling the prim method + private void doEmitDelegatingToPrim(ObjExpr fn, ClassVisitor cv, Type returnType) { Method m = new Method(getMethodName(), OBJECT_TYPE, getArgTypes()); - + Method ms = new Method("invokePrim", returnType, argtypes); + GeneratorAdapter gen; gen = new GeneratorAdapter(ACC_PUBLIC, m, null, diff --git a/test/clojure/test_clojure/metadata.clj b/test/clojure/test_clojure/metadata.clj index 42f7dfe..1c0b767 100644 --- a/test/clojure/test_clojure/metadata.clj +++ b/test/clojure/test_clojure/metadata.clj @@ -209,3 +209,24 @@ ;; join, index, map-invert: Currently always returns a value with ;; no metadata. This seems reasonable. )) + +(deftest defn-primitive-args + (testing "Hinting the arg vector of a primitive-taking fn with a non-primitive type should not result in AbstractMethodError when invoked." + (testing "CLJ-850 is fixed when this case passes." + (is (= "foo" + (eval-in-temp-ns + (defn f ^String [^String s ^long i] s) + (f "foo" 1))))) + (testing "These cases should pass, even without a fix for CLJ-850." + (is (= "foo" + (eval-in-temp-ns + (defn f ^String [^String s] s) + (f "foo")))) + (is (= 1 + (eval-in-temp-ns + (defn f ^long [^String s ^long i] i) + (f "foo" 1)))) + (is (= 1 + (eval-in-temp-ns + (defn f ^long [^long i] i) + (f 1))))))) -- 1.8.0