/**
   *
   *
   * <h3>Generic-switch</h3>
   *
   * <pre>
   * switch (v) {
   * case key1: ...
   * case key2: ...
   * }
   *
   * var $v = v;
   * if (strictEquals($v, key1)) goto L1
   * if (strictEquals($v, key2)) goto L2
   * goTo (default | break)
   * L1: ...
   * L2: ...
   * </pre>
   *
   * @param clauses the switch clauses
   * @param labels the labels for each switch clause
   * @param defaultClause the label for the default clause
   * @param lblExit the exit label
   * @param switchValue the variable which holds the switch value
   * @param mv the statement visitor
   */
  private void emitGenericSwitch(
      List<SwitchClause> clauses,
      Jump[] labels,
      Jump defaultClause,
      Jump lblExit,
      Variable<?> switchValue,
      StatementVisitor mv) {
    assert switchValue.getType().equals(Types.Object);
    Jump switchDefault = defaultClause != null ? defaultClause : lblExit;

    int index = 0;
    for (SwitchClause switchClause : clauses) {
      Jump caseLabel = labels[index++];
      Expression expr = switchClause.getExpression();
      if (expr != null) {
        mv.load(switchValue);
        // 13.11.10 Runtime Semantics: CaseSelectorEvaluation
        expressionBoxed(expr, mv);
        invokeDynamicOperator(BinaryExpression.Operator.SHEQ, mv);
        mv.ifne(caseLabel);
      }
    }

    mv.goTo(switchDefault);
  }
 private static boolean hasDefaultClause(SwitchStatement node) {
   for (SwitchClause switchClause : node.getClauses()) {
     if (switchClause.isDefaultClause()) {
       return true;
     }
   }
   return false;
 }
 private static boolean isStringSwitch(SwitchStatement node) {
   for (SwitchClause switchClause : node.getClauses()) {
     Expression expr = switchClause.getExpression();
     if (expr != null && !(expr instanceof StringLiteral)) {
       return false;
     }
   }
   return true;
 }
 private static boolean isCharSwitch(SwitchStatement node) {
   for (SwitchClause switchClause : node.getClauses()) {
     Expression expr = switchClause.getExpression();
     if (expr != null) {
       if (expr instanceof StringLiteral && ((StringLiteral) expr).getValue().length() == 1) {
         continue;
       }
       return false;
     }
   }
   return true;
 }
 private static boolean isIntSwitch(SwitchStatement node) {
   for (SwitchClause switchClause : node.getClauses()) {
     Expression expr = switchClause.getExpression();
     if (expr != null) {
       if (expr instanceof NumericLiteral && ((NumericLiteral) expr).isInt()) {
         continue;
       }
       if (expr instanceof UnaryExpression
           && ((UnaryExpression) expr).getOperator() == UnaryExpression.Operator.NEG
           && ((UnaryExpression) expr).getOperand() instanceof NumericLiteral
           && ((NumericLiteral) ((UnaryExpression) expr).getOperand()).isInt()
           && ((NumericLiteral) ((UnaryExpression) expr).getOperand()).intValue() != 0) {
         continue;
       }
       return false;
     }
   }
   return true;
 }
  /**
   * 13.12.9 Runtime Semantics: CaseBlockEvaluation
   *
   * @param node the switch statement
   * @param type the switch statement type
   * @param lblExit the exit label
   * @param switchValue the variable which holds the switch value
   * @param mv the statement visitor
   * @return the completion value
   */
  private Completion CaseBlockEvaluation(
      SwitchStatement node,
      SwitchType type,
      Jump lblExit,
      Variable<?> switchValue,
      StatementVisitor mv) {
    List<SwitchClause> clauses = node.getClauses();
    Jump lblDefault = null;
    Jump[] labels = new Jump[clauses.size()];
    for (int i = 0, size = clauses.size(); i < size; ++i) {
      labels[i] = new Jump();
      if (clauses.get(i).isDefaultClause()) {
        assert lblDefault == null;
        lblDefault = labels[i];
      }
    }

    if (type == SwitchType.Int) {
      emitIntSwitch(clauses, labels, lblDefault, lblExit, switchValue, mv);
    } else if (type == SwitchType.Char) {
      emitCharSwitch(clauses, labels, lblDefault, lblExit, switchValue, mv);
    } else if (type == SwitchType.String) {
      emitStringSwitch(clauses, labels, lblDefault, lblExit, switchValue, mv);
    } else if (type == SwitchType.Generic) {
      emitGenericSwitch(clauses, labels, lblDefault, lblExit, switchValue, mv);
    } else {
      assert type == SwitchType.Default;
      assert switchValue == null;
      // Directly jump to default clause; since switch clauses before default clause are not
      // emitted, jump instruction can be elided as well, so we directly fall into the default
      // clause.
    }

    Completion result = Completion.Normal, lastResult = Completion.Normal;
    if (type == SwitchType.Default) {
      Iterator<SwitchClause> iter = clauses.iterator();
      // skip leading clauses until default clause found
      while (iter.hasNext()) {
        SwitchClause switchClause = iter.next();
        if (switchClause.isDefaultClause()) {
          lastResult = switchClause.accept(this, mv);
          break;
        }
      }
      // handle clauses following default clause until abrupt completion
      while (iter.hasNext() && !lastResult.isAbrupt()) {
        lastResult = iter.next().accept(this, mv);
      }
      result = lastResult;
    } else {
      int index = 0;
      for (SwitchClause switchClause : clauses) {
        Jump caseLabel = labels[index++];
        if (caseLabel != null) {
          mv.mark(caseLabel);
        } else if (lastResult.isAbrupt()) {
          // Ignore unreachable targets
          continue;
        }
        Completion innerResult = switchClause.accept(this, mv);
        if (innerResult.isAbrupt()) {
          // not fall-thru
          result = result.isAbrupt() ? result.select(innerResult) : innerResult;
        }
        lastResult = innerResult;
      }
    }
    return result.normal(lblDefault == null || !lastResult.isAbrupt());
  }
 /** 13.12.11 Runtime Semantics: Evaluation */
 @Override
 public Completion visit(SwitchClause node, StatementVisitor mv) {
   return codegen.statements(node.getStatements(), mv);
 }