// line 1 "SyntheticAccessorFSM.rl"
/*
 * 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.Opcodes;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction;
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction;

import javax.annotation.Nonnull;
import java.util.List;

public class SyntheticAccessorFSM {
    
// line 43 "SyntheticAccessorFSM.rl"
    
// line 48 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
private static byte[] init__SyntheticAccessorFSM_actions_0()
{
	return new byte [] {
	    0,    1,    0,    1,    1,    1,    2,    1,   13,    1,   14,    1,
	   15,    1,   16,    1,   17,    1,   18,    1,   19,    1,   20,    1,
	   21,    1,   25,    2,    3,    7,    2,    4,    7,    2,    5,    7,
	    2,    6,    7,    2,    8,   12,    2,    9,   12,    2,   10,   12,
	    2,   11,   12,    2,   22,   23,    2,   22,   24,    2,   22,   25,
	    2,   22,   26,    2,   22,   27,    2,   22,   28
	};
}

private static final byte _SyntheticAccessorFSM_actions[] = init__SyntheticAccessorFSM_actions_0();


private static short[] init__SyntheticAccessorFSM_key_offsets_0()
{
	return new short [] {
	    0,    0,   12,   82,   98,  102,  104,  166,  172,  174,  180,  184,
	  190,  192,  196,  198,  201,  203
	};
}

private static final short _SyntheticAccessorFSM_key_offsets[] = init__SyntheticAccessorFSM_key_offsets_0();


private static short[] init__SyntheticAccessorFSM_trans_keys_0()
{
	return new short [] {
	   82,   88,   89,   95,   96,  102,  103,  109,  110,  114,  116,  120,
	  145,  146,  147,  148,  149,  150,  151,  152,  153,  154,  155,  156,
	  157,  158,  159,  160,  161,  162,  163,  164,  165,  166,  167,  168,
	  169,  170,  171,  172,  173,  174,  175,  177,  179,  180,  181,  182,
	  183,  184,  185,  186,  187,  188,  190,  191,  192,  193,  194,  195,
	  196,  197,  198,  199,  201,  202,  203,  204,  206,  207,  208,  216,
	   15,   17,   18,   25,  129,  143,  144,  176,  178,  205,  144,  145,
	  155,  156,  166,  167,  171,  172,  176,  177,  187,  188,  198,  199,
	  203,  204,   89,   95,  103,  109,   15,   17,  145,  146,  147,  148,
	  149,  150,  151,  152,  153,  154,  155,  156,  157,  158,  159,  160,
	  161,  162,  163,  164,  165,  166,  167,  168,  169,  170,  171,  172,
	  173,  174,  175,  177,  179,  180,  181,  182,  183,  184,  185,  186,
	  187,  188,  190,  191,  192,  193,  194,  195,  196,  197,  198,  199,
	  201,  202,  203,  204,  206,  207,  144,  176,  178,  205,   89,   95,
	  103,  109,  129,  143,   15,   17,   89,   95,  103,  109,  129,  143,
	   89,   95,  103,  109,   89,   95,  103,  109,  129,  143,   15,   17,
	   89,   95,  103,  109,   15,   17,   14,   10,   12,   15,   17,    0
	};
}

private static final short _SyntheticAccessorFSM_trans_keys[] = init__SyntheticAccessorFSM_trans_keys_0();


private static byte[] init__SyntheticAccessorFSM_single_lengths_0()
{
	return new byte [] {
	    0,    0,   60,   16,    0,    0,   58,    0,    0,    0,    0,    0,
	    0,    0,    0,    1,    0,    0
	};
}

private static final byte _SyntheticAccessorFSM_single_lengths[] = init__SyntheticAccessorFSM_single_lengths_0();


private static byte[] init__SyntheticAccessorFSM_range_lengths_0()
{
	return new byte [] {
	    0,    6,    5,    0,    2,    1,    2,    3,    1,    3,    2,    3,
	    1,    2,    1,    1,    1,    0
	};
}

private static final byte _SyntheticAccessorFSM_range_lengths[] = init__SyntheticAccessorFSM_range_lengths_0();


private static short[] init__SyntheticAccessorFSM_index_offsets_0()
{
	return new short [] {
	    0,    0,    7,   73,   90,   93,   95,  156,  160,  162,  166,  169,
	  173,  175,  178,  180,  183,  185
	};
}

private static final short _SyntheticAccessorFSM_index_offsets[] = init__SyntheticAccessorFSM_index_offsets_0();


private static byte[] init__SyntheticAccessorFSM_indicies_0()
{
	return new byte [] {
	    0,    2,    0,    2,    3,    3,    1,    8,    9,   10,   11,   12,
	   13,   14,   15,   16,   17,   18,   19,    9,   10,   11,   12,   13,
	   14,   15,   16,   17,   20,   21,    9,   10,   11,   22,   23,    9,
	   10,   11,    8,   10,   11,   12,   13,   14,   15,   16,   17,   18,
	   19,   10,   11,   12,   13,   14,   15,   16,   17,   20,   21,   10,
	   11,   22,   23,   10,   11,   24,   24,    4,    5,    6,    7,    9,
	    1,   25,   26,   27,   28,   29,   30,   31,   32,   25,   26,   27,
	   28,   29,   30,   31,   32,    1,   33,   33,    1,   34,    1,    8,
	    9,   10,   11,   12,   13,   14,   15,   16,   17,   18,   19,    9,
	   10,   11,   12,   13,   14,   15,   16,   17,   20,   21,    9,   10,
	   11,   22,   23,    9,   10,   11,    8,   10,   11,   12,   13,   14,
	   15,   16,   17,   18,   19,   10,   11,   12,   13,   14,   15,   16,
	   17,   20,   21,   10,   11,   22,   23,   10,   11,    7,    9,    1,
	   35,   35,   36,    1,   37,    1,   35,   35,   38,    1,   35,   35,
	    1,   39,   39,   40,    1,   41,    1,   39,   39,    1,   42,    1,
	   44,   43,    1,   45,    1,    1,    0
	};
}

private static final byte _SyntheticAccessorFSM_indicies[] = init__SyntheticAccessorFSM_indicies_0();


private static byte[] init__SyntheticAccessorFSM_trans_targs_0()
{
	return new byte [] {
	    2,    0,   14,   15,   17,    3,    6,    7,    7,    7,    7,    7,
	    7,    7,    7,    7,    7,    7,    7,    7,    7,    7,    7,    7,
	   11,    4,    4,    4,    4,    4,    4,    4,    4,    5,   17,    8,
	    9,   17,   10,   12,   13,   17,   17,   16,   17,   17
	};
}

private static final byte _SyntheticAccessorFSM_trans_targs[] = init__SyntheticAccessorFSM_trans_targs_0();


private static byte[] init__SyntheticAccessorFSM_trans_actions_0()
{
	return new byte [] {
	    0,    0,    1,    0,   51,    3,    0,   27,   39,    7,    9,   11,
	   13,   15,   17,   19,   21,   23,   30,   42,   33,   45,   36,   48,
	    5,   27,   39,   30,   42,   33,   45,   36,   48,    1,   63,    1,
	    0,   66,    0,    1,    0,   60,   54,    0,   25,   57
	};
}

private static final byte _SyntheticAccessorFSM_trans_actions[] = init__SyntheticAccessorFSM_trans_actions_0();


static final int SyntheticAccessorFSM_start = 1;
static final int SyntheticAccessorFSM_first_final = 17;
static final int SyntheticAccessorFSM_error = 0;

static final int SyntheticAccessorFSM_en_main = 1;


// line 44 "SyntheticAccessorFSM.rl"

    // 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;

        
// line 242 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
	{
	cs = SyntheticAccessorFSM_start;
	}

// line 247 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
	{
	int _klen;
	int _trans = 0;
	int _acts;
	int _nacts;
	int _keys;
	int _goto_targ = 0;

	_goto: while (true) {
	switch ( _goto_targ ) {
	case 0:
	if ( p == pe ) {
		_goto_targ = 4;
		continue _goto;
	}
	if ( cs == 0 ) {
		_goto_targ = 5;
		continue _goto;
	}
case 1:
	_match: do {
	_keys = _SyntheticAccessorFSM_key_offsets[cs];
	_trans = _SyntheticAccessorFSM_index_offsets[cs];
	_klen = _SyntheticAccessorFSM_single_lengths[cs];
	if ( _klen > 0 ) {
		int _lower = _keys;
		int _mid;
		int _upper = _keys + _klen - 1;
		while (true) {
			if ( _upper < _lower )
				break;

			_mid = _lower + ((_upper-_lower) >> 1);
			if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) < _SyntheticAccessorFSM_trans_keys[_mid] )
				_upper = _mid - 1;
			else if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) > _SyntheticAccessorFSM_trans_keys[_mid] )
				_lower = _mid + 1;
			else {
				_trans += (_mid - _keys);
				break _match;
			}
		}
		_keys += _klen;
		_trans += _klen;
	}

	_klen = _SyntheticAccessorFSM_range_lengths[cs];
	if ( _klen > 0 ) {
		int _lower = _keys;
		int _mid;
		int _upper = _keys + (_klen<<1) - 2;
		while (true) {
			if ( _upper < _lower )
				break;

			_mid = _lower + (((_upper-_lower) >> 1) & ~1);
			if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) < _SyntheticAccessorFSM_trans_keys[_mid] )
				_upper = _mid - 2;
			else if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) > _SyntheticAccessorFSM_trans_keys[_mid+1] )
				_lower = _mid + 2;
			else {
				_trans += ((_mid - _keys)>>1);
				break _match;
			}
		}
		_trans += _klen;
	}
	} while (false);

	_trans = _SyntheticAccessorFSM_indicies[_trans];
	cs = _SyntheticAccessorFSM_trans_targs[_trans];

	if ( _SyntheticAccessorFSM_trans_actions[_trans] != 0 ) {
		_acts = _SyntheticAccessorFSM_trans_actions[_trans];
		_nacts = (int) _SyntheticAccessorFSM_actions[_acts++];
		while ( _nacts-- > 0 )
	{
			switch ( _SyntheticAccessorFSM_actions[_acts++] )
			{
	case 0:
// line 100 "SyntheticAccessorFSM.rl"
	{
                putRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
            }
	break;
	case 1:
// line 107 "SyntheticAccessorFSM.rl"
	{
                constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral();
            }
	break;
	case 2:
// line 111 "SyntheticAccessorFSM.rl"
	{
                mathType = INT;
                mathOp = ADD;
                constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral();
            }
	break;
	case 3:
// line 117 "SyntheticAccessorFSM.rl"
	{ mathType = INT; }
	break;
	case 4:
// line 118 "SyntheticAccessorFSM.rl"
	{ mathType = LONG; }
	break;
	case 5:
// line 119 "SyntheticAccessorFSM.rl"
	{ mathType = FLOAT; }
	break;
	case 6:
// line 120 "SyntheticAccessorFSM.rl"
	{mathType = DOUBLE; }
	break;
	case 7:
// line 120 "SyntheticAccessorFSM.rl"
	{
                mathOp = ADD;
            }
	break;
	case 8:
// line 123 "SyntheticAccessorFSM.rl"
	{ mathType = INT; }
	break;
	case 9:
// line 124 "SyntheticAccessorFSM.rl"
	{ mathType = LONG; }
	break;
	case 10:
// line 125 "SyntheticAccessorFSM.rl"
	{ mathType = FLOAT; }
	break;
	case 11:
// line 126 "SyntheticAccessorFSM.rl"
	{mathType = DOUBLE; }
	break;
	case 12:
// line 126 "SyntheticAccessorFSM.rl"
	{
                mathOp = SUB;
            }
	break;
	case 13:
// line 130 "SyntheticAccessorFSM.rl"
	{
                mathOp = MUL;
            }
	break;
	case 14:
// line 134 "SyntheticAccessorFSM.rl"
	{
                mathOp = DIV;
            }
	break;
	case 15:
// line 138 "SyntheticAccessorFSM.rl"
	{
                mathOp = REM;
            }
	break;
	case 16:
// line 141 "SyntheticAccessorFSM.rl"
	{
                mathOp = AND;
            }
	break;
	case 17:
// line 144 "SyntheticAccessorFSM.rl"
	{
                mathOp = OR;
            }
	break;
	case 18:
// line 147 "SyntheticAccessorFSM.rl"
	{
                mathOp = XOR;
            }
	break;
	case 19:
// line 150 "SyntheticAccessorFSM.rl"
	{
                mathOp = SHL;
            }
	break;
	case 20:
// line 153 "SyntheticAccessorFSM.rl"
	{
                mathOp = SHR;
            }
	break;
	case 21:
// line 156 "SyntheticAccessorFSM.rl"
	{
                mathOp = USHR;
            }
	break;
	case 22:
// line 162 "SyntheticAccessorFSM.rl"
	{
                returnRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
            }
	break;
	case 23:
// line 168 "SyntheticAccessorFSM.rl"
	{
                accessorType = SyntheticAccessorResolver.GETTER; { p += 1; _goto_targ = 5; if (true)  continue _goto;}
            }
	break;
	case 24:
// line 172 "SyntheticAccessorFSM.rl"
	{
                accessorType = SyntheticAccessorResolver.SETTER; { p += 1; _goto_targ = 5; if (true)  continue _goto;}
            }
	break;
	case 25:
// line 176 "SyntheticAccessorFSM.rl"
	{
                accessorType = SyntheticAccessorResolver.METHOD; { p += 1; _goto_targ = 5; if (true)  continue _goto;}
            }
	break;
	case 26:
// line 180 "SyntheticAccessorFSM.rl"
	{
                accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
            }
	break;
	case 27:
// line 184 "SyntheticAccessorFSM.rl"
	{
                accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
            }
	break;
	case 28:
// line 192 "SyntheticAccessorFSM.rl"
	{
                accessorType = mathOp; { p += 1; _goto_targ = 5; if (true)  continue _goto;}
            }
	break;
// line 487 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
			}
		}
	}

case 2:
	if ( cs == 0 ) {
		_goto_targ = 5;
		continue _goto;
	}
	if ( ++p != pe ) {
		_goto_targ = 1;
		continue _goto;
	}
case 4:
case 5:
	}
	break; }
	}

// line 205 "SyntheticAccessorFSM.rl"


        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;
            }
        }
    }
}