/* * Copyright 2016, 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.smali;import com.google.common.collect.Lists; import com.google.common.collect.Maps; import junit.framework.Assert; import org.antlr.runtime.RecognitionException; import org.jf.dexlib2.Opcode; import org.jf.dexlib2.ValueType; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.Field; import org.jf.dexlib2.iface.Method; import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.formats.Instruction21c; import org.jf.dexlib2.iface.instruction.formats.Instruction35c; import org.jf.dexlib2.iface.reference.FieldReference; import org.jf.dexlib2.iface.reference.MethodReference; import org.jf.dexlib2.iface.value.FieldEncodedValue; import org.jf.dexlib2.iface.value.MethodEncodedValue; import org.jf.dexlib2.iface.value.TypeEncodedValue; import org.jf.smali.SmaliTestUtils; import org.junit.Test; import java.io.IOException; import java.util.List; import java.util.Map; /** * Tests for method/field references that use an implicit type */ public class ImplicitReferenceTest extends SmaliTestUtils { @Test public void testImplicitMethodReference() throws RecognitionException, IOException { ClassDef classDef = SmaliTestUtils.compileSmali("" + ".class public LHelloWorld;\n" + ".super Ljava/lang/Object;\n" + ".method public static main([Ljava/lang/String;)V\n" + " .registers 1\n" + " invoke-static {p0}, toString()V\n" + " invoke-static {p0}, V()V\n" + " invoke-static {p0}, I()V\n" + " return-void\n" + ".end method"); Method mainMethod = null; for (Method method: classDef.getMethods()) { if (method.getName().equals("main")) { mainMethod = method; } } Assert.assertNotNull(mainMethod); MethodImplementation methodImpl = mainMethod.getImplementation(); Assert.assertNotNull(methodImpl); List<Instruction> instructions = Lists.newArrayList(methodImpl.getInstructions()); Instruction35c instruction = (Instruction35c)instructions.get(0); Assert.assertNotNull(instruction); Assert.assertEquals(Opcode.INVOKE_STATIC, instruction.getOpcode()); MethodReference method = (MethodReference)instruction.getReference(); Assert.assertEquals(classDef.getType(), method.getDefiningClass()); Assert.assertEquals("toString", method.getName()); instruction = (Instruction35c)instructions.get(1); Assert.assertNotNull(instruction); Assert.assertEquals(Opcode.INVOKE_STATIC, instruction.getOpcode()); method = (MethodReference)instruction.getReference(); Assert.assertEquals(classDef.getType(), method.getDefiningClass()); Assert.assertEquals("V", method.getName()); instruction = (Instruction35c)instructions.get(2); Assert.assertNotNull(instruction); Assert.assertEquals(Opcode.INVOKE_STATIC, instruction.getOpcode()); method = (MethodReference)instruction.getReference(); Assert.assertEquals(classDef.getType(), method.getDefiningClass()); Assert.assertEquals("I", method.getName()); } @Test public void testImplicitMethodLiteral() throws RecognitionException, IOException { ClassDef classDef = SmaliTestUtils.compileSmali("" + ".class public LHelloWorld;\n" + ".super Ljava/lang/Object;\n" + ".field public static field1:Ljava/lang/reflect/Method; = toString()V\n" + ".field public static field2:Ljava/lang/reflect/Method; = V()V\n" + ".field public static field3:Ljava/lang/reflect/Method; = I()V\n" + ".field public static field4:Ljava/lang/Class; = I"); Map<String, Field> fields = Maps.newHashMap(); for (Field field: classDef.getFields()) { fields.put(field.getName(), field); } Field field = fields.get("field1"); Assert.assertNotNull(field); Assert.assertNotNull(field.getInitialValue()); Assert.assertEquals(ValueType.METHOD, field.getInitialValue().getValueType()); MethodEncodedValue methodEncodedValue = (MethodEncodedValue)field.getInitialValue(); Assert.assertEquals(classDef.getType(), methodEncodedValue.getValue().getDefiningClass()); Assert.assertEquals("toString", methodEncodedValue.getValue().getName()); field = fields.get("field2"); Assert.assertNotNull(field); Assert.assertNotNull(field.getInitialValue()); Assert.assertEquals(ValueType.METHOD, field.getInitialValue().getValueType()); methodEncodedValue = (MethodEncodedValue)field.getInitialValue(); Assert.assertEquals(classDef.getType(), methodEncodedValue.getValue().getDefiningClass()); Assert.assertEquals("V", methodEncodedValue.getValue().getName()); field = fields.get("field3"); Assert.assertNotNull(field); Assert.assertNotNull(field.getInitialValue()); Assert.assertEquals(ValueType.METHOD, field.getInitialValue().getValueType()); methodEncodedValue = (MethodEncodedValue)field.getInitialValue(); Assert.assertEquals(classDef.getType(), methodEncodedValue.getValue().getDefiningClass()); Assert.assertEquals("I", methodEncodedValue.getValue().getName()); field = fields.get("field4"); Assert.assertNotNull(field); Assert.assertNotNull(field.getInitialValue()); Assert.assertEquals(ValueType.TYPE, field.getInitialValue().getValueType()); TypeEncodedValue typeEncodedValue = (TypeEncodedValue)field.getInitialValue(); Assert.assertEquals("I", typeEncodedValue.getValue()); } @Test public void testImplicitFieldReference() throws RecognitionException, IOException { ClassDef classDef = SmaliTestUtils.compileSmali("" + ".class public LHelloWorld;\n" + ".super Ljava/lang/Object;\n" + ".method public static main([Ljava/lang/String;)V\n" + " .registers 1\n" + " sget-object v0, someField:I\n" + " sget-object v0, V:I\n" + " sget-object v0, I:I\n" + " return-void\n" + ".end method"); Method mainMethod = null; for (Method method: classDef.getMethods()) { if (method.getName().equals("main")) { mainMethod = method; } } Assert.assertNotNull(mainMethod); MethodImplementation methodImpl = mainMethod.getImplementation(); Assert.assertNotNull(methodImpl); List<Instruction> instructions = Lists.newArrayList(methodImpl.getInstructions()); Instruction21c instruction = (Instruction21c)instructions.get(0); Assert.assertNotNull(instruction); Assert.assertEquals(Opcode.SGET_OBJECT, instruction.getOpcode()); FieldReference field = (FieldReference)instruction.getReference(); Assert.assertEquals(classDef.getType(), field.getDefiningClass()); Assert.assertEquals("someField", field.getName()); instruction = (Instruction21c)instructions.get(1); Assert.assertNotNull(instruction); Assert.assertEquals(Opcode.SGET_OBJECT, instruction.getOpcode()); field = (FieldReference)instruction.getReference(); Assert.assertEquals(classDef.getType(), field.getDefiningClass()); Assert.assertEquals("V", field.getName()); instruction = (Instruction21c)instructions.get(2); Assert.assertNotNull(instruction); Assert.assertEquals(Opcode.SGET_OBJECT, instruction.getOpcode()); field = (FieldReference)instruction.getReference(); Assert.assertEquals(classDef.getType(), field.getDefiningClass()); Assert.assertEquals("I", field.getName()); } @Test public void testImplicitFieldLiteral() throws RecognitionException, IOException { ClassDef classDef = SmaliTestUtils.compileSmali("" + ".class public LHelloWorld;\n" + ".super Ljava/lang/Object;\n" + ".field public static field1:Ljava/lang/reflect/Field; = someField:I\n" + ".field public static field2:Ljava/lang/reflect/Field; = V:I\n" + ".field public static field3:Ljava/lang/reflect/Field; = I:I\n"); Map<String, Field> fields = Maps.newHashMap(); for (Field field: classDef.getFields()) { fields.put(field.getName(), field); } Field field = fields.get("field1"); Assert.assertNotNull(field); Assert.assertNotNull(field.getInitialValue()); Assert.assertEquals(ValueType.FIELD, field.getInitialValue().getValueType()); FieldEncodedValue fieldEncodedValue = (FieldEncodedValue)field.getInitialValue(); Assert.assertEquals(classDef.getType(), fieldEncodedValue.getValue().getDefiningClass()); Assert.assertEquals("someField", fieldEncodedValue.getValue().getName()); field = fields.get("field2"); Assert.assertNotNull(field); Assert.assertNotNull(field.getInitialValue()); Assert.assertEquals(ValueType.FIELD, field.getInitialValue().getValueType()); fieldEncodedValue = (FieldEncodedValue)field.getInitialValue(); Assert.assertEquals(classDef.getType(), fieldEncodedValue.getValue().getDefiningClass()); Assert.assertEquals("V", fieldEncodedValue.getValue().getName()); field = fields.get("field3"); Assert.assertNotNull(field); Assert.assertNotNull(field.getInitialValue()); Assert.assertEquals(ValueType.FIELD, field.getInitialValue().getValueType()); fieldEncodedValue = (FieldEncodedValue)field.getInitialValue(); Assert.assertEquals(classDef.getType(), fieldEncodedValue.getValue().getDefiningClass()); Assert.assertEquals("I", fieldEncodedValue.getValue().getName()); } }