/** * Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com> * * 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 brut.androlib.src; import brut.androlib.AndrolibException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jf.dexlib.Code.Analysis.RegisterType; import org.jf.dexlib.Code.Opcode; /** * @author Ryszard Wiśniewski <brut.alll@gmail.com> */ public class DebugInjector { public static void inject(ListIterator<String> it, StringBuilder out) throws AndrolibException { new DebugInjector(it, out).inject(); } private DebugInjector(ListIterator<String> it, StringBuilder out) { mIt = it; mOut = out; } private void inject() throws AndrolibException { String definition = nextAndAppend(); if ( definition.contains(" abstract ") || definition.contains(" native ") ) { nextAndAppend(); return; } injectParameters(definition); boolean end = false; while (!end) { end = step(); } } private void injectParameters(String definition) throws AndrolibException { int pos = definition.indexOf('('); if (pos == -1) { throw new AndrolibException(); } int pos2 = definition.indexOf(')', pos); if (pos2 == -1) { throw new AndrolibException(); } String params = definition.substring(pos + 1, pos2); int i = definition.contains(" static ") ? 0 : 1; int argc = TypeName.listFromInternalName(params).size() + i; while(i < argc) { mOut.append(".parameter \"p").append(i).append("\"\n"); i++; } } private boolean step() { String line = next(); if (line.length()==0) { return false; } switch (line.charAt(0)) { case '#': return processComment(line); case ':': append(line); return false; case '.': return processDirective(line); default: return processInstruction(line); } } private boolean processComment(String line) { if (mFirstInstruction) { return false; } Matcher m = REGISTER_INFO_PATTERN.matcher(line); while (m.find()) { String localName = m.group(1); String localType = null; switch (RegisterType.Category.valueOf(m.group(2))) { case Reference: case Null: case UninitRef: case UninitThis: localType = "Ljava/lang/Object;"; break; case Boolean: localType = "Z"; break; case Integer: case One: case Unknown: localType = "I"; break; case Uninit: case Conflicted: if (mInitializedRegisters.remove(localName)) { mOut.append(".end local ").append(localName) .append('\n'); } continue; case Short: case PosShort: localType = "S"; break; case Byte: case PosByte: localType = "B"; break; case Char: localType = "C"; break; case Float: localType = "F"; break; case LongHi: case LongLo: localType = "J"; break; case DoubleHi: case DoubleLo: localType = "D"; break; default: assert false; } mInitializedRegisters.add(localName); mOut.append(".local ").append(localName).append(", ") .append(localName).append(':').append(localType).append('\n'); } return false; } private boolean processDirective(String line) { String line2 = line.substring(1); if ( line2.startsWith("line ") || line2.equals("prologue") || line2.startsWith("parameter") || line2.startsWith("local ") || line2.startsWith("end local ") ) { return false; } append(line); if (line2.equals("end method")) { return true; } if ( line2.startsWith("annotation ") || line2.equals("sparse-switch") || line2.startsWith("packed-switch ") || line2.startsWith("array-data ") ) { while(true) { line2 = nextAndAppend(); if (line2.startsWith(".end ")) { break; } } } return false; } private boolean processInstruction(String line) { if (mFirstInstruction) { mOut.append(".prologue\n"); mFirstInstruction = false; } mOut.append(".line ").append(mIt.nextIndex()).append('\n') .append(line).append('\n'); return false; } private String next() { return mIt.next().trim(); } private String nextAndAppend() { String line = next(); append(line); return line; } private void append(String append) { mOut.append(append).append('\n'); } private final ListIterator<String> mIt; private final StringBuilder mOut; private boolean mFirstInstruction = true; private final Set<String> mInitializedRegisters = new HashSet<String>(); private static final Pattern REGISTER_INFO_PATTERN = Pattern.compile("((?:p|v)\\d+)=\\(([^)]+)\\);"); }