blob: 5ad98fe08ae43bd5c87dc076c3b8fff92ca81c53 [file] [log] [blame]
/*
* [The "BSD licence"]
* Copyright (c) 2011 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code.Analysis;
import org.jf.dexlib.*;
import org.jf.dexlib.Code.Format.Instruction22c;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.Util.AccessFlags;
import java.util.HashMap;
public class SyntheticAccessorResolver {
public static final int METHOD = 0;
public static final int GETTER = 1;
public static final int SETTER = 2;
private final DexFileClassMap classMap;
private final HashMap<MethodIdItem, AccessedMember> resolvedAccessors = new HashMap<MethodIdItem, AccessedMember>();
public SyntheticAccessorResolver(DexFile dexFile) {
classMap = new DexFileClassMap(dexFile);
}
public static boolean looksLikeSyntheticAccessor(MethodIdItem methodIdItem) {
return methodIdItem.getMethodName().getStringValue().startsWith("access$");
}
public AccessedMember getAccessedMember(MethodIdItem methodIdItem) {
AccessedMember accessedMember = resolvedAccessors.get(methodIdItem);
if (accessedMember != null) {
return accessedMember;
}
ClassDefItem classDefItem = classMap.getClassDefByType(methodIdItem.getContainingClass());
if (classDefItem == null) {
return null;
}
ClassDataItem classDataItem = classDefItem.getClassData();
if (classDataItem == null) {
return null;
}
ClassDataItem.EncodedMethod encodedMethod = classDataItem.findDirectMethodByMethodId(methodIdItem);
if (encodedMethod == null) {
return null;
}
//A synthetic accessor will be marked synthetic
if ((encodedMethod.accessFlags & AccessFlags.SYNTHETIC.getValue()) == 0) {
return null;
}
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
//TODO: add support for odexed formats
switch (instructions[0].opcode.format) {
case Format35c:
case Format3rc: {
//a synthetic method access should be either 2 or 3 instructions, depending on if the method returns
//anything or not
if (instructions.length < 2 || instructions.length > 3) {
return null;
}
InstructionWithReference instruction = (InstructionWithReference)instructions[0];
Item referencedItem = instruction.getReferencedItem();
if (!(referencedItem instanceof MethodIdItem)) {
return null;
}
MethodIdItem referencedMethodIdItem = (MethodIdItem)referencedItem;
accessedMember = new AccessedMember(METHOD, referencedMethodIdItem);
resolvedAccessors.put(methodIdItem, accessedMember);
return accessedMember;
}
case Format22c: {
//a synthetic field access should be exactly 2 instructions. The set/put, and then the return
if (instructions.length != 2) {
return null;
}
Instruction22c instruction = (Instruction22c)instructions[0];
Item referencedItem = instruction.getReferencedItem();
if (!(referencedItem instanceof FieldIdItem)) {
return null;
}
FieldIdItem referencedFieldIdItem = (FieldIdItem)referencedItem;
if (instruction.opcode.setsRegister() || instruction.opcode.setsWideRegister()) {
//If the instruction sets a register, that means it is a getter - it gets the field value and
//stores it in the register
accessedMember = new AccessedMember(GETTER, referencedFieldIdItem);
} else {
accessedMember = new AccessedMember(SETTER, referencedFieldIdItem);
}
resolvedAccessors.put(methodIdItem, accessedMember);
return accessedMember;
}
default:
return null;
}
}
public static class AccessedMember {
public final int accessedMemberType;
public final Item accessedMember;
public AccessedMember(int accessedMemberType, Item accessedMember) {
this.accessedMemberType = accessedMemberType;
this.accessedMember = accessedMember;
}
}
}