From 9f277c80258b3d2951128ce26a07c30ad0b47af0 Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Thu, 4 Dec 2014 18:45:53 +0100 Subject: [PATCH] CLJ-979: make clojure resolve to the correct Class instances Signed-off-by: Stuart Halloway --- src/clj/clojure/genclass.clj | 8 ++--- src/clj/clojure/reflect/java.clj | 2 +- src/jvm/clojure/lang/Compiler.java | 13 ++++--- src/jvm/clojure/lang/DynamicClassLoader.java | 24 +++++++++++-- src/jvm/clojure/lang/RT.java | 35 +++++++++++-------- test/clojure/test_clojure/compilation.clj | 15 ++++++++ .../test_clojure/compilation/examples.clj | 12 +++++++ 7 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 test/clojure/test_clojure/compilation/examples.clj diff --git a/src/clj/clojure/genclass.clj b/src/clj/clojure/genclass.clj index b1ec2473bc..393b0df392 100644 --- a/src/clj/clojure/genclass.clj +++ b/src/clj/clojure/genclass.clj @@ -717,10 +717,10 @@ [& options] (let [options-map (apply hash-map options) [cname bytecode] (generate-interface options-map)] - (if *compile-files* - (clojure.lang.Compiler/writeClassFile cname bytecode) - (.defineClass ^DynamicClassLoader (deref clojure.lang.Compiler/LOADER) - (str (:name options-map)) bytecode options)))) + (when *compile-files* + (clojure.lang.Compiler/writeClassFile cname bytecode)) + (.defineClass ^DynamicClassLoader (deref clojure.lang.Compiler/LOADER) + (str (:name options-map)) bytecode options))) (comment diff --git a/src/clj/clojure/reflect/java.clj b/src/clj/clojure/reflect/java.clj index 2690f1856d..b66c7f633e 100644 --- a/src/clj/clojure/reflect/java.clj +++ b/src/clj/clojure/reflect/java.clj @@ -166,7 +166,7 @@ the kinds of objects to which they can apply."} (deftype JavaReflector [classloader] Reflector (do-reflect [_ typeref] - (let [cls (Class/forName (typename typeref) false classloader)] + (let [cls (clojure.lang.RT/classForName (typename typeref) false classloader)] {:bases (not-empty (set (map typesym (bases cls)))) :flags (parse-flags (.getModifiers cls) :class) :members (set/union (declared-fields cls) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 647ccb8cfa..98910a940a 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1596,7 +1596,7 @@ static class StaticMethodExpr extends MethodExpr{ public final int column; public final java.lang.reflect.Method method; public final Symbol tag; - final static Method forNameMethod = Method.getMethod("Class forName(String)"); + final static Method forNameMethod = Method.getMethod("Class classForName(String)"); final static Method invokeStaticMethodMethod = Method.getMethod("Object invokeStaticMethod(Class,String,Object[])"); final static Keyword warnOnBoxedKeyword = Keyword.intern("warn-on-boxed"); @@ -1773,7 +1773,7 @@ else if(retClass != void.class) else { gen.push(c.getName()); - gen.invokeStatic(CLASS_TYPE, forNameMethod); + gen.invokeStatic(RT_TYPE, forNameMethod); gen.push(methodName); emitArgsAsArray(args, objx, gen); if(context == C.RETURN) @@ -2482,8 +2482,7 @@ public static class NewExpr implements Expr{ public final Class c; final static Method invokeConstructorMethod = Method.getMethod("Object invokeConstructor(Class,Object[])"); -// final static Method forNameMethod = Method.getMethod("Class classForName(String)"); - final static Method forNameMethod = Method.getMethod("Class forName(String)"); + final static Method forNameMethod = Method.getMethod("Class classForName(String)"); public NewExpr(Class c, IPersistentVector args, int line, int column) { @@ -2556,7 +2555,7 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){ else { gen.push(destubClassName(c.getName())); - gen.invokeStatic(CLASS_TYPE, forNameMethod); + gen.invokeStatic(RT_TYPE, forNameMethod); MethodExpr.emitArgsAsArray(args, objx, gen); if(context == C.RETURN) { @@ -4601,7 +4600,7 @@ else if(value instanceof Class) else { gen.push(destubClassName(cc.getName())); - gen.invokeStatic(Type.getType(Class.class), Method.getMethod("Class forName(String)")); + gen.invokeStatic(RT_TYPE, Method.getMethod("Class classForName(String)")); } } else if(value instanceof Symbol) @@ -7421,7 +7420,7 @@ CONSTANT_IDS, new IdentityHashMap(), clinitgen.invokeStatic(objx.objtype, Method.getMethod("void __init" + n + "()")); clinitgen.push(objx.internalName.replace('/','.')); - clinitgen.invokeStatic(CLASS_TYPE, Method.getMethod("Class forName(String)")); + clinitgen.invokeStatic(RT_TYPE, Method.getMethod("Class classForName(String)")); clinitgen.invokeVirtual(CLASS_TYPE,Method.getMethod("ClassLoader getClassLoader()")); clinitgen.invokeStatic(Type.getType(Compiler.class), Method.getMethod("void pushNSandLoader(ClassLoader)")); clinitgen.mark(startTry); diff --git a/src/jvm/clojure/lang/DynamicClassLoader.java b/src/jvm/clojure/lang/DynamicClassLoader.java index 17fa5a1004..61b9ca5291 100644 --- a/src/jvm/clojure/lang/DynamicClassLoader.java +++ b/src/jvm/clojure/lang/DynamicClassLoader.java @@ -48,7 +48,7 @@ public Class defineClass(String name, byte[] bytes, Object srcForm){ return c; } -protected Class findClass(String name) throws ClassNotFoundException{ +static Class findInMemoryClass(String name) { Reference cr = classCache.get(name); if(cr != null) { @@ -58,7 +58,27 @@ protected Class findClass(String name) throws ClassNotFoundException{ else classCache.remove(name, cr); } - return super.findClass(name); + return null; +} + +protected ClassfindClass(String name) throws ClassNotFoundException { + Class c = findInMemoryClass(name); + if (c != null) + return c; + else + return super.findClass(name); +} + +protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class c = findLoadedClass(name); + if (c == null) { + c = findInMemoryClass(name); + if (c == null) + c = getParent().loadClass(name); + } + if (resolve) + resolveClass(c); + return c; } public void registerConstants(int id, Object[] val){ diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index a3b8013361..d09fd65b54 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -2106,11 +2106,16 @@ static public URL getResource(ClassLoader loader, String name){ } } -static public Class classForName(String name) { +static public Class classForName(String name, boolean load, ClassLoader loader) { try { - return Class.forName(name, true, baseLoader()); + Class c = null; + if (!(loader instanceof DynamicClassLoader)) + c = DynamicClassLoader.findInMemoryClass(name); + if (c != null) + return c; + return Class.forName(name, load, loader); } catch(ClassNotFoundException e) { @@ -2118,27 +2123,27 @@ static public Class classForName(String name) { } } +static public Class classForName(String name) { + return classForName(name, true, baseLoader()); +} + static public Class classForNameNonLoading(String name) { - try - { - return Class.forName(name, false, baseLoader()); - } - catch(ClassNotFoundException e) - { - throw Util.sneakyThrow(e); - } + return classForName(name, false, baseLoader()); } -static public Class loadClassForName(String name) throws ClassNotFoundException{ +static public Class loadClassForName(String name) { try { - Class.forName(name, false, baseLoader()); + classForNameNonLoading(name); } - catch(ClassNotFoundException e) + catch(Exception e) { - return null; + if (e instanceof ClassNotFoundException) + return null; + else + throw Util.sneakyThrow(e); } - return Class.forName(name, true, baseLoader()); + return classForName(name); } static public float aget(float[] xs, int i){ diff --git a/test/clojure/test_clojure/compilation.clj b/test/clojure/test_clojure/compilation.clj index 7ea0c9b4e6..6ab669ec3e 100644 --- a/test/clojure/test_clojure/compilation.clj +++ b/test/clojure/test_clojure/compilation.clj @@ -249,3 +249,18 @@ (load-string "(.submit (java.util.concurrent.Executors/newCachedThreadPool) #())"))) (is (try (load-string "(.submit (java.util.concurrent.Executors/newCachedThreadPool) ^Runnable #())") (catch Compiler$CompilerException e nil))))) + +(defrecord Y [a]) +#clojure.test_clojure.compilation.Y[1] +(defrecord Y [b]) + +(binding [*compile-path* "target/test-classes"] + (compile 'clojure.test-clojure.compilation.examples)) + +(deftest CLJ-979 + (is (= clojure.test_clojure.compilation.examples.X + (class (clojure.test-clojure.compilation.examples/->X)))) + (is (.b (clojure.test_clojure.compilation.Y. 1))) + (is (= clojure.test_clojure.compilation.examples.T + (class (clojure.test_clojure.compilation.examples.T.)) + (class (clojure.test-clojure.compilation.examples/->T))))) diff --git a/test/clojure/test_clojure/compilation/examples.clj b/test/clojure/test_clojure/compilation/examples.clj new file mode 100644 index 0000000000..298cb8d988 --- /dev/null +++ b/test/clojure/test_clojure/compilation/examples.clj @@ -0,0 +1,12 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; 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. + +(ns clojure.test-clojure.compilation.examples) + +(eval '(deftype X [])) +(deftype T [])