View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.generic;
20  
21  import java.util.Arrays;
22  
23  /**
24   * SWITCH - Branch depending on int value, generates either LOOKUPSWITCH or TABLESWITCH instruction, depending on
25   * whether the match values (int[]) can be sorted with no gaps between the numbers.
26   */
27  public final class SWITCH implements CompoundInstruction {
28  
29      /**
30       * @return match is sorted in ascending order with no gap bigger than maxGap?
31       */
32      private static boolean matchIsOrdered(final int[] match, final int matchLength, final int maxGap) {
33          for (int i = 1; i < matchLength; i++) {
34              if (match[i] - match[i - 1] > maxGap) {
35                  return false;
36              }
37          }
38          return true;
39      }
40  
41      /**
42       * Sorts match and targets array with QuickSort.
43       */
44      private static void sort(final int l, final int r, final int[] match, final InstructionHandle[] targets) {
45          int i = l;
46          int j = r;
47          int h;
48          final int m = match[l + r >>> 1];
49          InstructionHandle h2;
50          do {
51              while (match[i] < m) {
52                  i++;
53              }
54              while (m < match[j]) {
55                  j--;
56              }
57              if (i <= j) {
58                  h = match[i];
59                  match[i] = match[j];
60                  match[j] = h; // Swap elements
61                  h2 = targets[i];
62                  targets[i] = targets[j];
63                  targets[j] = h2; // Swap instructions, too
64                  i++;
65                  j--;
66              }
67          } while (i <= j);
68          if (l < j) {
69              sort(l, j, match, targets);
70          }
71          if (i < r) {
72              sort(i, r, match, targets);
73          }
74      }
75  
76      private final Select instruction;
77  
78      public SWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle target) {
79          this(match, targets, target, 1);
80      }
81  
82      /**
83       * Template for switch() constructs. If the match array can be sorted in ascending order with gaps no larger than
84       * maxGap between the numbers, a TABLESWITCH instruction is generated, and a LOOKUPSWITCH otherwise. The former may be
85       * more efficient, but needs more space.
86       *
87       * Note, that the key array always will be sorted, though we leave the original arrays unaltered.
88       *
89       * @param match array of match values (case 2: ... case 7: ..., etc.)
90       * @param targets the instructions to be branched to for each case
91       * @param target the default target
92       * @param maxGap maximum gap that may between case branches
93       */
94      public SWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle target, final int maxGap) {
95          final int[] matchClone = match.clone();
96          final InstructionHandle[] targetsClone = targets.clone();
97          final int matchLength = match.length;
98          if (matchLength < 2) {
99              instruction = new TABLESWITCH(match, targets, target);
100         } else {
101             sort(0, matchLength - 1, matchClone, targetsClone);
102             if (matchIsOrdered(matchClone, matchLength, maxGap)) {
103                 final int maxSize = matchLength + matchLength * maxGap;
104                 final int[] mVec = new int[maxSize];
105                 final InstructionHandle[] tVec = new InstructionHandle[maxSize];
106                 int count = 1;
107                 mVec[0] = match[0];
108                 tVec[0] = targets[0];
109                 for (int i = 1; i < matchLength; i++) {
110                     final int prev = match[i - 1];
111                     final int gap = match[i] - prev;
112                     for (int j = 1; j < gap; j++) {
113                         mVec[count] = prev + j;
114                         tVec[count] = target;
115                         count++;
116                     }
117                     mVec[count] = match[i];
118                     tVec[count] = targets[i];
119                     count++;
120                 }
121                 instruction = new TABLESWITCH(Arrays.copyOf(mVec, count), Arrays.copyOf(tVec, count), target);
122             } else {
123                 instruction = new LOOKUPSWITCH(matchClone, targetsClone, target);
124             }
125         }
126     }
127 
128     public Instruction getInstruction() {
129         return instruction;
130     }
131 
132     @Override
133     public InstructionList getInstructionList() {
134         return new InstructionList(instruction);
135     }
136 }