# (C) 2024 by Rocky Bernstein
"""
Python Graal 3.10 bytecode opcodes

See com.oracle.graal.python/src/com/oracle/graal/python/compiler/OpCodes.java
"""

from xdis.opcodes.base import init_opdata

opc = locals()
init_opdata(opc, None, None)


def def_graal_op(
    loc: dict,
    op_name: str,
    opcode: int,
    pop: int = -2,
    push: int = -2,
    unknown: int = 0,
    fallthrough: bool = True,
) -> None:
    loc["opname"][opcode] = op_name
    loc["opmap"][op_name] = opcode
    loc["oppush"][opcode] = push
    loc["oppop"][opcode] = pop
    if not fallthrough:
        loc["nofollow"].append(opcode)


# Instruction opcodes for compiled code
# Blank lines correspond to available opcodes

# If the POP field is -1 and the opcode is a var args operation
# then the operand holds the size.
#
# If the POP field is negative and the opcode is a nargs operation
# then pop the operand amount plus the negative of the POP amount.

# Pop a single item from the stack.
def_graal_op(opc, "POP_TOP", 0, 0, 1, 0)

# Exchange two top stack items.
def_graal_op(opc, "ROT_TWO", 1, 0, 2, 2)

# Exchange three top stack items. [a, b, c] (a is top) becomes [b, c, a]
def_graal_op(opc, "ROT_THREE", 2, 0, 3, 3)

# Exchange N top stack items. [a, b, c, ..., N] (a is top) becomes [b, c, ..., N, a].
def_graal_op(
    opc, "ROT_N", 3, -1, -1
)  #  (oparg, followingArgs, withJump) -> oparg, (oparg, followingArgs, withJump) -> oparg)

# Duplicates the top stack item.
def_graal_op(opc, "DUP_TOP", 4, 0, 1, 2)

# Does nothing. Might still be useful to maintain a line number.
def_graal_op(opc, "NOP", 5, 0, 0, 0)

# Performs a unary operation specified by the immediate operand. It
# has to be the ordinal of one of {@link UnaryOps} constants.
#  Pops: operand
#  Pushes: result
def_graal_op(opc, "UNARY_OP", 6, 1, 1, 1)

# Performs a binary operation specified by the immediate operand. It has to be the ordinal of
# one of {@link BinaryOps} constants.
#   Pops: right operand, then left operand
#   Pushes: result
def_graal_op(opc, "BINARY_OP", 7, 1, 2, 1)

# Performs subscript get operation - {@code a[b]}.
#   Pops: {@code b}, then {@code a}
#   Pushes: result
def_graal_op(opc, "BINARY_SUBSCR", 8, 0, 2, 1)

# Performs subscript set operation - {@code a[b] = c}.
#   Pops: {@code b}, then {@code a}, then {@code c}
def_graal_op(opc, "STORE_SUBSCR", 9, 0, 3, 0)

# Performs subscript delete operation - {@code del a[b]}.
#   Pops: {@code b}, then {@code a}
#
def_graal_op(opc, "DELETE_SUBSCR", 10, 0, 2, 0)

# Gets an iterator of an object.
#   Pops: object
#   Pushes: iterator
def_graal_op(opc, "GET_ITER", 11, 0, 1, 1)

# Gets an iterator of an object, does nothing for a generator iterator or a coroutine.
#   Pops: object
#   Pushes: iterator
def_graal_op(opc, "GET_YIELD_FROM_ITER", 12, 0, 1, 1)

# Gets an awaitable of an object.
#   Pops: object
#   Pushes: awaitable
def_graal_op(opc, "GET_AWAITABLE", 13, 0, 1, 1)

# Gets the async iterator of an object - error if a coroutine is returned.
#   Pops: object
#   Pushes: async iterator
def_graal_op(opc, "GET_AITER", 14, 0, 1, 1)

# Get the awaitable that will return the next element of an async iterator.
#   Pops: object
#   Pushes: awaitable
def_graal_op(opc, "GET_ANEXT", 15, 0, 1, 1)

# Pushes: {@code __build_class__} builtin
def_graal_op(opc, "LOAD_BUILD_CLASS", 16, 0, 0, 1)

# Pushes: {@code AssertionError} builtin exception type
def_graal_op(opc, "LOAD_ASSERTION_ERROR", 17, 0, 0, 1)

# Returns the value to the caller. In generators, performs generator return.
#   Pops: return value
def_graal_op(opc, "RETURN_VALUE", 18, 0, 1, 0)
#
# Reads a name from locals dict, globals or builtins determined by the
# immediate operand which indexes the names array ({@code co_names}).
#  Pushes: read object
def_graal_op(opc, "LOAD_NAME", 19, 1, 0, 1)
opc["nullaryloadop"].add(19)


# Writes the stack top into a name in locals dict or globals
# determined by the immediate operand which indexes the names array
# ({@code co_names}).
#  Pops: object to be written
def_graal_op(opc, "STORE_NAME", 20, 1, 1, 0)

# Deletes the name in locals dict or globals determined by the
# immediate operand which indexes the names array ({@code co_names}).
def_graal_op(opc, "DELETE_NAME", 21, 1, 0, 0)

# Reads an attribute - {@code a.b}. {@code b} is determined by the immediate operand which
# indexes the names array ({@code co_names}).
#  Pops: {@code a}
#  Pushes: read attribute
def_graal_op(opc, "LOAD_ATTR", 22, 1, 1, 1)
#
# Reads method on an object. The method name is determined by the
# first immediate operand which indexes the names array ({@code
# co_names}).
#   Pushes: read method
def_graal_op(opc, "LOAD_METHOD", 23, 1, 1, 2)

# Writes an attribute - {@code a.b = c}. {@code b} is determined by
# the immediate operand which indexes the names array ({@code
# co_names}).
#  Pops: {@code c}, then {@code a}
def_graal_op(opc, "STORE_ATTR", 24, 1, 2, 0)

# Deletes an attribute - {@code del a.b}. {@code b} is determined by
# the immediate operand which indexes the names array ({@code
# co_names}).
#  Pops: {@code a}
def_graal_op(opc, "DELETE_ATTR", 25, 1, 1, 0)

# Reads a global variable. The name is determined by the immediate
# operand which indexes the names array ({@code co_names}).
#   Pushes: read object
def_graal_op(opc, "LOAD_GLOBAL", 26, 1, 0, 1)
opc["nullaryloadop"].add(26)


# Writes a global variable. The name is determined by the immediate
# operand which indexes the names array ({@code co_names}).
#  Pops: value to be written
def_graal_op(opc, "STORE_GLOBAL", 27, 1, 1, 0)

# Deletes a global variable. The name is determined by the immediate operand which indexes the
# names array ({@code co_names}).
def_graal_op(opc, "DELETE_GLOBAL", 28, 1, 0, 0)

# Reads a constant object from constants array ({@code co_consts}). Performs no conversion.
#   Pushes: read constant
def_graal_op(opc, "LOAD_CONST", 29, 1, 0, 1)
opc["nullaryloadop"].add(29)

# Reads a local variable determined by the immediate operand which indexes a stack slot and a
# variable name in varnames array ({@code co_varnames}).
#   Pushes: read value
def_graal_op(opc, "LOAD_FAST", 30, 1, 0, 1)
opc["nullaryloadop"].add(30)


# Writes a local variable determined by the immediate operand which indexes a stack slot and a
# variable name in varnames array ({@code co_varnames}).
#  Pops: value to be written
def_graal_op(opc, "STORE_FAST", 31, 1, 1, 0)

# Deletes a local variable determined by the immediate operand which indexes a stack slot and a
# variable name in varnames array ({@code co_varnames}).
def_graal_op(opc, "DELETE_FAST", 32, 1, 0, 0)

# Reads a local cell variable determined by the immediate operand
# which indexes a stack slot after celloffset and a variable name in
# cellvars or freevars array ({@code co_cellvars}, {@code
# co_freevars}).
#  Pushes: cell contents
def_graal_op(opc, "LOAD_DEREF", 33, 1, 0, 1)

# Writes a local cell variable determined by the immediate operand
# which indexes a stack slot after celloffset and a variable name in
# cellvars or freevars array ({@code co_cellvars}, {@code
# co_freevars}).
#  Pops: value to be written into the cell contents
# FIXME: this should be tagged as both a "free" and as "store" op.
def_graal_op(opc, "STORE_DEREF", 34, 1, 1, 0)

# Deletes a local cell variable determined by the immediate operand
# which indexes a stack slot after celloffset and a variable name in
# cellvars or freevars array ({@code co_cellvars}, {@code
# co_freevars}). Note that it doesn't delete the cell, just its
# contents.
def_graal_op(opc, "DELETE_DEREF", 35, 1, 0, 0)

# TODO not implemented
def_graal_op(opc, "LOAD_CLASSDEREF", 36, 1, 0, 1)

# Raises an exception. If the immediate operand is 0, it pops nothing
# and is equivalent to {@code raise} without arguments. If the
# immediate operand is 1, it is equivalent to {@code raise e} and it
# pops {@code e}. If the immediate operand is 2, it is equivalent to
# {@code raise e from c} and it pops {@code c}, then {@code e}. Other
# immediate operand values are illegal.
def_graal_op(
    opc, "RAISE_VARARGS", 37, 1
)  # , (oparg, followingArgs, withJump) -> oparg, 0)

# Creates a slice object. If the immediate argument is 2, it is equivalent to a slice
# {@code a:b}. It pops {@code b}, then {@code a}. If the immediate argument is 3, it is
# equivalent to a slice {@code a:b:c}. It pops {@code c}, then {@code b}, then {@code a}. Other
# immediate operand values are illegal.
#   Pushes: the created slice object
def_graal_op(opc, "BUILD_SLICE", 38, 1)  # (oparg, followingArgs, withJump) -> oparg, 1)

# Formats a value. If the immediate argument contains flag {@link FormatOptions#FVS_HAVE_SPEC},
# it is equivalent to {@code format(conv(v), spec)}. It pops {@code spec}, then {@code v}.
# Otherwise, it is equivalent to {@code format(conv(v), None)}. It pops {@code v}. {@code conv}
# is determined by the immediate operand which contains one of the {@code FVC} options in
# {@link FormatOptions}.
#  Pushes: the formatted value
def_graal_op(
    opc, "FORMAT_VALUE", 39, 1
)  # , (oparg, followingArgs, withJump) -> (oparg & FormatOptions.FVS_MASK) == FormatOptions.FVS_HAVE_SPEC ? 2 : 1, 1)

# Extends the immediate operand of the following instruction by its own operand shifted left by
# a byte.
def_graal_op(opc, "EXTENDED_ARG", 40, 1, 0, 0)

# Imports a module by name determined by the immediate operand which
# indexes the names array ({@code co_names}).
#  Pops: fromlist (must be a constant {@code TruffleString[]}), then level (must be {@code int})
#  Pushes: imported module
def_graal_op(opc, "IMPORT_NAME", 41, 1, 2, 1)
opc["nullaryloadop"].add(41)


# Imports a name from a module. The name determined by the immediate operand which indexes the
# names array ({@code co_names}).
#  Pops: module object
#   Pushes: module object, imported object
def_graal_op(opc, "IMPORT_FROM", 42, 1, 1, 2)

# Imports all names from a module of name determined by the immediate operand which indexes the
# names array ({@code co_names}). The imported names are written to locals dict (can only be
# invoked on module level).
#   Pops: level (must be {@code int})
def_graal_op(opc, "IMPORT_STAR", 43, 1, 1, 0)

# Prints the top of the stack. Used by "single" parsing mode to echo
# expressions.
#   Pops: the value to print
def_graal_op(opc, "PRINT_EXPR", 44, 0, 1, 0)

# Creates annotations dict in locals
def_graal_op(opc, "SETUP_ANNOTATIONS", 45, 0, 0, 0)

# Determines if a python object is a sequence.
def_graal_op(opc, "MATCH_SEQUENCE", 46, 0, 0, 1)

# Determines if a Python object is a mapping.
def_graal_op(opc, "MATCH_MAPPING", 47, 0, 0, 1)

# Determines if a Python object is of a particular type.
def_graal_op(opc, "MATCH_CLASS", 48, 1, 3, 2)

# Matches the keys (stack top) in a dict (stack second). On successful
# match pushes the values and True, otherwise None and False.
def_graal_op(opc, "MATCH_KEYS", 49, 0, 2, 4)

# Creates a copy of a dict (stack second) without elements matching a
# tuple of keys (stack top).
def_graal_op(opc, "COPY_DICT_WITHOUT_KEYS", 50, 0, 1, 1)

# Retrieves the length of a Python object and stores it on top.
def_graal_op(opc, "GET_LEN", 51, 0, 0, 1)

# -------------------------------------
# load bytecodes for special constants
# -------------------------------------

def_graal_op(opc, "LOAD_NONE", 52, 0, 0, 1)
opc["nullaryloadop"].add(52)

def_graal_op(opc, "LOAD_ELLIPSIS", 53, 0, 0, 1)
def_graal_op(opc, "LOAD_TRUE", 54, 0, 0, 1)
opc["nullaryloadop"].add(54)

def_graal_op(opc, "LOAD_FALSE", 55, 0, 0, 1)
opc["nullaryloadop"].add(55)


#### Continue adding opcode numbers here...


# Loads signed byte from immediate operand.
#
def_graal_op(opc, "LOAD_BYTE", 1, 0, 1)
#
# Loads {@code int} from primitiveConstants array indexed by the immediate operand.
#
def_graal_op(opc, "LOAD_INT", 1, 0, 1)
#
# Loads {@code long} from primitiveConstants array indexed by the immediate operand.
#
def_graal_op(opc, "LOAD_LONG", 1, 0, 1)
#
# Loads {@code double} from primitiveConstants array indexed by the immediate operand
# (converted from long).
#
def_graal_op(opc, "LOAD_DOUBLE", 1, 0, 1)
#
# Creates a {@link PInt} from a {@link BigInteger} in constants array indexed by the immediate
# operand.
#
def_graal_op(opc, "LOAD_BIGINT", 1, 0, 1)
#
# Currently the same as {@link #LOAD_CONST}.
#
def_graal_op(opc, "LOAD_STRING", 1, 0, 1)

#
# Creates python {@code bytes} from a {@code byte[]} array in constants array indexed by the
# immediate operand.
#
def_graal_op(opc, "LOAD_BYTES", 1, 0, 1)
#
# Creates python {@code complex} from a {@code double[]} array of size 2 in constants array
# indexed by the immediate operand.
#
def_graal_op(opc, "LOAD_COMPLEX", 1, 0, 1)

# Creates a collection out of a Java array in constants array indexed by the immediate operand.
# The second immediate operand determines the array type and kind, using values from {@link
# CollectionBits}. The only allowed kinds are list and tuple.
#
def_graal_op(opc, "LOAD_CONST_COLLECTION", 2, 0, 1)

# -------
# calling
# -------

#
# Calls method on an object using an array as args. The receiver is taken from the first
# element of the array. The method name is determined by the immediate operand which indexes
# the names array ({@code co_names}).
#
# Pops: args ({@code Object[]} of size >= 1)
#
# Pushes: call result
#
def_graal_op(opc, "CALL_METHOD_VARARGS", 1, 1, 1)
#
# Calls method on an object using a number of stack args determined by the first immediate
# operand.
#
# Pops: multiple arguments depending on the first immediate operand, then the method and the
# receiver
#
# Pushes: call result
#
def_graal_op(
    opc, "CALL_METHOD", 1
)  # , (oparg, followingArgs, withJump) -> oparg + 2, 1)
#
# Calls a callable using a number of stack args determined by the immediate operand.
#
# Pops: multiple arguments depending on the immediate operand (0 - 4), then the callable
#
# Pushes: call result
#
def_graal_op(
    opc, "CALL_FUNCTION", 1
)  # , (oparg, followingArgs, withJump) -> oparg + 1, 1)
#
# Calls a comprehension function with a single iterator argument. Comprehension functions have
# to always have the same call target at given calls site. The instruction makes use of this
# fact and bypasses function object inline caching which would otherwise slow down warmup since
# comprehensions functions are always created anew and thus the cache would always miss.
#
# Pops: iterator, then the function
#
# Pushes: call result
#
def_graal_op(opc, "CALL_COMPREHENSION", 0, 2, 1)
#
# Calls a callable using an arguments array and keywords array.
#
# Pops: keyword args ({@code PKeyword[]}), then args ({@code Object[]}), then callable
#
# Pushes: call result
#
def_graal_op(opc, "CALL_FUNCTION_KW", 0, 3, 1)
#
# Calls a callable using an arguments array. No keywords are passed.
#
# Pops: args ({@code Object[]}), then callable
#
# Pushes: call result
#
def_graal_op(opc, "CALL_FUNCTION_VARARGS", 0, 2, 1)

# ----------------------
# destructuring bytecodes
# ----------------------

# Unpacks an iterable into multiple stack items.
#
# Pops: iterable
#
# Pushed: unpacked items, the count is determined by the immediate operand
#
def_graal_op(
    opc, "UNPACK_SEQUENCE", 1, 1
)  # , (oparg, followingArgs, withJump) -> oparg)

# Unpacks an iterable into multiple stack items with a star item that gets the rest. The first
# immediate operand determines the count before the star item, the second determines the count
# after.
#
# Pops: iterable
#
# Pushed: unpacked items (count = first operand), star item, unpacked items (count = second
# operand)
#
def_graal_op(
    opc, "UNPACK_EX", 2, 1
)  #  (oparg, followingArgs, withJump) -> oparg + 1 + Byte.toUnsignedInt(followingArgs[0]))

# jumps

#
# Get next value from an iterator. If the iterable is exhausted, jump forward by the offset in
# the immediate argument.
#
# Pops: iterator
#
# Pushes (only if not jumping): the iterator, then the next value
#
def_graal_op(
    opc, "FOR_ITER", 1, 1
)  # (, (oparg, followingArgs, withJump) -> withJump ? 0 : 2)
#
# Jump forward by the offset in the immediate operand.
#
def_graal_op(opc, "JUMP_FORWARD", 1, 0, 0)

# Jump backward by the offset in the immediate operand. May trigger OSR compilation.
#
def_graal_op(opc, "JUMP_BACKWARD", 1, 0, 0)

# Jump forward by the offset in the immediate operand if the top of the stack is false (in
# Python sense).
#
# Pops (if not jumping): top of the stack
def_graal_op(
    opc, "JUMP_IF_FALSE_OR_POP", 3
)  # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0)

# Jump forward by the offset in the immediate operand if the top of the stack is true (in
# Python sense).
#
# Pops (if not jumping): top of the stack
#
def_graal_op(
    opc, "JUMP_IF_TRUE_OR_POP", 3
)  # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0)
#
# Jump forward by the offset in the immediate operand if the top of the stack is false (in
# Python sense).
#
# Pops: top of the stack
#
def_graal_op(opc, "POP_AND_JUMP_IF_FALSE", 3, 1, 0)
#
# Jump forward by the offset in the immediate operand if the top of the stack is true (in
# Python sense).
#
# Pops: top of the stack
#
def_graal_op(opc, "POP_AND_JUMP_IF_TRUE", 3, 1, 0)


# ----------------
# making callables
# ----------------

#
# Like {@link #LOAD_DEREF}, but loads the cell itself, not the contents.
#
# Pushes: the cell object
#
def_graal_op(opc, "LOAD_CLOSURE", 1, 0, 1)
#
# Reduces multiple stack items into an array of cell objects.
#
# Pops: multiple cells (count = immediate argument)
#
# Pushes: cell object array ({@code PCell[]})
#
def_graal_op(
    opc, "CLOSURE_FROM_STACK", 1
)  # , (oparg, followingArgs, withJump) -> oparg, 1)
#
# Creates a function object. The first immediate argument is an index to the constants array
# that determines the {@link CodeUnit} object that will provide the function's code.
#
# Pops: The second immediate arguments contains flags (defined in {@link CodeUnit}) that
# determine whether it will need to pop (in this order): closure, annotations, keyword only
# defaults, defaults.
#
# Pushes: created function
#
def_graal_op(
    opc, "MAKE_FUNCTION", 2
)  # , (oparg, followingArgs, withJump) -> Integer.bitCount(followingArgs[0]), 1)

# -------------------
# collection literals
# -------------------

#
# Creates a collection from multiple elements from the stack. Collection type is determined by
# {@link CollectionBits} in immediate operand.
#
# Pops: items for the collection (count = immediate argument)
#
# Pushes: new collection
#
def_graal_op(
    opc, "COLLECTION_FROM_STACK", 1
)  # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg), 1)
#
# Add multiple elements from the stack to the collection below them. Collection type is
# determined by {@link CollectionBits} in immediate operand. Tuple is not supported.
#
# Pops: items to be added (count = immediate argument)
#
def_graal_op(
    opc, "COLLECTION_ADD_STACK", 1
)  # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg) + 1, 1)
#
# Concatenates two collection of the same type. Collection type is determined by
# {@link CollectionBits} in immediate operand. Tuple is not supported.
#
# Pops: second collection, first collection
#
# Pushes: concatenated collection
#
def_graal_op(opc, "COLLECTION_ADD_COLLECTION", 1, 2, 1)
#
# Converts collection to another type determined by {@link CollectionBits} in immediate
# operand. The converted collection is expected to be an independent copy (they don't share
# storage).
#
# Pops: original collection
#
# Pushes: converted collection
#
def_graal_op(opc, "COLLECTION_FROM_COLLECTION", 1, 1, 1)
#
# Converts list to tuple by reusing the underlying storage.
#
# Pops: list
#
# Pushes: tuple
#
def_graal_op(opc, "TUPLE_FROM_LIST", 0, 1, 1)
#
# Converts list to frozenset.
#
# Pops: list
#
# Pushes: frozenset
#
def_graal_op(opc, "FROZENSET_FROM_LIST", 0, 1, 1)
#
# Adds an item to a collection that is multiple items deep under the top of the stack,
# determined by the immediate argument.
#
# Pops: item to be added
#
def_graal_op(
    opc, "ADD_TO_COLLECTION", 1
)  #  (oparg, followingArgs, withJump) -> CollectionBits.collectionKind(oparg) == CollectionBits.KIND_DICT ? 2 : 1, 0)
#
# Like {@link #COLLECTION_ADD_COLLECTION} for dicts, but with checks for duplicate keys
# necessary for keyword arguments merge. Note it works with dicts. Keyword arrays need to be
# converted to dicts first.
#
def_graal_op(opc, "KWARGS_DICT_MERGE", 0, 2, 1)
#
# Create a single {@link PKeyword} object. The name is determined by the immediate operand
# which indexes the names array ({@code co_names})
#
# Pops: keyword value
#
# Pushes: keyword object
#
def_graal_op(opc, "MAKE_KEYWORD", 1, 1, 1)

# -----------
# exceptions
# -----------

#
# Jump forward by the offset in the immediate argument if the exception doesn't match the
# expected type. The exception object is {@link PException}, not a python exception.
#
# Pops: expected type, then exception
#
# Pushes (if jumping): the exception
#
def_graal_op(opc, "MATCH_EXC_OR_JUMP", 3, 2, 1)
#
# Save the current exception state on the stack and set it to the exception on the stack. The
# exception object is {@link PException}, not a python exception. The exception is pushed back
# to the top.
#
# Pops: the exception
#
# Pushes: the saved exception state, the exception
#
def_graal_op(opc, "PUSH_EXC_INFO", 0, 0, 1)

# Sets the current exception state to the saved state (by {@link #PUSH_EXC_INFO}) on the stack
# and pop it.
#
# Pops: save exception state
def_graal_op(opc, "POP_EXCEPT", 0, 1, 0)

# Restore exception state and reraise exception.
#
# Pops: exception to reraise, then saved exception state
def_graal_op(opc, "END_EXC_HANDLER", 0, 2, 0)

# Gets the python-level exception object from a {@link PException}.
#
# Pops: a {@link PException} Pushes: python exception
#
def_graal_op(opc, "UNWRAP_EXC", 0, 1, 1)

# ----------
# generators
# ----------
#
# Yield value from the stack to the caller. Saves execution state. The generator will resume at
# the next instruction.
#
# Pops: yielded value
#
def_graal_op(opc, "YIELD_VALUE", 0, 1, 0)
#
# Wrap value from the stack in a {@link PAsyncGenWrappedValue}. CPython 3.11 opcode, used here
# to avoid a runtime check
#
# Pops: an object Pushes: async_generator_wrapped_value
#
def_graal_op(opc, "ASYNCGEN_WRAP", 0, 1, 1)
#
# Resume after yield. Will raise exception passed by {@code throw} if any.
#
# Pushes: value received from {@code send} or {@code None}.
#
def_graal_op(opc, "RESUME_YIELD", 0, 0, 1)
#
# Send value into a generator. Jumps forward by the offset in the immediate argument if the
# generator is exhausted. Used to implement {@code yield from}.
#
# Pops: value to be sent, then generator
#
# Pushes (if not jumping): the generator, then the yielded value
#
# Pushes (if jumping): the generator return value
#
def_graal_op(
    opc, "SEND", 1, 2
)  # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2)

# Exception handler for forwarding {@code throw} calls into {@code yield from}.
#
# Pops: exception, then the generator
#
# Pushes (if not jumping): the generator, then the yielded value
#
# Pushes (if jumping): the generator return value
def_graal_op(
    opc, "THROW", 1, 2
)  # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2)

# Exception handler for async for loops. If the current exception is StopAsyncIteration, handle
# it, otherwise, reraise.
#
# Pops: exception, then the anext coroutine, then the async iterator
def_graal_op(opc, "END_ASYNC_FOR", 0, 3, 0)

# with statements
#
# Enter a context manager and save data for its exit.
#
# Pops: the context manager
#
# Pushes: the context manager, then maybe-bound {@code __exit__}, then the result of
# {@code __enter__}
def_graal_op(opc, "SETUP_WITH", 0, 1, 3)

# Run the exit handler of a context manager and reraise if necessary.
#
# Pops: exception or {@code None}, then maybe-bound {@code __exit__}, then the context manager

def_graal_op(opc, "EXIT_WITH", 0, 3, 0)

# Enter a context manager and save data for its exit
#
# Pops: the context manager
#
# Pushes: the context manager, then the maybe-bound async function {@code __aexit__}, then the
# awaitable returned by {@code __aenter__}
#
def_graal_op(opc, "SETUP_AWITH", 0, 1, 3)

# Run the exit handler of a context manager
#
# Pops: exception or {@code None}, then maybe-bound {@code __aexit__}, then the context manager
#
# Pushes: the exception or {@code None}, then the awaitable returned by {@code __aexit__}
def_graal_op(opc, "GET_AEXIT_CORO", 0, 3, 2)

# Reraise the exception passed to {@code __aexit__} if appropriate
#
#!Pops: The result of awaiting {@code __aexit__}, then the exception
def_graal_op(opc, "EXIT_AWITH", 0, 2, 0)
