/**
   * Generate instructions for field initializers or static initialization statements.
   *
   * @param node - the AST at the root of the statement.
   * @param isStatic - true if the code should be generated in a static context.
   */
  private void generateInstructions(IASNode node, final boolean isStatic) {
    //  Do we need to create new information for the class'
    //  static initialization method?  Note that this may
    //  be undone if the codgen fails or doesn't produce
    //  any instructions.
    final boolean createNewCinit = isStatic && this.cinfo.cInit == null;

    if (createNewCinit) {
      //  Speculatively initialize the class' cinit
      //  (static class initializer routine)'s data
      //  structures; the code generator may need to
      //  store information in them.
      this.cinfo.cInit = new MethodInfo();
      MethodBodyInfo cinit_info = new MethodBodyInfo();
      cinit_info.setMethodInfo(this.cinfo.cInit);

      this.classStaticScope.setMethodInfo(this.cinfo.cInit);
      this.classStaticScope.methodVisitor = emitter.visitMethod(this.cinfo.cInit);
      this.classStaticScope.methodVisitor.visit();
      this.classStaticScope.methodBodyVisitor =
          this.classStaticScope.methodVisitor.visitBody(cinit_info);
      this.classStaticScope.methodBodyVisitor.visit();
    }

    InstructionList cgResult = null;

    LexicalScope ls = isStatic ? this.classStaticScope : this.classScope;

    ls.resetDebugInfo();
    cgResult = ls.getGenerator().generateInstructions(node, CmcEmitter.__statement_NT, ls);

    //  If nothing came back, revert any change made to the cinit information.
    if ((cgResult == null || cgResult.isEmpty()) && createNewCinit) {
      this.cinfo.cInit = null;
      this.classStaticScope.resetMethodInfo();
      this.classStaticScope.methodVisitor = null;
      this.classStaticScope.methodBodyVisitor = null;
    }

    //  Save the generated instructions, if present.
    if (cgResult != null) {
      if (isStatic) this.cinitInsns.addAll(cgResult);
      else this.iinitInsns.addAll(cgResult);
    }
  }
  /** Finish the class' definition. */
  void finishClassDefinition() {
    // the generation of instructions for variable initialization is delayed
    // until now, so we can add that initialization to the front of
    // the cinit instruction list.
    if (!staticVariableInitializers.isEmpty()) {
      InstructionList exisitingCinitInsns = null;
      if (!this.cinitInsns.isEmpty()) {
        exisitingCinitInsns = new InstructionList();
        exisitingCinitInsns.addAll(this.cinitInsns);
        this.cinitInsns = new InstructionList();
      }

      for (VariableNode var : staticVariableInitializers) generateInstructions(var, true);

      if (exisitingCinitInsns != null) this.cinitInsns.addAll(exisitingCinitInsns);
    }

    // add "goto_definition_help" metadata to user defined metadata.
    ITraitVisitor tv =
        classScope.getGlobalScope().traitsVisitor.visitClassTrait(TRAIT_Class, className, 0, cinfo);
    IMetaInfo[] metaTags = getAllMetaTags(classDefinition);

    // Add "goto definition help" metadata for the constructor.
    if (this.ctorFunction != null) {
      FunctionDefinition ctorDef = this.ctorFunction.getDefinition();
      MetaTag metaTag =
          MetaTag.createGotoDefinitionHelp(
              classDefinition,
              classDefinition.getContainingFilePath(),
              Integer.toString(ctorDef.getNameStart()),
              true);
      if (metaTag != null) metaTags = MetaTag.addMetaTag(metaTags, metaTag);
    }

    this.classScope.processMetadata(tv, metaTags);
    tv.visitEnd();

    // Need to do this before generating the CTOR as the generated code may result in insns that
    // need to be added to the ctor.
    generateBindableImpl();

    generateRequiredContingentDefinitions();

    addAnyEmbeddedAsset();

    // Make any vistEnd method calls
    // that were deferred.
    // callVisitEnds must be called on the same thread
    // that started code generation.  Since we don't generate
    // classes in parallel yet, we know that we are on the thread
    // that started code generation here.
    classScope.callVisitEnds();
    classStaticScope.callVisitEnds();

    //  Create the class' constructor function.
    if (this.ctorFunction != null) {
      MethodInfo mi =
          classScope
              .getGenerator()
              .generateFunction(this.ctorFunction, classScope, this.iinitInsns);

      if (mi != null) this.iinfo.iInit = mi;
    } else if (!this.iinitInsns.isEmpty()) {
      //  Synthesize a constructor.

      this.iinfo.iInit = new MethodInfo();
      MethodBodyInfo iinit = new MethodBodyInfo();
      iinit.setMethodInfo(this.iinfo.iInit);

      IMethodVisitor mv = emitter.visitMethod(this.iinfo.iInit);
      mv.visit();
      IMethodBodyVisitor mbv = mv.visitBody(iinit);
      InstructionList ctor_insns = new InstructionList();
      ctor_insns.addInstruction(OP_getlocal0);
      ctor_insns.addInstruction(OP_pushscope);

      ctor_insns.addAll(this.iinitInsns);

      //  Call the superclass' constructor after the instance
      //  init instructions; this doesn't seem like an abstractly
      //  correct sequence, but it's what ASC does.
      ctor_insns.addInstruction(OP_getlocal0);
      ctor_insns.addInstruction(OP_constructsuper, 0);

      ctor_insns.addInstruction(OP_returnvoid);
      mbv.visit();
      mbv.visitInstructionList(ctor_insns);
      mbv.visitEnd();
      mv.visitEnd();
    }

    //  If the class has static initialization
    //  logic, emit a cinit routine.
    if (!this.cinitInsns.isEmpty()) {
      InstructionList cinit_insns = new InstructionList();
      cinit_insns.addInstruction(OP_getlocal0);
      cinit_insns.addInstruction(OP_pushscope);

      //  TODO: Examine other end-of-function processing
      //  and ensure it's completed.
      this.classStaticScope.finishClassStaticInitializer(this.cinitInsns);
      cinit_insns.addAll(this.cinitInsns);

      cinit_insns.addInstruction(OP_returnvoid);

      this.classStaticScope.methodBodyVisitor.visitInstructionList(cinit_insns);
      this.classStaticScope.methodBodyVisitor.visitEnd();
      this.classStaticScope.methodVisitor.visitEnd();
    } else {
      //  Ensure nothing got dropped.
      assert (this.classStaticScope.methodBodyVisitor == null);
    }

    itraits.visitEnd();
    ctraits.visitEnd();

    cv.visitEnd();
  }