// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Text;
using Microsoft.Scripting.Utils;
using IronPython.Runtime;
using IronPython.Runtime.Exceptions;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;
[assembly: PythonModule("_opcode", typeof(IronPython.Modules.PythonOpcode))]
namespace IronPython.Modules {
public static class PythonOpcode {
/* Instruction opcodes for compiled code */
private const int POP_TOP = 1;
private const int ROT_TWO = 2;
private const int ROT_THREE = 3;
private const int DUP_TOP = 4;
private const int DUP_TOP_TWO = 5;
private const int ROT_FOUR = 6;
private const int NOP = 9;
private const int UNARY_POSITIVE = 10;
private const int UNARY_NEGATIVE = 11;
private const int UNARY_NOT = 12;
private const int UNARY_INVERT = 15;
private const int BINARY_MATRIX_MULTIPLY = 16;
private const int INPLACE_MATRIX_MULTIPLY = 17;
private const int BINARY_POWER = 19;
private const int BINARY_MULTIPLY = 20;
private const int BINARY_MODULO = 22;
private const int BINARY_ADD = 23;
private const int BINARY_SUBTRACT = 24;
private const int BINARY_SUBSCR = 25;
private const int BINARY_FLOOR_DIVIDE = 26;
private const int BINARY_TRUE_DIVIDE = 27;
private const int INPLACE_FLOOR_DIVIDE = 28;
private const int INPLACE_TRUE_DIVIDE = 29;
private const int GET_AITER = 50;
private const int GET_ANEXT = 51;
private const int BEFORE_ASYNC_WITH = 52;
private const int BEGIN_FINALLY = 53;
private const int END_ASYNC_FOR = 54;
private const int INPLACE_ADD = 55;
private const int INPLACE_SUBTRACT = 56;
private const int INPLACE_MULTIPLY = 57;
private const int INPLACE_MODULO = 59;
private const int STORE_SUBSCR = 60;
private const int DELETE_SUBSCR = 61;
private const int BINARY_LSHIFT = 62;
private const int BINARY_RSHIFT = 63;
private const int BINARY_AND = 64;
private const int BINARY_XOR = 65;
private const int BINARY_OR = 66;
private const int INPLACE_POWER = 67;
private const int GET_ITER = 68;
private const int GET_YIELD_FROM_ITER = 69;
private const int PRINT_EXPR = 70;
private const int LOAD_BUILD_CLASS = 71;
private const int YIELD_FROM = 72;
private const int GET_AWAITABLE = 73;
private const int INPLACE_LSHIFT = 75;
private const int INPLACE_RSHIFT = 76;
private const int INPLACE_AND = 77;
private const int INPLACE_XOR = 78;
private const int INPLACE_OR = 79;
private const int WITH_CLEANUP_START = 81;
private const int WITH_CLEANUP_FINISH = 82;
private const int RETURN_VALUE = 83;
private const int IMPORT_STAR = 84;
private const int SETUP_ANNOTATIONS = 85;
private const int YIELD_VALUE = 86;
private const int POP_BLOCK = 87;
private const int END_FINALLY = 88;
private const int POP_EXCEPT = 89;
private const int HAVE_ARGUMENT = 90;
private const int STORE_NAME = 90;
private const int DELETE_NAME = 91;
private const int UNPACK_SEQUENCE = 92;
private const int FOR_ITER = 93;
private const int UNPACK_EX = 94;
private const int STORE_ATTR = 95;
private const int DELETE_ATTR = 96;
private const int STORE_GLOBAL = 97;
private const int DELETE_GLOBAL = 98;
private const int LOAD_CONST = 100;
private const int LOAD_NAME = 101;
private const int BUILD_TUPLE = 102;
private const int BUILD_LIST = 103;
private const int BUILD_SET = 104;
private const int BUILD_MAP = 105;
private const int LOAD_ATTR = 106;
private const int COMPARE_OP = 107;
private const int IMPORT_NAME = 108;
private const int IMPORT_FROM = 109;
private const int JUMP_FORWARD = 110;
private const int JUMP_IF_FALSE_OR_POP = 111;
private const int JUMP_IF_TRUE_OR_POP = 112;
private const int JUMP_ABSOLUTE = 113;
private const int POP_JUMP_IF_FALSE = 114;
private const int POP_JUMP_IF_TRUE = 115;
private const int LOAD_GLOBAL = 116;
private const int SETUP_FINALLY = 122;
private const int LOAD_FAST = 124;
private const int STORE_FAST = 125;
private const int DELETE_FAST = 126;
private const int RAISE_VARARGS = 130;
private const int CALL_FUNCTION = 131;
private const int MAKE_FUNCTION = 132;
private const int BUILD_SLICE = 133;
private const int LOAD_CLOSURE = 135;
private const int LOAD_DEREF = 136;
private const int STORE_DEREF = 137;
private const int DELETE_DEREF = 138;
private const int CALL_FUNCTION_KW = 141;
private const int CALL_FUNCTION_EX = 142;
private const int SETUP_WITH = 143;
private const int EXTENDED_ARG = 144;
private const int LIST_APPEND = 145;
private const int SET_ADD = 146;
private const int MAP_ADD = 147;
private const int LOAD_CLASSDEREF = 148;
private const int BUILD_LIST_UNPACK = 149;
private const int BUILD_MAP_UNPACK = 150;
private const int BUILD_MAP_UNPACK_WITH_CALL = 151;
private const int BUILD_TUPLE_UNPACK = 152;
private const int BUILD_SET_UNPACK = 153;
private const int SETUP_ASYNC_WITH = 154;
private const int FORMAT_VALUE = 155;
private const int BUILD_CONST_KEY_MAP = 156;
private const int BUILD_STRING = 157;
private const int BUILD_TUPLE_UNPACK_WITH_CALL = 158;
private const int LOAD_METHOD = 160;
private const int CALL_METHOD = 161;
private const int CALL_FINALLY = 162;
private const int POP_FINALLY = 163;
private static readonly int PY_INVALID_STACK_EFFECT = int.MaxValue;
/* Masks and values used by FORMAT_VALUE opcode. */
private const int FVC_MASK = 0x3;
private const int FVC_NONE = 0x0;
private const int FVC_STR = 0x1;
private const int FVC_REPR = 0x2;
private const int FVC_ASCII = 0x3;
private const int FVS_MASK = 0x4;
private const int FVS_HAVE_SPEC = 0x4;
public static int stack_effect(CodeContext context, int opcode, object oparg=null) {
int ioparg = 0;
if (opcode >= HAVE_ARGUMENT) {
if (oparg == null) {
throw PythonOps.ValueError("stack_effect: opcode requires oparg but oparg was not specified");
}
if (!Converter.TryConvertToIndex(oparg, out ioparg)) { // supported since CPython 3.8
ioparg = Converter.ImplicitConvertToInt32(oparg) ?? // warning since CPython 3.8, unsupported in 3.10
throw PythonOps.TypeError($"an integer is required (got type {PythonOps.GetPythonTypeName(oparg)})");
}
} else if (oparg != null) {
throw PythonOps.ValueError("stack_effect: opcode does not permit oparg but oparg was specified");
}
int effect = stack_effect(opcode, ioparg);
if (effect == PY_INVALID_STACK_EFFECT) {
throw PythonOps.ValueError("invalid opcode or oparg");
}
return effect;
}
private static int stack_effect(int opcode, int oparg, int jump = -1) {
switch (opcode) {
case NOP:
case EXTENDED_ARG:
return 0;
/* Stack manipulation */
case POP_TOP:
return -1;
case ROT_TWO:
case ROT_THREE:
case ROT_FOUR:
return 0;
case DUP_TOP:
return 1;
case DUP_TOP_TWO:
return 2;
/* Unary operators */
case UNARY_POSITIVE:
case UNARY_NEGATIVE:
case UNARY_NOT:
case UNARY_INVERT:
return 0;
case SET_ADD:
case LIST_APPEND:
return -1;
case MAP_ADD:
return -2;
/* Binary operators */
case BINARY_POWER:
case BINARY_MULTIPLY:
case BINARY_MATRIX_MULTIPLY:
case BINARY_MODULO:
case BINARY_ADD:
case BINARY_SUBTRACT:
case BINARY_SUBSCR:
case BINARY_FLOOR_DIVIDE:
case BINARY_TRUE_DIVIDE:
return -1;
case INPLACE_FLOOR_DIVIDE:
case INPLACE_TRUE_DIVIDE:
return -1;
case INPLACE_ADD:
case INPLACE_SUBTRACT:
case INPLACE_MULTIPLY:
case INPLACE_MATRIX_MULTIPLY:
case INPLACE_MODULO:
return -1;
case STORE_SUBSCR:
return -3;
case DELETE_SUBSCR:
return -2;
case BINARY_LSHIFT:
case BINARY_RSHIFT:
case BINARY_AND:
case BINARY_XOR:
case BINARY_OR:
return -1;
case INPLACE_POWER:
return -1;
case GET_ITER:
return 0;
case PRINT_EXPR:
return -1;
case LOAD_BUILD_CLASS:
return 1;
case INPLACE_LSHIFT:
case INPLACE_RSHIFT:
case INPLACE_AND:
case INPLACE_XOR:
case INPLACE_OR:
return -1;
case SETUP_WITH:
/* 1 in the normal flow.
* Restore the stack position and push 6 values before jumping to
* the handler if an exception be raised. */
return jump != 0 ? 6 : 1;
case WITH_CLEANUP_START:
return 2; /* or 1, depending on TOS */
case WITH_CLEANUP_FINISH:
/* Pop a variable number of values pushed by WITH_CLEANUP_START
* + __exit__ or __aexit__. */
return -3;
case RETURN_VALUE:
return -1;
case IMPORT_STAR:
return -1;
case SETUP_ANNOTATIONS:
return 0;
case YIELD_VALUE:
return 0;
case YIELD_FROM:
return -1;
case POP_BLOCK:
return 0;
case POP_EXCEPT:
return -3;
case END_FINALLY:
case POP_FINALLY:
/* Pop 6 values when an exception was raised. */
return -6;
case STORE_NAME:
return -1;
case DELETE_NAME:
return 0;
case UNPACK_SEQUENCE:
return oparg - 1;
case UNPACK_EX:
return (oparg & 0xFF) + (oparg >> 8);
case FOR_ITER:
/* -1 at end of iterator, 1 if continue iterating. */
return jump > 0 ? -1 : 1;
case STORE_ATTR:
return -2;
case DELETE_ATTR:
return -1;
case STORE_GLOBAL:
return -1;
case DELETE_GLOBAL:
return 0;
case LOAD_CONST:
return 1;
case LOAD_NAME:
return 1;
case BUILD_TUPLE:
case BUILD_LIST:
case BUILD_SET:
case BUILD_STRING:
return 1 - oparg;
case BUILD_LIST_UNPACK:
case BUILD_TUPLE_UNPACK:
case BUILD_TUPLE_UNPACK_WITH_CALL:
case BUILD_SET_UNPACK:
case BUILD_MAP_UNPACK:
case BUILD_MAP_UNPACK_WITH_CALL:
return 1 - oparg;
case BUILD_MAP:
return 1 - 2 * oparg;
case BUILD_CONST_KEY_MAP:
return -oparg;
case LOAD_ATTR:
return 0;
case COMPARE_OP:
return -1;
case IMPORT_NAME:
return -1;
case IMPORT_FROM:
return 1;
/* Jumps */
case JUMP_FORWARD:
case JUMP_ABSOLUTE:
return 0;
case JUMP_IF_TRUE_OR_POP:
case JUMP_IF_FALSE_OR_POP:
return jump != 0 ? 0 : -1;
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
return -1;
case LOAD_GLOBAL:
return 1;
/* Exception handling */
case SETUP_FINALLY:
/* 0 in the normal flow.
* Restore the stack position and push 6 values before jumping to
* the handler if an exception be raised. */
return jump != 0 ? 6 : 0;
case BEGIN_FINALLY:
/* Actually pushes 1 value, but count 6 for balancing with
* END_FINALLY and POP_FINALLY.
* This is the main reason of using this opcode instead of
* "LOAD_CONST None". */
return 6;
case CALL_FINALLY:
return jump != 0 ? 1 : 0;
case LOAD_FAST:
return 1;
case STORE_FAST:
return -1;
case DELETE_FAST:
return 0;
case RAISE_VARARGS:
return -oparg;
/* Functions and calls */
case CALL_FUNCTION:
return -oparg;
case CALL_METHOD:
return -oparg - 1;
case CALL_FUNCTION_KW:
return -oparg - 1;
case CALL_FUNCTION_EX:
return -1 - (((oparg & 0x01) != 0) ? 1 : 0);
case MAKE_FUNCTION:
return -1 - (((oparg & 0x01) != 0) ? 1 : 0) - (((oparg & 0x02) != 0) ? 1 : 0) -
(((oparg & 0x04) != 0) ? 1 : 0) - (((oparg & 0x08) != 0) ? 1 : 0);
case BUILD_SLICE:
if (oparg == 3)
return -2;
else
return -1;
/* Closures */
case LOAD_CLOSURE:
return 1;
case LOAD_DEREF:
case LOAD_CLASSDEREF:
return 1;
case STORE_DEREF:
return -1;
case DELETE_DEREF:
return 0;
/* Iterators and generators */
case GET_AWAITABLE:
return 0;
case SETUP_ASYNC_WITH:
/* 0 in the normal flow.
* Restore the stack position to the position before the result
* of __aenter__ and push 6 values before jumping to the handler
* if an exception be raised. */
return jump != 0 ? -1 + 6 : 0;
case BEFORE_ASYNC_WITH:
return 1;
case GET_AITER:
return 0;
case GET_ANEXT:
return 1;
case GET_YIELD_FROM_ITER:
return 0;
case END_ASYNC_FOR:
return -7;
case FORMAT_VALUE:
/* If there's a fmt_spec on the stack, we go from 2->1,
else 1->1. */
return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0;
case LOAD_METHOD:
return 1;
default:
return PY_INVALID_STACK_EFFECT;
}
}
}
}