1 /* 2 * Copyright 2012, Google LLC 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google LLC nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 package com.android.tools.smali.dexlib2.util; 32 33 import com.android.tools.smali.dexlib2.iface.ClassDef; 34 import com.android.tools.smali.dexlib2.iface.Method; 35 import com.android.tools.smali.dexlib2.iface.MethodImplementation; 36 import com.android.tools.smali.dexlib2.AccessFlags; 37 import com.android.tools.smali.dexlib2.Opcodes; 38 import com.android.tools.smali.dexlib2.iface.instruction.Instruction; 39 import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction; 40 import com.android.tools.smali.dexlib2.iface.reference.MethodReference; 41 import com.android.tools.smali.dexlib2.iface.reference.Reference; 42 import com.android.tools.smali.util.IteratorUtils; 43 44 import javax.annotation.Nonnull; 45 import javax.annotation.Nullable; 46 47 import java.util.Collections; 48 import java.util.HashMap; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.concurrent.ConcurrentHashMap; 52 53 public class SyntheticAccessorResolver { 54 public static final int METHOD = 0; 55 public static final int GETTER = 1; 56 public static final int SETTER = 2; 57 public static final int POSTFIX_INCREMENT = 3; 58 public static final int PREFIX_INCREMENT = 4; 59 public static final int POSTFIX_DECREMENT = 5; 60 public static final int PREFIX_DECREMENT = 6; 61 public static final int ADD_ASSIGNMENT = 7; 62 public static final int SUB_ASSIGNMENT = 8; 63 public static final int MUL_ASSIGNMENT = 9; 64 public static final int DIV_ASSIGNMENT = 10; 65 public static final int REM_ASSIGNMENT = 11; 66 public static final int AND_ASSIGNMENT = 12; 67 public static final int OR_ASSIGNMENT = 13; 68 public static final int XOR_ASSIGNMENT = 14; 69 public static final int SHL_ASSIGNMENT = 15; 70 public static final int SHR_ASSIGNMENT = 16; 71 public static final int USHR_ASSIGNMENT = 17; 72 73 private final SyntheticAccessorFSM syntheticAccessorFSM; 74 private final Map<String, ClassDef> classDefMap; 75 private final Map<MethodReference, AccessedMember> resolvedAccessors = new ConcurrentHashMap<>(); 76 SyntheticAccessorResolver(@onnull Opcodes opcodes, @Nonnull Iterable<? extends ClassDef> classDefs)77 public SyntheticAccessorResolver(@Nonnull Opcodes opcodes, @Nonnull Iterable<? extends ClassDef> classDefs) { 78 this.syntheticAccessorFSM = new SyntheticAccessorFSM(opcodes); 79 HashMap<String, ClassDef> classDefMap = new HashMap<>(); 80 81 for (ClassDef classDef: classDefs) { 82 classDefMap.put(classDef.getType(), classDef); 83 } 84 85 this.classDefMap = Collections.unmodifiableMap(classDefMap); 86 } 87 looksLikeSyntheticAccessor(String methodName)88 public static boolean looksLikeSyntheticAccessor(String methodName) { 89 return methodName.startsWith("access$"); 90 } 91 92 @Nullable getAccessedMember(@onnull MethodReference methodReference)93 public AccessedMember getAccessedMember(@Nonnull MethodReference methodReference) { 94 AccessedMember accessedMember = resolvedAccessors.get(methodReference); 95 if (accessedMember != null) { 96 return accessedMember; 97 } 98 99 String type = methodReference.getDefiningClass(); 100 ClassDef classDef = classDefMap.get(type); 101 if (classDef == null) { 102 return null; 103 } 104 105 Method matchedMethod = null; 106 MethodImplementation matchedMethodImpl = null; 107 for (Method method: classDef.getMethods()) { 108 MethodImplementation methodImpl = method.getImplementation(); 109 if (methodImpl != null) { 110 if (methodReferenceEquals(method, methodReference)) { 111 matchedMethod = method; 112 matchedMethodImpl = methodImpl; 113 break; 114 } 115 } 116 } 117 118 if (matchedMethod == null) { 119 return null; 120 } 121 122 //A synthetic accessor will be marked synthetic 123 if (!AccessFlags.SYNTHETIC.isSet(matchedMethod.getAccessFlags())) { 124 return null; 125 } 126 127 List<Instruction> instructions = Collections.unmodifiableList( 128 IteratorUtils.toList(matchedMethodImpl.getInstructions())); 129 130 131 int accessType = syntheticAccessorFSM.test(instructions); 132 133 if (accessType >= 0) { 134 AccessedMember member = 135 new AccessedMember(accessType, ((ReferenceInstruction)instructions.get(0)).getReference()); 136 resolvedAccessors.put(methodReference, member); 137 return member; 138 } 139 return null; 140 } 141 142 public static class AccessedMember { 143 public final int accessedMemberType; 144 @Nonnull public final Reference accessedMember; 145 AccessedMember(int accessedMemberType, @Nonnull Reference accessedMember)146 public AccessedMember(int accessedMemberType, @Nonnull Reference accessedMember) { 147 this.accessedMemberType = accessedMemberType; 148 this.accessedMember = accessedMember; 149 } 150 } 151 methodReferenceEquals(@onnull MethodReference ref1, @Nonnull MethodReference ref2)152 private static boolean methodReferenceEquals(@Nonnull MethodReference ref1, @Nonnull MethodReference ref2) { 153 // we already know the containing class matches 154 return ref1.getName().equals(ref2.getName()) && 155 ref1.getReturnType().equals(ref2.getReturnType()) && 156 ref1.getParameterTypes().equals(ref2.getParameterTypes()); 157 } 158 } 159