/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.pojavdx.dx.cf.code;

import com.pojavdx.dx.rop.code.LocalItem;
import com.pojavdx.dx.rop.cst.Constant;
import com.pojavdx.dx.rop.type.Prototype;
import com.pojavdx.dx.rop.type.Type;
import java.util.ArrayList;

/**
 * Interface for machines capable of executing bytecode by acting
 * upon a {@link Frame}. A machine conceptually contains four arbitrary-value
 * argument slots, slots for several literal-value arguments, and slots for
 * branch target information.
 */
public interface Machine {
    /**
     * Gets the effective prototype of the method that this instance is
     * being used for. The <i>effective</i> prototype includes an initial
     * {@code this} argument for instance methods.
     *
     * @return {@code non-null;} the method prototype
     */
    public Prototype getPrototype();

    /**
     * Clears the regular and auxiliary arguments area.
     */
    public void clearArgs();

    /**
     * Pops the given number of values from the stack (of either category),
     * and store them in the arguments area, indicating that there are now
     * that many arguments. Also, clear the auxiliary arguments.
     *
     * @param frame {@code non-null;} frame to operate on
     * @param count {@code >= 0;} number of values to pop
     */
    public void popArgs(Frame frame, int count);

    /**
     * Pops values from the stack of the types indicated by the given
     * {@code Prototype} (popped in reverse of the argument
     * order, so the first prototype argument type is for the deepest
     * element of the stack), and store them in the arguments area,
     * indicating that there are now that many arguments. Also, clear
     * the auxiliary arguments.
     *
     * @param frame {@code non-null;} frame to operate on
     * @param prototype {@code non-null;} prototype indicating arguments to pop
     */
    public void popArgs(Frame frame, Prototype prototype);

    /**
     * Pops a value from the stack of the indicated type, and store it
     * in the arguments area, indicating that there are now that many
     * arguments. Also, clear the auxiliary arguments.
     *
     * @param frame {@code non-null;} frame to operate on
     * @param type {@code non-null;} type of the argument
     */
    public void popArgs(Frame frame, Type type);

    /**
     * Pops values from the stack of the indicated types (popped in
     * reverse argument order, so the first indicated type is for the
     * deepest element of the stack), and store them in the arguments
     * area, indicating that there are now that many arguments. Also,
     * clear the auxiliary arguments.
     *
     * @param frame {@code non-null;} frame to operate on
     * @param type1 {@code non-null;} type of the first argument
     * @param type2 {@code non-null;} type of the second argument
     */
    public void popArgs(Frame frame, Type type1, Type type2);

    /**
     * Pops values from the stack of the indicated types (popped in
     * reverse argument order, so the first indicated type is for the
     * deepest element of the stack), and store them in the arguments
     * area, indicating that there are now that many arguments. Also,
     * clear the auxiliary arguments.
     *
     * @param frame {@code non-null;} frame to operate on
     * @param type1 {@code non-null;} type of the first argument
     * @param type2 {@code non-null;} type of the second argument
     * @param type3 {@code non-null;} type of the third argument
     */
    public void popArgs(Frame frame, Type type1, Type type2, Type type3);

    /**
     * Loads the local variable with the given index as the sole argument in
     * the arguments area. Also, clear the auxiliary arguments.
     *
     * @param frame {@code non-null;} frame to operate on
     * @param idx {@code >= 0;} the local variable index
     */
    public void localArg(Frame frame, int idx);

    /**
     * Used to specify if a loaded local variable has info in the local
     * variable table.
     *
     * @param local {@code true} if local arg has info in local variable table
     */
    public void localInfo(boolean local);

    /**
     * Indicates that the salient type of this operation is as
     * given. This differentiates between, for example, the various
     * arithmetic opcodes, which, by the time they hit a
     * {@code Machine} are collapsed to the {@code int}
     * variant. (See {@link BytecodeArray#parseInstruction} for
     * details.)
     *
     * @param type {@code non-null;} the salient type of the upcoming operation
     */
    public void auxType(Type type);

    /**
     * Indicates that there is an auxiliary (inline, not stack)
     * argument of type {@code int}, with the given value.
     *
     * <p><b>Note:</b> Perhaps unintuitively, the stack manipulation
     * ops (e.g., {@code dup} and {@code swap}) use this to
     * indicate the result stack pattern with a straightforward hex
     * encoding of the push order starting with least-significant
     * nibbles getting pushed first). For example, an all-category-1
     * {@code dup2_x1} sets this to {@code 0x12312}, and the
     * other form of that op sets this to
     * {@code 0x121}.</p>
     *
     * <p><b>Also Note:</b> For {@code switch*} instructions, this is
     * used to indicate the padding value (which is only useful for
     * verification).</p>
     *
     * @param value the argument value
     */
    public void auxIntArg(int value);

    /**
     * Indicates that there is an auxiliary (inline, not stack) object
     * argument, with the value based on the given constant.
     *
     * <p><b>Note:</b> Some opcodes use both {@code int} and
     * constant auxiliary arguments.</p>
     *
     * @param cst {@code non-null;} the constant containing / referencing
     * the value
     */
    public void auxCstArg(Constant cst);

    /**
     * Indicates that there is an auxiliary (inline, not stack) argument
     * indicating a branch target.
     *
     * @param target the argument value
     */
    public void auxTargetArg(int target);

    /**
     * Indicates that there is an auxiliary (inline, not stack) argument
     * consisting of a {@code switch*} table.
     *
     * <p><b>Note:</b> This is generally used in conjunction with
     * {@link #auxIntArg} (which holds the padding).</p>
     *
     * @param cases {@code non-null;} the list of key-target pairs, plus the default
     * target
     */
    public void auxSwitchArg(SwitchList cases);

    /**
     * Indicates that there is an auxiliary (inline, not stack) argument
     * consisting of a list of initial values for a newly created array.
     *
     * @param initValues {@code non-null;} the list of constant values to initialize
     * the array
     */
    public void auxInitValues(ArrayList<Constant> initValues);

    /**
     * Indicates that the target of this operation is the given local.
     *
     * @param idx {@code >= 0;} the local variable index
     * @param type {@code non-null;} the type of the local
     * @param local {@code null-ok;} the name and signature of the local, if known
     */
    public void localTarget(int idx, Type type, LocalItem local);

    /**
     * "Runs" the indicated opcode in an appropriate way, using the arguments
     * area as appropriate, and modifying the given frame in response.
     *
     * @param frame {@code non-null;} frame to operate on
     * @param offset {@code >= 0;} byte offset in the method to the opcode being
     * run
     * @param opcode {@code >= 0;} the opcode to run
     */
    public void run(Frame frame, int offset, int opcode);
}
