gt:         int k = recordTypeData(n, localsTypes, tLocalsTypes);
context: /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
   *
   * The contents of this file are subject to the Mozilla Public License Version
   * 1.1 (the "License"); you may not use this file except in compliance with
   * the License.  Alternatively, the contents of this file may be used under
   * the terms of the GNU Lesser General Public License Version 2.1 or later,
   * or the Apache License Version 2.0.
   *
   * Software distributed under the License is distributed on an "AS IS" basis,
   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   * for the specific language governing rights and limitations under the
   * License.
   */
  
  package io.github.proxyhotswap.javassist.bytecode.stackmap;
  
  import io.github.proxyhotswap.javassist.ClassPool;
  import io.github.proxyhotswap.javassist.CtClass;
  import io.github.proxyhotswap.javassist.NotFoundException;
  import io.github.proxyhotswap.javassist.bytecode.*;
  
  import java.util.ArrayList;
  
  
  /**
   * Stack map maker.
   */
  public class MapMaker extends Tracer {
      /*
      public static void main(String[] args) throws Exception {
          boolean useMain2 = args[0].equals("0");
          if (useMain2 && args.length > 1) {
              main2(args);
              return;
          }
  
          for (int i = 0; i < args.length; i++)
              main1(args[i]);
      }
  
      public static void main1(String className) throws Exception {
          ClassPool cp = ClassPool.getDefault();
          //javassist.CtClass cc = cp.get(className);
          javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(className));
          System.out.println(className);
          ClassFile cf = cc.getClassFile();
          java.util.List minfos = cf.getMethods();
          for (int i = 0; i < minfos.size(); i++) {
              MethodInfo minfo = (MethodInfo)minfos.get(i);
              CodeAttribute ca = minfo.getCodeAttribute();
              if (ca != null)
                  ca.setAttribute(make(cp, minfo));
          }
  
          cc.writeFile("tmp");
      }
  
      public static void main2(String[] args) throws Exception {
          ClassPool cp = ClassPool.getDefault();
          //javassist.CtClass cc = cp.get(args[1]);
          javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(args[1]));
          MethodInfo minfo;
          if (args[2].equals("_init_"))
              minfo = cc.getDeclaredConstructors()[0].getMethodInfo();
              // minfo = cc.getClassInitializer().getMethodInfo();
          else
              minfo = cc.getDeclaredMethod(args[2]).getMethodInfo();
  
          CodeAttribute ca = minfo.getCodeAttribute();
          if (ca == null) {
              System.out.println("abstarct method");
              return;
          }
  
          TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, false);
          MapMaker mm = new MapMaker(cp, minfo, ca);
          mm.make(blocks, ca.getCode());
          for (int i = 0; i < blocks.length; i++)
              System.out.println(blocks[i]);
      }
      */
  
      /**
       * Computes the stack map table of the given method and returns it.
       * It returns null if the given method does not have to have a
       * stack map table or it includes JSR.
       */
      public static StackMapTable make(ClassPool classes, MethodInfo minfo)
          throws BadBytecode
      {
          CodeAttribute ca = minfo.getCodeAttribute();
          if (ca == null)
              return null;
  
          TypedBlock[] blocks;
          try {
              blocks = TypedBlock.makeBlocks(minfo, ca, true);
          }
          catch (BasicBlock.JsrBytecode e) {
              return null;
          }
  
          if (blocks == null)
              return null;
  
          MapMaker mm = new MapMaker(classes, minfo, ca);
          try {
              mm.make(blocks, ca.getCode());
          }
          catch (BadBytecode bb) {
              throw new BadBytecode(minfo, bb);
          }
  
          return mm.toStackMap(blocks);
      }
  
      /**
       * Computes the stack map table for J2ME.
       * It returns null if the given method does not have to have a
       * stack map table or it includes JSR.
       */
      public static StackMap make2(ClassPool classes, MethodInfo minfo)
          throws BadBytecode
      {
          CodeAttribute ca = minfo.getCodeAttribute();
          if (ca == null)
              return null;
  
          TypedBlock[] blocks;
          try {
              blocks = TypedBlock.makeBlocks(minfo, ca, true);
          }
          catch (BasicBlock.JsrBytecode e) {
              return null;
          }
  
          if (blocks == null)
              return null;
  
          MapMaker mm = new MapMaker(classes, minfo, ca);
          try {
              mm.make(blocks, ca.getCode());
          }
          catch (BadBytecode bb) {
              throw new BadBytecode(minfo, bb);
          }
          return mm.toStackMap2(minfo.getConstPool(), blocks);
      }
  
      public MapMaker(ClassPool classes, MethodInfo minfo, CodeAttribute ca) {
          super(classes, minfo.getConstPool(),
                ca.getMaxStack(), ca.getMaxLocals(),
                TypedBlock.getRetType(minfo.getDescriptor()));
      }
  
      protected MapMaker(MapMaker old) { super(old); }
  
      /**
       * Runs an analyzer (Phase 1 and 2).
       */
      void make(TypedBlock[] blocks, byte[] code)
          throws BadBytecode
      {
          make(code, blocks[0]);
          findDeadCatchers(code, blocks);
          try {
              fixTypes(code, blocks);
          } catch (NotFoundException e) {
              throw new BadBytecode("failed to resolve types", e);
          }
      }
  
      // Phase 1
  
      private void make(byte[] code, TypedBlock tb)
          throws BadBytecode
      {
          copyTypeData(tb.stackTop, tb.stackTypes, stackTypes);
          stackTop = tb.stackTop;
          copyTypeData(tb.localsTypes.length, tb.localsTypes, localsTypes);
  
          traceException(code, tb.toCatch);
  
          int pos = tb.position;
          int end = pos + tb.length;
          while (pos < end) {
              pos += doOpcode(pos, code);
              traceException(code, tb.toCatch);
          }
  
          if (tb.exit != null) {
              for (int i = 0; i < tb.exit.length; i++) {
                  TypedBlock e = (TypedBlock)tb.exit[i];
                  if (e.alreadySet())
                      mergeMap(e, true);
                  else {
                      recordStackMap(e);
                      MapMaker maker = new MapMaker(this);
                      maker.make(code, e);
                  }
              }
          }
      }
  
      private void traceException(byte[] code, TypedBlock.Catch handler)
          throws BadBytecode
      {
          while (handler != null) {
              TypedBlock tb = (TypedBlock)handler.body;
              if (tb.alreadySet()) {
                  mergeMap(tb, false);
                  if (tb.stackTop < 1)
                      throw new BadBytecode("bad catch clause: " + handler.typeIndex);
  
                  tb.stackTypes[0] = merge(toExceptionType(handler.typeIndex),
                                           tb.stackTypes[0]);
              }
              else {
                  recordStackMap(tb, handler.typeIndex);
                  MapMaker maker = new MapMaker(this);
                  maker.make(code, tb);
              }
  
              handler = handler.next;
          }
      }
  
      private void mergeMap(TypedBlock dest, boolean mergeStack) throws BadBytecode {
          int n = localsTypes.length;
          for (int i = 0; i < n; i++)
              dest.localsTypes[i] = merge(validateTypeData(localsTypes, n, i),
                                          dest.localsTypes[i]);
  
          if (mergeStack) {
              n = stackTop;
              for (int i = 0; i < n; i++)
                  dest.stackTypes[i] = merge(stackTypes[i], dest.stackTypes[i]);
          }
      }
  
      private TypeData merge(TypeData src, TypeData target) throws BadBytecode {
          if (src == target)
              return target;
          else if (target instanceof TypeData.ClassName
                   || target instanceof TypeData.BasicType)  // a parameter
              return target;
          else if (target instanceof TypeData.AbsTypeVar) {
              ((TypeData.AbsTypeVar)target).merge(src);
              return target;
          }
          else
              throw new RuntimeException("fatal: this should never happen");
      }
  
      private void recordStackMap(TypedBlock target)
          throws BadBytecode
      {
          TypeData[] tStackTypes = TypeData.make(stackTypes.length);
          int st = stackTop;
          recordTypeData(st, stackTypes, tStackTypes);
          recordStackMap0(target, st, tStackTypes);
      }
  
      private void recordStackMap(TypedBlock target, int exceptionType)
          throws BadBytecode
      {
          TypeData[] tStackTypes = TypeData.make(stackTypes.length);
          tStackTypes[0] = toExceptionType(exceptionType).join();
          recordStackMap0(target, 1, tStackTypes);
      }
  
      private TypeData.ClassName toExceptionType(int exceptionType) {
          String type;
          if (exceptionType == 0)     // for finally clauses
              type= "java.lang.Throwable";
          else
              type = cpool.getClassInfo(exceptionType);
  
          return new TypeData.ClassName(type);
      }
  
      private void recordStackMap0(TypedBlock target, int st, TypeData[] tStackTypes)
          throws BadBytecode
      {
          int n = localsTypes.length;
          TypeData[] tLocalsTypes = TypeData.make(n);
  