source: branches/0.15.x/abcl/src/org/armedbear/lisp/Load.java

Last change on this file was 11889, checked in by vvoutilainen, 16 years ago

Remove CompiledFunction?, we don't need it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 25.9 KB
Line 
1/*
2 * Load.java
3 *
4 * Copyright (C) 2002-2007 Peter Graves
5 * $Id: Load.java 11889 2009-05-17 11:36:40Z vvoutilainen $
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 java.io.File;
37import java.io.FileInputStream;
38import java.io.FileNotFoundException;
39import java.io.IOException;
40import java.io.InputStream;
41import java.net.URL;
42import java.net.URLDecoder;
43import java.util.zip.ZipEntry;
44import java.util.zip.ZipException;
45import java.util.zip.ZipFile;
46
47public final class Load extends Lisp
48{
49    public static final LispObject load(String filename)
50        throws ConditionThrowable
51    {
52        final LispThread thread = LispThread.currentThread();
53        return load(new Pathname(filename),
54                    filename,
55                    Symbol.LOAD_VERBOSE.symbolValue(thread) != NIL,
56                    Symbol.LOAD_PRINT.symbolValue(thread) != NIL,
57                    true);
58    }
59
60    private static final File findLoadableFile(final String filename,
61                                               final String dir)
62    {
63        File file = new File(dir, filename);
64  if (!file.isFile()) {
65      String extension = getExtension(filename);
66      if (extension == null) {
67    // No extension specified. Try appending ".lisp" or ".abcl".
68    File lispFile = new File(dir, filename.concat(".lisp"));
69    File abclFile = new File(dir, filename.concat(".abcl"));
70    if (lispFile.isFile() && abclFile.isFile()) {
71        if (abclFile.lastModified() > lispFile.lastModified()) {
72      return abclFile;
73        } else {
74      return lispFile;
75        }
76    } else if (abclFile.isFile()) {
77        return abclFile;
78    } else if (lispFile.isFile()) {
79        return lispFile;
80                }
81            }
82        } else
83            return file; // the file exists
84        return null; // this is the error case: the file does not exist
85                     // no need to check again at the caller
86    }
87
88    public static final LispObject load(Pathname pathname,
89                                        String filename,
90                                        boolean verbose,
91                                        boolean print,
92                                        boolean ifDoesNotExist)
93        throws ConditionThrowable {
94  return load(pathname, filename, verbose, print, ifDoesNotExist, false);
95    }
96
97
98    public static final LispObject load(Pathname pathname,
99                                        String filename,
100                                        boolean verbose,
101                                        boolean print,
102                                        boolean ifDoesNotExist,
103          boolean returnLastResult)
104        throws ConditionThrowable
105    {
106  String dir = null;
107        if (!Utilities.isFilenameAbsolute(filename)) {
108      dir =
109                coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()).getNamestring();
110        }
111
112  File file = findLoadableFile(filename, dir);
113        if (file == null) {
114            if (ifDoesNotExist)
115                return error(new FileError("File not found: " + filename,
116                                            pathname));
117            else
118                return NIL;
119        }
120
121  filename = file.getPath();
122        ZipFile zipfile = null;
123        if (checkZipFile(file))
124        {
125            try {
126                zipfile = new ZipFile(file);
127            }
128            catch (Throwable t) {
129                // Fall through.
130            }
131        }
132        String truename = filename;
133        InputStream in = null;
134        if (zipfile != null) {
135            String name = file.getName();
136            int index = name.lastIndexOf('.');
137            Debug.assertTrue(index >= 0);
138            name = name.substring(0, index).concat("._");
139            ZipEntry entry = zipfile.getEntry(name);
140            if (entry != null) {
141                try {
142                    in = zipfile.getInputStream(entry);
143                }
144                catch (IOException e) {
145                    return error(new LispError(e.getMessage()));
146                }
147            }
148        } else {
149            try {
150                in = new FileInputStream(file);
151                truename = file.getCanonicalPath();
152            }
153            catch (FileNotFoundException e) {
154                if (ifDoesNotExist)
155                    return error(new FileError("File not found: " + filename,
156                                                pathname));
157                else
158                    return NIL;
159            }
160            catch (IOException e) {
161                return error(new LispError(e.getMessage()));
162            }
163        }
164        try {
165            return loadFileFromStream(null, truename,
166                                      new Stream(in, Symbol.CHARACTER),
167                                      verbose, print, false, returnLastResult);
168        }
169        catch (FaslVersionMismatch e) {
170            FastStringBuffer sb =
171                new FastStringBuffer("Incorrect fasl version: ");
172            sb.append(truename);
173            return error(new SimpleError(sb.toString()));
174        }
175        finally {
176            if (in != null) {
177                try {
178                    in.close();
179                }
180                catch (IOException e) {
181                    return error(new LispError(e.getMessage()));
182                }
183            }
184            if (zipfile != null) {
185                try {
186                    zipfile.close();
187                }
188                catch (IOException e) {
189                    return error(new LispError(e.getMessage()));
190                }
191            }
192        }
193    }
194
195    public static final LispObject loadSystemFile(String filename)
196        throws ConditionThrowable
197    {
198        final LispThread thread = LispThread.currentThread();
199        return loadSystemFile(filename,
200                              Symbol.LOAD_VERBOSE.symbolValue(thread) != NIL,
201                              Symbol.LOAD_PRINT.symbolValue(thread) != NIL,
202                              false);
203    }
204
205    public static final LispObject loadSystemFile(String filename, boolean auto)
206        throws ConditionThrowable
207    {
208        LispThread thread = LispThread.currentThread();
209        if (auto) {
210            SpecialBinding lastSpecialBinding = thread.lastSpecialBinding;
211            thread.bindSpecial(Symbol.CURRENT_READTABLE,
212                               STANDARD_READTABLE.symbolValue(thread));
213            thread.bindSpecial(Symbol._PACKAGE_, PACKAGE_CL_USER);
214            try {
215                return loadSystemFile(filename,
216                                      _AUTOLOAD_VERBOSE_.symbolValue(thread) != NIL,
217                                      Symbol.LOAD_PRINT.symbolValue(thread) != NIL,
218                                      auto);
219            }
220            finally {
221                thread.lastSpecialBinding = lastSpecialBinding;
222            }
223        } else {
224            return loadSystemFile(filename,
225                                  Symbol.LOAD_VERBOSE.symbolValue(thread) != NIL,
226                                  Symbol.LOAD_PRINT.symbolValue(thread) != NIL,
227                                  auto);
228        }
229    }
230
231    public static final LispObject loadSystemFile(final String filename,
232                                                  boolean verbose,
233                                                  boolean print,
234                                                  boolean auto)
235        throws ConditionThrowable
236    {
237        final int ARRAY_SIZE = 2;
238        String[] candidates = new String[ARRAY_SIZE];
239        final String extension = getExtension(filename);
240        if (extension == null) {
241            // No extension specified.
242            candidates[0] = filename + '.' + COMPILE_FILE_TYPE;
243            candidates[1] = filename.concat(".lisp");
244        } else if (extension.equals(".abcl")) {
245            candidates[0] = filename;
246            candidates[1] =
247                filename.substring(0, filename.length() - 5).concat(".lisp");
248        } else
249            candidates[0] = filename;
250        InputStream in = null;
251        Pathname pathname = null;
252        String truename = null;
253        for (int i = 0; i < ARRAY_SIZE; i++) {
254            String s = candidates[i];
255            if (s == null)
256                break;
257            ZipFile zipfile = null;
258            final String dir = Site.getLispHome();
259            try {
260                if (dir != null) {
261                    File file = new File(dir, s);
262                    if (file.isFile()) {
263                        // File exists. For system files, we know the extension
264                        // will be .abcl if it is a compiled file.
265                        String ext = getExtension(s);
266                        if (ext.equalsIgnoreCase(".abcl")) {
267                            try {
268                                zipfile = new ZipFile(file);
269                                String name = file.getName();
270                                int index = name.lastIndexOf('.');
271                                Debug.assertTrue(index >= 0);
272                                name = name.substring(0, index).concat("._");
273                                ZipEntry entry = zipfile.getEntry(name);
274                                if (entry != null) {
275                                    in = zipfile.getInputStream(entry);
276                                    truename = file.getCanonicalPath();
277                                }
278                            }
279                            catch (ZipException e) {
280                                // Fall through.
281                            }
282                            catch (Throwable t) {
283                                Debug.trace(t);
284                                in = null;
285                                // Fall through.
286                            }
287                        }
288                        if (in == null) {
289                            try {
290                                in = new FileInputStream(file);
291                                truename = file.getCanonicalPath();
292                            }
293                            catch (IOException e) {
294                                in = null;
295                            }
296                        }
297                    }
298                } else {
299                    URL url = Lisp.class.getResource(s);
300                    if (url != null) {
301                        try {
302                            in = url.openStream();
303                            if ("jar".equals(url.getProtocol()))
304                                pathname = new Pathname(url);
305                            truename = getPath(url);
306                        }
307                        catch (IOException e) {
308                            in = null;
309                        }
310                    }
311                }
312                if (in != null) {
313                    final LispThread thread = LispThread.currentThread();
314                    final SpecialBinding lastSpecialBinding = thread.lastSpecialBinding;
315                    thread.bindSpecial(_WARN_ON_REDEFINITION_, NIL);
316                    try {
317                        return loadFileFromStream(pathname, truename,
318                                                  new Stream(in, Symbol.CHARACTER),
319                                                  verbose, print, auto);
320                    }
321                    catch (FaslVersionMismatch e) {
322                        FastStringBuffer sb =
323                            new FastStringBuffer("; Incorrect fasl version: ");
324                        sb.append(truename);
325                        System.err.println(sb.toString());
326                    }
327                    finally {
328                        thread.lastSpecialBinding = lastSpecialBinding;
329                        try {
330                            in.close();
331                        }
332                        catch (IOException e) {
333                            return error(new LispError(e.getMessage()));
334                        }
335                    }
336                }
337            }
338            finally {
339                if (zipfile != null) {
340                    try {
341                        zipfile.close();
342                    }
343                    catch (IOException e) {
344                        return error(new LispError(e.getMessage()));
345                    }
346                }
347            }
348        }
349        return error(new LispError("File not found: " + filename));
350    }
351
352    // ### *fasl-version*
353    // internal symbol
354    private static final Symbol _FASL_VERSION_ =
355        exportConstant("*FASL-VERSION*", PACKAGE_SYS, Fixnum.getInstance(32));
356
357    // ### *fasl-anonymous-package*
358    // internal symbol
359    /**
360     * This variable gets bound to a package with no name in which the
361     * reader can intern its uninterned symbols.
362     *
363     */
364    public static final Symbol _FASL_ANONYMOUS_PACKAGE_ =
365        internSpecial("*FASL-ANONYMOUS-PACKAGE*", PACKAGE_SYS, NIL);
366
367    // ### init-fasl
368    private static final Primitive INIT_FASL =
369        new Primitive("init-fasl", PACKAGE_SYS, true, "&key version")
370    {
371        @Override
372        public LispObject execute(LispObject first, LispObject second)
373            throws ConditionThrowable
374        {
375            if (first == Keyword.VERSION) {
376                if (second.eql(_FASL_VERSION_.getSymbolValue())) {
377                    // OK
378                    final LispThread thread = LispThread.currentThread();
379                    thread.bindSpecial(_FASL_ANONYMOUS_PACKAGE_, NIL);
380                    thread.bindSpecial(_SOURCE_, NIL);
381                    return faslLoadStream(thread);
382                }
383            }
384            throw new FaslVersionMismatch(second);
385        }
386    };
387
388    private static final LispObject loadFileFromStream(LispObject pathname,
389                                                       String truename,
390                                                       Stream in,
391                                                       boolean verbose,
392                                                       boolean print,
393                                                       boolean auto)
394  throws ConditionThrowable {
395  return loadFileFromStream(pathname, truename, in, verbose, print, auto, false);
396    }
397
398    private static final LispObject loadFileFromStream(LispObject pathname,
399                                                       String truename,
400                                                       Stream in,
401                                                       boolean verbose,
402                                                       boolean print,
403                                                       boolean auto,
404                   boolean returnLastResult)
405        throws ConditionThrowable
406    {
407        long start = System.currentTimeMillis();
408        final LispThread thread = LispThread.currentThread();
409        final SpecialBinding lastSpecialBinding = thread.lastSpecialBinding;
410        // "LOAD binds *READTABLE* and *PACKAGE* to the values they held before
411        // loading the file."
412        thread.bindSpecialToCurrentValue(Symbol.CURRENT_READTABLE);
413        thread.bindSpecialToCurrentValue(Symbol._PACKAGE_);
414        int loadDepth = Fixnum.getValue(_LOAD_DEPTH_.symbolValue(thread));
415        thread.bindSpecial(_LOAD_DEPTH_, Fixnum.getInstance(++loadDepth));
416        // Compiler policy.
417        thread.bindSpecialToCurrentValue(_SPEED_);
418        thread.bindSpecialToCurrentValue(_SPACE_);
419        thread.bindSpecialToCurrentValue(_SAFETY_);
420        thread.bindSpecialToCurrentValue(_DEBUG_);
421        thread.bindSpecialToCurrentValue(_EXPLAIN_);
422        final String prefix = getLoadVerbosePrefix(loadDepth);
423        try {
424            if (pathname == null && truename != null)
425                pathname = Pathname.parseNamestring(truename);
426            thread.bindSpecial(Symbol.LOAD_PATHNAME,
427                               pathname != null ? pathname : NIL);
428            thread.bindSpecial(Symbol.LOAD_TRUENAME,
429                               pathname != null ? pathname : NIL);
430            thread.bindSpecial(_SOURCE_,
431                               pathname != null ? pathname : NIL);
432            if (verbose) {
433                Stream out = getStandardOutput();
434                out.freshLine();
435                out._writeString(prefix);
436                out._writeString(auto ? " Autoloading " : " Loading ");
437                out._writeString(truename != null ? truename : "stream");
438                out._writeLine(" ...");
439                out._finishOutput();
440                LispObject result = loadStream(in, print, thread, returnLastResult);
441                long elapsed = System.currentTimeMillis() - start;
442                out.freshLine();
443                out._writeString(prefix);
444                out._writeString(auto ? " Autoloaded " : " Loaded ");
445                out._writeString(truename != null ? truename : "stream");
446                out._writeString(" (");
447                out._writeString(String.valueOf(((float)elapsed)/1000));
448                out._writeLine(" seconds)");
449                out._finishOutput();
450                return result;
451            } else
452                return loadStream(in, print, thread, returnLastResult);
453        }
454        finally {
455            thread.lastSpecialBinding = lastSpecialBinding;
456        }
457    }
458
459    public static String getLoadVerbosePrefix(int loadDepth)
460    {
461        FastStringBuffer sb = new FastStringBuffer(";");
462        for (int i = loadDepth - 1; i-- > 0;)
463            sb.append(' ');
464        return sb.toString();
465    }
466
467    private static final LispObject loadStream(Stream in, boolean print,
468                                               LispThread thread)
469  throws ConditionThrowable {
470  return loadStream(in, print, thread, false);
471    }
472
473    private static final LispObject loadStream(Stream in, boolean print,
474                                               LispThread thread, boolean returnLastResult)
475        throws ConditionThrowable
476    {
477        SpecialBinding lastSpecialBinding = thread.lastSpecialBinding;
478        thread.bindSpecial(_LOAD_STREAM_, in);
479        SpecialBinding sourcePositionBinding =
480            new SpecialBinding(_SOURCE_POSITION_, Fixnum.ZERO,
481                               thread.lastSpecialBinding);
482        thread.lastSpecialBinding = sourcePositionBinding;
483        try {
484            final Environment env = new Environment();
485      LispObject result = NIL;
486            while (true) {
487                sourcePositionBinding.value = Fixnum.getInstance(in.getOffset());
488                LispObject obj = in.read(false, EOF, false, thread);
489                if (obj == EOF)
490                    break;
491                result = eval(obj, env, thread);
492                if (print) {
493                    Stream out =
494                        checkCharacterOutputStream(Symbol.STANDARD_OUTPUT.symbolValue(thread));
495                    out._writeLine(result.writeToString());
496                    out._finishOutput();
497                }
498            }
499      if(returnLastResult) {
500    return result;
501      } else {
502    return T;
503      }
504        }
505        finally {
506            thread.lastSpecialBinding = lastSpecialBinding;
507        }
508    }
509
510    private static final LispObject faslLoadStream(LispThread thread)
511        throws ConditionThrowable
512    {
513        Stream in = (Stream) _LOAD_STREAM_.symbolValue(thread);
514        final Environment env = new Environment();
515        final SpecialBinding lastSpecialBinding = thread.lastSpecialBinding;
516  LispObject result = NIL;
517        try {
518            thread.bindSpecial(_FASL_ANONYMOUS_PACKAGE_, new Package());
519            while (true) {
520                LispObject obj = in.faslRead(false, EOF, true, thread);
521                if (obj == EOF)
522                    break;
523                result = eval(obj, env, thread);
524            }
525        }
526        finally {
527            thread.lastSpecialBinding = lastSpecialBinding;
528        }
529        return result;
530  //There's no point in using here the returnLastResult flag like in
531  //loadStream(): this function is only called from init-fasl, which is
532  //only called from load, which already has its own policy for choosing
533  //whether to return T or the last value.
534    }
535
536    // Returns extension including leading '.'
537    private static final String getExtension(String filename)
538    {
539        int index = filename.lastIndexOf('.');
540        if (index < 0)
541            return null;
542        if (index < filename.lastIndexOf(File.separatorChar))
543            return null; // Last dot was in path part of filename.
544        return filename.substring(index);
545    }
546
547    private static final String getPath(URL url)
548    {
549        if (url != null) {
550            String path;
551            try {
552                path = URLDecoder.decode(url.getPath(),"UTF-8");
553            }
554            catch (java.io.UnsupportedEncodingException uee) {
555                // Can't happen: every Java is supposed to support
556                // at least UTF-8 encoding
557                path = null;
558            }
559            if (path != null) {
560                if (Utilities.isPlatformWindows) {
561                    if (path.length() > 0 && path.charAt(0) == '/')
562                        path = path.substring(1);
563                }
564                return path;
565            }
566        }
567        return null;
568    }
569
570    private static final boolean checkZipFile(File file)
571    {
572        InputStream in = null;
573        try {
574            in = new FileInputStream(file);
575            byte[] bytes = new byte[4];
576            int bytesRead = in.read(bytes);
577            return (bytesRead == 4
578                    && bytes[0] == 0x50
579                    && bytes[1] == 0x4b
580                    && bytes[2] == 0x03
581                    && bytes[3] == 0x04);
582        }
583        catch (Throwable t) {
584            return false;
585        }
586        finally {
587            if (in != null) {
588                try {
589                    in.close();
590                }
591                catch (Throwable t) {}
592            }
593        }
594    }
595
596    // ### %load filespec verbose print if-does-not-exist => generalized-boolean
597    private static final Primitive _LOAD =
598        new Primitive("%load", PACKAGE_SYS, false,
599                      "filespec verbose print if-does-not-exist")
600    {
601        @Override
602        public LispObject execute(LispObject filespec, LispObject verbose,
603          LispObject print, LispObject ifDoesNotExist)
604      throws ConditionThrowable {
605      return load(filespec, verbose, print, ifDoesNotExist, NIL);
606  }
607    };
608
609    // ### %load-returning-last-result filespec verbose print if-does-not-exist => object
610    private static final Primitive _LOAD_RETURNING_LAST_RESULT =
611        new Primitive("%load-returning-last-result", PACKAGE_SYS, false,
612                      "filespec verbose print if-does-not-exist")
613    {
614        @Override
615        public LispObject execute(LispObject filespec, LispObject verbose,
616          LispObject print, LispObject ifDoesNotExist)
617      throws ConditionThrowable {
618      return load(filespec, verbose, print, ifDoesNotExist, T);
619  }
620    };
621
622    private static final LispObject load(LispObject filespec,
623           LispObject verbose,
624           LispObject print,
625           LispObject ifDoesNotExist,
626           LispObject returnLastResult)
627  throws ConditionThrowable {
628  if (filespec instanceof Stream) {
629      if (((Stream)filespec).isOpen()) {
630    LispObject pathname;
631    if (filespec instanceof FileStream)
632        pathname = ((FileStream)filespec).getPathname();
633    else
634        pathname = NIL;
635    String truename;
636    if (pathname instanceof Pathname)
637        truename = ((Pathname)pathname).getNamestring();
638    else
639        truename = null;
640    return loadFileFromStream(pathname,
641            truename,
642            (Stream) filespec,
643            verbose != NIL,
644            print != NIL,
645            false,
646            returnLastResult != NIL);
647      }
648      // If stream is closed, fall through...
649  }
650  Pathname pathname = coerceToPathname(filespec);
651  if (pathname instanceof LogicalPathname)
652      pathname = LogicalPathname.translateLogicalPathname((LogicalPathname)pathname);
653  return load(pathname,
654        pathname.getNamestring(),
655        verbose != NIL,
656        print != NIL,
657        ifDoesNotExist != NIL,
658        returnLastResult != NIL);
659    }
660
661    // ### load-system-file
662    private static final Primitive LOAD_SYSTEM_FILE =
663        new Primitive("load-system-file", PACKAGE_SYS, true)
664    {
665        @Override
666        public LispObject execute(LispObject arg) throws ConditionThrowable
667        {
668            final LispThread thread = LispThread.currentThread();
669            return loadSystemFile(arg.getStringValue(),
670                                  Symbol.LOAD_VERBOSE.symbolValue(thread) != NIL,
671                                  Symbol.LOAD_PRINT.symbolValue(thread) != NIL,
672                                  false);
673        }
674    };
675
676    private static class FaslVersionMismatch extends Error
677    {
678        private final LispObject version;
679
680        public FaslVersionMismatch(LispObject version)
681        {
682            this.version = version;
683        }
684
685        public LispObject getVersion()
686        {
687            return version;
688        }
689    }
690}
Note: See TracBrowser for help on using the repository browser.