gt:     ArrayList<Symbol> vars = new ArrayList<Symbol>();
context: /*
   * Closure.java
   *
   * Copyright (C) 2002-2008 Peter Graves
   * Copyright (C) 2008 Ville Voutilainen
   * $Id: Closure.java 11778 2009-04-23 20:46:27Z vvoutilainen $
   *
   * 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 static org.armedbear.lisp.Nil.NIL;
  import static org.armedbear.lisp.Lisp.*;
  
  import java.util.ArrayList;
  
  public class Closure extends Function
  {
    // Parameter types.
    static final int REQUIRED = 0;
    static final int OPTIONAL = 1;
    static final int KEYWORD  = 2;
    static final int REST     = 3;
    static final int AUX      = 4;
  
    // States.
    private static final int STATE_REQUIRED = REQUIRED;
    private static final int STATE_OPTIONAL = OPTIONAL;
    private static final int STATE_KEYWORD  = KEYWORD;
    private static final int STATE_REST     = REST;
    private static final int STATE_AUX      = AUX;
  
    private static final Parameter[] emptyParameterArray;
    static 
      {
          emptyParameterArray = new Parameter[0];
      }
    private Parameter[] requiredParameters = emptyParameterArray;
    private Parameter[] optionalParameters = emptyParameterArray;
    private Parameter[] keywordParameters = emptyParameterArray;
    private Parameter[] auxVars = emptyParameterArray;
    private final LispObject body;
    private final LispObject executionBody;
    private final Environment environment;
    private final boolean andKey;
    private final boolean allowOtherKeys;
    private Symbol restVar;
    private Symbol envVar;
    private int arity;
  
    private int minArgs;
    private int maxArgs;
  
    private static final Symbol[] emptySymbolArray;
    static 
      {
          emptySymbolArray = new Symbol[0];
      }
    private Symbol[] variables = emptySymbolArray;
    private LispObject specials = NIL;
  
    private boolean bindInitForms;
  
    public Closure(LispObject lambdaExpression, Environment env)
      throws ConditionThrowable
    {
      this(null, lambdaExpression, env);
    }
  
    public Closure(final LispObject name, final LispObject lambdaExpression,
                   final Environment env)
      throws ConditionThrowable
    {
      super(name, lambdaExpression.CADR());
      final LispObject lambdaList = lambdaExpression.CADR();
      setLambdaList(lambdaList);
      if (!(lambdaList == NIL || lambdaList instanceof Cons))
        error(new LispError("The lambda list " + lambdaList.writeToString() +
                             " is invalid."));
      boolean _andKey = false;
      boolean _allowOtherKeys = false;
      if (lambdaList instanceof Cons)
        {
          final int length = lambdaList.size();
          ArrayList<Parameter> required = null;
          ArrayList<Parameter> optional = null;
          ArrayList<Parameter> keywords = null;
          ArrayList<Parameter> aux = null;
          int state = STATE_REQUIRED;
          LispObject remaining = lambdaList;
          while (remaining != NIL)
            {
              LispObject obj = remaining.CAR();
              if (obj instanceof Symbol)
                {
                  if (state == STATE_AUX)
                    {
                      if (aux == null)
                        aux = new ArrayList<Parameter>();
                      aux.add(new Parameter((Symbol)obj, NIL, AUX));
                    }
                  else if (obj == SymbolConstants.AND_OPTIONAL)
                    {
                      state = STATE_OPTIONAL;
                      arity = -1;
                    }
                  else if (obj == SymbolConstants.AND_REST || obj == SymbolConstants.AND_BODY)
                    {
                      state = STATE_REST;
                      arity = -1;
                      maxArgs = -1;
                      remaining = remaining.CDR();
                      if (remaining == NIL)
                        {
                          error(new LispError(
                            "&REST/&BODY must be followed by a variable."));
                        }
                      Debug.assertTrue(restVar == null);
                      final LispObject remainingcar =  remaining.CAR();
                      if (remainingcar instanceof Symbol)
                        {
                          restVar = (Symbol) remainingcar;
                        }
                      else
                        {
                          error(new LispError(
                            "&REST/&BODY must be followed by a variable."));
                        }
                    }
                  else if (obj == SymbolConstants.AND_ENVIRONMENT)
                    {
                      remaining = remaining.CDR();
                      envVar = (Symbol) remaining.CAR();
                      arity = -1; // FIXME
                    }
                  else if (obj == SymbolConstants.AND_KEY)
                    {
                      state = STATE_KEYWORD;
                      _andKey = true;
                      arity = -1;
                    }
                  else if (obj == SymbolConstants.AND_ALLOW_OTHER_KEYS)
                    {
                      _allowOtherKeys = true;
                      maxArgs = -1;
                    }
                  else if (obj == SymbolConstants.AND_AUX)
                    {
                      // All remaining specifiers are aux variable specifiers.
                      state = STATE_AUX;
                      arity = -1; // FIXME
                    }
                  else
                    {
                      if (state == STATE_OPTIONAL)
                        {
                          if (optional == null)
                            optional = new ArrayList<Parameter>();
                          optional.add(new Parameter((Symbol)obj, NIL, OPTIONAL));
                          if (maxArgs >= 0)
                            ++maxArgs;
                        }
                      else if (state == STATE_KEYWORD)
                        {
                          if (keywords == null)
                            keywords = new ArrayList<Parameter>();
                          keywords.add(new Parameter((Symbol)obj, NIL, KEYWORD));
                          if (maxArgs >= 0)
                            maxArgs += 2;
                        }
                      else
                        {
                          Debug.assertTrue(state == STATE_REQUIRED);
                          if (required == null)
                            required = new ArrayList<Parameter>();
                          required.add(new Parameter((Symbol)obj));
                          if (maxArgs >= 0)
                            ++maxArgs;
                        }
                    }
                }
              else if (obj instanceof Cons)
                {
                  if (state == STATE_AUX)
                    {
                      Symbol sym = checkSymbol(obj.CAR());
                      LispObject initForm = obj.CADR();
                      Debug.assertTrue(initForm != null);
                      if (aux == null)
                        aux = new ArrayList<Parameter>();
                      aux.add(new Parameter(sym, initForm, AUX));
                    }
                  else if (state == STATE_OPTIONAL)
                    {
                      Symbol sym = checkSymbol(obj.CAR());
                      LispObject initForm = obj.CADR();
                      LispObject svar = obj.CDR().CDR().CAR();
                      if (optional == null)
                        optional = new ArrayList<Parameter>();
                      optional.add(new Parameter(sym, initForm, svar, OPTIONAL));
                      if (maxArgs >= 0)
                        ++maxArgs;
                    }
                  else if (state == STATE_KEYWORD)
                    {
                      Symbol keyword;
                      Symbol var;
                      LispObject initForm = NIL;
                      LispObject svar = NIL;
                      LispObject first = obj.CAR();
                      if (first instanceof Cons)
                        {
                          keyword = checkSymbol(first.CAR());
                          var = checkSymbol(first.CADR());
                        }
                      else
                        {
                          var = checkSymbol(first);
                          keyword =
                            PACKAGE_KEYWORD.intern(var.getSymbolName());
                        }
                      obj = obj.CDR();
                      if (obj != NIL)
                        {
                          initForm = obj.CAR();
                          obj = obj.CDR();
                          if (obj != NIL)
                            svar = obj.CAR();
                        }
                      if (keywords == null)
                        keywords = new ArrayList<Parameter>();
                      keywords.add(new Parameter(keyword, var, initForm, svar));
                      if (maxArgs >= 0)
                        maxArgs += 2;
                    }
                  else
                    invalidParameter(obj);
                }
              else
                invalidParameter(obj);
              remaining = remaining.CDR();
            }
          if (arity == 0)
            arity = length;
          if (required != null)
            {
              requiredParameters = new Parameter[required.size()];
              required.toArray(requiredParameters);
            }
          if (optional != null)
            {
              optionalParameters = new Parameter[optional.size()];
              optional.toArray(optionalParameters);
            }
          if (keywords != null)
            {
              keywordParameters = new Parameter[keywords.size()];
              keywords.toArray(keywordParameters);
            }
          if (aux != null)
            {
              auxVars = new Parameter[aux.size()];
              aux.toArray(auxVars);
            }
        }
      else
        {
          // Lambda list is empty.
          Debug.assertTrue(lambdaList == NIL);
          arity = 0;
          maxArgs = 0;
        }
      this.body = lambdaExpression.CDDR();
      LispObject bodyAndDecls = parseBody(this.body, false);
      this.executionBody = bodyAndDecls.CAR();
      this.specials = parseSpecials(bodyAndDecls.NTH(1));
  
      this.environment = env;
      this.andKey = _andKey;
      this.allowOtherKeys = _allowOtherKeys;
      minArgs = requiredParameters.length;
      if (arity >= 0)
        Debug.assertTrue(arity == minArgs);
      variables = processVariables();
    }
  
    private final void processParameters(ArrayList<Symbol> vars,
                                         final Parameter[] parameters)
    {
      for (Parameter parameter : parameters)
        {
          vars.add(parameter.var);
          if (parameter.svar != NIL)
            vars.add((Symbol)parameter.svar);
          if (!bindInitForms)
            if (!parameter.initForm.constantp())
              bindInitForms = true;
        }
    }
  
    // Also sets bindInitForms.
    private final Symbol[] processVariables()
    {
  