private static int checkSingleArgument(
     String subroutineName,
     Class expectedType,
     ArgumentsList arguments,
     List<LispObject> args,
     int argsCounter,
     int i) {
   try {
     if (!(expectedType.isInstance(args.get(argsCounter)))) {
       if (expectedType.equals(LispList.class)
           && args.get(argsCounter).equals(LispSymbol.ourNil)) {
         arguments.setValue(i, LispList.list());
         return argsCounter + 1;
       }
       if (expectedType.equals(LispSymbol.class)
           && args.get(argsCounter).equals(LispSymbol.ourNil)) {
         arguments.setValue(i, LispSymbol.ourNil);
         return argsCounter + 1;
       }
       if (arguments.isOptional(i)) return -1;
       throw new WrongTypeArgumentException(expectedType.getSimpleName(), args.get(argsCounter));
     }
     arguments.setValue(i, args.get(argsCounter));
     return argsCounter + 1;
   } catch (IndexOutOfBoundsException e) {
     if (arguments.isOptional(i)) return -1;
     throw new WrongNumberOfArgumentsException(subroutineName, i);
   }
 }
 public static LispObject evaluate(String name, Environment environment, List<LispObject> args) {
   for (Class c : getSubroutineContainers()) {
     for (Method m : c.getMethods()) {
       Subroutine annotation = m.getAnnotation(Subroutine.class);
       if (annotation == null || !annotation.value().equals(name)) continue;
       if (!Arrays.asList(mySpecialForms).contains(c)) {
         if (!environment.areArgumentsEvaluated()) {
           for (int i = 0, dataSize = args.size(); i < dataSize; i++) {
             args.set(i, args.get(i).evaluate(environment));
           }
         } else {
           environment.setArgumentsEvaluated(false);
         }
       }
       ArgumentsList arguments = parseArguments(m, environment, args);
       checkArguments(name, arguments, args);
       try {
         return (LispObject) m.invoke(null, arguments.getValues());
       } catch (IllegalAccessException | InvocationTargetException e) {
         if (e.getCause().getMessage() == null) e.getCause().printStackTrace();
         else System.err.println(e.getCause().getMessage());
         Throwable cause = getCause(e);
         if (cause instanceof LispThrow) throw (LispThrow) cause;
         if (cause instanceof VoidVariableException && TestMode.TEST && c == Key.class) {
           System.err.println("Skip keymap errors in test mode");
           return LispSymbol.ourNil;
         }
         if (cause instanceof LispException) throw (LispException) cause;
         cause.printStackTrace();
         throw new LispException(e.getCause().getMessage());
       }
     }
   }
   throw new InternalException(JelispBundle.message("unknown.subroutine", name));
 }
 private static int checkArray(
     Class expectedType, ArgumentsList arguments, List<LispObject> args, int argsCounter, int i) {
   Class componentType = expectedType.getComponentType();
   ArrayList<LispObject> array = new ArrayList<>();
   while (argsCounter != args.size()) {
     if (!componentType.isInstance(args.get(argsCounter)))
       throw new WrongTypeArgumentException(componentType.toString(), args.get(argsCounter));
     array.add(args.get(argsCounter));
     ++argsCounter;
   }
   arguments.setValue(i, customizeArrayList(componentType, array));
   return argsCounter;
 }