source: trunk/abcl/src/org/armedbear/lisp/JavaClassLoader.java

Last change on this file was 15439, checked in by Mark Evenson, 3 years ago

Teach our classloader about URLPathname objects

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.5 KB
Line 
1/*
2 * JavaClassLoader.java
3 *
4 * Copyright (C) 2003-2005 Peter Graves
5 * $Id: JavaClassLoader.java 15439 2020-10-29 16:54:32Z mevenson $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38import java.util.Collections;
39import java.util.HashSet;
40import java.util.Set;
41import java.io.DataInputStream;
42import java.io.IOException;
43import java.io.InputStream;
44import java.net.URL;
45import java.net.URLClassLoader;
46
47public class JavaClassLoader extends URLClassLoader {
48
49    private static JavaClassLoader persistentInstance;
50
51    public static boolean checkPreCompiledClassLoader = true;
52   
53    public Class<?> loadClass(String name) throws ClassNotFoundException {
54        if (checkPreCompiledClassLoader) {
55            Class<?> c = findPrecompiledClassOrNull(name);
56            if (c != null) {
57                return c;                       
58            }
59        }
60        return loadClass(name, false);
61    }
62   
63    /**
64     * Returns a class loaded by the system or bootstrap class loader;
65     * or return null if not found.
66     *
67     * On AOT systems like GCJ and IKVM this means a class implemented in ASM or CLR
68     *
69     * like findLoadedClass it does not throw an exception if a class is not found
70     */
71    public Class<?> findPrecompiledClassOrNull(String name) {
72        ClassLoader ourCL = JavaClassLoader.class.getClassLoader();
73        while (ourCL != null) {
74            try {
75                return Class.forName(name, true, ourCL);
76            } catch (ClassNotFoundException cnf) {
77            }
78            ourCL = ourCL.getParent();
79        }
80        try {
81            return findSystemClass(name);
82        } catch (ClassNotFoundException e) {
83            return null;
84        }
85    }
86   
87    public byte[] getFunctionClassBytes(String name) {
88        Pathname pathname
89          = (Pathname)Pathname.create(name.substring("org/armedbear/lisp/".length()) 
90                    + "." + Lisp._COMPILE_FILE_CLASS_EXTENSION_.symbolValue().getStringValue());
91        return readFunctionBytes(pathname);
92    }
93
94    public byte[] getFunctionClassBytes(Class<?> functionClass) {
95        String className = functionClass.getName();
96        try {
97            String ext = Lisp._COMPILE_FILE_CLASS_EXTENSION_.symbolValue().getStringValue();
98            InputStream is = getResourceAsStream(className.replace('.', '/') + "." + ext);
99            if (is != null) {
100                byte[] imgDataBa = new byte[(int) is.available()];
101                DataInputStream dataIs = new DataInputStream(is);
102                dataIs.readFully(imgDataBa);
103                return imgDataBa;
104            }
105        } catch (IOException e) {
106        }
107        return getFunctionClassBytes(className);
108    }
109
110    final public byte[] getFunctionClassBytes(Function f) {
111        byte[] b = getFunctionClassBytes(f.getClass());
112        f.setClassBytes(b);
113        return b;
114    }
115
116    private static Set<String> packages = Collections.synchronizedSet(new HashSet<String>());
117
118    public JavaClassLoader()
119    {
120        this(JavaClassLoader.class.getClassLoader());
121    }
122
123    public JavaClassLoader(ClassLoader parent) {
124        super(new URL[] {}, parent);
125    }
126   
127    public JavaClassLoader(JavaClassLoader parent) {
128        super(new URL[] {}, (ClassLoader)parent);
129    }
130
131    public JavaClassLoader(URL[] classpath, ClassLoader parent) {
132        super(classpath, parent);
133    }
134
135    public static JavaClassLoader getPersistentInstance()
136    {
137        return getPersistentInstance(null);
138    }
139
140    public static JavaClassLoader getPersistentInstance(String packageName)
141    {
142        if (persistentInstance == null)
143            persistentInstance = new JavaClassLoader();
144        definePackage(packageName);
145        return persistentInstance;
146    }
147
148    private static void definePackage(String packageName)
149    {
150        if (packageName != null && !packages.contains(packageName)) {
151            persistentInstance.definePackage(packageName,"","1.0","","","1.0","",null);
152            packages.add(packageName);
153        }
154    }
155
156    public Class<?> loadClassFromByteArray(byte[] classbytes) {
157        return loadClassFromByteArray(null, classbytes);
158    }
159
160    public Class<?> loadClassFromByteArray(String className,
161                                                byte[] classbytes)
162    {
163        try {
164            long length = classbytes.length; 
165            if (length < Integer.MAX_VALUE) {
166                Class<?> c =
167                    defineLispClass(className, classbytes, 0, (int) length);
168                if (c != null) {
169                    resolveClass(c);
170                    return c;
171                }
172            }
173        }
174        catch (LinkageError e) {
175                throw e;
176        }
177        catch (Throwable t) {
178            Debug.trace(t);
179        }
180        return null;
181    }
182
183    protected final Class<?> defineLispClass(String name, byte[] b, int off, int len)
184                throws ClassFormatError {       
185        ///if (checkPreCompiledClassLoader) Debug.trace("DEFINE JAVA CLASS " + name + " " + len);
186        return defineClass(name, b, off, len);
187    }
188   
189    public Class<?> loadClassFromByteArray(String className, byte[] bytes,
190                                                int offset, int length)
191    {
192        try {
193            Class<?> c = defineLispClass(className, bytes, offset, length);
194            if (c != null) {
195                resolveClass(c);
196                return c;
197            }
198        }
199        catch (VerifyError e)
200          {
201            error(new LispError("Class verification failed: " + e.getMessage()));
202          }
203        catch (Throwable t) {
204            Debug.trace(t);
205        }
206        return null;
207    }
208
209    @Override
210    public void addURL(URL url) {
211        super.addURL(url);
212    }
213
214    public static final Symbol CLASSLOADER = PACKAGE_JAVA.intern("*CLASSLOADER*");
215
216    private static final Primitive GET_DEFAULT_CLASSLOADER = new pf_get_default_classloader();
217    private static final class pf_get_default_classloader extends Primitive {
218       
219        private final LispObject defaultClassLoader = new JavaObject(new JavaClassLoader());
220
221        pf_get_default_classloader() {
222            super("get-default-classloader", PACKAGE_JAVA, true, "");
223        }
224
225        @Override
226        public LispObject execute() {
227            return defaultClassLoader;
228        }
229    };
230
231    // ### make-classloader &optional parent => java-class-loader
232    private static final Primitive MAKE_CLASSLOADER = new pf_make_classloader();
233    private static final class pf_make_classloader extends Primitive
234    {
235        pf_make_classloader() 
236        {
237            super("make-classloader", PACKAGE_JAVA, true, "&optional parent");
238        }
239
240        @Override
241        public LispObject execute() {
242            return new JavaObject(new JavaClassLoader(getCurrentClassLoader()));
243        }
244
245        @Override
246        public LispObject execute(LispObject parent) {
247            return new JavaObject(new JavaClassLoader((ClassLoader) parent.javaInstance(ClassLoader.class)));
248        }
249    };
250
251    // ### dump-classpath &optional classloader => list-of-pathname-lists
252    private static final Primitive DUMP_CLASSPATH = new pf_dump_classpath();
253    private static final class pf_dump_classpath extends Primitive
254    {
255        pf_dump_classpath() 
256        {
257            super("dump-classpath", PACKAGE_JAVA, true, "&optional classloader");
258        }
259
260        @Override
261        public LispObject execute() {
262            return execute(new JavaObject(getCurrentClassLoader()));
263        }
264
265        @Override
266        public LispObject execute(LispObject classloader) {
267            LispObject list = NIL;
268            Object o = classloader.javaInstance();
269            while(o instanceof ClassLoader) {
270                ClassLoader cl = (ClassLoader) o;
271                list = list.push(dumpClassPath(cl));
272                o = cl.getParent();
273            }
274            return list.nreverse();
275        }
276    };
277
278    private static final Primitive GET_CURRENT_CLASSLOADER = new pf_get_current_classloader();
279    @DocString(name="get-current-classloader")
280    private static final class pf_get_current_classloader extends Primitive {
281        pf_get_current_classloader() {
282            super("get-current-classloader", PACKAGE_JAVA, true);
283        }
284        @Override 
285        public LispObject execute() {
286            return new JavaObject(getCurrentClassLoader());
287        }
288    };
289       
290    // ### %add-to-classpath jar-or-jars &optional (classloader (get-current-classloader))
291    private static final Primitive ADD_TO_CLASSPATH = new pf_add_to_classpath();
292    private static final class pf_add_to_classpath extends Primitive
293    {
294        pf_add_to_classpath() 
295        {
296            super("%add-to-classpath", PACKAGE_JAVA, false, 
297                  "jar-or-jars &optional (classloader (get-current-classloader))");
298        }
299
300        @Override
301        public LispObject execute(LispObject jarOrJars) {
302            return execute(jarOrJars, new JavaObject(getCurrentClassLoader()));
303        }
304
305        @Override
306        public LispObject execute(LispObject jarOrJars, LispObject classloader) {
307            Object o = classloader.javaInstance();
308            if(o instanceof JavaClassLoader) {
309                JavaClassLoader jcl = (JavaClassLoader) o;
310                if(jarOrJars instanceof Cons) {
311                    while(jarOrJars != NIL) {
312                        addURL(jcl, jarOrJars.car());
313                        jarOrJars = jarOrJars.cdr();
314                    }
315                } else {
316                    addURL(jcl, jarOrJars);
317                }
318                return T;
319            } else {
320                return error(new TypeError(o + " must be an instance of " + JavaClassLoader.class.getName()));
321            }
322        }
323    };
324
325    protected static void addURL(JavaClassLoader jcl, LispObject jar) {
326      URLPathname urlPathname = null;
327      if (jar instanceof URLPathname) {
328        urlPathname = (URLPathname)jar;
329      } else if (jar instanceof Pathname) {
330        urlPathname = URLPathname.createFromFile((Pathname)jar);
331      } else if (jar instanceof AbstractString) {
332        String namestring = jar.getStringValue();
333        if (!Pathname.isValidURL(namestring)) {
334          Pathname p = Pathname.create(namestring);
335          if (p != null) {
336            urlPathname = URLPathname.create(p);
337          }
338        } else {
339          urlPathname = URLPathname.create(namestring);
340        }
341      }
342      if (urlPathname == null) {
343        error(new TypeError(jar + " must be a pathname designator"));
344      }
345      jcl.addURL(urlPathname.toURL());
346    }
347
348
349    public static LispObject dumpClassPath(ClassLoader o) {
350        if(o instanceof URLClassLoader) {
351            LispObject list = NIL;
352            for(URL u : ((URLClassLoader) o).getURLs()) {
353                list = list.push(URLPathname.create(u));
354            }
355            return new Cons(new JavaObject(o), list.nreverse());
356        } else {
357            return new JavaObject(o);
358        }
359    }
360
361    public static ClassLoader getCurrentClassLoader() {
362        LispObject classLoader = CLASSLOADER.symbolValueNoThrow();
363        if(classLoader != null) {
364            return (ClassLoader) classLoader.javaInstance(ClassLoader.class);
365        } else {
366            return Lisp.class.getClassLoader();
367        }
368    }
369
370
371
372}
Note: See TracBrowser for help on using the repository browser.