1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dasm;
18 
19 import com.android.dx.dex.code.CatchBuilder;
20 import com.android.dx.dex.code.CatchHandlerList;
21 import com.android.dx.dex.code.CatchTable;
22 import com.android.dx.dex.code.CodeAddress;
23 import com.android.dx.rop.cst.CstType;
24 import com.android.dx.rop.type.Type;
25 
26 import dasm.DAsm.LabelTableEntry;
27 
28 import java.util.Enumeration;
29 import java.util.HashSet;
30 import java.util.Hashtable;
31 import java.util.Vector;
32 
33 /**
34  * Constructor of (@link CatchTable) instances from table of labels and list of
35  * catch blocks defined in method.
36  */
37 public class DasmCatchBuilder implements CatchBuilder {
38 
39     /**
40      * Represents catch block that was not processed yet. Holds "from" and "to"
41      * labels as well as list of exceptions to catch.
42      */
43     private class UnprocessedCatch {
44 
45         String from;
46         String to;
47         Hashtable<CstType, String> type_branch =
48                 new Hashtable<CstType, String>();
49 
50         /**
51          * Constructs an instance.
52          *
53          * @param exception
54          *            exception type
55          * @param from
56          *            "from" label
57          * @param to
58          *            "to" label
59          * @param branch
60          *            "with" label
61          */
UnprocessedCatch(String exception, String from, String to, String branch)62         UnprocessedCatch(String exception, String from, String to,
63                 String branch) {
64             this.from = from;
65             this.to = to;
66             add(exception, branch);
67         }
68 
69         /**
70          * Adds new exception type and branch label to current "from-to" block
71          * to allow to have code like try { // do something } catch(Exception1
72          * e1) { } catch(Exception2 e2) { } or in Dasm: Label1: ; do something
73          * Labe2: ; .... Label3: ; .... Label4: ; .... .catch
74          * java/lang/Exception from Label1 to Label2 using Label3 .catch
75          * java/lang/Throwable from Label1 to Label2 using Label4
76          *
77          * @param exception
78          *            exception type
79          * @param branch
80          *            "with" label
81          */
add(String exception, String branch)82         void add(String exception, String branch) {
83             CstType type;
84             if (exception.compareToIgnoreCase("all") == 0)
85                 type = CstType.OBJECT;
86             else
87                 type = CstType.intern(Type.internClassName(exception));
88 
89             String s = type_branch.get(type);
90             if (s != null && s.compareToIgnoreCase(branch) != 0)
91                 throw new RuntimeException(
92                         "Bad .catch directive: same exception (" + exception
93                                 + ") but different branch addresses (" + s
94                                 + " and " + branch + ")");
95             type_branch.put(type, branch);
96         }
97     }
98 
99     private Vector<UnprocessedCatch> unprocessed_catches =
100             new Vector<UnprocessedCatch>();
101 
102     private Hashtable<String, LabelTableEntry> labels_table;
103 
104     /**
105      * Constructs an instance.
106      *
107      * @param labels_table
108      *            holds list of labels defined in method being processed
109      */
DasmCatchBuilder(Hashtable<String, LabelTableEntry> labels_table)110     public DasmCatchBuilder(Hashtable<String, LabelTableEntry> labels_table) {
111         this.labels_table = labels_table;
112     }
113 
114     /**
115      * Gets the set of catch types associated with this instance.
116      */
getCatchTypes()117     public HashSet<Type> getCatchTypes() {
118         int sz = unprocessed_catches.size();
119         HashSet<Type> result = new HashSet<Type>(sz);
120         for (int i = 0; i < sz; i++) {
121             Enumeration<CstType> keys = unprocessed_catches.elementAt(i)
122                     .type_branch.keys();
123             while (keys.hasMoreElements()) {
124                 result.add(keys.nextElement().getClassType());
125             }
126         }
127         return result;
128     }
129 
130     /**
131      * Gets whether this instance has any catches at all.
132      */
hasAnyCatches()133     public boolean hasAnyCatches() {
134         return unprocessed_catches.size() != 0;
135     }
136 
137     /**
138      * Adds new exception handler
139      *
140      * @param exception
141      *            type of exception to catch
142      * @param start
143      *            "from" label
144      * @param end
145      *            "to" label
146      * @param branch
147      *            "with" label
148      */
add(String exception, String start, String end, String branch)149     public void add(String exception, String start, String end, String branch) {
150         int sz = unprocessed_catches.size();
151         for (int i = 0; i < sz; i++) {
152             UnprocessedCatch uc = unprocessed_catches.elementAt(i);
153             if (uc.from.compareToIgnoreCase(start) == 0) {
154                 if (uc.to.compareToIgnoreCase(end) != 0)
155                     throw new RuntimeException(
156                             "Bad .catch directive: two blocks have the same "
157                                     + "start address ("
158                                     + uc.from
159                                     + ") and different end addresses (" + uc.to
160                                     + " and " + end + ")");
161                 uc.add(exception, branch);
162                 return;
163             }
164         }
165 
166         unprocessed_catches.add(new UnprocessedCatch(exception, start, end,
167                 branch));
168     }
169 
170     /**
171      * Builds and returns the catch table for this instance.
172      */
build()173     public CatchTable build() {
174         int sz = unprocessed_catches.size();
175         CatchTable result = new CatchTable(sz);
176         for (int i = 0; i < sz; i++) {
177             UnprocessedCatch uc = unprocessed_catches.elementAt(i);
178             LabelTableEntry lte = labels_table.get(uc.from);
179             // get "from" address
180             if (lte == null || lte.planted == false)
181                 throw new RuntimeException("Label " + uc.from + " not defined");
182             CodeAddress from = lte.code_address;
183             // get "to" address
184             lte = labels_table.get(uc.to);
185             if (lte == null || lte.planted == false)
186                 throw new RuntimeException("Label " + uc.to + " not defined");
187             CodeAddress to = lte.code_address;
188 
189             // build handlers list
190             CatchHandlerList chl = new CatchHandlerList(uc.type_branch.size());
191             Enumeration<CstType> keys = uc.type_branch.keys();
192             int j = 0;
193             CatchHandlerList.Entry catch_all = null;
194             while (keys.hasMoreElements()) {
195                 CstType type = keys.nextElement();
196                 String branch = uc.type_branch.get(type);
197                 lte = labels_table.get(branch);
198                 if (lte == null || lte.planted == false)
199                     throw new RuntimeException("Label " + branch
200                             + " not defined");
201                 CatchHandlerList.Entry chle = new CatchHandlerList.Entry(type,
202                         lte.code_address.getAddress());
203                 // catch_all shall be the last handler in the list
204                 if (type.equals(CstType.OBJECT)) {
205                     catch_all = chle;
206                 } else {
207                     chl.set(j, chle);
208                     j++;
209                 }
210             }
211             if (catch_all != null) chl.set(j, catch_all);
212             chl.setImmutable();
213 
214             CatchTable.Entry entry = new CatchTable.Entry(from.getAddress(), to
215                     .getAddress(), chl);
216             result.set(i, entry);
217         }
218         return result;
219     }
220 }
221