blob: 96ac53678b578aa0c04dcb03e5f5d380c91673a4 [file] [log] [blame]
/*
* Copyright 2012, 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.util;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction;
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction;
import org.jf.dexlib2.Opcodes;
import java.util.List;
public class SyntheticAccessorFSM {
%% machine SyntheticAccessorFSM;
%% write data;
// math type constants
public static final int ADD = SyntheticAccessorResolver.ADD_ASSIGNMENT;
public static final int SUB = SyntheticAccessorResolver.SUB_ASSIGNMENT;
public static final int MUL = SyntheticAccessorResolver.MUL_ASSIGNMENT;
public static final int DIV = SyntheticAccessorResolver.DIV_ASSIGNMENT;
public static final int REM = SyntheticAccessorResolver.REM_ASSIGNMENT;
public static final int AND = SyntheticAccessorResolver.AND_ASSIGNMENT;
public static final int OR = SyntheticAccessorResolver.OR_ASSIGNMENT;
public static final int XOR = SyntheticAccessorResolver.XOR_ASSIGNMENT;
public static final int SHL = SyntheticAccessorResolver.SHL_ASSIGNMENT;
public static final int SHR = SyntheticAccessorResolver.SHR_ASSIGNMENT;
public static final int USHR = SyntheticAccessorResolver.USHR_ASSIGNMENT;
public static final int INT = 0;
public static final int LONG = 1;
public static final int FLOAT = 2;
public static final int DOUBLE = 3;
public static final int POSITIVE_ONE = 1;
public static final int NEGATIVE_ONE = -1;
public static final int OTHER = 0;
@Nonnull private final Opcodes opcodes;
public SyntheticAccessorFSM(@Nonnull Opcodes opcodes) {
this.opcodes = opcodes;
}
public int test(List<? extends Instruction> instructions) {
int accessorType = -1;
int cs, p = 0;
int pe = instructions.size();
// one of the math type constants representing the type of math operation being performed
int mathOp = -1;
// for increments an decrements, the type of value the math operation is on
int mathType = -1;
// for increments and decrements, the value of the constant that is used
long constantValue = 0;
// The source register for the put instruction
int putRegister = -1;
// The return register;
int returnRegister = -1;
%%{
import "Opcodes.rl";
alphtype short;
getkey opcodes.getOpcodeValue(instructions.get(p).getOpcode());
get = (0x52 .. 0x58) | (0x60 .. 0x66); # all igets/sgets
# all iputs/sputs
put = ((0x59 .. 0x5f) | (0x67 .. 0x6d)) @ {
putRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
};
invoke = (0x6e .. 0x72) | (0x74 .. 0x78); # all invokes
# all numeric const instructions
const_literal = (0x12 .. 0x19) @ {
constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral();
};
add_const = (add_int_lit8 | add_int_lit16) @ {
mathType = INT;
mathOp = ADD;
constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral();
};
arbitrary_add = (((add_int | add_int_2addr) @ { mathType = INT; }) |
((add_long | add_long_2addr) @ { mathType = LONG; }) |
((add_float | add_float_2addr) @ { mathType = FLOAT; }) |
((add_double | add_double_2addr) @ {mathType = DOUBLE; })) @ {
mathOp = ADD;
};
arbitrary_sub = (((sub_int | sub_int_2addr) @ { mathType = INT; }) |
((sub_long | sub_long_2addr) @ { mathType = LONG; }) |
((sub_float | sub_float_2addr) @ { mathType = FLOAT; }) |
((sub_double | sub_double_2addr) @ {mathType = DOUBLE; })) @ {
mathOp = SUB;
};
arbitrary_mul = (mul_int | mul_int_2addr | mul_long | mul_long_2addr |
mul_float | mul_float_2addr | mul_double | mul_double_2addr) @ {
mathOp = MUL;
};
arbitrary_div = (div_int | div_int_2addr | div_long | div_long_2addr |
div_float | div_float_2addr | div_double | div_double_2addr) @ {
mathOp = DIV;
};
arbitrary_rem = (rem_int | rem_int_2addr | rem_long | rem_long_2addr |
rem_float | rem_float_2addr | rem_double | rem_double_2addr) @ {
mathOp = REM;
};
arbitrary_and = (and_int | and_int_2addr | and_long | and_long_2addr) @ {
mathOp = AND;
};
arbitrary_or = (or_int | or_int_2addr | or_long | or_long_2addr) @ {
mathOp = OR;
};
arbitrary_xor = (xor_int | xor_int_2addr | xor_long | xor_long_2addr) @ {
mathOp = XOR;
};
arbitrary_shl = (shl_int | shl_int_2addr | shl_long | shl_long_2addr) @ {
mathOp = SHL;
};
arbitrary_shr = (shr_int | shr_int_2addr | shr_long | shr_long_2addr) @ {
mathOp = SHR;
};
arbitrary_ushr = (ushr_int | ushr_int_2addr | ushr_long | ushr_long_2addr) @ {
mathOp = USHR;
};
type_conversion = 0x81 .. 0x8f; # all type-conversion opcodes
return_something = (return | return_wide | return_object) @ {
returnRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
};
any_move_result = move_result | move_result_wide | move_result_object;
get_accessor = get return_something @ {
accessorType = SyntheticAccessorResolver.GETTER; fbreak;
};
put_accessor = put return_something @ {
accessorType = SyntheticAccessorResolver.SETTER; fbreak;
};
invoke_accessor = invoke (return_void | (any_move_result return_something)) @ {
accessorType = SyntheticAccessorResolver.METHOD; fbreak;
};
increment_accessor = get add_const type_conversion? put return_something @ {
accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
};
alt_increment_accessor = get const_literal (arbitrary_add | arbitrary_sub) put return_something @ {
accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
};
math_assignment_accessor = get type_conversion?
(arbitrary_add | arbitrary_sub | arbitrary_mul | arbitrary_div | arbitrary_rem |
arbitrary_and | arbitrary_or | arbitrary_xor | arbitrary_shl | arbitrary_shr |
arbitrary_ushr)
type_conversion{0,2} put return_something @ {
accessorType = mathOp; fbreak;
};
main := get_accessor |
put_accessor |
invoke_accessor |
increment_accessor |
alt_increment_accessor |
math_assignment_accessor;
write init;
write exec;
}%%
return accessorType;
}
private static int getIncrementType(int mathOp, int mathType, long constantValue, int putRegister,
int returnRegister) {
boolean isPrefix = putRegister == returnRegister;
boolean negativeConstant = false;
switch (mathType) {
case INT:
case LONG: {
if (constantValue == 1) {
negativeConstant = false;
} else if (constantValue == -1) {
negativeConstant = true;
} else {
return -1;
}
break;
}
case FLOAT: {
float val = Float.intBitsToFloat((int)constantValue);
if (val == 1) {
negativeConstant = false;
} else if (val == -1) {
negativeConstant = true;
} else {
return -1;
}
break;
}
case DOUBLE: {
double val = Double.longBitsToDouble(constantValue);
if (val == 1) {
negativeConstant = false;
} else if (val == -1) {
negativeConstant = true;
} else {
return -1;
}
break;
}
}
boolean isAdd = ((mathOp == ADD) && !negativeConstant) ||
((mathOp == SUB) && negativeConstant);
if (isPrefix) {
if (isAdd) {
return SyntheticAccessorResolver.PREFIX_INCREMENT;
} else {
return SyntheticAccessorResolver.PREFIX_DECREMENT;
}
} else {
if (isAdd) {
return SyntheticAccessorResolver.POSTFIX_INCREMENT;
} else {
return SyntheticAccessorResolver.POSTFIX_DECREMENT;
}
}
}
}