/*
 * Lisp.java
 *
 * Copyright (C) 2002-2007 Peter Graves <peter@armedbear.org>
 * $Id$
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent
 * modules, and to copy and distribute the resulting executable under
 * terms of your choice, provided that you also meet, for each linked
 * independent module, the terms and conditions of the license of that
 * module.  An independent module is a module which is not derived from
 * or based on this library.  If you modify this library, you may extend
 * this exception to your version of the library, but you are not
 * obligated to do so.  If you do not wish to do so, delete this
 * exception statement from your version.
 */

package org.armedbear.lisp;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;

public final class Lisp
{
  public static final boolean debug = true;

  public static boolean cold = true;

  public static boolean initialized;

  // Packages.
  public static final Package PACKAGE_CL =
    Packages.createPackage("COMMON-LISP", 2048); // EH 10-10-2010: Actual number = 1014
  public static final Package PACKAGE_CL_USER =
    Packages.createPackage("COMMON-LISP-USER", 1024);
  public static final Package PACKAGE_KEYWORD =
    Packages.createPackage("KEYWORD", 1024);
  public static final Package PACKAGE_SYS =
    Packages.createPackage("SYSTEM", 2048); // EH 10-10-2010: Actual number = 1216
  public static final Package PACKAGE_MOP =
    Packages.createPackage("MOP", 512); // EH 10-10-2010: Actual number = 277
  public static final Package PACKAGE_TPL =
    Packages.createPackage("TOP-LEVEL", 128); // EH 10-10-2010: Actual number = 6
  public static final Package PACKAGE_EXT =
    Packages.createPackage("EXTENSIONS", 256); // EH 10-10-2010: Actual number = 131
  public static final Package PACKAGE_JVM =
    Packages.createPackage("JVM", 2048); // EH 10-10-2010: Actual number = 1518
  public static final Package PACKAGE_LOOP =
    Packages.createPackage("LOOP", 512); // EH 10-10-2010: Actual number = 305
  public static final Package PACKAGE_PROF =
    Packages.createPackage("PROFILER");
  public static final Package PACKAGE_JAVA =
    Packages.createPackage("JAVA");
  public static final Package PACKAGE_LISP =
    Packages.createPackage("LISP");
  public static final Package PACKAGE_THREADS =
    Packages.createPackage("THREADS");
  public static final Package PACKAGE_FORMAT =
    Packages.createPackage("FORMAT");
  public static final Package PACKAGE_XP =
    Packages.createPackage("XP");
  public static final Package PACKAGE_PRECOMPILER =
    Packages.createPackage("PRECOMPILER");
  public static final Package PACKAGE_SEQUENCE =
    Packages.createPackage("SEQUENCE", 128); // EH 10-10-2010: Actual number 62
  public static final Package PACKAGE_GRAY_STREAMS_JAVA =
    Packages.createPackage("GRAY-STREAMS/JAVA");

  @DocString(name="nil")
  public static final Symbol NIL = Nil.NIL;

  // We need NIL before we can call usePackage().
  static
  {
    PACKAGE_CL.addNickname("CL");
    PACKAGE_CL_USER.addNickname("CL-USER");
    PACKAGE_CL_USER.usePackage(PACKAGE_CL);
    PACKAGE_CL_USER.usePackage(PACKAGE_EXT);
    PACKAGE_CL_USER.usePackage(PACKAGE_JAVA);
    PACKAGE_SYS.addNickname("SYS");
    PACKAGE_SYS.usePackage(PACKAGE_CL);
    PACKAGE_SYS.usePackage(PACKAGE_EXT);
    PACKAGE_MOP.usePackage(PACKAGE_CL);
    PACKAGE_MOP.usePackage(PACKAGE_EXT);
    PACKAGE_MOP.usePackage(PACKAGE_SYS);
    PACKAGE_TPL.addNickname("TPL");
    PACKAGE_TPL.usePackage(PACKAGE_CL);
    PACKAGE_TPL.usePackage(PACKAGE_EXT);
    PACKAGE_EXT.addNickname("EXT");
    PACKAGE_EXT.usePackage(PACKAGE_CL);
    PACKAGE_EXT.usePackage(PACKAGE_THREADS);
    PACKAGE_JVM.usePackage(PACKAGE_CL);
    PACKAGE_JVM.usePackage(PACKAGE_EXT);
    PACKAGE_JVM.usePackage(PACKAGE_SYS);
    PACKAGE_LOOP.usePackage(PACKAGE_CL);
    PACKAGE_PROF.addNickname("PROF");
    PACKAGE_PROF.usePackage(PACKAGE_CL);
    PACKAGE_PROF.usePackage(PACKAGE_EXT);
    PACKAGE_JAVA.usePackage(PACKAGE_CL);
    PACKAGE_JAVA.usePackage(PACKAGE_EXT);
    PACKAGE_LISP.usePackage(PACKAGE_CL);
    PACKAGE_LISP.usePackage(PACKAGE_EXT);
    PACKAGE_LISP.usePackage(PACKAGE_SYS);
    PACKAGE_THREADS.usePackage(PACKAGE_CL);
    PACKAGE_THREADS.usePackage(PACKAGE_EXT);
    PACKAGE_THREADS.usePackage(PACKAGE_SYS);
    PACKAGE_FORMAT.usePackage(PACKAGE_CL);
    PACKAGE_FORMAT.usePackage(PACKAGE_EXT);
    PACKAGE_XP.usePackage(PACKAGE_CL);
    PACKAGE_PRECOMPILER.addNickname("PRE");
    PACKAGE_PRECOMPILER.usePackage(PACKAGE_CL);
    PACKAGE_PRECOMPILER.usePackage(PACKAGE_EXT);
    PACKAGE_PRECOMPILER.usePackage(PACKAGE_SYS);
    PACKAGE_SEQUENCE.usePackage(PACKAGE_CL);
    PACKAGE_GRAY_STREAMS_JAVA.usePackage(PACKAGE_CL);
  }

  // End-of-file marker.
  public static final LispObject EOF = new LispObject();

  // String hash randomization base
  // Sets a base offset hashing value per JVM session, as an antidote to
  // http://www.nruns.com/_downloads/advisory28122011.pdf
  //    (Denial of Service through hash table multi-collisions)
  public static final int randomStringHashBase =
          (int)(new java.util.Date().getTime());

  public static boolean profiling;

  public static boolean sampling;

  public static volatile boolean sampleNow;

  // args must not be null!
  public static final LispObject funcall(LispObject fun, LispObject[] args,
                                         LispThread thread)

  {
    thread._values = null;

    // 26-07-2009: For some reason we cannot "just" call the array version;
    // it causes an error (Wrong number of arguments for LOOP-FOR-IN)
    // which is probably a sign of an issue in our design?
    switch (args.length)
      {
      case 0:
        return thread.execute(fun);
      case 1:
        return thread.execute(fun, args[0]);
      case 2:
        return thread.execute(fun, args[0], args[1]);
      case 3:
        return thread.execute(fun, args[0], args[1], args[2]);
      case 4:
        return thread.execute(fun, args[0], args[1], args[2], args[3]);
      case 5:
        return thread.execute(fun, args[0], args[1], args[2], args[3],
                              args[4]);
      case 6:
        return thread.execute(fun, args[0], args[1], args[2], args[3],
                              args[4], args[5]);
      case 7:
        return thread.execute(fun, args[0], args[1], args[2], args[3],
                              args[4], args[5], args[6]);
      case 8:
        return thread.execute(fun, args[0], args[1], args[2], args[3],
                              args[4], args[5], args[6], args[7]);
      default:
        return thread.execute(fun, args);
    }
  }

  public static final LispObject macroexpand(LispObject form,
                                             final Environment env,
                                             final LispThread thread)

  {
    LispObject expanded = NIL;
    while (true)
      {
        form = macroexpand_1(form, env, thread);
        LispObject[] values = thread._values;
        if (values[1] == NIL)
          {
            values[1] = expanded;
            return form;
          }
        expanded = T;
      }
  }

  public static final LispObject macroexpand_1(final LispObject form,
                                               final Environment env,
                                               final LispThread thread)

  {
    if (form instanceof Cons)
      {
        LispObject car = ((Cons)form).car;
        if (car instanceof Symbol)
          {
            LispObject obj = env.lookupFunction(car);
            if (obj instanceof AutoloadMacro)
              {
                // Don't autoload function objects here:
                // we want that to happen upon the first use.
                // in case of macro functions, this *is* the first use.
                Autoload autoload = (Autoload) obj;
                autoload.load();
                obj = car.getSymbolFunction();
              }
            if (obj instanceof SpecialOperator)
              {
                obj = get(car, Symbol.MACROEXPAND_MACRO, null);
                if (obj instanceof Autoload)
                  {
                    Autoload autoload = (Autoload) obj;
                    autoload.load();
                    obj = get(car, Symbol.MACROEXPAND_MACRO, null);
                  }
              }
            if (obj instanceof MacroObject)
              {
                LispObject expander = ((MacroObject)obj).expander;
                if (profiling)
                  if (!sampling)
                    expander.incrementCallCount();
                LispObject hook =
                  coerceToFunction(Symbol.MACROEXPAND_HOOK.symbolValue(thread));
                return thread.setValues(hook.execute(expander, form, env),
                                        T);
              }
          }
      }
    else if (form instanceof Symbol)
      {
        Symbol symbol = (Symbol) form;
        LispObject obj = env.lookup(symbol);
        if (obj == null) {
          obj = symbol.getSymbolMacro();
        }
        if (obj instanceof SymbolMacro) {
          return thread.setValues(((SymbolMacro)obj).getExpansion(), T);
        }
      }
    // Not a macro.
    return thread.setValues(form, NIL);
  }

  @DocString(name="interactive-eval")
  private static final Primitive INTERACTIVE_EVAL =
    new Primitive("interactive-eval", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute(LispObject object)
      {
        final LispThread thread = LispThread.currentThread();
        thread.setSpecialVariable(Symbol.MINUS, object);
        LispObject result;
        try
          {
            result = thread.execute(Symbol.EVAL.getSymbolFunction(), object);
          }
        catch (OutOfMemoryError e)
          {
            return error(new StorageCondition("Out of memory " + e.getMessage()));
          }
        catch (StackOverflowError e)
          {
            thread.setSpecialVariable(_SAVED_BACKTRACE_,
                                      thread.backtrace(0));
            return error(new StorageCondition("Stack overflow."));
          }
        catch (ControlTransfer c)
          {
            throw c;
          }
        catch (ProcessingTerminated c)
          {
            throw c;
          }
        catch (IntegrityError c)
          {
            throw c;
          }
        catch (Throwable t) // ControlTransfer handled above
          {
            Debug.trace(t);
            thread.setSpecialVariable(_SAVED_BACKTRACE_,
                                      thread.backtrace(0));
            return error(new LispError("Caught " + t + "."));
          }
        Debug.assertTrue(result != null);
        thread.setSpecialVariable(Symbol.STAR_STAR_STAR,
                                  thread.safeSymbolValue(Symbol.STAR_STAR));
        thread.setSpecialVariable(Symbol.STAR_STAR,
                                  thread.safeSymbolValue(Symbol.STAR));
        thread.setSpecialVariable(Symbol.STAR, result);
        thread.setSpecialVariable(Symbol.PLUS_PLUS_PLUS,
                                  thread.safeSymbolValue(Symbol.PLUS_PLUS));
        thread.setSpecialVariable(Symbol.PLUS_PLUS,
                                  thread.safeSymbolValue(Symbol.PLUS));
        thread.setSpecialVariable(Symbol.PLUS,
                                  thread.safeSymbolValue(Symbol.MINUS));
        LispObject[] values = thread._values;
        thread.setSpecialVariable(Symbol.SLASH_SLASH_SLASH,
                                  thread.safeSymbolValue(Symbol.SLASH_SLASH));
        thread.setSpecialVariable(Symbol.SLASH_SLASH,
                                  thread.safeSymbolValue(Symbol.SLASH));
        if (values != null)
          {
            LispObject slash = NIL;
            for (int i = values.length; i-- > 0;)
              slash = new Cons(values[i], slash);
            thread.setSpecialVariable(Symbol.SLASH, slash);
          }
        else
          thread.setSpecialVariable(Symbol.SLASH, new Cons(result));
        return result;
      }
    };

  private static final void pushJavaStackFrames()
  {
      final LispThread thread = LispThread.currentThread();
      final StackTraceElement[] frames = thread.getJavaStackTrace();

      // frames[0] java.lang.Thread.getStackTrace
      // frames[1] org.armedbear.lisp.LispThread.getJavaStackTrace
      // frames[2] org.armedbear.lisp.Lisp.pushJavaStackFrames

      if (frames.length > 5
        && frames[3].getClassName().equals("org.armedbear.lisp.Lisp")
        && frames[3].getMethodName().equals("error")
        && frames[4].getClassName().startsWith("org.armedbear.lisp.Lisp")
        && frames[4].getMethodName().equals("eval")) {
          // Error condition arising from within Lisp.eval(), so no
          // Java stack frames should be visible to the consumer of the stack abstraction
          return;
      }
      // Search for last Primitive in the StackTrace; that was the
      // last entry point from Lisp.
      int last = frames.length - 1;
      for (int i = 0; i<= last; i++) {
          if (frames[i].getClassName().startsWith("org.armedbear.lisp.Primitive"))
            last = i;
      }
      // Do not include the first three frames which, as noted above, constitute
      // the invocation of this method.
      while (last > 2) {
        thread.pushStackFrame(new JavaStackFrame(frames[last]));
        last--;
      }
  }


  public static final LispObject error(LispObject condition)
  {
    pushJavaStackFrames();
    return Symbol.ERROR.execute(condition);
  }

  public static final LispObject stackError()
  {
    pushJavaStackFrames();
    return Symbol.ERROR.execute(new StorageCondition("Stack overflow."));
  }

  public static final LispObject memoryError(OutOfMemoryError exception)
  {
    pushJavaStackFrames();
    return Symbol.ERROR.execute(new StorageCondition("Out of memory: "
                                                     + exception.getMessage()));
  }

  public static final int ierror(LispObject condition)
  {
    error(condition);
    return 0; // Not reached
  }

  public static final String serror(LispObject condition)
  {
    error(condition);
    return ""; // Not reached
  }


  public static final LispObject error(LispObject condition, LispObject message)
  {
    pushJavaStackFrames();
    return Symbol.ERROR.execute(condition, Keyword.FORMAT_CONTROL, message);
  }

  public static final int ierror(LispObject condition, LispObject message)
  {
    error(condition, message);
    return 0; // Not reached
  }

  public static final String serror(LispObject condition, LispObject message)
  {
    error(condition, message);
    return ""; // Not reached
  }

  public static final LispObject parse_error(String message) {
    return error(new ParseError(message));
  }

  public static final LispObject simple_error(String formatControl, Object... args) {
    LispObject lispArgs = NIL;
    for (int i = 0; i < args.length; i++) {
      if (args[i] instanceof LispObject) {
        lispArgs = lispArgs.push((LispObject)args[i]);
      } else if (args[i] instanceof String) {
        lispArgs = lispArgs.push(new SimpleString((String)args[i]));
      } else {
        lispArgs = lispArgs.push(new JavaObject(args[i]));
      }
    }
    lispArgs = lispArgs.nreverse();

    LispObject format = new SimpleString(formatControl);

    SimpleError s = new SimpleError(format, lispArgs);
    return error(s);
  }

  public static final LispObject type_error(LispObject datum,
                                            LispObject expectedType)
  {
    return error(new TypeError(datum, expectedType));
  }

  public static final LispObject type_error(String message,
                                            LispObject datum,
                                            LispObject expectedType)  {
    return error(new TypeError(message, datum, expectedType));
  }

  public static final LispObject program_error(String message)
  {
    return error(new ProgramError(message));
  }

  public static final LispObject program_error(LispObject initArgs)
  {
    return error(new ProgramError(initArgs));
  }

  public static volatile boolean interrupted;
  public static volatile LispThread threadToInterrupt;

  public static synchronized final void setInterrupted(LispThread thread, boolean b)
  {
    if (b)
      { threadToInterrupt = thread; }
    else
      { threadToInterrupt = null; }
    interrupted = b;
  }

public static synchronized final void handleInterrupt()
  {
    LispThread currentThread = LispThread.currentThread();
    LispThread checkThread = threadToInterrupt;
    setInterrupted(null, false);
    if ((currentThread == threadToInterrupt) || (threadToInterrupt == null))
      {
        //        Symbol.BREAK.getSymbolFunction().execute();
        currentThread.processThreadInterrupts();
      }
    setInterrupted(null, false);
  }

  // Used by the compiler.
  public static final LispObject loadTimeValue(LispObject obj)

  {
    final LispThread thread = LispThread.currentThread();
    if (Symbol.LOAD_TRUENAME.symbolValue(thread) != NIL)
      return eval(obj, new Environment(), thread);
    else
      return NIL;
  }

  public static final LispObject eval(LispObject obj)

  {
    return eval(obj, new Environment(), LispThread.currentThread());
  }

  public static final LispObject eval(final LispObject obj,
                                      final Environment env,
                                      final LispThread thread)

  {
    thread._values = null;
    if (interrupted)
      handleInterrupt();
    if (thread.isDestroyed())
      throw new ThreadDestroyed();
    if (obj instanceof Symbol)
      {
        Symbol symbol = (Symbol)obj;
        LispObject result;
        if (symbol.isSpecialVariable())
          {
            if (symbol.constantp())
              return symbol.getSymbolValue();
            else
              result = thread.lookupSpecial(symbol);
          }
        else if (env.isDeclaredSpecial(symbol))
          result = thread.lookupSpecial(symbol);
        else
          result = env.lookup(symbol);
        if (result == null)
          {
            result = symbol.getSymbolMacro();
            if (result == null) {
                result = symbol.getSymbolValue();
            }
            if(result == null) {
              return error(new UnboundVariable(obj));
            }
          }
        if (result instanceof SymbolMacro)
          return eval(((SymbolMacro)result).getExpansion(), env, thread);
        return result;
      }
    else if (obj instanceof Cons)
      {
        LispObject first = ((Cons)obj).car;
        if (first instanceof Symbol)
          {
            LispObject fun = env.lookupFunction(first);
            if (fun instanceof SpecialOperator)
              {
                if (profiling)
                  if (!sampling)
                    fun.incrementCallCount();
                // Don't eval args!
                LispObject stepInSymbolResult = stepInSymbolP(fun, obj);
                long stepNumberInternal = 0;
                if (stepInSymbolResult != NIL) {
                  stepNumber += 1;
                  stepNumberInternal = stepNumber;
                  handleStepping(fun, (obj != NIL) ? ((Cons)obj).cdr : obj, env,
                                 LispInteger.getInstance(stepNumberInternal));
                }
                LispObject result = fun.execute(((Cons)obj).cdr, env);
                if (stepInSymbolResult != NIL) {
                  printStepValue(stepNumberInternal, result, thread);
                }
                setStepCounterCompleted(stepNumberInternal);
                return result;
              }
            if (fun instanceof MacroObject)
              {
                try
                  {
                    thread.envStack.push(new Environment(null,NIL,fun));
                    return eval(macroexpand(obj, env, thread), env, thread);}
                finally
                  {
                    thread.envStack.pop();
                  }
              }
            if (fun instanceof Autoload)
              {
                Autoload autoload = (Autoload) fun;
                autoload.load();
                return eval(obj, env, thread);
              }
            return evalCall(fun != null ? fun : first,
                            ((Cons)obj).cdr, env, thread);
          }
        else
          {
            if (first instanceof Cons && first.car() == Symbol.LAMBDA)
              {
                Closure closure = new Closure(first, env);
                return evalCall(closure, ((Cons)obj).cdr, env, thread);
              }
            else
              return program_error("Illegal function object: "
                                   + first.princToString() + ".");
          }
      }
    else
      return obj;
  }

  public static final int CALL_REGISTERS_MAX = 8;

  // Also used in JProxy.java.
  public static final LispObject evalCall(LispObject function,
                                          LispObject args,
                                          Environment env,
                                          LispThread thread)

  {
    LispObject stepInSymbolResult = stepInSymbolP(function, args);
    long stepNumberInternal = 0;
    if (stepInSymbolResult != NIL) {
      stepNumber += 1;
      stepNumberInternal = stepNumber;
      handleStepping(function, args != NIL ? ((Cons)args) : args, env,
                     LispInteger.getInstance(stepNumber));
    }
    LispObject result = NIL;
    if (args == NIL) {
      result = thread.execute(function);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    LispObject first = eval(args.car(), env, thread);
    args = ((Cons)args).cdr;
    if (args == NIL) {
      thread._values = null;
      result = thread.execute(function, first);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    LispObject second = eval(args.car(), env, thread);
    args = ((Cons)args).cdr;
    if (args == NIL) {
      thread._values = null;
      result = thread.execute(function, first, second);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    LispObject third = eval(args.car(), env, thread);
    args = ((Cons)args).cdr;
    if (args == NIL) {
      thread._values = null;
      result = thread.execute(function, first, second, third);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    LispObject fourth = eval(args.car(), env, thread);
    args = ((Cons)args).cdr;
    if (args == NIL) {
      thread._values = null;
      result = thread.execute(function, first, second, third, fourth);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    LispObject fifth = eval(args.car(), env, thread);
    args = ((Cons)args).cdr;
    if (args == NIL) {
      thread._values = null;
      result = thread.execute(function, first, second, third, fourth, fifth);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    LispObject sixth = eval(args.car(), env, thread);
    args = ((Cons)args).cdr;
    if (args == NIL) {
      thread._values = null;
      result = thread.execute(function, first, second, third, fourth, fifth,
                            sixth);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    LispObject seventh = eval(args.car(), env, thread);
    args = ((Cons)args).cdr;
    if (args == NIL) {
      thread._values = null;
      result = thread.execute(function, first, second, third, fourth, fifth,
                            sixth, seventh);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    LispObject eighth = eval(args.car(), env, thread);
    args = ((Cons)args).cdr;
    if (args == NIL) {
      thread._values = null;
      result = thread.execute(function, first, second, third, fourth, fifth,
                            sixth, seventh, eighth);
      if (stepInSymbolResult != NIL) {
        printStepValue(stepNumberInternal, result, thread);
      }
      setStepCounterCompleted(stepNumberInternal);
      return result;
    }
    // More than CALL_REGISTERS_MAX arguments.
    final int length = args.length() + CALL_REGISTERS_MAX;
    LispObject[] array = new LispObject[length];
    array[0] = first;
    array[1] = second;
    array[2] = third;
    array[3] = fourth;
    array[4] = fifth;
    array[5] = sixth;
    array[6] = seventh;
    array[7] = eighth;
    for (int i = CALL_REGISTERS_MAX; i < length; i++) {
      array[i] = eval(args.car(), env, thread);
      args = args.cdr();
    }
    thread._values = null;
    result = thread.execute(function, array);
    if (stepInSymbolResult != NIL) {
      printStepValue(stepNumberInternal, result, thread);
    }
    setStepCounterCompleted(stepNumberInternal);
    return result;

  }

  public static final LispObject parseBody(LispObject body,
                                           boolean documentationAllowed)

  {
      LispObject decls = NIL;
      LispObject doc = NIL;

      while (body != NIL) {
        LispObject form = body.car();
        if (documentationAllowed && form instanceof AbstractString
            && body.cdr() != NIL) {
          doc = body.car();
          documentationAllowed = false;
        } else if (form instanceof Cons && form.car() == Symbol.DECLARE)
          decls = new Cons(form, decls);
        else
          break;

        body = body.cdr();
      }
      return list(body, decls.nreverse(), doc);
  }

  public static final LispObject parseSpecials(LispObject forms)

  {
    LispObject specials = NIL;
    while (forms != NIL) {
      LispObject decls = forms.car();

      Debug.assertTrue(decls instanceof Cons);
      Debug.assertTrue(decls.car() == Symbol.DECLARE);
      decls = decls.cdr();
      while (decls != NIL) {
        LispObject decl = decls.car();

        if (decl instanceof Cons && decl.car() == Symbol.SPECIAL) {
            decl = decl.cdr();
            while (decl != NIL) {
              specials = new Cons(checkSymbol(decl.car()), specials);
              decl = decl.cdr();
            }
        }

        decls = decls.cdr();
      }

      forms = forms.cdr();
    }

    return specials;
  }

  public static final LispObject progn(LispObject body, Environment env,
                                       LispThread thread)

  {
    LispObject result = NIL;
    while (body != NIL)
      {
        result = eval(body.car(), env, thread);
        body = ((Cons)body).cdr;
      }
    return result;
  }

  public static final LispObject preprocessTagBody(LispObject body,
                                                   Environment env)

  {
    LispObject localTags = NIL; // Tags that are local to this TAGBODY.
    while (body != NIL)
      {
        LispObject current = body.car();
        body = ((Cons)body).cdr;
        if (current instanceof Cons)
          continue;
        // It's a tag.
        env.addTagBinding(current, body);
        localTags = new Cons(current, localTags);
      }
    return localTags;
  }

  /** Throws a Go exception to cause a non-local transfer
   * of control event, after checking that the extent of
   * the catching tagbody hasn't ended yet.
   *
   * This version is used by the compiler.
   */
  public static final LispObject nonLocalGo(LispObject tagbody,
                                            LispObject tag)

  {
    if (tagbody == null)
      return error(new ControlError("Unmatched tag "
                                    + tag.princToString() +
                                    " for GO outside lexical extent."));

    throw new Go(tagbody, tag);
  }

  /** Throws a Go exception to cause a non-local transfer
   * of control event, after checking that the extent of
   * the catching tagbody hasn't ended yet.
   *
   * This version is used by the interpreter.
   */
  static final LispObject nonLocalGo(Binding binding,
                                     LispObject tag)
  {
    if (binding.env.inactive)
      return error(new ControlError("Unmatched tag "
                                    + binding.symbol.princToString() +
                                    " for GO outside of lexical extent."));

    throw new Go(binding.env, binding.symbol);
  }

  /** Throws a Return exception to cause a non-local transfer
   * of control event, after checking that the extent of
   * the catching block hasn't ended yet.
   *
   * This version is used by the compiler.
   */
  public static final LispObject nonLocalReturn(LispObject blockId,
                                                LispObject blockName,
                                                LispObject result)

  {
    if (blockId == null)
      return error(new ControlError("Unmatched block "
                                    + blockName.princToString() + " for " +
                                    "RETURN-FROM outside lexical extent."));

    throw new Return(blockId, result);
  }

  /** Throws a Return exception to cause a non-local transfer
   * of control event, after checking that the extent of
   * the catching block hasn't ended yet.
   *
   * This version is used by the interpreter.
   */
  static final LispObject nonLocalReturn(Binding binding,
                                         Symbol block,
                                         LispObject result)
  {
    if (binding == null)
      {
        return error(new LispError("No block named " + block.getName() +
                                   " is currently visible."));
      }

    if (binding.env.inactive)
      return error(new ControlError("Unmatched block "
                                    + binding.symbol.princToString() +
                                    " for RETURN-FROM outside of" +
                                    " lexical extent."));

    throw new Return(binding.symbol, binding.value, result);
  }

  public static final LispObject processTagBody(LispObject body,
                                                LispObject localTags,
                                                Environment env)

  {
    LispObject remaining = body;
    LispThread thread = LispThread.currentThread();
    while (remaining != NIL)
      {
        LispObject current = remaining.car();
        if (current instanceof Cons)
          {
            try {
              // Handle GO inline if possible.
              if (((Cons)current).car == Symbol.GO)
                {
                  if (interrupted)
                    handleInterrupt();
                  LispObject tag = current.cadr();
                  Binding binding = env.getTagBinding(tag);
                  if (binding == null)
                    return error(new ControlError("No tag named " +
                                                  tag.princToString() +
                                                  " is currently visible."));
                  else if (memql(tag, localTags))
                    {
                      if (binding.value != null)
                        {
                          remaining = binding.value;
                          continue;
                        }
                    }
                  throw new Go(binding.env, tag);
                }
              eval(current, env, thread);
            }
            catch (Go go)
              {
                LispObject tag;
                if (go.getTagBody() == env
                    && memql(tag = go.getTag(), localTags))
                  {
                    Binding binding = env.getTagBinding(tag);
                    if (binding != null && binding.value != null)
                      {
                        remaining = binding.value;
                        continue;
                      }
                  }
                throw go;
              }
          }
        remaining = ((Cons)remaining).cdr;
      }
    thread._values = null;
    return NIL;
  }

  // Environment wrappers.
  static final boolean isSpecial(Symbol sym, LispObject ownSpecials)
  {
    if (ownSpecials != null)
      {
        if (sym.isSpecialVariable())
          return true;
        for (; ownSpecials != NIL; ownSpecials = ownSpecials.cdr())
          {
            if (sym == ownSpecials.car())
              return true;
          }
      }
    return false;
  }

  public static final void bindArg(LispObject ownSpecials,
                                      Symbol sym, LispObject value,
                                      Environment env, LispThread thread)

  {
    if (isSpecial(sym, ownSpecials)) {
      env.declareSpecial(sym);
      thread.bindSpecial(sym, value);
    }
    else
      env.bind(sym, value);
  }

  public static void bindArg(boolean special, Symbol sym, LispObject value,
                             Environment env, LispThread thread)
  {
      if (special) {
          env.declareSpecial(sym);
          thread.bindSpecial(sym, value);
      }
      else
          env.bind(sym, value);
  }

  public static LispObject list(LispObject[] obj) {
      LispObject theList = NIL;
      if (obj.length > 0)
      for (int i = obj.length - 1; i >= 0; i--)
          theList = new Cons(obj[i], theList);
      return theList;
  }

  public static final Cons list(LispObject obj1, LispObject... remaining)
  {
    Cons theList = null;
    if (remaining.length > 0) {
      theList = new Cons(remaining[remaining.length-1]);
      for (int i = remaining.length - 2; i >= 0; i--)
        theList = new Cons(remaining[i], theList);
    }
    return (theList == null) ? new Cons(obj1) : new Cons(obj1, theList);
  }

  @Deprecated
  public static final Cons list1(LispObject obj1)
  {
    return new Cons(obj1);
  }

  @Deprecated
  public static final Cons list2(LispObject obj1, LispObject obj2)
  {
    return new Cons(obj1, new Cons(obj2));
  }

  @Deprecated
  public static final Cons list3(LispObject obj1, LispObject obj2,
                                 LispObject obj3)
  {
    return new Cons(obj1, new Cons(obj2, new Cons(obj3)));
  }

  @Deprecated
  public static final Cons list4(LispObject obj1, LispObject obj2,
                                 LispObject obj3, LispObject obj4)
  {
    return new Cons(obj1,
                    new Cons(obj2,
                             new Cons(obj3,
                                      new Cons(obj4))));
  }

  @Deprecated
  public static final Cons list5(LispObject obj1, LispObject obj2,
                                 LispObject obj3, LispObject obj4,
                                 LispObject obj5)
  {
    return new Cons(obj1,
                    new Cons(obj2,
                             new Cons(obj3,
                                      new Cons(obj4,
                                               new Cons(obj5)))));
  }

  @Deprecated
  public static final Cons list6(LispObject obj1, LispObject obj2,
                                 LispObject obj3, LispObject obj4,
                                 LispObject obj5, LispObject obj6)
  {
    return new Cons(obj1,
                    new Cons(obj2,
                             new Cons(obj3,
                                      new Cons(obj4,
                                               new Cons(obj5,
                                                        new Cons(obj6))))));
  }

  @Deprecated
  public static final Cons list7(LispObject obj1, LispObject obj2,
                                 LispObject obj3, LispObject obj4,
                                 LispObject obj5, LispObject obj6,
                                 LispObject obj7)
  {
    return new Cons(obj1,
                    new Cons(obj2,
                             new Cons(obj3,
                                      new Cons(obj4,
                                               new Cons(obj5,
                                                        new Cons(obj6,
                                                                 new Cons(obj7)))))));
  }

  @Deprecated
  public static final Cons list8(LispObject obj1, LispObject obj2,
                                 LispObject obj3, LispObject obj4,
                                 LispObject obj5, LispObject obj6,
                                 LispObject obj7, LispObject obj8)
  {
    return new Cons(obj1,
                    new Cons(obj2,
                             new Cons(obj3,
                                      new Cons(obj4,
                                               new Cons(obj5,
                                                        new Cons(obj6,
                                                                 new Cons(obj7,
                                                                          new Cons(obj8))))))));
  }

  @Deprecated
  public static final Cons list9(LispObject obj1, LispObject obj2,
                                 LispObject obj3, LispObject obj4,
                                 LispObject obj5, LispObject obj6,
                                 LispObject obj7, LispObject obj8,
                                 LispObject obj9)
  {
    return new Cons(obj1,
                    new Cons(obj2,
                             new Cons(obj3,
                                      new Cons(obj4,
                                               new Cons(obj5,
                                                        new Cons(obj6,
                                                                 new Cons(obj7,
                                                                          new Cons(obj8,
                                                                                   new Cons(obj9)))))))));
  }

  // Used by the compiler.
  public static final LispObject multipleValueList(LispObject result)

  {
    LispThread thread = LispThread.currentThread();
    LispObject[] values = thread._values;
    if (values == null)
      return new Cons(result);
    thread._values = null;
    LispObject list = NIL;
    for (int i = values.length; i-- > 0;)
      list = new Cons(values[i], list);
    return list;
  }

  // Used by the compiler for MULTIPLE-VALUE-CALLs with a single values form.
  public static final LispObject multipleValueCall1(LispObject result,
                                                    LispObject function,
                                                    LispThread thread)

  {
    LispObject[] values = thread._values;
    thread._values = null;
    if (values == null)
      return thread.execute(coerceToFunction(function), result);
    else
      return funcall(coerceToFunction(function), values, thread);
  }

  public static final void progvBindVars(LispObject symbols,
                                         LispObject values,
                                         LispThread thread)

  {
    for (LispObject list = symbols; list != NIL; list = list.cdr())
      {
        Symbol symbol = checkSymbol(list.car());
        LispObject value;
        if (values != NIL)
          {
            value = values.car();
            values = values.cdr();
          }
        else
          {
            // "If too few values are supplied, the remaining symbols are
            // bound and then made to have no value."
            value = null;
          }
        thread.bindSpecial(symbol, value);
      }
  }

  public static final LispInteger checkInteger(LispObject obj) {
    if (obj instanceof LispInteger)
      return (LispInteger) obj;
    return (LispInteger) // Not reached.
      type_error(obj, Symbol.INTEGER);
  }

  public static final Symbol checkSymbol(LispObject obj)
  {
          if (obj instanceof Symbol)
                  return (Symbol) obj;
          return (Symbol)// Not reached.
              type_error(obj, Symbol.SYMBOL);
  }

  public static final LispObject checkList(LispObject obj)

  {
    if (obj.listp())
      return obj;
    return type_error(obj, Symbol.LIST);
  }

  public static final AbstractArray checkArray(LispObject obj)

  {
          if (obj instanceof AbstractArray)
                  return (AbstractArray) obj;
          return (AbstractArray)// Not reached.
        type_error(obj, Symbol.ARRAY);
  }

  public static final AbstractVector checkVector(LispObject obj)

  {
          if (obj instanceof AbstractVector)
                  return (AbstractVector) obj;
          return (AbstractVector)// Not reached.
        type_error(obj, Symbol.VECTOR);
  }

  public static final DoubleFloat checkDoubleFloat(LispObject obj)

  {
          if (obj instanceof DoubleFloat)
                  return (DoubleFloat) obj;
          return (DoubleFloat)// Not reached.
            type_error(obj, Symbol.DOUBLE_FLOAT);
  }

  public static final SingleFloat checkSingleFloat(LispObject obj)

  {
          if (obj instanceof SingleFloat)
                  return (SingleFloat) obj;
          return (SingleFloat)// Not reached.
            type_error(obj, Symbol.SINGLE_FLOAT);
  }

  public static final StackFrame checkStackFrame(LispObject obj)

  {
          if (obj instanceof StackFrame)
                  return (StackFrame) obj;
          return (StackFrame)// Not reached.
            type_error(obj, Symbol.STACK_FRAME);
  }

  static
  {
    // ### *gensym-counter*
    Symbol.GENSYM_COUNTER.initializeSpecial(Fixnum.ZERO);
  }

  public static final Symbol gensym(LispThread thread)

  {
    return gensym("G", thread);
  }

  public static final Symbol gensym(String prefix, LispThread thread)

  {
    StringBuilder sb = new StringBuilder(prefix);
    final Symbol gensymCounter = Symbol.GENSYM_COUNTER;
    SpecialBinding binding = thread.getSpecialBinding(gensymCounter);
    final LispObject oldValue;
    if (binding != null) {
        oldValue = binding.value;
        if ((oldValue instanceof Fixnum
                || oldValue instanceof Bignum) && Fixnum.ZERO.isLessThanOrEqualTo(oldValue)) {
            binding.value = oldValue.incr();
        }
        else {
           binding.value = Fixnum.ZERO;
           error(new TypeError("The value of *GENSYM-COUNTER* was not a nonnegative integer. Old value: " +
                                oldValue.princToString() + " New value: 0"));
        }
    } else {
        // we're manipulating a global resource
        // make sure we operate thread-safely
        synchronized (gensymCounter) {
            oldValue = gensymCounter.getSymbolValue();
            if ((oldValue instanceof Fixnum
                    || oldValue instanceof Bignum) && Fixnum.ZERO.isLessThanOrEqualTo(oldValue))  {
                gensymCounter.setSymbolValue(oldValue.incr());
            }
            else {
               gensymCounter.setSymbolValue(Fixnum.ZERO);
               error(new TypeError("The value of *GENSYM-COUNTER* was not a nonnegative integer. Old value: " +
                                    oldValue.princToString() + " New value: 0"));
            }
        }
    }

    // Decimal representation.
    if (oldValue instanceof Fixnum)
      sb.append(((Fixnum)oldValue).value);
    else if (oldValue instanceof Bignum)
      sb.append(((Bignum)oldValue).value.toString());

    return new Symbol(new SimpleString(sb));
  }

  public static final String javaString(LispObject arg)

  {
    if (arg instanceof AbstractString)
      return arg.getStringValue();
    if (arg instanceof Symbol)
      return ((Symbol)arg).getName();
    if (arg instanceof LispCharacter)
      return String.valueOf(new char[] {((LispCharacter)arg).value});
    type_error(arg, list(Symbol.OR, Symbol.STRING, Symbol.SYMBOL,
                               Symbol.CHARACTER));
    // Not reached.
    return null;
  }

  public static final LispObject number(long n)
  {
    if (n >= Integer.MIN_VALUE && n <= Integer.MAX_VALUE)
      return Fixnum.getInstance((int)n);
    else
      return Bignum.getInstance(n);
  }

  private static final BigInteger INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE);
  private static final BigInteger INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE);

  public static final LispObject number(BigInteger numerator,
                                        BigInteger denominator)

  {
    if (denominator.signum() == 0) {
      LispObject operands = new Cons(Bignum.getInstance(numerator),
                                     new Cons(Bignum.getInstance(denominator)));
      LispObject args = new Cons(Keyword.OPERATION,
                                 new Cons(Symbol.SLASH,
                                          new Cons(Keyword.OPERANDS,
                                                   new Cons(operands))));

      error(new DivisionByZero(args));
    }
    if (denominator.signum() < 0)
      {
        numerator = numerator.negate();
        denominator = denominator.negate();
      }
    BigInteger gcd = numerator.gcd(denominator);
    if (!gcd.equals(BigInteger.ONE))
      {
        numerator = numerator.divide(gcd);
        denominator = denominator.divide(gcd);
      }
    if (denominator.equals(BigInteger.ONE))
      return number(numerator);
    else
      return new Ratio(numerator, denominator);
  }

  public static final LispObject number(BigInteger n)
  {
    if (n.compareTo(INT_MIN) >= 0 && n.compareTo(INT_MAX) <= 0)
      return Fixnum.getInstance(n.intValue());
    else
      return Bignum.getInstance(n);
  }

  public static final int mod(int number, int divisor)

  {
    final int r;
    try
      {
        r = number % divisor;
      }
    catch (ArithmeticException e)
      {
        error(new ArithmeticError("Division by zero."));
        // Not reached.
        return 0;
      }
    if (r == 0)
      return r;
    if (divisor < 0)
      {
        if (number > 0)
          return r + divisor;
      }
    else
      {
        if (number < 0)
          return r + divisor;
      }
    return r;
  }

  // Adapted from SBCL.
  public static final int mix(long x, long y)
  {
    long xy = x * 3 + y;
    return (int) (536870911L & (441516657L ^ xy ^ (xy >> 5)));
  }

  // Used by the compiler.
  public static LispObject readObjectFromString(String s)
  {
      return readObjectFromReader(new StringReader(s));
  }

  final static Charset UTF8CHARSET = Charset.forName("UTF-8");
  public static LispObject readObjectFromStream(InputStream s)
  {
      return readObjectFromReader(new InputStreamReader(s));
  }

  public static LispObject readObjectFromReader(Reader r)
  {
    LispThread thread = LispThread.currentThread();
    SpecialBindingsMark mark = thread.markSpecialBindings();
    try {
        thread.bindSpecial(Symbol.READ_BASE, LispInteger.getInstance(10));
        thread.bindSpecial(Symbol.READ_EVAL, Symbol.T);
        thread.bindSpecial(Symbol.READ_SUPPRESS, Nil.NIL);
        // No need to bind read default float format: all floats are written
        // with their correct exponent markers due to the fact that DUMP-FORM
        // binds read-default-float-format to NIL

        // No need to bind the default read table, because the default fasl
        // read table is used below
        return new Stream(Symbol.SYSTEM_STREAM, r).read(true, NIL, false,
                                             LispThread.currentThread(),
                                             Stream.faslReadtable);
    }
    finally {
        thread.resetSpecialBindings(mark);
    }
  }

  @Deprecated
  public static final LispObject loadCompiledFunction(final String namestring)
  {
    Pathname name = (Pathname)Pathname.create(namestring);
      byte[] bytes = readFunctionBytes(name);
      if (bytes != null)
        return loadClassBytes(bytes);

      return null;
  }

  public static byte[] readFunctionBytes(final Pathname name) {
      final LispThread thread = LispThread.currentThread();
      Pathname load = null;
      LispObject truenameFasl = Symbol.LOAD_TRUENAME_FASL.symbolValue(thread);
      LispObject truename = Symbol.LOAD_TRUENAME.symbolValue(thread);
      if (truenameFasl instanceof Pathname) {
          load = Pathname.mergePathnames(name, (Pathname)truenameFasl, Keyword.NEWEST);
      } else if (truename instanceof Pathname) {
          load = Pathname.mergePathnames(name, (Pathname)truename, Keyword.NEWEST);
      } else {
        if (!Symbol.PROBE_FILE.execute(name).equals(NIL)) {
          load = name;
        } else {
          load = null;
        }
      }
      InputStream input = null;
      if (load != null) {
          input = load.getInputStream();
      } else {
          // Make a last-ditch attempt to load from the boot classpath XXX OSGi hack
          URL url = null;
          try {
              url = Lisp.class.getResource(name.getNamestring());
              input = url.openStream();
          } catch (IOException e) {
              System.err.println("Failed to read class bytes from boot class " + url);
              error(new LispError("Failed to read class bytes from boot class " + url));
          }
      }
      byte[] bytes = new byte[4096];
      try {
          if (input == null) {
                  Debug.trace("Pathname: " + name);
                  Debug.trace("load: " + load);
                  Debug.trace("LOAD_TRUENAME_FASL: " + truenameFasl);
                  Debug.trace("LOAD_TRUENAME: " + truename);
                  Debug.assertTrue(input != null);
          }

          int n = 0;
          java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
          try {
              while (n >= 0) {
                  n = input.read(bytes, 0, 4096);
                if (n >= 0) {
                    baos.write(bytes, 0, n);
                }
            }
          } catch (IOException e) {
              Debug.trace("Failed to read bytes from "
                          + "'" + name.getNamestring() + "'");
              return null;
          }
          bytes = baos.toByteArray();
      } finally {
          try {
              input.close();
          } catch (IOException e) {
              Debug.trace("Failed to close InputStream: " + e);
          }
      }
      return bytes;
  }

    public static final Function makeCompiledFunctionFromClass(Class<?> c) {
      try {
        if (c != null) {
            Function obj = (Function)c.newInstance();
            return obj;
        } else {
            return null;
        }
      }
      catch (InstantiationException e) {} // ### FIXME
      catch (IllegalAccessException e) {} // ### FIXME

      return null;
    }


  public static final LispObject loadCompiledFunction(InputStream in, int size)
  {
      byte[] bytes = readFunctionBytes(in, size);
      if (bytes != null)
        return loadClassBytes(bytes);
      else
        return error(new FileError("Can't read file off stream."));
  }



  private static final byte[] readFunctionBytes(InputStream in, int size)
  {
    try
      {
        byte[] bytes = new byte[size];
        int bytesRemaining = size;
        int bytesRead = 0;
        while (bytesRemaining > 0)
          {
            int n = in.read(bytes, bytesRead, bytesRemaining);
            if (n < 0)
              break;
            bytesRead += n;
            bytesRemaining -= n;
          }
        in.close();
        if (bytesRemaining > 0)
          Debug.trace("bytesRemaining = " + bytesRemaining);

        return bytes;
      }
    catch (IOException t)
      {
        Debug.trace(t); // FIXME: call error()?
      }
    return null;
  }

    public static final Function loadClassBytes(byte[] bytes)
    {
        return loadClassBytes(bytes, new JavaClassLoader());
    }

    public static final Function loadClassBytes(byte[] bytes,
                                                JavaClassLoader cl)
    {
        Class<?> c = cl.loadClassFromByteArray(null, bytes, 0, bytes.length);
        Function obj = makeCompiledFunctionFromClass(c);
        if (obj != null) {
            obj.setClassBytes(bytes);
        }
        return obj;
    }


  public static final LispObject makeCompiledClosure(LispObject template,
                                                     ClosureBinding[] context)

  {
    return ((CompiledClosure)template).dup().setContext(context);
  }

  public static final String safeWriteToString(LispObject obj)
  {
    try {
        return obj.printObject();
      }
    catch (NullPointerException e)
      {
        Debug.trace(e);
        return "null";
      }
  }

  public static final boolean isValidSetfFunctionName(LispObject obj)
  {
    if (obj instanceof Cons)
      {
        Cons cons = (Cons) obj;
        if (cons.car == Symbol.SETF && cons.cdr instanceof Cons)
          {
            Cons cdr = (Cons) cons.cdr;
            return (cdr.car instanceof Symbol && cdr.cdr == NIL);
          }
      }
    return false;
  }

  public static final boolean isValidMacroFunctionName(LispObject obj)
  {
    if (obj instanceof Cons)
      {
        Cons cons = (Cons) obj;
        if (cons.car == Symbol.MACRO_FUNCTION && cons.cdr instanceof Cons)
          {
            Cons cdr = (Cons) cons.cdr;
            return (cdr.car instanceof Symbol && cdr.cdr == NIL);
          }
      }
    return false;
  }


  public static final LispObject FUNCTION_NAME =
    list(Symbol.OR,
          Symbol.SYMBOL,
          list(Symbol.CONS,
                list(Symbol.EQL, Symbol.SETF),
                list(Symbol.CONS, Symbol.SYMBOL, Symbol.NULL)));

  public static final LispObject UNSIGNED_BYTE_8 =
    list(Symbol.UNSIGNED_BYTE, Fixnum.constants[8]);

  public static final LispObject UNSIGNED_BYTE_16 =
    list(Symbol.UNSIGNED_BYTE, Fixnum.constants[16]);

  public static final LispObject UNSIGNED_BYTE_32 =
    list(Symbol.UNSIGNED_BYTE, Fixnum.constants[32]);

  public static final LispObject UNSIGNED_BYTE_32_MAX_VALUE
    = Bignum.getInstance(4294967295L);

  public static final LispObject getUpgradedArrayElementType(LispObject type)

  {
    if (type instanceof Symbol)
      {
        if (type == Symbol.CHARACTER || type == Symbol.BASE_CHAR ||
            type == Symbol.STANDARD_CHAR)
          return Symbol.CHARACTER;
        if (type == Symbol.BIT)
          return Symbol.BIT;
        if (type == NIL)
          return NIL;
      }
    if (type == BuiltInClass.CHARACTER)
      return Symbol.CHARACTER;
    if (type instanceof Cons)
      {
        if (type.equal(UNSIGNED_BYTE_8))
          return type;
        if (type.equal(UNSIGNED_BYTE_16))
          return type;
        if (type.equal(UNSIGNED_BYTE_32))
          return type;
        LispObject car = type.car();
        if (car == Symbol.INTEGER)
          {
            LispObject lower = type.cadr();
            LispObject upper = type.cdr().cadr();
            // Convert to inclusive bounds.
            if (lower instanceof Cons)
              lower = lower.car().incr();
            if (upper instanceof Cons)
              upper = upper.car().decr();
            if (lower.integerp() && upper.integerp())
              {
                if (lower instanceof Fixnum && upper instanceof Fixnum)
                  {
                    int l = ((Fixnum)lower).value;
                    if (l >= 0)
                      {
                        int u = ((Fixnum)upper).value;
                        if (u <= 1)
                          return Symbol.BIT;
                        if (u <= 255)
                          return UNSIGNED_BYTE_8;
                        if (u <= 65535)
                          return UNSIGNED_BYTE_16;
                        return UNSIGNED_BYTE_32;
                      }
                  }
                if (lower.isGreaterThanOrEqualTo(Fixnum.ZERO))
                  {
                    if (lower.isLessThanOrEqualTo(UNSIGNED_BYTE_32_MAX_VALUE))
                      {
                        if (upper.isLessThanOrEqualTo(UNSIGNED_BYTE_32_MAX_VALUE))
                          return UNSIGNED_BYTE_32;
                      }
                  }
              }
          }
        else if (car == Symbol.EQL)
          {
            LispObject obj = type.cadr();
            if (obj instanceof Fixnum)
              {
                int val = ((Fixnum)obj).value;
                if (val >= 0)
                  {
                    if (val <= 1)
                      return Symbol.BIT;
                    if (val <= 255)
                      return UNSIGNED_BYTE_8;
                    if (val <= 65535)
                      return UNSIGNED_BYTE_16;
                    return UNSIGNED_BYTE_32;
                  }
              }
            else if (obj instanceof Bignum)
              {
                if (obj.isGreaterThanOrEqualTo(Fixnum.ZERO))
                  {
                    if (obj.isLessThanOrEqualTo(UNSIGNED_BYTE_32_MAX_VALUE))
                      return UNSIGNED_BYTE_32;
                  }
              }
          }
        else if (car == Symbol.MEMBER)
          {
            LispObject rest = type.cdr();
            while (rest != NIL)
              {
                LispObject obj = rest.car();
                if (obj instanceof LispCharacter)
                  rest = rest.cdr();
                else
                  return T;
              }
            return Symbol.CHARACTER;
          }
      }
    return T;
  }

  // TODO rename to coerceToJavaChar
  public static final char coerceToJavaChar(LispObject obj) {
    return (char)Fixnum.getValue(obj);
  }

  public static final byte coerceToJavaByte(LispObject obj) {
          return (byte)Fixnum.getValue(obj);
  }

  public static final int coerceToJavaUnsignedInt(LispObject obj) {
    return (int) (obj.longValue() & 0xffffffffL);
  }

  public static final LispObject coerceFromJavaByte(byte b) {
    return Fixnum.constants[((int)b) & 0xff];
  }

  public static final LispCharacter checkCharacter(LispObject obj)

  {
          if (obj instanceof LispCharacter)
                  return (LispCharacter) obj;
          return (LispCharacter) // Not reached.
        type_error(obj, Symbol.CHARACTER);
  }

  public static final Package checkPackage(LispObject obj)

  {
          if (obj instanceof Package)
                  return (Package) obj;
          return (Package) // Not reached.
        type_error(obj, Symbol.PACKAGE);
  }

  public static Pathname checkPathname(LispObject obj)
  {
          if (obj instanceof Pathname)
                  return (Pathname) obj;
          return (Pathname) // Not reached.
        type_error(obj, Symbol.PATHNAME);
  }

  public static final Function checkFunction(LispObject obj)

  {
          if (obj instanceof Function)
                  return (Function) obj;
          return (Function) // Not reached.
        type_error(obj, Symbol.FUNCTION);
  }

  public static final Stream checkStream(LispObject obj)

  {
    if (obj instanceof Stream) {
      return (Stream) obj;
    }
    if (obj.typep(Symbol.STREAM).equal(T)) {
      Stream result = GrayStream.findOrCreate(obj);
      return result;
    }
    return (Stream) // Not reached.
    type_error(obj, Symbol.STREAM);
  }

  public static final Stream checkCharacterInputStream(LispObject obj)

  {
          final Stream stream = checkStream(obj);
          if (stream.isCharacterInputStream())
                  return stream;
          return (Stream) // Not reached.
          error(new TypeError("The value " + obj.princToString() +
                        " is not a character input stream."));
  }

  public static final Stream checkCharacterOutputStream(LispObject obj)

  {
          final Stream stream = checkStream(obj);
          if (stream.isCharacterOutputStream())
                  return stream;
        return (Stream) // Not reached.
        error(new TypeError("The value " + obj.princToString() +
                            " is not a character output stream."));
  }

  public static final Stream checkBinaryInputStream(LispObject obj)

  {
          final Stream stream = checkStream(obj);
          if (stream.isBinaryInputStream())
                  return stream;
        return (Stream) // Not reached.
        error(new TypeError("The value " + obj.princToString() +
                             " is not a binary input stream."));
  }

  public static final Stream outSynonymOf(LispObject obj)

  {
          if (obj instanceof Stream)
            return (Stream) obj;
          if (obj instanceof StandardObject)
            return checkStream(obj);
          if (obj == T)
            return checkCharacterOutputStream(Symbol.TERMINAL_IO.symbolValue());
          if (obj == NIL)
            return checkCharacterOutputStream(Symbol.STANDARD_OUTPUT.symbolValue());
          return (Stream)         // Not reached.
          type_error(obj, Symbol.STREAM);
  }

  public static final Stream inSynonymOf(LispObject obj)

  {
    if (obj instanceof Stream)
      return (Stream) obj;
    if (obj instanceof StandardObject)
      return checkStream(obj);
    if (obj == T)
      return checkCharacterInputStream(Symbol.TERMINAL_IO.symbolValue());
    if (obj == NIL)
      return checkCharacterInputStream(Symbol.STANDARD_INPUT.symbolValue());
          return (Stream)         // Not reached.
          type_error(obj, Symbol.STREAM);
  }

  public static final void writeByte(int n, LispObject obj)

  {
    if (n < 0 || n > 255)
      type_error(Fixnum.getInstance(n), UNSIGNED_BYTE_8);
    checkStream(obj)._writeByte(n);
  }

  public static final Readtable checkReadtable(LispObject obj)

  {
          if (obj instanceof Readtable)
                  return (Readtable) obj;
          return (Readtable)// Not reached.
          type_error(obj, Symbol.READTABLE);
  }

  public final static AbstractString checkString(LispObject obj)

  {
          if (obj instanceof AbstractString)
                  return (AbstractString) obj;
          return (AbstractString)// Not reached.
              type_error(obj, Symbol.STRING);
  }

  public final static Layout checkLayout(LispObject obj)

  {
          if (obj instanceof Layout)
                  return (Layout) obj;
          return (Layout)// Not reached.
                type_error(obj, Symbol.LAYOUT);
  }

  public static final Readtable designator_readtable(LispObject obj)

  {
    if (obj == NIL)
      obj = STANDARD_READTABLE.symbolValue();
    if (obj == null)
        throw new NullPointerException();
    return checkReadtable(obj);
  }

  public static final Environment checkEnvironment(LispObject obj)

  {
          if (obj instanceof Environment)
                  return (Environment) obj;
          return (Environment)// Not reached.
        type_error(obj, Symbol.ENVIRONMENT);
  }

  public static final void checkBounds(int start, int end, int length)

  {
    if (start < 0 || end < 0 || start > end || end > length)
      {
        StringBuilder sb = new StringBuilder("The bounding indices ");
        sb.append(start);
        sb.append(" and ");
        sb.append(end);
        sb.append(" are bad for a sequence of length ");
        sb.append(length);
        sb.append('.');
        error(new TypeError(sb.toString()));
      }
  }

  public static final LispObject coerceToFunction(LispObject obj)

  {
    if (obj instanceof Function)
      return obj;
    if (obj instanceof FuncallableStandardObject)
      return obj;
    if (obj instanceof Symbol)
      {
        LispObject fun = obj.getSymbolFunction();
        if (fun instanceof Function)
          return (Function) fun;
        if (fun instanceof FuncallableStandardObject)
          return fun;
      }
    else if (obj instanceof Cons && obj.car() == Symbol.LAMBDA)
      return new Closure(obj, new Environment());
    if (obj instanceof Cons && obj.car() == Symbol.NAMED_LAMBDA) {
        LispObject name = obj.cadr();
        if (name instanceof Symbol || isValidSetfFunctionName(name)) {
            return new Closure(name,
                               new Cons(Symbol.LAMBDA, obj.cddr()),
                               new Environment());
        }
        return type_error(name, FUNCTION_NAME);
    }
    error(new UndefinedFunction(obj));
    // Not reached.
    return null;
  }

  // Returns package or throws exception.
  public static final Package coerceToPackage(LispObject obj)

  {
    if (obj instanceof Package)
      return (Package) obj;
    String name = javaString(obj);
    Package pkg = getCurrentPackage().findPackage(name);
    if (pkg != null)
      return pkg;
    error(new PackageError(obj.princToString() + " is not the name of a package.", obj));
    // Not reached.
    return null;
  }

  public static Pathname coerceToPathname(LispObject arg)

  {
    if (arg instanceof Pathname)
      return (Pathname) arg;
    if (arg instanceof AbstractString)
      return (Pathname)Pathname.create(((AbstractString)arg).toString());
    if (arg instanceof FileStream)
      return ((FileStream)arg).getPathname();
    if (arg instanceof JarStream)
      return ((JarStream)arg).getPathname();
    if (arg instanceof URLStream)
      return ((URLStream)arg).getPathname();
    type_error(arg, list(Symbol.OR,
                         Symbol.STRING,
                         Symbol.PATHNAME, Symbol.JAR_PATHNAME, Symbol.URL_PATHNAME,
                         Symbol.FILE_STREAM, Symbol.JAR_STREAM, Symbol.URL_STREAM));
    // Not reached.
    return null;
  }

  public static LispObject assq(LispObject item, LispObject alist)

  {
    while (alist instanceof Cons)
      {
        LispObject entry = ((Cons)alist).car;
        if (entry instanceof Cons)
          {
            if (((Cons)entry).car == item)
              return entry;
          }
        else if (entry != NIL)
          return type_error(entry, Symbol.LIST);
        alist = ((Cons)alist).cdr;
      }
    if (alist != NIL)
      return type_error(alist, Symbol.LIST);
    return NIL;
  }

  public static final boolean memq(LispObject item, LispObject list)

  {
    while (list instanceof Cons)
      {
        if (item == ((Cons)list).car)
          return true;
        list = ((Cons)list).cdr;
      }
    if (list != NIL)
      type_error(list, Symbol.LIST);
    return false;
  }

  public static final boolean memql(LispObject item, LispObject list)

  {
    while (list instanceof Cons)
      {
        if (item.eql(((Cons)list).car))
          return true;
        list = ((Cons)list).cdr;
      }
    if (list != NIL)
      type_error(list, Symbol.LIST);
    return false;
  }

  // Property lists.
  public static final LispObject getf(LispObject plist, LispObject indicator,
                                      LispObject defaultValue)

  {
    LispObject list = plist;
    while (list != NIL)
      {
        if (list.car() == indicator)
          return list.cadr();
        if (list.cdr() instanceof Cons)
          list = list.cddr();
        else
          return error(new TypeError("Malformed property list: " +
                                      plist.princToString()));
      }
    return defaultValue;
  }

  public static final LispObject get(LispObject symbol, LispObject indicator)

  {
    LispObject list = checkSymbol(symbol).getPropertyList();
    while (list != NIL)
      {
        if (list.car() == indicator)
          return list.cadr();
        list = list.cddr();
      }
    return NIL;
  }

  public static final LispObject get(LispObject symbol, LispObject indicator,
                                     LispObject defaultValue)

  {
    LispObject list = checkSymbol(symbol).getPropertyList();
    while (list != NIL)
      {
        if (list.car() == indicator)
          return list.cadr();
        list = list.cddr();
      }
    return defaultValue;
  }

  public static final LispObject put(Symbol symbol, LispObject indicator,
                                     LispObject value)

  {
    LispObject list = symbol.getPropertyList();
    while (list != NIL)
      {
        if (list.car() == indicator)
          {
            // Found it!
            LispObject rest = list.cdr();
            rest.setCar(value);
            return value;
          }
        list = list.cddr();
      }
    // Not found.
    symbol.setPropertyList(new Cons(indicator,
                                    new Cons(value,
                                             symbol.getPropertyList())));
    return value;
  }

  public static final LispObject putf(LispObject plist, LispObject indicator,
                                      LispObject value)

  {
    LispObject list = plist;
    while (list != NIL)
      {
        if (list.car() == indicator)
          {
            // Found it!
            LispObject rest = list.cdr();
            rest.setCar(value);
            return plist;
          }
        list = list.cddr();
      }
    // Not found.
    return new Cons(indicator, new Cons(value, plist));
  }

  public static final LispObject remprop(Symbol symbol, LispObject indicator)

  {
    LispObject list = checkList(symbol.getPropertyList());
    LispObject prev = null;
    while (list != NIL)
      {
        if (!(list.cdr() instanceof Cons))
          error(new ProgramError("The symbol " + symbol.princToString() +
                                  " has an odd number of items in its property list."));
        if (list.car() == indicator)
          {
            // Found it!
            if (prev != null)
              prev.setCdr(list.cddr());
            else
              symbol.setPropertyList(list.cddr());
            return T;
          }
        prev = list.cdr();
        list = list.cddr();
      }
    // Not found.
    return NIL;
  }

  public static final String format(LispObject formatControl,
                                    LispObject formatArguments)

  {
    final LispThread thread = LispThread.currentThread();
    String control = formatControl.getStringValue();
    LispObject[] args = formatArguments.copyToArray();
    StringBuffer sb = new StringBuffer();
    if (control != null)
      {
        final int limit = control.length();
        int j = 0;
        final int NEUTRAL = 0;
        final int TILDE = 1;
        int state = NEUTRAL;
        for (int i = 0; i < limit; i++)
          {
            char c = control.charAt(i);
            if (state == NEUTRAL)
              {
                if (c == '~')
                  state = TILDE;
                else
                  sb.append(c);
              }
            else if (state == TILDE)
              {
                if (c == 'A' || c == 'a')
                  {
                    if (j < args.length)
                      {
                        LispObject obj = args[j++];
                        final SpecialBindingsMark mark = thread.markSpecialBindings();
                        thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
                        thread.bindSpecial(Symbol.PRINT_READABLY, NIL);
                        try {
                            sb.append(obj.printObject());
                        }
                        finally {
                            thread.resetSpecialBindings(mark);
                        }
                      }
                  }
                else if (c == 'S' || c == 's')
                  {
                    if (j < args.length)
                      {
                        LispObject obj = args[j++];
                        final SpecialBindingsMark mark = thread.markSpecialBindings();
                        thread.bindSpecial(Symbol.PRINT_ESCAPE, T);
                        try {
                            sb.append(obj.printObject());
                        }
                        finally {
                            thread.resetSpecialBindings(mark);
                        }
                      }
                  }
                else if (c == 'D' || c == 'd')
                  {
                    if (j < args.length)
                      {
                        LispObject obj = args[j++];
                        final SpecialBindingsMark mark = thread.markSpecialBindings();
                        thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
                        thread.bindSpecial(Symbol.PRINT_RADIX, NIL);
                        thread.bindSpecial(Symbol.PRINT_BASE, Fixnum.constants[10]);
                        try {
                            sb.append(obj.printObject());
                        }
                        finally {
                            thread.resetSpecialBindings(mark);
                        }
                      }
                  }
                else if (c == 'X' || c == 'x')
                  {
                    if (j < args.length)
                      {
                        LispObject obj = args[j++];
                        final SpecialBindingsMark mark = thread.markSpecialBindings();
                        thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
                        thread.bindSpecial(Symbol.PRINT_RADIX, NIL);
                        thread.bindSpecial(Symbol.PRINT_BASE, Fixnum.constants[16]);
                        try {
                            sb.append(obj.printObject());
                        }
                        finally {
                            thread.resetSpecialBindings(mark);
                        }
                      }
                  }
                else if (c == '%')
                  {
                    sb.append('\n');
                  }
                state = NEUTRAL;
              }
            else
              {
                // There are no other valid states.
                Debug.assertTrue(false);
              }
          }
      }
    return sb.toString();
  }

  public static final Symbol intern(String name, Package pkg)
  {
    return pkg.intern(name);
  }

  // Used by the compiler.
  public static final Symbol internInPackage(String name, String packageName)

  {
    Package pkg = getCurrentPackage().findPackage(packageName);
    if (pkg == null)
      error(new LispError(packageName + " is not the name of a package."));
    return pkg.intern(name);
  }

  public static final Symbol internKeyword(String s)
  {
    return PACKAGE_KEYWORD.intern(s);
  }

  // The compiler's object table.
  static final ConcurrentHashMap<String,LispObject> objectTable =
          new ConcurrentHashMap<String,LispObject>();

  public static LispObject recall(String key)
  {
    return objectTable.remove(key);
  }

  public static LispObject recall(SimpleString key)
  {
    return objectTable.remove(key.getStringValue());
  }

  // ### remember
  public static final Primitive REMEMBER =
    new Primitive("remember", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute(LispObject key, LispObject value)

      {
        objectTable.put(key.getStringValue(), value);
        return NIL;
      }
    };


  public static final Symbol internSpecial(String name, Package pkg,
                                           LispObject value)
  {
    Symbol symbol = pkg.intern(name);
    symbol.setSpecial(true);
    symbol.setSymbolValue(value);
    return symbol;
  }

  public static final Symbol internConstant(String name, Package pkg,
                                            LispObject value)
  {
    Symbol symbol = pkg.intern(name);
    symbol.initializeConstant(value);
    return symbol;
  }

  public static final Symbol exportSpecial(String name, Package pkg,
                                           LispObject value)
  {
    Symbol symbol = pkg.intern(name);
    pkg.export(symbol); // FIXME Inefficient!
    symbol.setSpecial(true);
    symbol.setSymbolValue(value);
    return symbol;
  }

  public static final Symbol exportConstant(String name, Package pkg,
                                            LispObject value)
  {
    Symbol symbol = pkg.intern(name);
    pkg.export(symbol); // FIXME Inefficient!
    symbol.initializeConstant(value);
    return symbol;
  }

  static
  {
    String userDir = System.getProperty("user.dir");
    if (userDir != null && userDir.length() > 0)
      {
        if (userDir.charAt(userDir.length() - 1) != File.separatorChar)
          userDir = userDir.concat(File.separator);
      }
    // This string will be converted to a pathname when Pathname.java is loaded.
    Symbol.DEFAULT_PATHNAME_DEFAULTS.initializeSpecial(new SimpleString(userDir));
  }

  static
  {
    Symbol._PACKAGE_.initializeSpecial(PACKAGE_CL_USER);
  }

  public static final Package getCurrentPackage()
  {
    return (Package) Symbol._PACKAGE_.symbolValueNoThrow();
  }



  public static final void resetIO(Stream in, Stream out)
  {
    stdin = in;
    stdout = out;
    Symbol.STANDARD_INPUT.setSymbolValue(stdin);
    Symbol.STANDARD_OUTPUT.setSymbolValue(stdout);
    Symbol.ERROR_OUTPUT.setSymbolValue(stdout);
    Symbol.TRACE_OUTPUT.setSymbolValue(stdout);
    Symbol.TERMINAL_IO.setSymbolValue(new TwoWayStream(stdin, stdout, true));
    Symbol.QUERY_IO.setSymbolValue(new TwoWayStream(stdin, stdout, true));
    Symbol.DEBUG_IO.setSymbolValue(new TwoWayStream(stdin, stdout, true));
  }

  // Used in org/armedbear/j/JLisp.java.
  public static final void resetIO()
  {
    resetIO(new Stream(Symbol.SYSTEM_STREAM, System.in, Symbol.CHARACTER, true),
            new Stream(Symbol.SYSTEM_STREAM, System.out, Symbol.CHARACTER, true));
  }

  public static final TwoWayStream getTerminalIO()
  {
    return (TwoWayStream) Symbol.TERMINAL_IO.symbolValueNoThrow();
  }

  public static final Stream getStandardInput()
  {
    return (Stream) Symbol.STANDARD_INPUT.symbolValueNoThrow();
  }

  public static final Stream getStandardOutput()
  {
    LispObject value = Symbol.STANDARD_OUTPUT.symbolValue();
    value = SynonymStream.OUT_SYNONYM_OF.execute(value);
    Stream result = checkStream(value);
    return checkCharacterOutputStream(result);
  }

  static
  {
    Symbol.CURRENT_READTABLE.initializeSpecial(new Readtable());
  }

  // ### +standard-readtable+
  // internal symbol
  public static final Symbol STANDARD_READTABLE =
    internConstant("+STANDARD-READTABLE+", PACKAGE_SYS, new Readtable());

  public static final Readtable currentReadtable()
  {
    return (Readtable) Symbol.CURRENT_READTABLE.symbolValue();
  }

  static
  {
    Symbol.READ_SUPPRESS.initializeSpecial(NIL);
    Symbol.DEBUGGER_HOOK.initializeSpecial(NIL);
  }

  static
  {
    Symbol.MOST_POSITIVE_FIXNUM.initializeConstant(Fixnum.getInstance(Integer.MAX_VALUE));
    Symbol.MOST_NEGATIVE_FIXNUM.initializeConstant(Fixnum.getInstance(Integer.MIN_VALUE));
    Symbol.MOST_POSITIVE_JAVA_LONG.initializeConstant(Bignum.getInstance(Long.MAX_VALUE));
    Symbol.MOST_NEGATIVE_JAVA_LONG.initializeConstant(Bignum.getInstance(Long.MIN_VALUE));
  }

  public static void exit(int status)
  {
    Interpreter interpreter = Interpreter.getInstance();
    if (interpreter != null)
      interpreter.kill(status);
  }

  // ### t
  public static final Symbol T = Symbol.T;
  static
  {
    T.initializeConstant(T);
  }

  static
  {
    Symbol.READ_EVAL.initializeSpecial(T);
  }


  //
  // ### *features*
  //
  static
  {
    final String osName = System.getProperty("os.name");
    final String javaVersion = System.getProperty("java.version");
    final String osArch = System.getProperty("os.arch");

    // Common features
    LispObject featureList = list(Keyword.ARMEDBEAR, Keyword.ABCL,
                                  Keyword.COMMON_LISP, Keyword.ANSI_CL,
                                  Keyword.CDR6,
                                  Keyword.MOP,
                                  internKeyword("PACKAGE-LOCAL-NICKNAMES"));

    // add the contents of version as a keyword symbol regardless of runtime value
    featureList = featureList.push(internKeyword("JVM-" + javaVersion));
    {
      String platformVersion = null;
      if (javaVersion.startsWith("1.")) {
          // pre <https://openjdk.java.net/jeps/223>
          int i = javaVersion.indexOf(".", 2);
          platformVersion = javaVersion.substring(2, i);
        } else {
          int i = javaVersion.indexOf(".");
          if (i >= 0) {
            platformVersion = javaVersion.substring(0, i);
          } else {
            platformVersion = javaVersion;
          }
      }
      // We wish to declare an integer Java version, but specialized
      // builds can suffix further information upon the java.version
      // property.
      try {
        Integer.parseInt(javaVersion);
      } catch (NumberFormatException e) {
        for (int i = 0; i < javaVersion.length(); i++) {
          char c = javaVersion.charAt(i); // Unicode?
          if (!Character.isDigit(c)) {
            // Push the non-conforming keyword for completeness
            featureList.push(internKeyword("JAVA-" + javaVersion));
            platformVersion = javaVersion.substring(0, i);
            break;
          }
        }
      }
      featureList = featureList.push(internKeyword("JAVA-" + platformVersion));
    }

    {       // Deprecated java version
      if (javaVersion.startsWith("1.5")) {
        featureList = new Cons(Keyword.JAVA_1_5, featureList);
      } else if (javaVersion.startsWith("1.6")) {
        featureList = new Cons(Keyword.JAVA_1_6, featureList);
      } else if (javaVersion.startsWith("1.7")) {
        featureList = new Cons(Keyword.JAVA_1_7, featureList);
      } else if (javaVersion.startsWith("1.8")) {
        featureList = new Cons(Keyword.JAVA_1_8, featureList);
      }
    }


    // OS type
    if (osName.startsWith("Linux"))
      featureList = Primitives.APPEND.execute(list(Keyword.UNIX,
                                                  Keyword.LINUX),
                                              featureList);
    else if (osName.startsWith("SunOS"))
      featureList = Primitives.APPEND.execute(list(Keyword.UNIX,
                                                   Keyword.SUNOS,
                                                   Keyword.SOLARIS),
                                              featureList);
    else if (osName.startsWith("Mac OS X")
             || osName.startsWith("Darwin"))
      featureList = Primitives.APPEND.execute(list(Keyword.UNIX,
                                                   Keyword.DARWIN),
                                              featureList);
    else if (osName.startsWith("FreeBSD"))
      featureList = Primitives.APPEND.execute(list(Keyword.UNIX,
                                                   Keyword.FREEBSD),
                                              featureList);
    else if (osName.startsWith("OpenBSD"))
      featureList = Primitives.APPEND.execute(list(Keyword.UNIX,
                                                   Keyword.OPENBSD),
                                              featureList);
    else if (osName.startsWith("NetBSD"))
      featureList = Primitives.APPEND.execute(list(Keyword.UNIX,
                                                   Keyword.NETBSD),
                                              featureList);
    else if (osName.startsWith("Windows"))
      featureList = new Cons(Keyword.WINDOWS, featureList);

    // Processor architecture
    if (osArch != null) {
      if (osArch.equals("amd64") || osArch.equals("x86_64")) {
        featureList = featureList.push(Keyword.X86_64);
      } else if (osArch.equals("x86") || osArch.equals("i386")) {
        featureList = featureList.push(Keyword.X86);
      } else {
        // just push the value of 'os.arch' as a keyword
        featureList = featureList.push(internKeyword(osArch.toUpperCase()));
      }
    }

    // Available Threading models

    if (LispThread.virtualThreadingAvailable()) {
      featureList = featureList.push(internKeyword("VIRTUAL-THREADS"));
    }

    Symbol.FEATURES.initializeSpecial(featureList);
  }

  static
  {
    Symbol.MODULES.initializeSpecial(NIL);
  }

  static
  {
    Symbol.LOAD_VERBOSE.initializeSpecial(NIL);
    Symbol.LOAD_PRINT.initializeSpecial(NIL);
    Symbol.LOAD_PATHNAME.initializeSpecial(NIL);
    Symbol.LOAD_TRUENAME.initializeSpecial(NIL);
    Symbol.LOAD_TRUENAME_FASL.initializeSpecial(NIL);
    Symbol.COMPILE_VERBOSE.initializeSpecial(T);
    Symbol.COMPILE_PRINT.initializeSpecial(T);
    Symbol._COMPILE_FILE_PATHNAME_.initializeSpecial(NIL);
    Symbol.COMPILE_FILE_TRUENAME.initializeSpecial(NIL);
  }

  // ### *double-colon-package-separators*
  // internal symbol
  public static final Symbol DOUBLE_COLON_PACKAGE_SEPARATORS =
    internSpecial("*DOUBLE-COLON-PACKAGE-SEPARATORS*", PACKAGE_SYS, NIL);

  // ### *load-depth*
  // internal symbol
  public static final Symbol _LOAD_DEPTH_ =
    internSpecial("*LOAD-DEPTH*", PACKAGE_SYS, Fixnum.ZERO);

  // ### *load-stream*
  // internal symbol
  public static final Symbol _LOAD_STREAM_ =
    internSpecial("*LOAD-STREAM*", PACKAGE_SYS, NIL);

    // ### *fasl-loader*
    public static final Symbol _FASL_LOADER_ =
        exportSpecial("*FASL-LOADER*", PACKAGE_SYS, NIL);

  // ### *source*
  // internal symbol
  public static final Symbol _SOURCE_ =
    exportSpecial("*SOURCE*", PACKAGE_SYS, NIL);

  // ### *source-position*
  // internal symbol
  public static final Symbol _SOURCE_POSITION_ =
    exportSpecial("*SOURCE-POSITION*", PACKAGE_SYS, NIL);

  // ### *autoload-verbose*
  // internal symbol
  public static final Symbol _AUTOLOAD_VERBOSE_ =
    exportSpecial("*AUTOLOAD-VERBOSE*", PACKAGE_EXT, NIL);

  // ### *preloading-cache*
 public static final Symbol AUTOLOADING_CACHE =
   internSpecial("*AUTOLOADING-CACHE*", PACKAGE_SYS, NIL);

  // ### *compile-file-type*
  public static final Symbol _COMPILE_FILE_TYPE_ =
   exportSpecial("*COMPILE-FILE-TYPE*", PACKAGE_SYS, new SimpleString("abcl"));

  // ### *compile-file-class-extension*
  public static final Symbol _COMPILE_FILE_CLASS_EXTENSION_ =
   exportSpecial("*COMPILE-FILE-CLASS-EXTENSION*", PACKAGE_SYS, new SimpleString("cls"));

  // ### *compile-file-zip*
  public static final Symbol _COMPILE_FILE_ZIP_ =
    exportSpecial("*COMPILE-FILE-ZIP*", PACKAGE_SYS, T);

  static
  {
    Symbol.MACROEXPAND_HOOK.initializeSpecial(Symbol.FUNCALL);
  }

  public static final int ARRAY_DIMENSION_MAX = Integer.MAX_VALUE;
  static
  {
    // ### array-dimension-limit
    Symbol.ARRAY_DIMENSION_LIMIT.initializeConstant(Fixnum.getInstance(ARRAY_DIMENSION_MAX));
  }

  // ### char-code-limit
  // "The upper exclusive bound on the value returned by the function CHAR-CODE."
  public static final int CHAR_MAX = Character.MAX_VALUE;
  static
  {
    Symbol.CHAR_CODE_LIMIT.initializeConstant(Fixnum.getInstance(CHAR_MAX + 1));
  }

  static
  {
    Symbol.READ_BASE.initializeSpecial(Fixnum.constants[10]);
  }

  static
  {
    Symbol.READ_DEFAULT_FLOAT_FORMAT.initializeSpecial(Symbol.SINGLE_FLOAT);
  }

  // Printer control variables.
  static
  {
    Symbol.PRINT_ARRAY.initializeSpecial(T);
    Symbol.PRINT_BASE.initializeSpecial(Fixnum.constants[10]);
    Symbol.PRINT_CASE.initializeSpecial(Keyword.UPCASE);
    Symbol.PRINT_CIRCLE.initializeSpecial(NIL);
    Symbol.PRINT_ESCAPE.initializeSpecial(T);
    Symbol.PRINT_GENSYM.initializeSpecial(T);
    Symbol.PRINT_LENGTH.initializeSpecial(NIL);
    Symbol.PRINT_LEVEL.initializeSpecial(NIL);
    Symbol.PRINT_LINES.initializeSpecial(NIL);
    Symbol.PRINT_MISER_WIDTH.initializeSpecial(NIL);
    Symbol.PRINT_PPRINT_DISPATCH.initializeSpecial(NIL);
    Symbol.PRINT_PRETTY.initializeSpecial(NIL);
    Symbol.PRINT_RADIX.initializeSpecial(NIL);
    Symbol.PRINT_READABLY.initializeSpecial(NIL);
    Symbol.PRINT_RIGHT_MARGIN.initializeSpecial(NIL);
  }

  public static final Symbol _PRINT_STRUCTURE_ =
    exportSpecial("*PRINT-STRUCTURE*", PACKAGE_EXT, T);

  // ### *current-print-length*
  public static final Symbol _CURRENT_PRINT_LENGTH_ =
    exportSpecial("*CURRENT-PRINT-LENGTH*", PACKAGE_SYS, Fixnum.ZERO);

  // ### *current-print-level*
  public static final Symbol _CURRENT_PRINT_LEVEL_ =
    exportSpecial("*CURRENT-PRINT-LEVEL*", PACKAGE_SYS, Fixnum.ZERO);

  public static final Symbol _PRINT_FASL_ =
    internSpecial("*PRINT-FASL*", PACKAGE_SYS, NIL);

  static
  {
    Symbol._RANDOM_STATE_.initializeSpecial(new RandomState());
  }

  static
  {
    Symbol.STAR.initializeSpecial(NIL);
    Symbol.STAR_STAR.initializeSpecial(NIL);
    Symbol.STAR_STAR_STAR.initializeSpecial(NIL);
    Symbol.MINUS.initializeSpecial(NIL);
    Symbol.PLUS.initializeSpecial(NIL);
    Symbol.PLUS_PLUS.initializeSpecial(NIL);
    Symbol.PLUS_PLUS_PLUS.initializeSpecial(NIL);
    Symbol.SLASH.initializeSpecial(NIL);
    Symbol.SLASH_SLASH.initializeSpecial(NIL);
    Symbol.SLASH_SLASH_SLASH.initializeSpecial(NIL);
  }

  // Floating point constants.
  static
  {
    Symbol.PI.initializeConstant(new DoubleFloat(Math.PI));
    Symbol.SHORT_FLOAT_EPSILON.initializeConstant(new SingleFloat((float)5.960465E-8));
    Symbol.SINGLE_FLOAT_EPSILON.initializeConstant(new SingleFloat((float)5.960465E-8));
    Symbol.DOUBLE_FLOAT_EPSILON.initializeConstant(new DoubleFloat((double)1.1102230246251568E-16));
    Symbol.LONG_FLOAT_EPSILON.initializeConstant(new DoubleFloat((double)1.1102230246251568E-16));
    Symbol.SHORT_FLOAT_NEGATIVE_EPSILON.initializeConstant(new SingleFloat(2.9802326e-8f));
    Symbol.SINGLE_FLOAT_NEGATIVE_EPSILON.initializeConstant(new SingleFloat(2.9802326e-8f));
    Symbol.DOUBLE_FLOAT_NEGATIVE_EPSILON.initializeConstant(new DoubleFloat((double)5.551115123125784E-17));
    Symbol.LONG_FLOAT_NEGATIVE_EPSILON.initializeConstant(new DoubleFloat((double)5.551115123125784E-17));
    Symbol.MOST_POSITIVE_SHORT_FLOAT.initializeConstant(new SingleFloat(Float.MAX_VALUE));
    Symbol.MOST_POSITIVE_SINGLE_FLOAT.initializeConstant(new SingleFloat(Float.MAX_VALUE));
    Symbol.MOST_POSITIVE_DOUBLE_FLOAT.initializeConstant(new DoubleFloat(Double.MAX_VALUE));
    Symbol.MOST_POSITIVE_LONG_FLOAT.initializeConstant(new DoubleFloat(Double.MAX_VALUE));
    Symbol.LEAST_POSITIVE_SHORT_FLOAT.initializeConstant(new SingleFloat(Float.MIN_VALUE));
    Symbol.LEAST_POSITIVE_SINGLE_FLOAT.initializeConstant(new SingleFloat(Float.MIN_VALUE));
    Symbol.LEAST_POSITIVE_DOUBLE_FLOAT.initializeConstant(new DoubleFloat(Double.MIN_VALUE));
    Symbol.LEAST_POSITIVE_LONG_FLOAT.initializeConstant(new DoubleFloat(Double.MIN_VALUE));
    Symbol.LEAST_POSITIVE_NORMALIZED_SHORT_FLOAT.initializeConstant(new SingleFloat(1.17549435e-38f));
    Symbol.LEAST_POSITIVE_NORMALIZED_SINGLE_FLOAT.initializeConstant(new SingleFloat(1.17549435e-38f));
    Symbol.LEAST_POSITIVE_NORMALIZED_DOUBLE_FLOAT.initializeConstant(new DoubleFloat(2.2250738585072014e-308d));
    Symbol.LEAST_POSITIVE_NORMALIZED_LONG_FLOAT.initializeConstant(new DoubleFloat(2.2250738585072014e-308d));
    Symbol.MOST_NEGATIVE_SHORT_FLOAT.initializeConstant(new SingleFloat(- Float.MAX_VALUE));
    Symbol.MOST_NEGATIVE_SINGLE_FLOAT.initializeConstant(new SingleFloat(- Float.MAX_VALUE));
    Symbol.MOST_NEGATIVE_DOUBLE_FLOAT.initializeConstant(new DoubleFloat(- Double.MAX_VALUE));
    Symbol.MOST_NEGATIVE_LONG_FLOAT.initializeConstant(new DoubleFloat(- Double.MAX_VALUE));
    Symbol.LEAST_NEGATIVE_SHORT_FLOAT.initializeConstant(new SingleFloat(- Float.MIN_VALUE));
    Symbol.LEAST_NEGATIVE_SINGLE_FLOAT.initializeConstant(new SingleFloat(- Float.MIN_VALUE));
    Symbol.LEAST_NEGATIVE_DOUBLE_FLOAT.initializeConstant(new DoubleFloat(- Double.MIN_VALUE));
    Symbol.LEAST_NEGATIVE_LONG_FLOAT.initializeConstant(new DoubleFloat(- Double.MIN_VALUE));
    Symbol.LEAST_NEGATIVE_NORMALIZED_SHORT_FLOAT.initializeConstant(new SingleFloat(-1.17549435e-38f));
    Symbol.LEAST_NEGATIVE_NORMALIZED_SINGLE_FLOAT.initializeConstant(new SingleFloat(-1.17549435e-38f));
    Symbol.LEAST_NEGATIVE_NORMALIZED_DOUBLE_FLOAT.initializeConstant(new DoubleFloat(-2.2250738585072014e-308d));
    Symbol.LEAST_NEGATIVE_NORMALIZED_LONG_FLOAT.initializeConstant(new DoubleFloat(-2.2250738585072014e-308d));
  }

  static
  {
    Symbol.BOOLE_CLR.initializeConstant(Fixnum.ZERO);
    Symbol.BOOLE_SET.initializeConstant(Fixnum.ONE);
    Symbol.BOOLE_1.initializeConstant(Fixnum.TWO);
    Symbol.BOOLE_2.initializeConstant(Fixnum.constants[3]);
    Symbol.BOOLE_C1.initializeConstant(Fixnum.constants[4]);
    Symbol.BOOLE_C2.initializeConstant(Fixnum.constants[5]);
    Symbol.BOOLE_AND.initializeConstant(Fixnum.constants[6]);
    Symbol.BOOLE_IOR.initializeConstant(Fixnum.constants[7]);
    Symbol.BOOLE_XOR.initializeConstant(Fixnum.constants[8]);
    Symbol.BOOLE_EQV.initializeConstant(Fixnum.constants[9]);
    Symbol.BOOLE_NAND.initializeConstant(Fixnum.constants[10]);
    Symbol.BOOLE_NOR.initializeConstant(Fixnum.constants[11]);
    Symbol.BOOLE_ANDC1.initializeConstant(Fixnum.constants[12]);
    Symbol.BOOLE_ANDC2.initializeConstant(Fixnum.constants[13]);
    Symbol.BOOLE_ORC1.initializeConstant(Fixnum.constants[14]);
    Symbol.BOOLE_ORC2.initializeConstant(Fixnum.constants[15]);
  }

  static
  {
    // ### call-arguments-limit
    Symbol.CALL_ARGUMENTS_LIMIT.initializeConstant(Fixnum.getInstance(2147483647));
  }

  static
  {
    // ### lambda-parameters-limit
    Symbol.LAMBDA_PARAMETERS_LIMIT.initializeConstant(Fixnum.getInstance(1024));
    // A conservative value, as actual limit is unknown, dependent on
    // width of constants.  Arguments limited by the name length of
    // the arguments whose printed representation cannot execeed 65535
    // bytes.
  }

  static
  {
    // ### multiple-values-limit
    Symbol.MULTIPLE_VALUES_LIMIT.initializeConstant(Fixnum.constants[32]);
  }

  static
  {
    // ### internal-time-units-per-second
    Symbol.INTERNAL_TIME_UNITS_PER_SECOND.initializeConstant(Fixnum.getInstance(1000));
  }

  static
  {
    Symbol.LAMBDA_LIST_KEYWORDS
      .initializeConstant(list(Symbol.AND_OPTIONAL,
                               Symbol.AND_REST,
                               Symbol.AND_KEY,
                               Symbol.AND_AUX,
                               Symbol.AND_BODY,
                               Symbol.AND_WHOLE,
                               Symbol.AND_ALLOW_OTHER_KEYS,
                               Symbol.AND_ENVIRONMENT));
  }

  // ### call-registers-limit
  public static final Symbol CALL_REGISTERS_LIMIT =
    exportConstant("CALL-REGISTERS-LIMIT", PACKAGE_SYS,
                   Fixnum.constants[CALL_REGISTERS_MAX]);

  // ### *warn-on-redefinition*
  public static final Symbol _WARN_ON_REDEFINITION_ =
    exportSpecial("*WARN-ON-REDEFINITION*", PACKAGE_EXT, T);

  // ### *saved-backtrace*
  public static final Symbol _SAVED_BACKTRACE_ =
    exportSpecial("*SAVED-BACKTRACE*", PACKAGE_EXT, NIL);

  // ### *command-line-argument-list*
  public static final Symbol _COMMAND_LINE_ARGUMENT_LIST_ =
    exportSpecial("*COMMAND-LINE-ARGUMENT-LIST*", PACKAGE_EXT, NIL);

  // ### *batch-mode*
  public static final Symbol _BATCH_MODE_ =
    exportSpecial("*BATCH-MODE*", PACKAGE_EXT, NIL);

  // ### *noinform*
  public static final Symbol _NOINFORM_ =
    exportSpecial("*NOINFORM*", PACKAGE_SYS, NIL);

  // ### *disassembler*
  public static final Symbol _DISASSEMBLER_ =
    exportSpecial("*DISASSEMBLER*", PACKAGE_EXT,
                  new SimpleString("javap -c -verbose")); // or "jad -dis -p"

  // ### *speed* compiler policy
  public static final Symbol _SPEED_ =
    exportSpecial("*SPEED*", PACKAGE_SYS, Fixnum.ONE);

  // ### *space* compiler policy
  public static final Symbol _SPACE_ =
    exportSpecial("*SPACE*", PACKAGE_SYS, Fixnum.ONE);

  // ### *safety* compiler policy
  public static final Symbol _SAFETY_ =
    exportSpecial("*SAFETY*", PACKAGE_SYS, Fixnum.ONE);

  // ### *debug* compiler policy
  public static final Symbol _DEBUG_ =
    exportSpecial("*DEBUG*", PACKAGE_SYS, Fixnum.ONE);

  // ### *explain* compiler policy
  public static final Symbol _EXPLAIN_ =
    exportSpecial("*EXPLAIN*", PACKAGE_SYS, NIL);

  // ### *enable-inline-expansion*
  public static final Symbol _ENABLE_INLINE_EXPANSION_ =
    exportSpecial("*ENABLE-INLINE-EXPANSION*", PACKAGE_EXT, T);

  // ### *require-stack-frame*
  public static final Symbol _REQUIRE_STACK_FRAME_ =
    exportSpecial("*REQUIRE-STACK-FRAME*", PACKAGE_EXT, NIL);

  static
  {
    Symbol.SUPPRESS_COMPILER_WARNINGS.initializeSpecial(NIL);
  }

  public static final Symbol _COMPILE_FILE_ENVIRONMENT_ =
    exportSpecial("*COMPILE-FILE-ENVIRONMENT*", PACKAGE_SYS, NIL);

  public static final LispObject UNBOUND_VALUE = new unboundValue();
  static class unboundValue extends LispObject
  {
    @Override
    public String printObject()
    {
      return unreadableString("UNBOUND", false);
    }
  }

  public static final LispObject NULL_VALUE = new nullValue();
  static class nullValue extends LispObject
  {
    @Override
    public String printObject()
    {
      return unreadableString("null", false);
    }
  }

  public static final Symbol _SLOT_UNBOUND_ =
    exportConstant("+SLOT-UNBOUND+", PACKAGE_SYS, UNBOUND_VALUE);

  public static final Symbol _CL_PACKAGE_ =
    exportConstant("+CL-PACKAGE+", PACKAGE_SYS, PACKAGE_CL);

  public static final Symbol _KEYWORD_PACKAGE_ =
    exportConstant("+KEYWORD-PACKAGE+", PACKAGE_SYS, PACKAGE_KEYWORD);

  // ### *backquote-count*
  public static final Symbol _BACKQUOTE_COUNT_ =
    internSpecial("*BACKQUOTE-COUNT*", PACKAGE_SYS, Fixnum.ZERO);

  // ### *bq-vector-flag*
  public static final Symbol _BQ_VECTOR_FLAG_ =
    internSpecial("*BQ-VECTOR-FLAG*", PACKAGE_SYS, list(new Symbol("bqv")));

  // ### *traced-names*
  public static final Symbol _TRACED_NAMES_ =
    exportSpecial("*TRACED-NAMES*", PACKAGE_SYS, NIL);

  // Floating point traps.
  protected static boolean TRAP_OVERFLOW  = true;
  protected static boolean TRAP_UNDERFLOW = true;


  // Extentions
  static {
    Symbol._INSPECTOR_HOOK_.initializeSpecial(NIL);
  }

  private static final void loadClass(String className)
  {
    try
      {
        Class.forName(className);
      }
    catch (ClassNotFoundException e)
      {
        Debug.trace(e);
      }
  }

  static
  {
    loadClass("org.armedbear.lisp.Primitives");
    loadClass("org.armedbear.lisp.SpecialOperators");
    loadClass("org.armedbear.lisp.Extensions");
    loadClass("org.armedbear.lisp.CompiledClosure");
    loadClass("org.armedbear.lisp.Autoload");
    loadClass("org.armedbear.lisp.AutoloadMacro");
    loadClass("org.armedbear.lisp.AutoloadGeneralizedReference");
    loadClass("org.armedbear.lisp.cxr");
    loadClass("org.armedbear.lisp.Do");
    loadClass("org.armedbear.lisp.dolist");
    loadClass("org.armedbear.lisp.dotimes");
    loadClass("org.armedbear.lisp.Pathname");
    loadClass("org.armedbear.lisp.LispClass");
    loadClass("org.armedbear.lisp.BuiltInClass");
    loadClass("org.armedbear.lisp.StructureObject");
    loadClass("org.armedbear.lisp.ash");
    loadClass("org.armedbear.lisp.Java");
    loadClass("org.armedbear.lisp.PackageFunctions");
    cold = false;
  }

    private static Stream stdin = new Stream(Symbol.SYSTEM_STREAM, System.in, Symbol.CHARACTER, true);

    private static Stream stdout = new Stream(Symbol.SYSTEM_STREAM,System.out, Symbol.CHARACTER, true);

  static
  {
    Symbol.STANDARD_INPUT.initializeSpecial(stdin);
    Symbol.STANDARD_OUTPUT.initializeSpecial(stdout);
    Symbol.ERROR_OUTPUT.initializeSpecial(stdout);
    Symbol.TRACE_OUTPUT.initializeSpecial(stdout);
    Symbol.TERMINAL_IO.initializeSpecial(new TwoWayStream(stdin, stdout, true));
    Symbol.QUERY_IO.initializeSpecial(new TwoWayStream(stdin, stdout, true));
    Symbol.DEBUG_IO.initializeSpecial(new TwoWayStream(stdin, stdout, true));
  }

  private static final SpecialOperator WITH_INLINE_CODE = new with_inline_code();
  private static class with_inline_code extends SpecialOperator {
    with_inline_code() {
      super("with-inline-code", PACKAGE_JVM, true, "(&optional target repr) &body body");
    }
    @Override
    public LispObject execute(LispObject args, Environment env)
    {
        return error(new SimpleError("This is a placeholder. It should only be called in compiled code, and tranformed by the compiler using special form handlers."));
    }
  }

  // A synonym for the null reference which indicates to the reader of
  // the code that we have performed a non-local exit via the
  // condition system before this reference is reached.
  public static java.lang.Object UNREACHED = null;

  // stepping related code
  public static boolean steppingTask = false;
  public static boolean stepping = false;
  public static boolean delimitedStepping = false;
  public static Binding stepperBlock = null;
  public static long stepNumber = 0;

  public static LispObject stepInSymbolP (LispObject fun, LispObject obj) {
    Package stepper;
    Symbol symbol;
    LispThread currentThread = LispThread.currentThread();
    LispObject stepInSymbolPFunction;
    LispObject result;
    if (steppingTask) {
      return NIL;
    }
    if (stepping) {
      stepper = Packages.findPackageGlobally("ABCL-STEPPER");
      symbol = stepper.findAccessibleSymbol("STEP-IN-SYMBOL-P");
      stepInSymbolPFunction = coerceToFunction(symbol);
      result = funcall(stepInSymbolPFunction, new LispObject[] {
          fun, obj, LispObject.getInstance(delimitedStepping)
        },
        currentThread);
      return result;
    }
    return NIL;
  }

  public static synchronized final void handleStepping (LispObject function, LispObject args,
                                                        Environment env, LispInteger stepCount) {
    LispThread currentThread = LispThread.currentThread();
    Package stepper = Packages.findPackageGlobally("ABCL-STEPPER");
    Symbol symbolPprintFormToStep = stepper.findAccessibleSymbol("PPRINT-FORM-TO-STEP");
    Symbol symbolHandleUserInteraction = stepper.findAccessibleSymbol("HANDLE-USER-INTERACTION");
    LispObject functionPprintFormToStep = coerceToFunction(symbolPprintFormToStep);
    LispObject functionHandleUserInteraction = coerceToFunction(symbolHandleUserInteraction);
    if (stepperBlock == null) {
      stepperBlock = env.getOuterMostBlock();
    }
    if (function instanceof FuncallableStandardObject) {
      function = ((FuncallableStandardObject)function).function;
    }
    LispObject closureName = ((Operator)function).getLambdaName();
    setSteppingOff();
    if (closureName != null ) {
      funcall(functionPprintFormToStep, new LispObject[] {closureName, args, stepCount}, currentThread);
    }
    else {
      funcall(functionPprintFormToStep, new LispObject[] {((Operator)function), args, stepCount}, currentThread);
    }
    setSteppingOn();
    funcall(functionHandleUserInteraction, new LispObject[]{env}, currentThread);
  }

  public static final void printStepValue(long stepNumberInternal, LispObject result, LispThread thread) {
    Package stepper = Packages.findPackageGlobally("ABCL-STEPPER");
    Symbol symbolPrintStepperStr = stepper.findAccessibleSymbol("PRINT-STEPPER-STR");
    LispObject functionPrintStepperStr = coerceToFunction(symbolPrintStepperStr);
    LispObject[] values = thread._values;
    if (values != null) {
      for (int i = 0; i < values.length; i++) {
        funcall(functionPrintStepperStr,
                new LispObject[] {
                  new SimpleString("step " + stepNumberInternal + " ==> value: " + values[i].printObject()),
                  Symbol.T
                },
                thread);
      }
    }
    else {
      funcall(functionPrintStepperStr, new LispObject[] {
          new SimpleString("step " + stepNumberInternal + " ==> value: " + result.printObject()),
          Symbol.T
        },
        thread);
    }
    thread._values = values;
  }

  public static final void setStepCounterCompleted (long stepNumberInternal) {
    if (stepping) {
      LispThread currentThread = LispThread.currentThread();
      Package stepper = Packages.findPackageGlobally("ABCL-STEPPER");
      Symbol symbolSetStepCounterCompleted = stepper.findAccessibleSymbol("SET-STEP-COUNTER-COMPLETED");
      LispObject functionSetStepCounterCompleted = coerceToFunction(symbolSetStepCounterCompleted);
      LispObject[] values = currentThread._values;
      funcall(functionSetStepCounterCompleted,
              new LispObject[] {LispInteger.getInstance(stepNumberInternal)},
              currentThread);
      currentThread._values = values;
    }
  }

  public static void setSteppingTaskOn () {
    steppingTask = true;
  }

  public static void setSteppingTaskOff () {
    steppingTask = false;
  }

  public static void setDelimitedSteppingOn () {
    delimitedStepping = true;
  }

  public static void setDelimitedSteppingOff () {
    delimitedStepping = false;
  }

  public static void setSteppingOn () {
    stepping = true;
  }

  public static void initializeStepCounter () {
    stepNumber = 0;
  }

  public static LispObject getStepCounter () {
    return LispInteger.getInstance(stepNumber);
  }

  public static void setSteppingOff () {
    stepping = false;
  }

  public static void initializeStepBlock () {
    stepperBlock = null;
  }

  // ### %set-stepping-task-on
  public static final Primitive SET_STEPPING_TASK_ON =
    new Primitive("%set-stepping-task-on", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        setSteppingTaskOn();
        return NIL;
      }
    };

  // ### %set-stepping-task-off
  public static final Primitive SET_STEPPING_TASK_OFF =
    new Primitive("%set-stepping-task-off", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        setSteppingTaskOff();
        return NIL;
      }
    };

  // ### %set-stepper-on
  public static final Primitive SET_STEPPER_ON =
    new Primitive("%set-stepper-on", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        setSteppingOn();
        return NIL;
      }
    };

  // ### %return-from-stepper
  public static final Primitive RETURN_FROM_STEPPER =
    new Primitive("%return-from-stepper", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        throw new Return(stepperBlock.symbol, stepperBlock.value, NIL);
      }
    };

  // ### %set-stepper-off
  public static final Primitive SET_STEPPER_OFF =
    new Primitive("%set-stepper-off", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        setSteppingOff();
        return NIL;
      }
    };

  // ### %set-delimited-stepping-off
  public static final Primitive SET_DELIMITED_STEPPING_OFF =
    new Primitive("%set-delimited-stepping-off", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        setDelimitedSteppingOff();
        return NIL;
      }
    };

  // ### %set-delimited-stepping-on
  public static final Primitive SET_DELIMITED_STEPPING_ON =
    new Primitive("%set-delimited-stepping-on", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        setDelimitedSteppingOn();
        return NIL;
      }
    };

  // ### %initialize-step-counter
  public static final Primitive INITIALIZE_STEP_COUNTER =
    new Primitive("%initialize-step-counter", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        initializeStepCounter();
        return NIL;
      }
    };

  // ### %get-step-counter
  public static final Primitive GET_STEP_COUNTER =
    new Primitive("%get-step-counter", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        return getStepCounter();
      }
    };

  // ### %initialize-step-block
  public static final Primitive INITIALIZE_STEP_BLOCK =
    new Primitive("%initialize-step-block", PACKAGE_SYS, true)
    {
      @Override
      public LispObject execute()

      {
        initializeStepBlock();
        return NIL;
      }
    };
}
