source: branches/0.16.x/abcl/src/org/armedbear/lisp/Interpreter.java

Last change on this file was 12105, checked in by Mark Evenson, 16 years ago

Split StackFrame? abstraction into Java and Lisp stack frames.

From the original patch/idea from Tobias Rittweiler this introduces
more information of primary interest to ABCL implemnters such as when
a form like (make-thread #'(lambda ())) is evaluated

All users of EXT:BACKTRACE-AS-LIST should now use SYS:BACKTRACE, the
results of which is a list of the new builtin classes JAVA_STACK_FRAME
or LISP_STACK_FRAME. The methods SYS:FRAME-TO-STRING and
SYS:FRAME-TO-LIST are defined to break these new objects into
inspectable parts. As a convenience, there is a SYS:BACKTRACE-AS-LIST
which calls SYS:FRAME-TO-LIST to each element of the computed
backtrace.

Refactorings have occurred on the Java side: the misnamed
LispThread?.backtrace() is now LispThread?.printBacktrace().
LispThread?.backtraceAsList() is now LispThread?.backtrace() as it is
a shorter name, and more to the point.

Java stack frames only appear after a call through Lisp.error(), which
has only the top level as a restart as an option.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 20.3 KB
Line 
1/*
2 * Interpreter.java
3 *
4 * Copyright (C) 2002-2006 Peter Graves
5 * $Id: Interpreter.java 12105 2009-08-19 14:51:56Z 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 java.io.BufferedReader;
37import java.io.File;
38import java.io.IOException;
39import java.io.InputStream;
40import java.io.InputStreamReader;
41import java.io.OutputStream;
42
43public final class Interpreter extends Lisp
44{
45    // There can only be one interpreter.
46    public static Interpreter interpreter;
47
48    private final boolean jlisp;
49    private final InputStream inputStream;
50    private final OutputStream outputStream;
51
52    private static boolean noinit = false;
53    private static boolean noinform = false;
54
55    public static synchronized Interpreter getInstance()
56    {
57        return interpreter;
58    }
59
60    // Interface.
61    public static synchronized Interpreter createInstance()
62    {
63        if (interpreter != null)
64            return null;
65        interpreter = new Interpreter();
66        _NOINFORM_.setSymbolValue(T);
67        initializeLisp();
68        return interpreter;
69    }
70
71    public static synchronized Interpreter createDefaultInstance(String[] args)
72    {
73        if (interpreter != null)
74            return null;
75        interpreter = new Interpreter();
76        try {
77            if (args != null)
78                preprocessCommandLineArguments(args);
79            if (!noinform) {
80                Stream out = getStandardOutput();
81                out._writeString(banner());
82                out._finishOutput();
83            }
84            if (noinform)
85                _NOINFORM_.setSymbolValue(T);
86            else {
87                double uptime = (System.currentTimeMillis() - Main.startTimeMillis) / 1000.0;
88                getStandardOutput()._writeString("Low-level initialization completed in " +
89                                                 uptime + " seconds.\n");
90            }
91            initializeLisp();
92            initializeTopLevel();
93            if (!noinit)
94                processInitializationFile();
95            if (args != null)
96                postprocessCommandLineArguments(args);
97        }
98        catch (Throwable t) {
99            t.printStackTrace();
100        }
101        return interpreter;
102    }
103
104    public static synchronized Interpreter createJLispInstance(
105        InputStream in,
106        OutputStream out,
107        String initialDirectory,
108        String version)
109    {
110        if (interpreter != null)
111            return null;
112        interpreter = new Interpreter(in, out, initialDirectory);
113        try {
114            Stream stdout = getStandardOutput();
115            stdout._writeLine(version);
116            stdout._writeString(banner());
117            stdout._finishOutput();
118        }
119        catch (Throwable t) {
120            t.printStackTrace();
121        }
122        initializeJLisp();
123        initializeTopLevel();
124        processInitializationFile();
125        return interpreter;
126    }
127
128    private Interpreter()
129    {
130        jlisp = false;
131        inputStream = null;
132        outputStream = null;
133    }
134
135    private Interpreter(InputStream inputStream, OutputStream outputStream,
136                        String initialDirectory)
137    {
138        jlisp = true;
139        this.inputStream = inputStream;
140        this.outputStream = outputStream;
141        resetIO(new Stream(inputStream, Symbol.CHARACTER),
142                new Stream(outputStream, Symbol.CHARACTER));
143        if (!initialDirectory.endsWith(File.separator))
144            initialDirectory = initialDirectory.concat(File.separator);
145        try {
146            Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(new Pathname(initialDirectory));
147        }
148        catch (Throwable t) {
149            Debug.trace(t);
150        }
151    }
152
153    // Interface.
154    public LispObject eval(String s) throws ConditionThrowable
155    {
156        return eval(new StringInputStream(s).read(true, NIL, false,
157                                                  LispThread.currentThread()));
158    }
159
160    public static synchronized void initializeLisp()
161    {
162        if (!initialized) {
163            try {
164                Load.loadSystemFile("boot.lisp", false, false, false);
165            }
166            catch (ConditionThrowable c) {
167                reportError(c, LispThread.currentThread());
168            }
169            catch (Throwable t) {
170                t.printStackTrace();
171            }
172            initialized = true;
173        }
174    }
175
176    public static synchronized void initializeJLisp()
177    {
178        if (!initialized) {
179            try {
180                Symbol.FEATURES.setSymbolValue(new Cons(Keyword.J,
181                                                   Symbol.FEATURES.getSymbolValue()));
182                Load.loadSystemFile("boot.lisp", false, false, false);
183                Class.forName("org.armedbear.j.LispAPI");
184                Load.loadSystemFile("j.lisp");
185            }
186            catch (ConditionThrowable c) {
187                reportError(c, LispThread.currentThread());
188            }
189            catch (Throwable t) {
190                t.printStackTrace();
191            }
192            initialized = true;
193        }
194    }
195
196    private static boolean topLevelInitialized;
197
198    private static synchronized void initializeTopLevel()
199    {
200        if (!topLevelInitialized) {
201            try {
202                // Resolve top-level-loop autoload.
203                Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
204                LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
205                if (tplFun instanceof Autoload) {
206                    Autoload autoload = (Autoload) tplFun;
207                    autoload.load();
208                }
209            }
210            catch (Throwable t) {
211                t.printStackTrace();
212            }
213            topLevelInitialized = true;
214        }
215    }
216
217    private static synchronized void processInitializationFile()
218    {
219        try {
220            String userHome = System.getProperty("user.home");
221            File file = new File(userHome, ".abclrc");
222            if (file.isFile()) {
223                Load.load(file.getCanonicalPath());
224                return;
225            }
226        }
227        catch (Throwable t) {
228            t.printStackTrace();
229        }
230    }
231
232    // Check for --noinit; verify that arguments are supplied for --load and
233    // --eval options.
234    private static void preprocessCommandLineArguments(String[] args)
235        throws ConditionThrowable
236    {
237        if (args != null) {
238            for (int i = 0; i < args.length; ++i) {
239                String arg = args[i];
240                if (arg.equals("--noinit")) {
241                    noinit = true;
242                } else if (arg.equals("--noinform")) {
243                    noinform = true;
244                } else if (arg.equals("--batch")) {
245                    _BATCH_MODE_.setSymbolValue(T);
246                } else if (arg.equals("--eval")) {
247                    if (i + 1 < args.length) {
248                        ++i;
249                    } else {
250                        System.err.println("No argument supplied to --eval");
251                        System.exit(1);
252                    }
253                } else if (arg.equals("--load") ||
254                           arg.equals("--load-system-file")) {
255                    if (i + 1 < args.length) {
256                        ++i;
257                    } else {
258                        System.err.println("No argument supplied to --load");
259                        System.exit(1);
260                    }
261                }
262            }
263        }
264    }
265
266    // Do the --load and --eval actions.
267    private static void postprocessCommandLineArguments(String[] args)
268        throws ConditionThrowable
269    {
270        if (args != null) {
271            for (int i = 0; i < args.length; ++i) {
272                String arg = args[i];
273                if (arg.equals("--eval")) {
274                    if (i + 1 < args.length) {
275                        try {
276                            evaluate(args[i + 1]);
277                        }
278                        catch (ConditionThrowable c) {
279                            final String separator =
280                                System.getProperty("line.separator");
281                            FastStringBuffer sb = new FastStringBuffer();
282                            sb.append(separator);
283                            sb.append("Caught ");
284                            sb.append(c.getCondition().typeOf().writeToString());
285                            sb.append(" while processing --eval option \"" +
286                                      args[i + 1] + "\":");
287                            sb.append(separator);
288                            sb.append("  ");
289                            final LispThread thread = LispThread.currentThread();
290                            thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
291                            sb.append(c.getCondition().writeToString());
292                            sb.append(separator);
293                            System.err.print(sb.toString());
294                            System.exit(2);
295                        }
296                        ++i;
297                    } else {
298                        // Shouldn't happen.
299                        System.err.println("No argument supplied to --eval");
300                        System.exit(1);
301                    }
302                } else if (arg.equals("--load") ||
303                           arg.equals("--load-system-file")) {
304                    if (i + 1 < args.length) {
305                        try {
306                            if (arg.equals("--load"))
307                                Load.load(new Pathname(args[i + 1]),
308                                          args[i + 1],
309                                          false, false, true);
310                            else
311                                Load.loadSystemFile(args[i + 1]);
312                        }
313                        catch (ConditionThrowable c) {
314                            System.err.println("Caught condition: " +
315                                               c.getCondition().writeToString() +
316                                               " while loading: " +
317                                               args[i+1]);
318                            System.exit(2);
319                        }
320                        ++i;
321                    } else {
322                        // Shouldn't happen.
323                        System.err.println("No argument supplied to --load");
324                        System.exit(1);
325                    }
326                }
327            }
328        }
329    }
330
331    public void run()
332    {
333        final LispThread thread = LispThread.currentThread();
334        try {
335            Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
336            LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
337            if (tplFun instanceof Function) {
338                thread.execute(tplFun);
339                return;
340            }
341            // We only arrive here if something went wrong and we weren't able
342            // to load top-level.lisp and run the normal top-level loop.
343            Stream out = getStandardOutput();
344            while (true) {
345                try {
346                    thread.resetStack();
347                    thread.lastSpecialBinding = null;
348                    out._writeString("* ");
349                    out._finishOutput();
350                    LispObject object =
351                        getStandardInput().read(false, EOF, false, thread);
352                    if (object == EOF)
353                        break;
354                    out.setCharPos(0);
355                    Symbol.MINUS.setSymbolValue(object);
356                    LispObject result = eval(object, new Environment(), thread);
357                    Debug.assertTrue(result != null);
358                    Symbol.STAR_STAR_STAR.setSymbolValue(Symbol.STAR_STAR.getSymbolValue());
359                    Symbol.STAR_STAR.setSymbolValue(Symbol.STAR.getSymbolValue());
360                    Symbol.STAR.setSymbolValue(result);
361                    Symbol.PLUS_PLUS_PLUS.setSymbolValue(Symbol.PLUS_PLUS.getSymbolValue());
362                    Symbol.PLUS_PLUS.setSymbolValue(Symbol.PLUS.getSymbolValue());
363                    Symbol.PLUS.setSymbolValue(Symbol.MINUS.getSymbolValue());
364                    out = getStandardOutput();
365                    out.freshLine();
366                    LispObject[] values = thread.getValues();
367                    Symbol.SLASH_SLASH_SLASH.setSymbolValue(Symbol.SLASH_SLASH.getSymbolValue());
368                    Symbol.SLASH_SLASH.setSymbolValue(Symbol.SLASH.getSymbolValue());
369                    if (values != null) {
370                        LispObject slash = NIL;
371                        for (int i = values.length; i-- > 0;)
372                            slash = new Cons(values[i], slash);
373                        Symbol.SLASH.setSymbolValue(slash);
374                        for (int i = 0; i < values.length; i++)
375                            out._writeLine(values[i].writeToString());
376                    } else {
377                        Symbol.SLASH.setSymbolValue(new Cons(result));
378                        out._writeLine(result.writeToString());
379                    }
380                    out._finishOutput();
381                }
382                catch (StackOverflowError e) {
383                    getStandardInput().clearInput();
384                    out._writeLine("Stack overflow");
385                }
386                catch (ConditionThrowable c) {
387                    reportError(c, thread);
388                }
389                catch (Throwable t) {
390                    getStandardInput().clearInput();
391                    out.printStackTrace(t);
392                    thread.printBacktrace();
393                }
394            }
395        }
396        catch (Throwable t) {
397            t.printStackTrace();
398        }
399    }
400
401    private static void reportError(ConditionThrowable c, LispThread thread)
402    {
403        try {
404            getStandardInput().clearInput();
405            Stream out = getStandardOutput();
406            out.freshLine();
407            Condition condition = (Condition) c.getCondition();
408            out._writeLine("Error: unhandled condition: " +
409                           condition.writeToString());
410            if (thread != null)
411                thread.printBacktrace();
412        }
413        catch (Throwable t) {
414           
415        }
416    }
417
418    public void kill()
419    {
420        kill(0);
421    }
422
423    public void kill(int status)
424    {
425        if (jlisp) {
426            try {
427                inputStream.close();
428            }
429            catch (IOException e) {
430                Debug.trace(e);
431            }
432            try {
433                outputStream.close();
434            }
435            catch (IOException e) {
436                Debug.trace(e);
437            }
438        } else
439            System.exit(status);
440    }
441
442    public synchronized void dispose()
443    {
444        Debug.trace("Interpreter.dispose");
445        Debug.assertTrue(interpreter == this);
446        interpreter = null;
447    }
448
449    @Override
450    protected void finalize() throws Throwable
451    {
452        System.err.println("Interpreter.finalize");
453    }
454
455    private static final Primitive _DEBUGGER_HOOK_FUNCTION =
456        new Primitive("%debugger-hook-function", PACKAGE_SYS, false)
457    {
458        @Override
459        public LispObject execute(LispObject first, LispObject second)
460            throws ConditionThrowable
461        {
462            final Condition condition = (Condition) first;
463            if (interpreter == null) {
464                final LispThread thread = LispThread.currentThread();
465                final SpecialBinding lastSpecialBinding = thread.lastSpecialBinding;
466                thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
467                try {
468                    final LispObject truename =
469                        Symbol.LOAD_TRUENAME.symbolValue(thread);
470                    if (truename != NIL) {
471                        final LispObject stream =
472                            _LOAD_STREAM_.symbolValue(thread);
473                        if (stream instanceof Stream) {
474                            final int lineNumber =
475                                ((Stream)stream).getLineNumber() + 1;
476                            final int offset =
477                                ((Stream)stream).getOffset();
478                            Debug.trace("Error loading " +
479                                        truename.writeToString() +
480                                        " at line " + lineNumber +
481                                        " (offset " + offset + ")");
482                        }
483                    }
484                    Debug.trace("Encountered unhandled condition of type " +
485                                condition.typeOf().writeToString() + ':');
486                    Debug.trace("  " + condition.writeToString());
487                }
488                catch (Throwable t) {}
489                finally {
490                    thread.lastSpecialBinding = lastSpecialBinding;
491                }
492            }
493            throw new ConditionThrowable(condition);
494        }
495    };
496
497    public static final LispObject readFromString(String s)
498    {
499        try {
500            return new StringInputStream(s).read(true, NIL, false,
501                                                 LispThread.currentThread());
502        }
503        catch (Throwable t) {
504            return null;
505        }
506    }
507
508    // For j.
509    public static LispObject evaluate(String s) throws ConditionThrowable
510    {
511        if (!initialized)
512            initializeJLisp();
513        StringInputStream stream = new StringInputStream(s);
514        final LispThread thread = LispThread.currentThread();
515        LispObject obj = stream.read(false, EOF, false, thread);
516        if (obj == EOF)
517            return error(new EndOfFile(stream));
518        final SpecialBinding lastSpecialBinding = thread.lastSpecialBinding;
519        thread.bindSpecial(Symbol.DEBUGGER_HOOK, _DEBUGGER_HOOK_FUNCTION);
520        try {
521            return eval(obj, new Environment(), thread);
522        }
523        finally {
524            thread.lastSpecialBinding = lastSpecialBinding;
525        }
526    }
527
528    private static final String build;
529
530    static {
531        String s = null;
532        InputStream in = Interpreter.class.getResourceAsStream("build");
533        if (in != null) {
534            try {
535                BufferedReader reader =
536                    new BufferedReader(new InputStreamReader(in));
537                s = reader.readLine();
538                reader.close();
539            }
540            catch (IOException e) {}
541        }
542        build = s;
543    }
544
545    private static String banner()
546    {
547        final String sep = System.getProperty("line.separator");
548        FastStringBuffer sb = new FastStringBuffer("Armed Bear Common Lisp ");
549        sb.append(Version.getVersion());
550        if (build != null) {
551            sb.append(" (built ");
552            sb.append(build);
553            sb.append(')');
554        }
555        sb.append(sep);
556        sb.append("Java ");
557        sb.append(System.getProperty("java.version"));
558        sb.append(' ');
559        sb.append(System.getProperty("java.vendor"));
560        sb.append(sep);
561        String vm = System.getProperty("java.vm.name");
562        if (vm != null) {
563            sb.append(vm);
564            sb.append(sep);
565        }
566        return sb.toString();
567    }
568}
Note: See TracBrowser for help on using the repository browser.