docs: public void visitClass(ClassNode classNode) {
          referencedClasses.clear();
          WriterControllerFactory factory = classNode.getNodeMetaData(WriterControllerFactory.class);
          WriterController normalController = new WriterController();
          if (factory!=null) {
              this.controller = factory.makeController(normalController);
          } else {
              this.controller = normalController;
          }
          this.controller.init(this, context, cv, classNode);
          this.cv = this.controller.getClassVisitor();
  
          if (controller.shouldOptimizeForInt() || factory!=null) {
              OptimizingStatementWriter.setNodeMeta(controller.getTypeChooser(),classNode);
          }
  
          try {
              int bytecodeVersion = controller.getBytecodeVersion();
              Object min = classNode.getNodeMetaData(MINIMUM_BYTECODE_VERSION);
              if (min instanceof Integer) {
                  int minVersion = (int) min;
                  if ((bytecodeVersion ^ Opcodes.V_PREVIEW) < minVersion) {
                      bytecodeVersion = minVersion;
                  }
              }
              cv.visit(
                      bytecodeVersion,
                      adjustedClassModifiersForClassWriting(classNode),
                      controller.getInternalClassName(),
                      BytecodeHelper.getGenericsSignature(classNode),
                      controller.getInternalBaseClassName(),
                      BytecodeHelper.getClassInternalNames(classNode.getInterfaces())
              );
              cv.visitSource(sourceFile, null);
              if (classNode instanceof InnerClassNode) {
                  InnerClassNode innerClass = (InnerClassNode) classNode;
                  MethodNode enclosingMethod = innerClass.getEnclosingMethod();
                  if (enclosingMethod != null) {
                      String outerClassName = BytecodeHelper.getClassInternalName(innerClass.getOuterClass().getName());
                      cv.visitOuterClass(outerClassName, enclosingMethod.getName(), BytecodeHelper.getMethodDescriptor(enclosingMethod));
                  }
              }
              if (classNode.getName().endsWith("package-info")) {
                  PackageNode packageNode = classNode.getPackage();
                  if (packageNode != null) {
                      // pull them out of package node but treat them like they were on class node
                      visitAnnotations(classNode, packageNode, cv);
                  }
                  cv.visitEnd();
                  return;
              } else {
                  visitAnnotations(classNode, cv);
              }
  
              if (classNode.isInterface()) {
                  ClassNode owner = classNode;
                  if (owner instanceof InnerClassNode) {
                      owner = owner.getOuterClass();
                  }
                  String outerClassName = classNode.getName();
                  String name = outerClassName + "$" + context.getNextInnerClassIdx();
                  controller.setInterfaceClassLoadingClass(
                          new InterfaceHelperClassNode (
                                  owner, name, ACC_SUPER | ACC_SYNTHETIC | ACC_STATIC, ClassHelper.OBJECT_TYPE,
                                  controller.getCallSiteWriter().getCallSites()));
                  super.visitClass(classNode);
                  createInterfaceSyntheticStaticFields();
              } else {
                  super.visitClass(classNode);
                  MopWriter.Factory mopWriterFactory = classNode.getNodeMetaData(MopWriter.Factory.class);
                  if (mopWriterFactory==null) {
                      mopWriterFactory = MopWriter.FACTORY;
                  }
                  MopWriter mopWriter = mopWriterFactory.create(controller);
                  mopWriter.createMopMethods();
                  controller.getCallSiteWriter().generateCallSiteArray();
                  createSyntheticStaticFields();
              }
  
              // GROOVY-6750 and GROOVY-6808
              for (Iterator<InnerClassNode> iter = classNode.getInnerClasses(); iter.hasNext();) {
                  InnerClassNode innerClass = iter.next();
                  makeInnerClassEntry(innerClass);
              }
              makeInnerClassEntry(classNode);
  
              cv.visitEnd();
          } catch (GroovyRuntimeException e) {
              e.setModule(classNode.getModule());
              throw e;
          } catch (NegativeArraySizeException nase) {
              throw new GroovyRuntimeException("NegativeArraySizeException while processing "+sourceFile, nase);
          } catch (NullPointerException npe) {
              throw new GroovyRuntimeException("NPE while processing "+sourceFile, npe);
          }
      }