gt:                 for (long i = lo; i <= hi; i++) {  // leave space for jump table
context: /*
   * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package com.sun.tools.javac.jvm;
  
  import com.sun.tools.javac.util.*;
  import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  import com.sun.tools.javac.util.List;
  import com.sun.tools.javac.code.*;
  import com.sun.tools.javac.code.Attribute.TypeCompound;
  import com.sun.tools.javac.code.Symbol.VarSymbol;
  import com.sun.tools.javac.comp.*;
  import com.sun.tools.javac.tree.*;
  
  import com.sun.tools.javac.code.Symbol.*;
  import com.sun.tools.javac.code.Type.*;
  import com.sun.tools.javac.jvm.Code.*;
  import com.sun.tools.javac.jvm.Items.*;
  import com.sun.tools.javac.tree.EndPosTable;
  import com.sun.tools.javac.tree.JCTree.*;
  
  import static com.sun.tools.javac.code.Flags.*;
  import static com.sun.tools.javac.code.Kinds.Kind.*;
  import static com.sun.tools.javac.code.TypeTag.*;
  import static com.sun.tools.javac.jvm.ByteCodes.*;
  import static com.sun.tools.javac.jvm.CRTFlags.*;
  import static com.sun.tools.javac.main.Option.*;
  import static com.sun.tools.javac.tree.JCTree.Tag.*;
  
  /** This pass maps flat Java (i.e. without inner classes) to bytecodes.
   *
   *  <p><b>This is NOT part of any supported API.
   *  If you write code that depends on this, you do so at your own risk.
   *  This code and its internal interfaces are subject to change or
   *  deletion without notice.</b>
   */
  public class Gen extends JCTree.Visitor {
      protected static final Context.Key<Gen> genKey = new Context.Key<>();
  
      private final Log log;
      private final Symtab syms;
      private final Check chk;
      private final Resolve rs;
      private final TreeMaker make;
      private final Names names;
      private final Target target;
      private final Name accessDollar;
      private final Types types;
      private final Lower lower;
      private final Annotate annotate;
      private final StringConcat concat;
  
      /** Format of stackmap tables to be generated. */
      private final Code.StackMapFormat stackMap;
  
      /** A type that serves as the expected type for all method expressions.
       */
      private final Type methodType;
  
      /**
       * Are we presently traversing a let expression ? Yes if depth != 0
       */
      private int letExprDepth;
  
      public static Gen instance(Context context) {
          Gen instance = context.get(genKey);
          if (instance == null)
              instance = new Gen(context);
          return instance;
      }
  
      /** Constant pool, reset by genClass.
       */
      private final Pool pool;
  
      protected Gen(Context context) {
          context.put(genKey, this);
  
          names = Names.instance(context);
          log = Log.instance(context);
          syms = Symtab.instance(context);
          chk = Check.instance(context);
          rs = Resolve.instance(context);
          make = TreeMaker.instance(context);
          target = Target.instance(context);
          types = Types.instance(context);
          concat = StringConcat.instance(context);
  
          methodType = new MethodType(null, null, null, syms.methodClass);
          accessDollar = names.
              fromString("access" + target.syntheticNameChar());
          lower = Lower.instance(context);
  
          Options options = Options.instance(context);
          lineDebugInfo =
              options.isUnset(G_CUSTOM) ||
              options.isSet(G_CUSTOM, "lines");
          varDebugInfo =
              options.isUnset(G_CUSTOM)
              ? options.isSet(G)
              : options.isSet(G_CUSTOM, "vars");
          genCrt = options.isSet(XJCOV);
          debugCode = options.isSet("debug.code");
          allowBetterNullChecks = target.hasObjects();
          pool = new Pool(types);
  
          // ignore cldc because we cannot have both stackmap formats
          this.stackMap = StackMapFormat.JSR202;
          annotate = Annotate.instance(context);
      }
  
      /** Switches
       */
      private final boolean lineDebugInfo;
      private final boolean varDebugInfo;
      private final boolean genCrt;
      private final boolean debugCode;
      private final boolean allowBetterNullChecks;
  
      /** Code buffer, set by genMethod.
       */
      private Code code;
  
      /** Items structure, set by genMethod.
       */
      private Items items;
  
      /** Environment for symbol lookup, set by genClass
       */
      private Env<AttrContext> attrEnv;
  
      /** The top level tree.
       */
      private JCCompilationUnit toplevel;
  
      /** The number of code-gen errors in this class.
       */
      private int nerrs = 0;
  
      /** An object containing mappings of syntax trees to their
       *  ending source positions.
       */
      EndPosTable endPosTable;
  
      /** Generate code to load an integer constant.
       *  @param n     The integer to be loaded.
       */
      void loadIntConst(int n) {
          items.makeImmediateItem(syms.intType, n).load();
      }
  
      /** The opcode that loads a zero constant of a given type code.
       *  @param tc   The given type code (@see ByteCode).
       */
      public static int zero(int tc) {
          switch(tc) {
          case INTcode: case BYTEcode: case SHORTcode: case CHARcode:
              return iconst_0;
          case LONGcode:
              return lconst_0;
          case FLOATcode:
              return fconst_0;
          case DOUBLEcode:
              return dconst_0;
          default:
              throw new AssertionError("zero");
          }
      }
  
      /** The opcode that loads a one constant of a given type code.
       *  @param tc   The given type code (@see ByteCode).
       */
      public static int one(int tc) {
          return zero(tc) + 1;
      }
  
      /** Generate code to load -1 of the given type code (either int or long).
       *  @param tc   The given type code (@see ByteCode).
       */
      void emitMinusOne(int tc) {
          if (tc == LONGcode) {
              items.makeImmediateItem(syms.longType, Long.valueOf(-1)).load();
          } else {
              code.emitop0(iconst_m1);
          }
      }
  
      /** Construct a symbol to reflect the qualifying type that should
       *  appear in the byte code as per JLS 13.1.
       *
       *  For {@literal target >= 1.2}: Clone a method with the qualifier as owner (except
       *  for those cases where we need to work around VM bugs).
       *
       *  For {@literal target <= 1.1}: If qualified variable or method is defined in a
       *  non-accessible class, clone it with the qualifier class as owner.
       *
       *  @param sym    The accessed symbol
       *  @param site   The qualifier's type.
       */
      Symbol binaryQualifier(Symbol sym, Type site) {
  
          if (site.hasTag(ARRAY)) {
              if (sym == syms.lengthVar ||
                  sym.owner != syms.arrayClass)
                  return sym;
              // array clone can be qualified by the array type in later targets
              Symbol qualifier = new ClassSymbol(Flags.PUBLIC, site.tsym.name,
                                                 site, syms.noSymbol);
              return sym.clone(qualifier);
          }
  
          if (sym.owner == site.tsym ||
              (sym.flags() & (STATIC | SYNTHETIC)) == (STATIC | SYNTHETIC)) {
              return sym;
          }
  
          // leave alone methods inherited from Object
          // JLS 13.1.
          if (sym.owner == syms.objectType.tsym)
              return sym;
  
          return sym.clone(site.tsym);
      }
  
      /** Insert a reference to given type in the constant pool,
       *  checking for an array with too many dimensions;
       *  return the reference's index.
       *  @param type   The type for which a reference is inserted.
       */
      int makeRef(DiagnosticPosition pos, Type type) {
          checkDimension(pos, type);
          if (type.isAnnotated()) {
              return pool.put((Object)type);
          } else {
              return pool.put(type.hasTag(CLASS) ? (Object)type.tsym : (Object)type);
          }
      }
  
      /** Check if the given type is an array with too many dimensions.
       */
      private void checkDimension(DiagnosticPosition pos, Type t) {
          switch (t.getTag()) {
          case METHOD:
              checkDimension(pos, t.getReturnType());
              for (List<Type> args = t.getParameterTypes(); args.nonEmpty(); args = args.tail)
                  checkDimension(pos, args.head);
              break;
          case ARRAY:
              if (types.dimensions(t) > ClassFile.MAX_DIMENSIONS) {
                  log.error(pos, "limit.dimensions");
                  nerrs++;
              }
              break;
          default:
              break;
          }
      }
  
      /** Create a tempory variable.
       *  @param type   The variable's type.
       */
      LocalItem makeTemp(Type type) {
          VarSymbol v = new VarSymbol(Flags.SYNTHETIC,
                                      names.empty,
                                      type,
                                      env.enclMethod.sym);
          code.newLocal(v);
          return items.makeLocalItem(v);
      }
  
      /** Generate code to call a non-private method or constructor.
       *  @param pos         Position to be used for error reporting.
       *  @param site        The type of which the method is a member.
       *  @param name        The method's name.
       *  @param argtypes    The method's argument types.
       *  @param isStatic    A flag that indicates whether we call a
       *                     static or instance method.
       */
      void callMethod(DiagnosticPosition pos,
                      Type site, Name name, List<Type> argtypes,
                      boolean isStatic) {
          Symbol msym = rs.
              resolveInternalMethod(pos, attrEnv, site, name, argtypes, null);
          if (isStatic) items.makeStaticItem(msym).invoke();
          else items.makeMemberItem(msym, name == names.init).invoke();
      }
  
      /** Is the given method definition an access method
       *  resulting from a qualified super? This is signified by an odd
       *  access code.
       */
      private boolean isAccessSuper(JCMethodDecl enclMethod) {
          return
              (enclMethod.mods.flags & SYNTHETIC) != 0 &&
              isOddAccessName(enclMethod.name);
      }
  
      /** Does given name start with "access$" and end in an odd digit?
       */
      private boolean isOddAccessName(Name name) {
          return
              name.startsWith(accessDollar) &&
              (name.getByteAt(name.getByteLength() - 1) & 1) == 1;
      }
  
  /* ************************************************************************
   * Non-local exits
   *************************************************************************/
  
      /** Generate code to invoke the finalizer associated with given
       *  environment.
       *  Any calls to finalizers are appended to the environments `cont' chain.
       *  Mark beginning of gap in catch all range for finalizer.
       */
      void genFinalizer(Env<GenContext> env) {
          if (code.isAlive() && env.info.finalize != null)
              env.info.finalize.gen();
      }
  
      /** Generate code to call all finalizers of structures aborted by
       *  a non-local
       *  exit.  Return target environment of the non-local exit.
       *  @param target      The tree representing the structure that's aborted
       *  @param env         The environment current at the non-local exit.
       */
      Env<GenContext> unwind(JCTree target, Env<GenContext> env) {
          Env<GenContext> env1 = env;
          while (true) {
              genFinalizer(env1);
              if (env1.tree == target) break;
              env1 = env1.next;
          }
          return env1;
      }
  
      /** Mark end of gap in catch-all range for finalizer.
       *  @param env   the environment which might contain the finalizer
       *               (if it does, env.info.gaps != null).
       */
      void endFinalizerGap(Env<GenContext> env) {
          if (env.info.gaps != null && env.info.gaps.length() % 2 == 1)
              env.info.gaps.append(code.curCP());
      }
  
      /** Mark end of all gaps in catch-all ranges for finalizers of environments
       *  lying between, and including to two environments.
       *  @param from    the most deeply nested environment to mark
       *  @param to      the least deeply nested environment to mark
       */
      void endFinalizerGaps(Env<GenContext> from, Env<GenContext> to) {
          Env<GenContext> last = null;
          while (last != to) {
              endFinalizerGap(from);
              last = from;
              from = from.next;
          }
      }
  
      /** Do any of the structures aborted by a non-local exit have
       *  finalizers that require an empty stack?
       *  @param target      The tree representing the structure that's aborted
       *  @param env         The environment current at the non-local exit.
       */
      boolean hasFinally(JCTree target, Env<GenContext> env) {
          while (env.tree != target) {
              if (env.tree.hasTag(TRY) && env.info.finalize.hasFinalizer())
                  return true;
              env = env.next;
          }
          return false;
      }
  
  /* ************************************************************************
   * Normalizing class-members.
   *************************************************************************/
  
      /** Distribute member initializer code into constructors and {@code <clinit>}
       *  method.
       *  @param defs         The list of class member declarations.
       *  @param c            The enclosing class.
       */
      List<JCTree> normalizeDefs(List<JCTree> defs, ClassSymbol c) {
          ListBuffer<JCStatement> initCode = new ListBuffer<>();
          ListBuffer<Attribute.TypeCompound> initTAs = new ListBuffer<>();
          ListBuffer<JCStatement> clinitCode = new ListBuffer<>();
          ListBuffer<Attribute.TypeCompound> clinitTAs = new ListBuffer<>();
          ListBuffer<JCTree> methodDefs = new ListBuffer<>();
          // Sort definitions into three listbuffers:
          //  - initCode for instance initializers
          //  - clinitCode for class initializers
          //  - methodDefs for method definitions
          for (List<JCTree> l = defs; l.nonEmpty(); l = l.tail) {
              JCTree def = l.head;
              switch (def.getTag()) {
              case BLOCK:
                  JCBlock block = (JCBlock)def;
                  if ((block.flags & STATIC) != 0)
                      clinitCode.append(block);
                  else if ((block.flags & SYNTHETIC) == 0)
                      initCode.append(block);
                  break;
              case METHODDEF:
                  methodDefs.append(def);
                  break;
              case VARDEF:
                  JCVariableDecl vdef = (JCVariableDecl) def;
                  VarSymbol sym = vdef.sym;
                  checkDimension(vdef.pos(), sym.type);
                  if (vdef.init != null) {
                      if ((sym.flags() & STATIC) == 0) {
                          // Always initialize instance variables.
                          JCStatement init = make.at(vdef.pos()).
                              Assignment(sym, vdef.init);
                          initCode.append(init);
                          endPosTable.replaceTree(vdef, init);
                          initTAs.addAll(getAndRemoveNonFieldTAs(sym));
                      } else if (sym.getConstValue() == null) {
                          // Initialize class (static) variables only if
                          // they are not compile-time constants.
                          JCStatement init = make.at(vdef.pos).
                              Assignment(sym, vdef.init);
                          clinitCode.append(init);
                          endPosTable.replaceTree(vdef, init);
                          clinitTAs.addAll(getAndRemoveNonFieldTAs(sym));
                      } else {
                          checkStringConstant(vdef.init.pos(), sym.getConstValue());
                          /* if the init contains a reference to an external class, add it to the
                           * constant's pool
                           */
                          vdef.init.accept(classReferenceVisitor);
                      }
                  }
                  break;
              default:
                  Assert.error();
              }
          }
          // Insert any instance initializers into all constructors.
          if (initCode.length() != 0) {
              List<JCStatement> inits = initCode.toList();
              initTAs.addAll(c.getInitTypeAttributes());
              List<Attribute.TypeCompound> initTAlist = initTAs.toList();
              for (JCTree t : methodDefs) {
                  normalizeMethod((JCMethodDecl)t, inits, initTAlist);
              }
          }
          // If there are class initializers, create a <clinit> method
          // that contains them as its body.
          if (clinitCode.length() != 0) {
              MethodSymbol clinit = new MethodSymbol(
                  STATIC | (c.flags() & STRICTFP),
                  names.clinit,
                  new MethodType(
                      List.nil(), syms.voidType,
                      List.nil(), syms.methodClass),
                  c);
              c.members().enter(clinit);
              List<JCStatement> clinitStats = clinitCode.toList();
              JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats);
              block.endpos = TreeInfo.endPos(clinitStats.last());
              methodDefs.append(make.MethodDef(clinit, block));
  
              if (!clinitTAs.isEmpty())
                  clinit.appendUniqueTypeAttributes(clinitTAs.toList());
              if (!c.getClassInitTypeAttributes().isEmpty())
                  clinit.appendUniqueTypeAttributes(c.getClassInitTypeAttributes());
          }
          // Return all method definitions.
          return methodDefs.toList();
      }
  
      private List<Attribute.TypeCompound> getAndRemoveNonFieldTAs(VarSymbol sym) {
          List<TypeCompound> tas = sym.getRawTypeAttributes();
          ListBuffer<Attribute.TypeCompound> fieldTAs = new ListBuffer<>();
          ListBuffer<Attribute.TypeCompound> nonfieldTAs = new ListBuffer<>();
          for (TypeCompound ta : tas) {
              Assert.check(ta.getPosition().type != TargetType.UNKNOWN);
              if (ta.getPosition().type == TargetType.FIELD) {
                  fieldTAs.add(ta);
              } else {
                  nonfieldTAs.add(ta);
              }
          }
          sym.setTypeAttributes(fieldTAs.toList());
          return nonfieldTAs.toList();
      }
  
      /** Check a constant value and report if it is a string that is
       *  too large.
       */
      private void checkStringConstant(DiagnosticPosition pos, Object constValue) {
          if (nerrs != 0 || // only complain about a long string once
              constValue == null ||
              !(constValue instanceof String) ||
              ((String)constValue).length() < Pool.MAX_STRING_LENGTH)
              return;
          log.error(pos, "limit.string");
          nerrs++;
      }
  
      /** Insert instance initializer code into initial constructor.
       *  @param md        The tree potentially representing a
       *                   constructor's definition.
       *  @param initCode  The list of instance initializer statements.
       *  @param initTAs  Type annotations from the initializer expression.
       */
      void normalizeMethod(JCMethodDecl md, List<JCStatement> initCode, List<TypeCompound> initTAs) {
          if (md.name == names.init && TreeInfo.isInitialConstructor(md)) {
              // We are seeing a constructor that does not call another
              // constructor of the same class.
              List<JCStatement> stats = md.body.stats;
              ListBuffer<JCStatement> newstats = new ListBuffer<>();
  
              if (stats.nonEmpty()) {
                  // Copy initializers of synthetic variables generated in
                  // the translation of inner classes.
                  while (TreeInfo.isSyntheticInit(stats.head)) {
                      newstats.append(stats.head);
                      stats = stats.tail;
                  }
                  // Copy superclass constructor call
                  newstats.append(stats.head);
                  stats = stats.tail;
                  // Copy remaining synthetic initializers.
                  while (stats.nonEmpty() &&
                         TreeInfo.isSyntheticInit(stats.head)) {
                      newstats.append(stats.head);
                      stats = stats.tail;
                  }
                  // Now insert the initializer code.
                  newstats.appendList(initCode);
                  // And copy all remaining statements.
                  while (stats.nonEmpty()) {
                      newstats.append(stats.head);
                      stats = stats.tail;
                  }
              }
              md.body.stats = newstats.toList();
              if (md.body.endpos == Position.NOPOS)
                  md.body.endpos = TreeInfo.endPos(md.body.stats.last());
  
              md.sym.appendUniqueTypeAttributes(initTAs);
          }
      }
  
  /* ************************************************************************
   * Traversal methods
   *************************************************************************/
  
      /** Visitor argument: The current environment.
       */
      Env<GenContext> env;
  
      /** Visitor argument: The expected type (prototype).
       */
      Type pt;
  
      /** Visitor result: The item representing the computed value.
       */
      Item result;
  
      /** Visitor method: generate code for a definition, catching and reporting
       *  any completion failures.
       *  @param tree    The definition to be visited.
       *  @param env     The environment current at the definition.
       */
      public void genDef(JCTree tree, Env<GenContext> env) {
          Env<GenContext> prevEnv = this.env;
          try {
              this.env = env;
              tree.accept(this);
          } catch (CompletionFailure ex) {
              chk.completionError(tree.pos(), ex);
          } finally {
              this.env = prevEnv;
          }
      }
  
      /** Derived visitor method: check whether CharacterRangeTable
       *  should be emitted, if so, put a new entry into CRTable
       *  and call method to generate bytecode.
       *  If not, just call method to generate bytecode.
       *  @see    #genStat(JCTree, Env)
       *
       *  @param  tree     The tree to be visited.
       *  @param  env      The environment to use.
       *  @param  crtFlags The CharacterRangeTable flags
       *                   indicating type of the entry.
       */
      public void genStat(JCTree tree, Env<GenContext> env, int crtFlags) {
          if (!genCrt) {
              genStat(tree, env);
              return;
          }
          int startpc = code.curCP();
          genStat(tree, env);
          if (tree.hasTag(Tag.BLOCK)) crtFlags |= CRT_BLOCK;
          code.crt.put(tree, crtFlags, startpc, code.curCP());
      }
  
      /** Derived visitor method: generate code for a statement.
       */
      public void genStat(JCTree tree, Env<GenContext> env) {
          if (code.isAlive()) {
              code.statBegin(tree.pos);
              genDef(tree, env);
          } else if (env.info.isSwitch && tree.hasTag(VARDEF)) {
              // variables whose declarations are in a switch
              // can be used even if the decl is unreachable.
              code.newLocal(((JCVariableDecl) tree).sym);
          }
      }
  
      /** Derived visitor method: check whether CharacterRangeTable
       *  should be emitted, if so, put a new entry into CRTable
       *  and call method to generate bytecode.
       *  If not, just call method to generate bytecode.
       *  @see    #genStats(List, Env)
       *
       *  @param  trees    The list of trees to be visited.
       *  @param  env      The environment to use.
       *  @param  crtFlags The CharacterRangeTable flags
       *                   indicating type of the entry.
       */
      public void genStats(List<JCStatement> trees, Env<GenContext> env, int crtFlags) {
          if (!genCrt) {
              genStats(trees, env);
              return;
          }
          if (trees.length() == 1) {        // mark one statement with the flags
              genStat(trees.head, env, crtFlags | CRT_STATEMENT);
          } else {
              int startpc = code.curCP();
              genStats(trees, env);
              code.crt.put(trees, crtFlags, startpc, code.curCP());
          }
      }
  
      /** Derived visitor method: generate code for a list of statements.
       */
      public void genStats(List<? extends JCTree> trees, Env<GenContext> env) {
          for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail)
              genStat(l.head, env, CRT_STATEMENT);
      }
  
      /** Derived visitor method: check whether CharacterRangeTable
       *  should be emitted, if so, put a new entry into CRTable
       *  and call method to generate bytecode.
       *  If not, just call method to generate bytecode.
       *  @see    #genCond(JCTree,boolean)
       *
       *  @param  tree     The tree to be visited.
       *  @param  crtFlags The CharacterRangeTable flags
       *                   indicating type of the entry.
       */
      public CondItem genCond(JCTree tree, int crtFlags) {
          if (!genCrt) return genCond(tree, false);
          int startpc = code.curCP();
          CondItem item = genCond(tree, (crtFlags & CRT_FLOW_CONTROLLER) != 0);
          code.crt.put(tree, crtFlags, startpc, code.curCP());
          return item;
      }
  
      /** Derived visitor method: generate code for a boolean
       *  expression in a control-flow context.
       *  @param _tree         The expression to be visited.
       *  @param markBranches The flag to indicate that the condition is
       *                      a flow controller so produced conditions
       *                      should contain a proper tree to generate
       *                      CharacterRangeTable branches for them.
       */
      public CondItem genCond(JCTree _tree, boolean markBranches) {
          JCTree inner_tree = TreeInfo.skipParens(_tree);
          if (inner_tree.hasTag(CONDEXPR)) {
              JCConditional tree = (JCConditional)inner_tree;
              CondItem cond = genCond(tree.cond, CRT_FLOW_CONTROLLER);
              if (cond.isTrue()) {
                  code.resolve(cond.trueJumps);
                  CondItem result = genCond(tree.truepart, CRT_FLOW_TARGET);
                  if (markBranches) result.tree = tree.truepart;
                  return result;
              }
              if (cond.isFalse()) {
                  code.resolve(cond.falseJumps);
                  CondItem result = genCond(tree.falsepart, CRT_FLOW_TARGET);
                  if (markBranches) result.tree = tree.falsepart;
                  return result;
              }
              Chain secondJumps = cond.jumpFalse();
              code.resolve(cond.trueJumps);
              CondItem first = genCond(tree.truepart, CRT_FLOW_TARGET);
              if (markBranches) first.tree = tree.truepart;
              Chain falseJumps = first.jumpFalse();
              code.resolve(first.trueJumps);
              Chain trueJumps = code.branch(goto_);
              code.resolve(secondJumps);
              CondItem second = genCond(tree.falsepart, CRT_FLOW_TARGET);
              CondItem result = items.makeCondItem(second.opcode,
                                        Code.mergeChains(trueJumps, second.trueJumps),
                                        Code.mergeChains(falseJumps, second.falseJumps));
              if (markBranches) result.tree = tree.falsepart;
              return result;
          } else {
              CondItem result = genExpr(_tree, syms.booleanType).mkCond();
              if (markBranches) result.tree = _tree;
              return result;
          }
      }
  
      public Code getCode() {
          return code;
      }
  
      public Items getItems() {
          return items;
      }
  
      public Env<AttrContext> getAttrEnv() {
          return attrEnv;
      }
  
      /** Visitor class for expressions which might be constant expressions.
       *  This class is a subset of TreeScanner. Intended to visit trees pruned by
       *  Lower as long as constant expressions looking for references to any
       *  ClassSymbol. Any such reference will be added to the constant pool so
       *  automated tools can detect class dependencies better.
       */
      class ClassReferenceVisitor extends JCTree.Visitor {
  
          @Override
          public void visitTree(JCTree tree) {}
  
          @Override
          public void visitBinary(JCBinary tree) {
              tree.lhs.accept(this);
              tree.rhs.accept(this);
          }
  
          @Override
          public void visitSelect(JCFieldAccess tree) {
              if (tree.selected.type.hasTag(CLASS)) {
                  makeRef(tree.selected.pos(), tree.selected.type);
              }
          }
  
          @Override
          public void visitIdent(JCIdent tree) {
              if (tree.sym.owner instanceof ClassSymbol) {
                  pool.put(tree.sym.owner);
              }
          }
  
          @Override
          public void visitConditional(JCConditional tree) {
              tree.cond.accept(this);
              tree.truepart.accept(this);
              tree.falsepart.accept(this);
          }
  
          @Override
          public void visitUnary(JCUnary tree) {
              tree.arg.accept(this);
          }
  
          @Override
          public void visitParens(JCParens tree) {
              tree.expr.accept(this);
          }
  
          @Override
          public void visitTypeCast(JCTypeCast tree) {
              tree.expr.accept(this);
          }
      }
  
      private ClassReferenceVisitor classReferenceVisitor = new ClassReferenceVisitor();
  
      /** Visitor method: generate code for an expression, catching and reporting
       *  any completion failures.
       *  @param tree    The expression to be visited.
       *  @param pt      The expression's expected type (proto-type).
       */
      public Item genExpr(JCTree tree, Type pt) {
          Type prevPt = this.pt;
          try {
              if (tree.type.constValue() != null) {
                  // Short circuit any expressions which are constants
                  tree.accept(classReferenceVisitor);
                  checkStringConstant(tree.pos(), tree.type.constValue());
                  result = items.makeImmediateItem(tree.type, tree.type.constValue());
              } else {
                  this.pt = pt;
                  tree.accept(this);
              }
              return result.coerce(pt);
          } catch (CompletionFailure ex) {
              chk.completionError(tree.pos(), ex);
              code.state.stacksize = 1;
              return items.makeStackItem(pt);
          } finally {
              this.pt = prevPt;
          }
      }
  
      /** Derived visitor method: generate code for a list of method arguments.
       *  @param trees    The argument expressions to be visited.
       *  @param pts      The expression's expected types (i.e. the formal parameter
       *                  types of the invoked method).
       */
      public void genArgs(List<JCExpression> trees, List<Type> pts) {
          for (List<JCExpression> l = trees; l.nonEmpty(); l = l.tail) {
              genExpr(l.head, pts.head).load();
              pts = pts.tail;
          }
          // require lists be of same length
          Assert.check(pts.isEmpty());
      }
  
  /* ************************************************************************
   * Visitor methods for statements and definitions
   *************************************************************************/
  
      /** Thrown when the byte code size exceeds limit.
       */
      public static class CodeSizeOverflow extends RuntimeException {
          private static final long serialVersionUID = 0;
          public CodeSizeOverflow() {}
      }
  
      public void visitMethodDef(JCMethodDecl tree) {
          // Create a new local environment that points pack at method
          // definition.
          Env<GenContext> localEnv = env.dup(tree);
          localEnv.enclMethod = tree;
          // The expected type of every return statement in this method
          // is the method's return type.
          this.pt = tree.sym.erasure(types).getReturnType();
  
          checkDimension(tree.pos(), tree.sym.erasure(types));
          genMethod(tree, localEnv, false);
      }
  //where
          /** Generate code for a method.
           *  @param tree     The tree representing the method definition.
           *  @param env      The environment current for the method body.
           *  @param fatcode  A flag that indicates whether all jumps are
           *                  within 32K.  We first invoke this method under
           *                  the assumption that fatcode == false, i.e. all
           *                  jumps are within 32K.  If this fails, fatcode
           *                  is set to true and we try again.
           */
          void genMethod(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) {
              MethodSymbol meth = tree.sym;
              int extras = 0;
              // Count up extra parameters
              if (meth.isConstructor()) {
                  extras++;
                  if (meth.enclClass().isInner() &&
                      !meth.enclClass().isStatic()) {
                      extras++;
                  }
              } else if ((tree.mods.flags & STATIC) == 0) {
                  extras++;
              }
              //      System.err.println("Generating " + meth + " in " + meth.owner); //DEBUG
              if (Code.width(types.erasure(env.enclMethod.sym.type).getParameterTypes()) + extras >
                  ClassFile.MAX_PARAMETERS) {
                  log.error(tree.pos(), "limit.parameters");
                  nerrs++;
              }
  
              else if (tree.body != null) {
                  // Create a new code structure and initialize it.
                  int startpcCrt = initCode(tree, env, fatcode);
  
                  try {
                      genStat(tree.body, env);
                  } catch (CodeSizeOverflow e) {
                      // Failed due to code limit, try again with jsr/ret
                      startpcCrt = initCode(tree, env, fatcode);
                      genStat(tree.body, env);
                  }
  
                  if (code.state.stacksize != 0) {
                      log.error(tree.body.pos(), "stack.sim.error", tree);
                      throw new AssertionError();
                  }
  
                  // If last statement could complete normally, insert a
                  // return at the end.
                  if (code.isAlive()) {
                      code.statBegin(TreeInfo.endPos(tree.body));
                      if (env.enclMethod == null ||
                          env.enclMethod.sym.type.getReturnType().hasTag(VOID)) {
                          code.emitop0(return_);
                      } else {
                          // sometime dead code seems alive (4415991);
                          // generate a small loop instead
                          int startpc = code.entryPoint();
                          CondItem c = items.makeCondItem(goto_);
                          code.resolve(c.jumpTrue(), startpc);
                      }
                  }
                  if (genCrt)
                      code.crt.put(tree.body,
                                   CRT_BLOCK,
                                   startpcCrt,
                                   code.curCP());
  
                  code.endScopes(0);
  
                  // If we exceeded limits, panic
                  if (code.checkLimits(tree.pos(), log)) {
                      nerrs++;
                      return;
                  }
  
                  // If we generated short code but got a long jump, do it again
                  // with fatCode = true.
                  if (!fatcode && code.fatcode) genMethod(tree, env, true);
  
                  // Clean up
                  if(stackMap == StackMapFormat.JSR202) {
                      code.lastFrame = null;
                      code.frameBeforeLast = null;
                  }
  
                  // Compress exception table
                  code.compressCatchTable();
  
                  // Fill in type annotation positions for exception parameters
                  code.fillExceptionParameterPositions();
              }
          }
  
          private int initCode(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) {
              MethodSymbol meth = tree.sym;
  
              // Create a new code structure.
              meth.code = code = new Code(meth,
                                          fatcode,
                                          lineDebugInfo ? toplevel.lineMap : null,
                                          varDebugInfo,
                                          stackMap,
                                          debugCode,
                                          genCrt ? new CRTable(tree, env.toplevel.endPositions)
                                                 : null,
                                          syms,
                                          types,
                                          pool);
              items = new Items(pool, code, syms, types);
              if (code.debugCode) {
                  System.err.println(meth + " for body " + tree);
              }
  
              // If method is not static, create a new local variable address
              // for `this'.
              if ((tree.mods.flags & STATIC) == 0) {
                  Type selfType = meth.owner.type;
                  if (meth.isConstructor() && selfType != syms.objectType)
                      selfType = UninitializedType.uninitializedThis(selfType);
                  code.setDefined(
                          code.newLocal(
                              new VarSymbol(FINAL, names._this, selfType, meth.owner)));
              }
  
              // Mark all parameters as defined from the beginning of
              // the method.
              for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
                  checkDimension(l.head.pos(), l.head.sym.type);
                  code.setDefined(code.newLocal(l.head.sym));
              }
  
              // Get ready to generate code for method body.
              int startpcCrt = genCrt ? code.curCP() : 0;
              code.entryPoint();
  
              // Suppress initial stackmap
              code.pendingStackMap = false;
  
              return startpcCrt;
          }
  
      public void visitVarDef(JCVariableDecl tree) {
          VarSymbol v = tree.sym;
          code.newLocal(v);
          if (tree.init != null) {
              checkStringConstant(tree.init.pos(), v.getConstValue());
              if (v.getConstValue() == null || varDebugInfo) {
                  Assert.check(letExprDepth != 0 || code.state.stacksize == 0);
                  genExpr(tree.init, v.erasure(types)).load();
                  items.makeLocalItem(v).store();
                  Assert.check(letExprDepth != 0 || code.state.stacksize == 0);
              }
          }
          checkDimension(tree.pos(), v.type);
      }
  
      public void visitSkip(JCSkip tree) {
      }
  
      public void visitBlock(JCBlock tree) {
          int limit = code.nextreg;
          Env<GenContext> localEnv = env.dup(tree, new GenContext());
          genStats(tree.stats, localEnv);
          // End the scope of all block-local variables in variable info.
          if (!env.tree.hasTag(METHODDEF)) {
              code.statBegin(tree.endpos);
              code.endScopes(limit);
              code.pendingStatPos = Position.NOPOS;
          }
      }
  
      public void visitDoLoop(JCDoWhileLoop tree) {
          genLoop(tree, tree.body, tree.cond, List.nil(), false);
      }
  
      public void visitWhileLoop(JCWhileLoop tree) {
          genLoop(tree, tree.body, tree.cond, List.nil(), true);
      }
  
      public void visitForLoop(JCForLoop tree) {
          int limit = code.nextreg;
          genStats(tree.init, env);
          genLoop(tree, tree.body, tree.cond, tree.step, true);
          code.endScopes(limit);
      }
      //where
          /** Generate code for a loop.
           *  @param loop       The tree representing the loop.
           *  @param body       The loop's body.
           *  @param cond       The loop's controling condition.
           *  @param step       "Step" statements to be inserted at end of
           *                    each iteration.
           *  @param testFirst  True if the loop test belongs before the body.
           */
          private void genLoop(JCStatement loop,
                               JCStatement body,
                               JCExpression cond,
                               List<JCExpressionStatement> step,
                               boolean testFirst) {
              Env<GenContext> loopEnv = env.dup(loop, new GenContext());
              int startpc = code.entryPoint();
              if (testFirst) { //while or for loop
                  CondItem c;
                  if (cond != null) {
                      code.statBegin(cond.pos);
                      Assert.check(code.state.stacksize == 0);
                      c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER);
                  } else {
                      c = items.makeCondItem(goto_);
                  }
                  Chain loopDone = c.jumpFalse();
                  code.resolve(c.trueJumps);
                  Assert.check(code.state.stacksize == 0);
                  genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET);
                  code.resolve(loopEnv.info.cont);
                  genStats(step, loopEnv);
                  code.resolve(code.branch(goto_), startpc);
                  code.resolve(loopDone);
              } else {
                  genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET);
                  code.resolve(loopEnv.info.cont);
                  genStats(step, loopEnv);
                  if (code.isAlive()) {
                      CondItem c;
                      if (cond != null) {
                          code.statBegin(cond.pos);
                          Assert.check(code.state.stacksize == 0);
                          c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER);
                      } else {
                          c = items.makeCondItem(goto_);
                      }
                      code.resolve(c.jumpTrue(), startpc);
                      Assert.check(code.state.stacksize == 0);
                      code.resolve(c.falseJumps);
                  }
              }
              Chain exit = loopEnv.info.exit;
              if (exit != null) {
                  code.resolve(exit);
                  exit.state.defined.excludeFrom(code.nextreg);
              }
          }
  
      public void visitForeachLoop(JCEnhancedForLoop tree) {
          throw new AssertionError(); // should have been removed by Lower.
      }
  
      public void visitLabelled(JCLabeledStatement tree) {
          Env<GenContext> localEnv = env.dup(tree, new GenContext());
          genStat(tree.body, localEnv, CRT_STATEMENT);
          Chain exit = localEnv.info.exit;
          if (exit != null) {
              code.resolve(exit);
              exit.state.defined.excludeFrom(code.nextreg);
          }
      }
  
      public void visitSwitch(JCSwitch tree) {
          int limit = code.nextreg;
          Assert.check(!tree.selector.type.hasTag(CLASS));
          int startpcCrt = genCrt ? code.curCP() : 0;
          Assert.check(code.state.stacksize == 0);
          Item sel = genExpr(tree.selector, syms.intType);
          List<JCCase> cases = tree.cases;
          if (cases.isEmpty()) {
              // We are seeing:  switch <sel> {}
              sel.load().drop();
              if (genCrt)
                  code.crt.put(TreeInfo.skipParens(tree.selector),
                               CRT_FLOW_CONTROLLER, startpcCrt, code.curCP());
          } else {
              // We are seeing a nonempty switch.
              sel.load();
              if (genCrt)
                  code.crt.put(TreeInfo.skipParens(tree.selector),
                               CRT_FLOW_CONTROLLER, startpcCrt, code.curCP());
              Env<GenContext> switchEnv = env.dup(tree, new GenContext());
              switchEnv.info.isSwitch = true;
  
              // Compute number of labels and minimum and maximum label values.
              // For each case, store its label in an array.
              int lo = Integer.MAX_VALUE;  // minimum label.
              int hi = Integer.MIN_VALUE;  // maximum label.
              int nlabels = 0;               // number of labels.
  
              int[] labels = new int[cases.length()];  // the label array.
              int defaultIndex = -1;     // the index of the default clause.
  
              List<JCCase> l = cases;
              for (int i = 0; i < labels.length; i++) {
                  if (l.head.pat != null) {
                      int val = ((Number)l.head.pat.type.constValue()).intValue();
                      labels[i] = val;
                      if (val < lo) lo = val;
                      if (hi < val) hi = val;
                      nlabels++;
                  } else {
                      Assert.check(defaultIndex == -1);
                      defaultIndex = i;
                  }
                  l = l.tail;
              }
  
              // Determine whether to issue a tableswitch or a lookupswitch
              // instruction.
              long table_space_cost = 4 + ((long) hi - lo + 1); // words
              long table_time_cost = 3; // comparisons
              long lookup_space_cost = 3 + 2 * (long) nlabels;
              long lookup_time_cost = nlabels;
              int opcode =
                  nlabels > 0 &&
                  table_space_cost + 3 * table_time_cost <=
                  lookup_space_cost + 3 * lookup_time_cost
                  ?
                  tableswitch : lookupswitch;
  
              int startpc = code.curCP();    // the position of the selector operation
              code.emitop0(opcode);
              code.align(4);
              int tableBase = code.curCP();  // the start of the jump table
              int[] offsets = null;          // a table of offsets for a lookupswitch
              code.emit4(-1);                // leave space for default offset
              if (opcode == tableswitch) {
                  code.emit4(lo);            // minimum label
                  code.emit4(hi);            // maximum label
  