/** 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();
  }