static DelayedEvalBshMethod[] getDeclaredMethods(
      BSHBlock body, CallStack callstack, Interpreter interpreter, String defaultPackage)
      throws EvalError {
    List<DelayedEvalBshMethod> methods = new ArrayList<DelayedEvalBshMethod>();
    for (int child = 0; child < body.jjtGetNumChildren(); child++) {
      SimpleNode node = (SimpleNode) body.jjtGetChild(child);
      if (node instanceof BSHMethodDeclaration) {
        BSHMethodDeclaration md = (BSHMethodDeclaration) node;
        md.insureNodesParsed();
        Modifiers modifiers = md.modifiers;
        String name = md.name;
        String returnType = md.getReturnTypeDescriptor(callstack, interpreter, defaultPackage);
        BSHReturnType returnTypeNode = md.getReturnTypeNode();
        BSHFormalParameters paramTypesNode = md.paramsNode;
        String[] paramTypes =
            paramTypesNode.getTypeDescriptors(callstack, interpreter, defaultPackage);

        DelayedEvalBshMethod bm =
            new DelayedEvalBshMethod(
                name,
                returnType,
                returnTypeNode,
                md.paramsNode.getParamNames(),
                paramTypes,
                paramTypesNode,
                md.blockNode,
                null /*declaringNameSpace*/,
                modifiers,
                callstack,
                interpreter);

        methods.add(bm);
      }
    }
    return methods.toArray(new DelayedEvalBshMethod[methods.size()]);
  }
  static Variable[] getDeclaredVariables(
      BSHBlock body, CallStack callstack, Interpreter interpreter, String defaultPackage) {
    List<Variable> vars = new ArrayList<Variable>();
    for (int child = 0; child < body.jjtGetNumChildren(); child++) {
      SimpleNode node = (SimpleNode) body.jjtGetChild(child);
      if (node instanceof BSHTypedVariableDeclaration) {
        BSHTypedVariableDeclaration tvd = (BSHTypedVariableDeclaration) node;
        Modifiers modifiers = tvd.modifiers;
        String type = tvd.getTypeDescriptor(callstack, interpreter, defaultPackage);
        BSHVariableDeclarator[] vardec = tvd.getDeclarators();
        for (BSHVariableDeclarator aVardec : vardec) {
          String name = aVardec.name;
          try {
            Variable var = new Variable(name, type, null /*value*/, modifiers);
            vars.add(var);
          } catch (UtilEvalError e) {
            // value error shouldn't happen
          }
        }
      }
    }

    return vars.toArray(new Variable[vars.size()]);
  }
  /**
   * Parse the BSHBlock for for the class definition and generate the class using ClassGenerator.
   */
  public static Class generateClassImpl(
      String name,
      Modifiers modifiers,
      Class[] interfaces,
      Class superClass,
      BSHBlock block,
      boolean isInterface,
      CallStack callstack,
      Interpreter interpreter)
      throws EvalError {
    // Scripting classes currently requires accessibility
    // This can be eliminated with a bit more work.
    try {
      Capabilities.setAccessibility(true);
    } catch (Capabilities.Unavailable e) {
      throw new EvalError(
          "Defining classes currently requires reflective Accessibility.", block, callstack);
    }

    NameSpace enclosingNameSpace = callstack.top();
    String packageName = enclosingNameSpace.getPackage();
    String className =
        enclosingNameSpace.isClass ? (enclosingNameSpace.getName() + "$" + name) : name;
    String fqClassName = packageName == null ? className : packageName + "." + className;

    BshClassManager bcm = interpreter.getClassManager();
    // Race condition here...
    bcm.definingClass(fqClassName);

    // Create the class static namespace
    NameSpace classStaticNameSpace = new NameSpace(enclosingNameSpace, className);
    classStaticNameSpace.isClass = true;

    callstack.push(classStaticNameSpace);

    // Evaluate any inner class class definitions in the block
    // effectively recursively call this method for contained classes first
    block.evalBlock(callstack, interpreter, true /*override*/, ClassNodeFilter.CLASSCLASSES);

    // Generate the type for our class
    Variable[] variables = getDeclaredVariables(block, callstack, interpreter, packageName);
    DelayedEvalBshMethod[] methods = getDeclaredMethods(block, callstack, interpreter, packageName);

    ClassGeneratorUtil classGenerator =
        new ClassGeneratorUtil(
            modifiers,
            className,
            packageName,
            superClass,
            interfaces,
            variables,
            methods,
            classStaticNameSpace,
            isInterface);
    byte[] code = classGenerator.generateClass();

    // if debug, write out the class file to debugClasses directory
    if (DEBUG_DIR != null)
      try {
        FileOutputStream out = new FileOutputStream(DEBUG_DIR + '/' + className + ".class");
        out.write(code);
        out.close();
      } catch (IOException e) {
        throw new IllegalStateException(
            "cannot create file " + DEBUG_DIR + '/' + className + ".class", e);
      }

    // Define the new class in the classloader
    Class genClass = bcm.defineClass(fqClassName, code);

    // import the unq name into parent
    enclosingNameSpace.importClass(fqClassName.replace('$', '.'));

    try {
      classStaticNameSpace.setLocalVariable(
          ClassGeneratorUtil.BSHINIT, block, false /*strictJava*/);
    } catch (UtilEvalError e) {
      throw new InterpreterError("unable to init static: " + e);
    }

    // Give the static space its class static import
    // important to do this after all classes are defined
    classStaticNameSpace.setClassStatic(genClass);

    // evaluate the static portion of the block in the static space
    block.evalBlock(callstack, interpreter, true /*override*/, ClassNodeFilter.CLASSSTATIC);

    callstack.pop();

    if (!genClass.isInterface()) {
      // Set the static bsh This callback
      String bshStaticFieldName = ClassGeneratorUtil.BSHSTATIC + className;
      try {
        LHS lhs = Reflect.getLHSStaticField(genClass, bshStaticFieldName);
        lhs.assign(classStaticNameSpace.getThis(interpreter), false /*strict*/);
      } catch (Exception e) {
        throw new InterpreterError("Error in class gen setup: " + e);
      }
    }

    bcm.doneDefiningClass(fqClassName);
    return genClass;
  }