blob: 0ac38ecf7593be8a7cc7b4c84322f5c2716000e6 [file] [log] [blame]
/*
* 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<CstType, String> type_branch =
new Hashtable<CstType, String>();
/**
* 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<UnprocessedCatch> unprocessed_catches =
new Vector<UnprocessedCatch>();
private Hashtable<String, LabelTableEntry> labels_table;
/**
* Constructs an instance.
*
* @param labels_table
* holds list of labels defined in method being processed
*/
public DasmCatchBuilder(Hashtable<String, LabelTableEntry> labels_table) {
this.labels_table = labels_table;
}
/**
* Gets the set of catch types associated with this instance.
*/
public HashSet<Type> getCatchTypes() {
int sz = unprocessed_catches.size();
HashSet<Type> result = new HashSet<Type>(sz);
for (int i = 0; i < sz; i++) {
Enumeration<CstType> 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<CstType> 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;
}
}