/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dasm; import com.android.dx.dex.code.CatchBuilder; import com.android.dx.dex.code.CatchHandlerList; import com.android.dx.dex.code.CatchTable; import com.android.dx.dex.code.CodeAddress; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.Type; import dasm.DAsm.LabelTableEntry; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Vector; /** * Constructor of (@link CatchTable) instances from table of labels and list of * catch blocks defined in method. */ public class DasmCatchBuilder implements CatchBuilder { /** * Represents catch block that was not processed yet. Holds "from" and "to" * labels as well as list of exceptions to catch. */ private class UnprocessedCatch { String from; String to; Hashtable type_branch = new Hashtable(); /** * Constructs an instance. * * @param exception * exception type * @param from * "from" label * @param to * "to" label * @param branch * "with" label */ UnprocessedCatch(String exception, String from, String to, String branch) { this.from = from; this.to = to; add(exception, branch); } /** * Adds new exception type and branch label to current "from-to" block * to allow to have code like try { // do something } catch(Exception1 * e1) { } catch(Exception2 e2) { } or in Dasm: Label1: ; do something * Labe2: ; .... Label3: ; .... Label4: ; .... .catch * java/lang/Exception from Label1 to Label2 using Label3 .catch * java/lang/Throwable from Label1 to Label2 using Label4 * * @param exception * exception type * @param branch * "with" label */ void add(String exception, String branch) { CstType type; if (exception.compareToIgnoreCase("all") == 0) type = CstType.OBJECT; else type = CstType.intern(Type.internClassName(exception)); String s = type_branch.get(type); if (s != null && s.compareToIgnoreCase(branch) != 0) throw new RuntimeException( "Bad .catch directive: same exception (" + exception + ") but different branch addresses (" + s + " and " + branch + ")"); type_branch.put(type, branch); } } private Vector unprocessed_catches = new Vector(); private Hashtable labels_table; /** * Constructs an instance. * * @param labels_table * holds list of labels defined in method being processed */ public DasmCatchBuilder(Hashtable labels_table) { this.labels_table = labels_table; } /** * Gets the set of catch types associated with this instance. */ public HashSet getCatchTypes() { int sz = unprocessed_catches.size(); HashSet result = new HashSet(sz); for (int i = 0; i < sz; i++) { Enumeration keys = unprocessed_catches.elementAt(i) .type_branch.keys(); while (keys.hasMoreElements()) { result.add(keys.nextElement().getClassType()); } } return result; } /** * Gets whether this instance has any catches at all. */ public boolean hasAnyCatches() { return unprocessed_catches.size() != 0; } /** * Adds new exception handler * * @param exception * type of exception to catch * @param start * "from" label * @param end * "to" label * @param branch * "with" label */ public void add(String exception, String start, String end, String branch) { int sz = unprocessed_catches.size(); for (int i = 0; i < sz; i++) { UnprocessedCatch uc = unprocessed_catches.elementAt(i); if (uc.from.compareToIgnoreCase(start) == 0) { if (uc.to.compareToIgnoreCase(end) != 0) throw new RuntimeException( "Bad .catch directive: two blocks have the same " + "start address (" + uc.from + ") and different end addresses (" + uc.to + " and " + end + ")"); uc.add(exception, branch); return; } } unprocessed_catches.add(new UnprocessedCatch(exception, start, end, branch)); } /** * Builds and returns the catch table for this instance. */ public CatchTable build() { int sz = unprocessed_catches.size(); CatchTable result = new CatchTable(sz); for (int i = 0; i < sz; i++) { UnprocessedCatch uc = unprocessed_catches.elementAt(i); LabelTableEntry lte = labels_table.get(uc.from); // get "from" address if (lte == null || lte.planted == false) throw new RuntimeException("Label " + uc.from + " not defined"); CodeAddress from = lte.code_address; // get "to" address lte = labels_table.get(uc.to); if (lte == null || lte.planted == false) throw new RuntimeException("Label " + uc.to + " not defined"); CodeAddress to = lte.code_address; // build handlers list CatchHandlerList chl = new CatchHandlerList(uc.type_branch.size()); Enumeration keys = uc.type_branch.keys(); int j = 0; CatchHandlerList.Entry catch_all = null; while (keys.hasMoreElements()) { CstType type = keys.nextElement(); String branch = uc.type_branch.get(type); lte = labels_table.get(branch); if (lte == null || lte.planted == false) throw new RuntimeException("Label " + branch + " not defined"); CatchHandlerList.Entry chle = new CatchHandlerList.Entry(type, lte.code_address.getAddress()); // catch_all shall be the last handler in the list if (type.equals(CstType.OBJECT)) { catch_all = chle; } else { chl.set(j, chle); j++; } } if (catch_all != null) chl.set(j, catch_all); chl.setImmutable(); CatchTable.Entry entry = new CatchTable.Entry(from.getAddress(), to .getAddress(), chl); result.set(i, entry); } return result; } }