/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* 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 analyser.gui.actions.lookup;
import gui.actions.AbstractCanceableAction;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import analyser.gui.LineBuilder;
import analyser.gui.LineBuilderFormatter;
import analyser.gui.MainFrame;
import analyser.gui.Selection;
import analyser.gui.TextBuilder;
import analyser.gui.actions.ShowBytecodeAction;
import mereflect.BytecodeVisitor;
import mereflect.MEClass;
import mereflect.MEField;
import mereflect.MEMethod;
import mereflect.SimpleVisitor;
import mereflect.info.CiFieldRef;
import mereflect.info.CiNameAndType;
import mereflect.info.ClassInfo;
public class FindFieldAccessAction extends AbstractCanceableAction {
private static final long serialVersionUID = -4585625738006633388L;
protected static FindFieldAccessAction m_inst = null;
protected List<ResultEntry> result;
MEClass clazz;
MEField token;
ClassInfo[] cp;
MEMethod curMethod;
public static FindFieldAccessAction getInstance(MainFrame mainFrame) {
if (m_inst == null) {
m_inst = new FindFieldAccessAction("Find field accesses", null);
m_inst.setMainFrame(mainFrame);
}
return m_inst;
}
protected FindFieldAccessAction(String arg0, Icon arg1) {
super(arg0, arg1);
}
protected BytecodeVisitor getVisitor() {
return new SimpleVisitor() {
@Override
public void visitLocalFieldName(int pc, int bytecode, int len, int cpIndex) {
boolean setter = (bytecode == 181 /*putfield*/|| bytecode == 179 /*putstatic*/);
CiFieldRef ciFieldRef = (CiFieldRef) cp[cpIndex];
CiNameAndType ciNameType = (CiNameAndType) cp[ciFieldRef.getNameAndTypeIndex()];
if (token.getDescriptorIndex() == ciNameType.getDescriptorIndex() &&
token.getNameIndex() == ciNameType.getNameIndex()) {
result.add(new ResultEntry(curMethod, pc, setter));
}
}
@Override
public void visitNewBytecode(int pc, int bytecode) {
}
};
}
@Override
public void run(ActionEvent e) throws Throwable {
{
Object ref = Selection.getSelectedView();
if (ref == null || !(ref instanceof TextBuilder)) {
return;
}
TextBuilder tb = (TextBuilder) ref;
// END TODO
Object lineRef = tb.getLineBuilder().getReference(tb.getCurrentLine());
if (lineRef == null || !(lineRef instanceof MEField)) {
return;
}
result = new ArrayList<ResultEntry>();
token = (MEField) lineRef;
if (tb.getOwnerData() instanceof MEMethod) {
MEMethod method = (MEMethod) tb.getOwnerData();
clazz = method.getMEClass();
} else if (tb.getOwnerData() instanceof MEClass) {
clazz = (MEClass) tb.getOwnerData();
} else {
return;
}
cp = clazz.getConstantPool();
MEMethod[] meths = clazz.getMethods();
for (int i = 0; i < meths.length && isRunning(); i++) {
curMethod = meths[i];
curMethod.traverseBytecodes(getVisitor());
getMainFrame().actionReportWork(this, (100 * i) / meths.length);
}
if (isRunning()) {
getMainFrame().actionFinished(this);
showResult();
}
}
}
protected void showResult() {
LineBuilder lb = new LineBuilder();
if (result.size() > 0) {
for (int i = 0; i < result.size(); i++) {
ResultEntry e = result.get(i);
lb.newLine();
lb.append(e.setter ? "WR " : "RD ", e.setter ? 0x880000 : 0x008800);
LineBuilderFormatter.makeOutline(e.method, lb);
lb.append(" @ ", 0x000000);
lb.append(Integer.toHexString(e.pc), 0x000088);
Object[] data = { e.method, new Integer(e.pc) };
lb.setReferenceToCurrent(new LineBuilderFormatter.Link(
ShowBytecodeAction.getInstance((MainFrame) getMainFrame()),
data));
}
getMainFrame().showText("Field access search result: " + token, lb);
getMainFrame().setBottomInfo(result.size() + " access(es) found");
} else {
getMainFrame().setBottomInfo("No accesses found");
}
}
@Override
public void handleThrowable(Throwable t) {
t.printStackTrace();
getMainFrame().showError("Error resolving field accesses", t);
}
@Override
public String getWorkDescription() {
return "Resolving field accesses";
}
class ResultEntry {
MEMethod method;
int pc;
boolean setter;
public ResultEntry(MEMethod method, int pc, boolean setter) {
this.method = method;
this.pc = pc;
this.setter = setter;
}
}
}