Add support for custom inline tables

This commit is contained in:
Ben Gruver 2013-04-10 21:14:46 -07:00
parent a55990c876
commit 5fa302678c
9 changed files with 312 additions and 43 deletions

View File

@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import org.jf.baksmali.Adaptors.ClassDefinition; import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.dexlib2.analysis.ClassPath; import org.jf.dexlib2.analysis.ClassPath;
import org.jf.dexlib2.analysis.CustomInlineMethodResolver;
import org.jf.dexlib2.dexbacked.DexBackedOdexFile; import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.iface.DexFile;
@ -87,10 +88,9 @@ public class baksmali {
options.classPath = ClassPath.fromClassPath(Arrays.asList(classPathDirs), options.classPath = ClassPath.fromClassPath(Arrays.asList(classPathDirs),
Iterables.concat(bootClassPaths, extraBootClassPaths), dexFile); Iterables.concat(bootClassPaths, extraBootClassPaths), dexFile);
// TODO: uncomment if (inlineTable != null) {
/*if (inlineTable != null) { options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(inlineTable));
inlineResolver = new CustomInlineMethodResolver(inlineTable); }
}*/
} catch (Exception ex) { } catch (Exception ex) {
System.err.println("\n\nError occured while loading boot class path files. Aborting."); System.err.println("\n\nError occured while loading boot class path files. Aborting.");
ex.printStackTrace(System.err); ex.printStackTrace(System.err);

View File

@ -70,7 +70,7 @@ public class ClassProto implements TypeProto {
@Nonnull @Override public String getType() { return type; } @Nonnull @Override public String getType() { return type; }
@Nonnull @Nonnull
protected ClassDef getClassDef() { public ClassDef getClassDef() {
if (classDef == null) { if (classDef == null) {
classDef = classPath.getClassDef(type); classDef = classPath.getClassDef(type);
} }

View File

@ -0,0 +1,138 @@
/*
* Copyright 2013, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.dexlib2.analysis;
import com.google.common.io.Files;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.instruction.InlineIndexInstruction;
import org.jf.dexlib2.immutable.ImmutableMethod;
import org.jf.dexlib2.immutable.ImmutableMethodParameter;
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
import org.jf.dexlib2.immutable.util.ParamUtil;
import javax.annotation.Nonnull;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CustomInlineMethodResolver extends InlineMethodResolver {
@Nonnull private final ClassPath classPath;
@Nonnull private final Method[] inlineMethods;
public CustomInlineMethodResolver(@Nonnull ClassPath classPath, @Nonnull String inlineTable) {
this.classPath = classPath;
StringReader reader = new StringReader(inlineTable);
List<String> lines = new ArrayList<String>();
BufferedReader br = new BufferedReader(reader);
try {
String line = br.readLine();
while (line != null) {
if (line.length() > 0) {
lines.add(line);
}
line = br.readLine();
}
} catch (IOException ex) {
throw new RuntimeException("Error while parsing inline table", ex);
}
inlineMethods = new Method[lines.size()];
for (int i=0; i<inlineMethods.length; i++) {
inlineMethods[i] = parseAndResolveInlineMethod(lines.get(i));
}
}
public CustomInlineMethodResolver(@Nonnull ClassPath classPath, @Nonnull File inlineTable) throws IOException {
this(classPath, Files.toString(inlineTable, Charset.forName("UTF-8")));
}
@Override
@Nonnull
public Method resolveExecuteInline(@Nonnull AnalyzedInstruction analyzedInstruction) {
InlineIndexInstruction instruction = (InlineIndexInstruction)analyzedInstruction.instruction;
int methodIndex = instruction.getInlineIndex();
if (methodIndex < 0 || methodIndex >= inlineMethods.length) {
throw new RuntimeException("Invalid method index: " + methodIndex);
}
return inlineMethods[methodIndex];
}
private static final Pattern longMethodPattern = Pattern.compile("(L[^;]+;)->([^(]+)\\(([^)]*)\\)(.+)");
@Nonnull
private Method parseAndResolveInlineMethod(@Nonnull String inlineMethod) {
Matcher m = longMethodPattern.matcher(inlineMethod);
if (!m.matches()) {
assert false;
throw new RuntimeException("Invalid method descriptor: " + inlineMethod);
}
String className = m.group(1);
String methodName = m.group(2);
Iterable<ImmutableMethodParameter> methodParams = ParamUtil.parseParamString(m.group(3));
String methodRet = m.group(4);
ImmutableMethodReference methodRef = new ImmutableMethodReference(className, methodName, methodParams,
methodRet);
int accessFlags = 0;
boolean resolved = false;
TypeProto typeProto = classPath.getClass(className);
if (typeProto instanceof ClassProto) {
ClassDef classDef = ((ClassProto)typeProto).getClassDef();
for (Method method: classDef.getMethods()) {
if (method.equals(methodRef)) {
resolved = true;
accessFlags = method.getAccessFlags();
break;
}
}
}
if (!resolved) {
throw new RuntimeException("Cannot resolve inline method: " + inlineMethod);
}
return new ImmutableMethod(className, methodName, methodParams, methodRet, accessFlags, null, null);
}
}

View File

@ -46,7 +46,7 @@ public abstract class InlineMethodResolver {
// We can't use, e.g. AccessFlags.STATIC.value, because we need them to be a constant in order to use them as cases // We can't use, e.g. AccessFlags.STATIC.value, because we need them to be a constant in order to use them as cases
// in switch statements // in switch statements
public static final int STATIC = 0x8; // AccessFlags.STATIC.value; public static final int STATIC = 0x8; // AccessFlags.STATIC.value;
public static final int VIRTUAL = 0x1; // AccessFlags.PRIVATE.value; public static final int VIRTUAL = 0x1; // AccessFlags.PUBLIC.value;
public static final int DIRECT = 0x2; // AccessFlags.PRIVATE.value; public static final int DIRECT = 0x2; // AccessFlags.PRIVATE.value;
@Nonnull @Nonnull
@ -116,7 +116,6 @@ public abstract class InlineMethodResolver {
private final Method fastIndexOfMethod; private final Method fastIndexOfMethod;
private final Method isEmptyMethod; private final Method isEmptyMethod;
public InlineMethodResolver_version36() { public InlineMethodResolver_version36() {
//The 5th and 6th entries differ between froyo and gingerbread. We have to look at the parameters being //The 5th and 6th entries differ between froyo and gingerbread. We have to look at the parameters being
//passed to distinguish between them. //passed to distinguish between them.

View File

@ -31,7 +31,10 @@
package org.jf.dexlib2.analysis; package org.jf.dexlib2.analysis;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.Opcode; import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.iface.*; import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.instruction.*; import org.jf.dexlib2.iface.instruction.*;
@ -45,7 +48,6 @@ import org.jf.dexlib2.util.MethodUtil;
import org.jf.dexlib2.util.ReferenceUtil; import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.dexlib2.util.TypeUtils; import org.jf.dexlib2.util.TypeUtils;
import org.jf.util.BitSetUtils; import org.jf.util.BitSetUtils;
import org.jf.util.ExceptionWithContext;
import org.jf.util.SparseArray; import org.jf.util.SparseArray;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -298,6 +300,17 @@ public class MethodAnalyzer {
return analyzedInstructions.getValues(); return analyzedInstructions.getValues();
} }
public List<Instruction> getInstructions() {
return Lists.transform(analyzedInstructions.getValues(), new Function<AnalyzedInstruction, Instruction>() {
@Nullable @Override public Instruction apply(@Nullable AnalyzedInstruction input) {
if (input == null) {
return null;
}
return input.instruction;
}
});
}
@Nullable @Nullable
public AnalysisException getAnalysisException() { public AnalysisException getAnalysisException() {
return analysisException; return analysisException;
@ -1406,19 +1419,13 @@ public class MethodAnalyzer {
Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction); Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction);
Opcode deodexedOpcode; Opcode deodexedOpcode;
switch (resolvedMethod.getAccessFlags()) { int acccessFlags = resolvedMethod.getAccessFlags();
case InlineMethodResolver.DIRECT: if (AccessFlags.STATIC.isSet(acccessFlags)) {
deodexedOpcode = Opcode.INVOKE_DIRECT;
break;
case InlineMethodResolver.STATIC:
deodexedOpcode = Opcode.INVOKE_STATIC; deodexedOpcode = Opcode.INVOKE_STATIC;
break; } else if (AccessFlags.PRIVATE.isSet(acccessFlags)) {
case InlineMethodResolver.VIRTUAL: deodexedOpcode = Opcode.INVOKE_DIRECT;
} else {
deodexedOpcode = Opcode.INVOKE_VIRTUAL; deodexedOpcode = Opcode.INVOKE_VIRTUAL;
break;
default:
throw new ExceptionWithContext("Unexpected access flags on resolved inline method: %d, %s",
resolvedMethod.getAccessFlags(), ReferenceUtil.getReferenceString(resolvedMethod));
} }
Instruction35c deodexedInstruction = new ImmutableInstruction35c(deodexedOpcode, instruction.getRegisterCount(), Instruction35c deodexedInstruction = new ImmutableInstruction35c(deodexedOpcode, instruction.getRegisterCount(),
@ -1437,19 +1444,14 @@ public class MethodAnalyzer {
Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction; Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction;
Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction); Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction);
Opcode deodexedOpcode = null; Opcode deodexedOpcode;
switch (resolvedMethod.getAccessFlags()) { int acccessFlags = resolvedMethod.getAccessFlags();
case InlineMethodResolver.DIRECT: if (AccessFlags.STATIC.isSet(acccessFlags)) {
deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE;
break;
case InlineMethodResolver.STATIC:
deodexedOpcode = Opcode.INVOKE_STATIC_RANGE; deodexedOpcode = Opcode.INVOKE_STATIC_RANGE;
break; } else if (AccessFlags.PRIVATE.isSet(acccessFlags)) {
case InlineMethodResolver.VIRTUAL: deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE;
} else {
deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE; deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE;
break;
default:
assert false;
} }
Instruction3rc deodexedInstruction = new ImmutableInstruction3rc(deodexedOpcode, instruction.getRegisterCount(), Instruction3rc deodexedInstruction = new ImmutableInstruction3rc(deodexedOpcode, instruction.getRegisterCount(),

View File

@ -45,7 +45,6 @@ import org.jf.util.ImmutableUtils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List;
import java.util.Set; import java.util.Set;
public class ImmutableMethod extends BaseMethodReference implements Method { public class ImmutableMethod extends BaseMethodReference implements Method {
@ -59,7 +58,7 @@ public class ImmutableMethod extends BaseMethodReference implements Method {
public ImmutableMethod(@Nonnull String definingClass, public ImmutableMethod(@Nonnull String definingClass,
@Nonnull String name, @Nonnull String name,
@Nullable List<? extends MethodParameter> parameters, @Nullable Iterable<? extends MethodParameter> parameters,
@Nonnull String returnType, @Nonnull String returnType,
int accessFlags, int accessFlags,
@Nullable Set<? extends Annotation> annotations, @Nullable Set<? extends Annotation> annotations,

View File

@ -39,7 +39,6 @@ import org.jf.util.ImmutableUtils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List;
public class ImmutableMethodReference extends BaseMethodReference implements ImmutableReference { public class ImmutableMethodReference extends BaseMethodReference implements ImmutableReference {
@Nonnull protected final String definingClass; @Nonnull protected final String definingClass;
@ -49,11 +48,11 @@ public class ImmutableMethodReference extends BaseMethodReference implements Imm
public ImmutableMethodReference(@Nonnull String definingClass, public ImmutableMethodReference(@Nonnull String definingClass,
@Nonnull String name, @Nonnull String name,
@Nullable List<String> parameters, @Nullable Iterable<? extends CharSequence> parameters,
@Nonnull String returnType) { @Nonnull String returnType) {
this.definingClass = definingClass; this.definingClass = definingClass;
this.name = name; this.name = name;
this.parameters = ImmutableList.copyOf(parameters); this.parameters = CharSequenceConverter.immutableStringList(parameters);
this.returnType = returnType; this.returnType = returnType;
} }
@ -75,7 +74,7 @@ public class ImmutableMethodReference extends BaseMethodReference implements Imm
return new ImmutableMethodReference( return new ImmutableMethodReference(
methodReference.getDefiningClass(), methodReference.getDefiningClass(),
methodReference.getName(), methodReference.getName(),
CharSequenceConverter.immutableStringList(methodReference.getParameterTypes()), methodReference.getParameterTypes(),
methodReference.getReturnType()); methodReference.getReturnType());
} }

View File

@ -36,15 +36,14 @@ import org.jf.util.ImmutableConverter;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List;
public final class CharSequenceConverter { public final class CharSequenceConverter {
private CharSequenceConverter() { private CharSequenceConverter() {
} }
@Nonnull @Nonnull
public static ImmutableList<String> immutableStringList(@Nullable List<? extends CharSequence> list) { public static ImmutableList<String> immutableStringList(@Nullable Iterable<? extends CharSequence> iterable) {
return CONVERTER.toList(list); return CONVERTER.toList(iterable);
} }
private static final ImmutableConverter<String, CharSequence> CONVERTER = private static final ImmutableConverter<String, CharSequence> CONVERTER =

View File

@ -0,0 +1,133 @@
/*
* Copyright 2013, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.dexlib2.analysis;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.DexFile;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.formats.Instruction35c;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.immutable.ImmutableClassDef;
import org.jf.dexlib2.immutable.ImmutableDexFile;
import org.jf.dexlib2.immutable.ImmutableMethod;
import org.jf.dexlib2.immutable.ImmutableMethodImplementation;
import org.jf.dexlib2.immutable.instruction.ImmutableInstruction;
import org.jf.dexlib2.immutable.instruction.ImmutableInstruction10x;
import org.jf.dexlib2.immutable.instruction.ImmutableInstruction35mi;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
public class CustomMethodInlineTableTest {
@Test
public void testCustomMethodInlineTable_Virtual() {
List<ImmutableInstruction> instructions = Lists.newArrayList(
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
new ImmutableInstruction10x(Opcode.RETURN_VOID));
ImmutableMethodImplementation methodImpl = new ImmutableMethodImplementation(1, instructions, null, null);
ImmutableMethod method = new ImmutableMethod("Lblah;", "blah", null, "V", AccessFlags.PUBLIC.getValue(), null,
methodImpl);
ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
null, null, null, ImmutableList.of(method));
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
Assert.assertEquals(Opcode.INVOKE_VIRTUAL, deodexedInstruction.getOpcode());
MethodReference methodReference = (MethodReference)((Instruction35c)deodexedInstruction).getReference();
Assert.assertEquals(method, methodReference);
}
@Test
public void testCustomMethodInlineTable_Static() {
List<ImmutableInstruction> instructions = Lists.newArrayList(
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
new ImmutableInstruction10x(Opcode.RETURN_VOID));
ImmutableMethodImplementation methodImpl = new ImmutableMethodImplementation(1, instructions, null, null);
ImmutableMethod method = new ImmutableMethod("Lblah;", "blah", null, "V", AccessFlags.STATIC.getValue(), null,
methodImpl);
ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
null, null, null, ImmutableList.of(method));
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
Assert.assertEquals(Opcode.INVOKE_STATIC, deodexedInstruction.getOpcode());
MethodReference methodReference = (MethodReference)((Instruction35c)deodexedInstruction).getReference();
Assert.assertEquals(method, methodReference);
}
@Test
public void testCustomMethodInlineTable_Direct() {
List<ImmutableInstruction> instructions = Lists.newArrayList(
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
new ImmutableInstruction10x(Opcode.RETURN_VOID));
ImmutableMethodImplementation methodImpl = new ImmutableMethodImplementation(1, instructions, null, null);
ImmutableMethod method = new ImmutableMethod("Lblah;", "blah", null, "V", AccessFlags.PRIVATE.getValue(), null,
methodImpl);
ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
null, null, null, ImmutableList.of(method));
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
Assert.assertEquals(Opcode.INVOKE_DIRECT, deodexedInstruction.getOpcode());
MethodReference methodReference = (MethodReference)((Instruction35c)deodexedInstruction).getReference();
Assert.assertEquals(method, methodReference);
}
}