Norimasa Okamoto
/
pymite
python-on-a-chip online compiler
- http://pymbed.appspot.com/
- https://code.google.com/p/python-on-a-chip/
- http://www.youtube.com/watch?v=Oyqc2bFRW9I
- https://bitbucket.org/va009039/pymbed/
more info: python-on-a-chip
Diff: vm/interp.c
- Revision:
- 0:65f1469d6bfb
- Child:
- 1:28afb064a41c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/interp.c Sat Mar 02 11:54:20 2013 +0000 @@ -0,0 +1,2288 @@ +/* +# This file is Copyright 2002 Dean Hall. +# This file is part of the PyMite VM. +# This file is licensed under the MIT License. +# See the LICENSE file for details. +*/ + + +#undef __FILE_ID__ +#define __FILE_ID__ 0x09 + + +/** + * \file + * \brief VM Interpreter + * + * VM interpreter operations. + */ + + +#include "pm.h" + + +PmReturn_t +interpret(const uint8_t returnOnNoThreads) +{ + PmReturn_t retval = PM_RET_OK; + pPmObj_t pobj1 = C_NULL; + pPmObj_t pobj2 = C_NULL; + pPmObj_t pobj3 = C_NULL; + int16_t t16 = 0; + int8_t t8 = 0; + uint8_t bc; + uint8_t objid, objid2; + + /* Activate a thread the first time */ + retval = interp_reschedule(); + PM_RETURN_IF_ERROR(retval); + + /* Interpret loop */ + for (;;) + { + if (gVmGlobal.pthread == C_NULL) + { + if (returnOnNoThreads) + { + /* User chose to return on no threads left */ + return retval; + } + + /* + * Without a frame there is nothing to execute, so reschedule + * (possibly activating a recently added thread). + */ + retval = interp_reschedule(); + PM_BREAK_IF_ERROR(retval); + continue; + } + + /* Reschedule threads if flag is true? */ + if (gVmGlobal.reschedule) + { + retval = interp_reschedule(); + PM_BREAK_IF_ERROR(retval); + } + + /* Get byte; the func post-incrs PM_IP */ + bc = mem_getByte(PM_FP->fo_memspace, &PM_IP); + switch (bc) + { + case POP_TOP: + pobj1 = PM_POP(); + continue; + + case ROT_TWO: + pobj1 = TOS; + TOS = TOS1; + TOS1 = pobj1; + continue; + + case ROT_THREE: + pobj1 = TOS; + TOS = TOS1; + TOS1 = TOS2; + TOS2 = pobj1; + continue; + + case DUP_TOP: + pobj1 = TOS; + PM_PUSH(pobj1); + continue; + + case ROT_FOUR: + pobj1 = TOS; + TOS = TOS1; + TOS1 = TOS2; + TOS2 = TOS3; + TOS3 = pobj1; + continue; + + case NOP: + continue; + + case UNARY_POSITIVE: + /* Raise TypeError if TOS is not an int */ + if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT) +#ifdef HAVE_FLOAT + && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_FLT) +#endif /* HAVE_FLOAT */ + ) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + /* When TOS is an int, this is a no-op */ + continue; + + case UNARY_NEGATIVE: +#ifdef HAVE_FLOAT + if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT) + { + retval = float_negative(TOS, &pobj2); + } + else +#endif /* HAVE_FLOAT */ + { + retval = int_negative(TOS, &pobj2); + } + PM_BREAK_IF_ERROR(retval); + TOS = pobj2; + continue; + + case UNARY_NOT: + pobj1 = PM_POP(); + if (obj_isFalse(pobj1)) + { + PM_PUSH(PM_TRUE); + } + else + { + PM_PUSH(PM_FALSE); + } + continue; + +#ifdef HAVE_BACKTICK + /* #244 Add support for the backtick operation (UNARY_CONVERT) */ + case UNARY_CONVERT: + retval = obj_repr(TOS, &pobj3); + PM_BREAK_IF_ERROR(retval); + TOS = pobj3; + continue; +#endif /* HAVE_BACKTICK */ + + case UNARY_INVERT: + /* Raise TypeError if it's not an int */ + if (OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + /* Otherwise perform bit-wise complement */ + retval = int_bitInvert(TOS, &pobj2); + PM_BREAK_IF_ERROR(retval); + TOS = pobj2; + continue; + + case LIST_APPEND: + /* list_append will raise a TypeError if TOS1 is not a list */ + retval = list_append(TOS1, TOS); + PM_SP -= 2; + continue; + + case BINARY_POWER: + case INPLACE_POWER: + +#ifdef HAVE_FLOAT + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT) + || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT)) + { + /* Calculate float power */ + retval = float_op(TOS1, TOS, &pobj3, 'P'); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_FLOAT */ + + /* Calculate integer power */ + retval = int_pow(TOS1, TOS, &pobj3); + PM_BREAK_IF_ERROR(retval); + + /* Set return value */ + PM_SP--; + TOS = pobj3; + continue; + + case GET_ITER: +#ifdef HAVE_GENERATORS + /* Raise TypeError if TOS is an instance, but not iterable */ + if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI) + { + retval = class_getAttr(TOS, PM_NEXT_STR, &pobj1); + if (retval != PM_RET_OK) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + } + else +#endif /* HAVE_GENERATORS */ + { + /* Convert sequence to sequence-iterator */ + retval = seqiter_new(TOS, &pobj1); + PM_BREAK_IF_ERROR(retval); + + /* Put sequence-iterator on top of stack */ + TOS = pobj1; + } + continue; + + case BINARY_MULTIPLY: + case INPLACE_MULTIPLY: + /* If both objs are ints, perform the op */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)) + { + retval = int_new(((pPmInt_t)TOS1)->val * + ((pPmInt_t)TOS)->val, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + +#ifdef HAVE_FLOAT + else if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT) + || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT)) + { + retval = float_op(TOS1, TOS, &pobj3, '*'); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_FLOAT */ + +#ifdef HAVE_REPLICATION + /* If it's a list replication operation */ + else if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_LST)) + { + t16 = (int16_t)((pPmInt_t)TOS)->val; + if (t16 < 0) + { + t16 = 0; + } + + retval = list_replicate(TOS1, t16, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* If it's a tuple replication operation */ + else if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_TUP)) + { + t16 = (int16_t)((pPmInt_t)TOS)->val; + if (t16 < 0) + { + t16 = 0; + } + + retval = tuple_replicate(TOS1, t16, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* If it's a string replication operation */ + else if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_STR)) + { + t16 = (int16_t)((pPmInt_t)TOS)->val; + if (t16 < 0) + { + t16 = 0; + } + + pobj2 = TOS1; + pobj2 = (pPmObj_t)&((pPmString_t)pobj2)->val; + retval = string_replicate( + (uint8_t const **)(uint8_t *)&pobj2, t16, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_REPLICATION */ + + /* Otherwise raise a TypeError */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + + case BINARY_DIVIDE: + case INPLACE_DIVIDE: + case BINARY_FLOOR_DIVIDE: + case INPLACE_FLOOR_DIVIDE: + +#ifdef HAVE_FLOAT + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT) + || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT)) + { + retval = float_op(TOS1, TOS, &pobj3, '/'); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_FLOAT */ + + /* Otherwise perform operation */ + retval = int_divmod(TOS1, TOS, '/', &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + + case BINARY_MODULO: + case INPLACE_MODULO: + +#ifdef HAVE_STRING_FORMAT + /* If it's a string, perform string format */ + if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_STR) + { + retval = string_format((pPmString_t)TOS1, TOS, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_STRING_FORMAT */ + +#ifdef HAVE_FLOAT + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT) + || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT)) + { + retval = float_op(TOS1, TOS, &pobj3, '%'); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_FLOAT */ + + /* Otherwise perform operation */ + retval = int_divmod(TOS1, TOS, '%', &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + + case STORE_MAP: + /* #213: Add support for Python 2.6 bytecodes */ + C_ASSERT(OBJ_GET_TYPE(TOS2) == OBJ_TYPE_DIC); + retval = dict_setItem(TOS2, TOS, TOS1); + PM_BREAK_IF_ERROR(retval); + PM_SP -= 2; + continue; + + case BINARY_ADD: + case INPLACE_ADD: + +#ifdef HAVE_FLOAT + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT) + || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT)) + { + retval = float_op(TOS1, TOS, &pobj3, '+'); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_FLOAT */ + + /* If both objs are ints, perform the op */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)) + { + retval = int_new(((pPmInt_t)TOS1)->val + + ((pPmInt_t)TOS)->val, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* #242: If both objs are strings, perform concatenation */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_STR) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_STR)) + { + retval = string_concat((pPmString_t)TOS1, + (pPmString_t)TOS, + &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* Otherwise raise a TypeError */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + + case BINARY_SUBTRACT: + case INPLACE_SUBTRACT: + +#ifdef HAVE_FLOAT + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT) + || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT)) + { + retval = float_op(TOS1, TOS, &pobj3, '-'); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_FLOAT */ + + /* If both objs are ints, perform the op */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)) + { + retval = int_new(((pPmInt_t)TOS1)->val - + ((pPmInt_t)TOS)->val, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* Otherwise raise a TypeError */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + + case BINARY_SUBSCR: + /* Implements TOS = TOS1[TOS]. */ + + if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_DIC) + { + retval = dict_getItem(TOS1, TOS, &pobj3); + } + else + { + /* Raise a TypeError if index is not an Integer or Bool */ + if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_BOOL)) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + pobj1 = TOS1; +#ifdef HAVE_BYTEARRAY + /* If object is an instance, get the thing it contains */ + if (OBJ_GET_TYPE(pobj1) == OBJ_TYPE_CLI) + { + retval = dict_getItem((pPmObj_t)((pPmInstance_t)pobj1)->cli_attrs, + PM_NONE, + &pobj2); + PM_RETURN_IF_ERROR(retval); + pobj1 = pobj2; + } +#endif /* HAVE_BYTEARRAY */ + + /* Ensure the index doesn't overflow */ + C_ASSERT(((pPmInt_t)TOS)->val <= 0x0000FFFF); + t16 = (int16_t)((pPmInt_t)TOS)->val; + + retval = seq_getSubscript(pobj1, t16, &pobj3); + } + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + +#ifdef HAVE_FLOAT + /* #213: Add support for Python 2.6 bytecodes */ + case BINARY_TRUE_DIVIDE: + case INPLACE_TRUE_DIVIDE: + + /* Perform division; float_op() checks for types and zero-div */ + retval = float_op(TOS1, TOS, &pobj3, '/'); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; +#endif /* HAVE_FLOAT */ + + case SLICE_0: + /* Implements TOS = TOS[:], push a copy of the sequence */ + + /* Create a copy if it is a list */ + if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_LST) + { + retval = list_copy(TOS, &pobj2); + PM_BREAK_IF_ERROR(retval); + + TOS = pobj2; + } + + /* If TOS is an immutable sequence leave it (no op) */ + + /* Raise a TypeError for types that can not be sliced */ + else if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_STR) + && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_TUP)) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + continue; + + case STORE_SUBSCR: + /* Implements TOS1[TOS] = TOS2 */ + + /* If it's a list */ + if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_LST) + { + /* Ensure subscr is an int or bool */ + if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_BOOL)) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + /* Set the list item */ + retval = list_setItem(TOS1, + (int16_t)(((pPmInt_t)TOS)->val), + TOS2); + PM_BREAK_IF_ERROR(retval); + PM_SP -= 3; + continue; + } + + /* If it's a dict */ + if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_DIC) + { + /* Set the dict item */ + retval = dict_setItem(TOS1, TOS, TOS2); + PM_BREAK_IF_ERROR(retval); + PM_SP -= 3; + continue; + } + +#ifdef HAVE_BYTEARRAY + /* If object is an instance, get the thing it contains */ + if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_CLI) + { + retval = dict_getItem((pPmObj_t)((pPmInstance_t)TOS1)->cli_attrs, + PM_NONE, + &pobj2); + + /* Raise TypeError if instance isn't a ByteArray */ + if ((retval == PM_RET_EX_KEY) + || (OBJ_GET_TYPE(pobj2) != OBJ_TYPE_BYA)) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + PM_BREAK_IF_ERROR(retval); + + /* Ensure subscr is an int or bool */ + if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_BOOL)) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + retval = bytearray_setItem(pobj2, + (int16_t)(((pPmInt_t)TOS)->val), + TOS2); + PM_BREAK_IF_ERROR(retval); + PM_SP -= 3; + continue; + } +#endif /* HAVE_BYTEARRAY */ + + /* TypeError for all else */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + +#ifdef HAVE_DEL + case DELETE_SUBSCR: + + if ((OBJ_GET_TYPE(TOS1) == OBJ_TYPE_LST) + && (OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)) + { + retval = list_delItem(TOS1, + (int16_t)((pPmInt_t)TOS)->val); + } + + else if ((OBJ_GET_TYPE(TOS1) == OBJ_TYPE_DIC) + && (OBJ_GET_TYPE(TOS) <= OBJ_TYPE_HASHABLE_MAX)) + { + retval = dict_delItem(TOS1, TOS); + } + + /* Raise TypeError if obj is not a list or dict */ + else + { + PM_RAISE(retval, PM_RET_EX_TYPE); + } + + PM_BREAK_IF_ERROR(retval); + PM_SP -= 2; + continue; +#endif /* HAVE_DEL */ + + case BINARY_LSHIFT: + case INPLACE_LSHIFT: + /* If both objs are ints, perform the op */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)) + { + retval = int_new(((pPmInt_t)TOS1)->val << + ((pPmInt_t)TOS)->val, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* Otherwise raise a TypeError */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + + case BINARY_RSHIFT: + case INPLACE_RSHIFT: + /* If both objs are ints, perform the op */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)) + { + retval = int_new(((pPmInt_t)TOS1)->val >> + ((pPmInt_t)TOS)->val, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* Otherwise raise a TypeError */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + + case BINARY_AND: + case INPLACE_AND: + /* If both objs are ints, perform the op */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)) + { + retval = int_new(((pPmInt_t)TOS1)->val & + ((pPmInt_t)TOS)->val, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* Otherwise raise a TypeError */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + + case BINARY_XOR: + case INPLACE_XOR: + /* If both objs are ints, perform the op */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)) + { + retval = int_new(((pPmInt_t)TOS1)->val ^ + ((pPmInt_t)TOS)->val, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* Otherwise raise a TypeError */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + + case BINARY_OR: + case INPLACE_OR: + /* If both objs are ints, perform the op */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)) + { + retval = int_new(((pPmInt_t)TOS1)->val | + ((pPmInt_t)TOS)->val, &pobj3); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + TOS = pobj3; + continue; + } + + /* Otherwise raise a TypeError */ + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + +#ifdef HAVE_PRINT + case PRINT_EXPR: + /* Print interactive expression */ + /* Fallthrough */ + + case PRINT_ITEM: + if (gVmGlobal.needSoftSpace && (bc == PRINT_ITEM)) + { + retval = plat_putByte(' '); + PM_BREAK_IF_ERROR(retval); + } + gVmGlobal.needSoftSpace = C_TRUE; + + /* Print out topmost stack element */ + retval = obj_print(TOS, (uint8_t)(bc == PRINT_EXPR), C_FALSE); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + if (bc != PRINT_EXPR) + { + continue; + } + /* If PRINT_EXPR, Fallthrough to print a newline */ + + case PRINT_NEWLINE: + gVmGlobal.needSoftSpace = C_FALSE; + if (gVmGlobal.somethingPrinted) + { + retval = plat_putByte('\n'); + gVmGlobal.somethingPrinted = C_FALSE; + } + PM_BREAK_IF_ERROR(retval); + continue; +#endif /* HAVE_PRINT */ + + case BREAK_LOOP: + { + pPmBlock_t pb1 = PM_FP->fo_blockstack; + + /* Ensure there's a block */ + C_ASSERT(pb1 != C_NULL); + + /* Delete blocks until first loop block */ + while ((pb1->b_type != B_LOOP) && (pb1->next != C_NULL)) + { + pobj2 = (pPmObj_t)pb1; + pb1 = pb1->next; + retval = heap_freeChunk(pobj2); + PM_BREAK_IF_ERROR(retval); + } + + /* Test again outside while loop */ + PM_BREAK_IF_ERROR(retval); + + /* Restore PM_SP */ + PM_SP = pb1->b_sp; + + /* Goto handler */ + PM_IP = pb1->b_handler; + + /* Pop and delete this block */ + PM_FP->fo_blockstack = pb1->next; + retval = heap_freeChunk((pPmObj_t)pb1); + PM_BREAK_IF_ERROR(retval); + } + continue; + + case LOAD_LOCALS: + /* Pushes local attrs dict of current frame */ + /* WARNING: does not copy fo_locals to attrs */ + PM_PUSH((pPmObj_t)PM_FP->fo_attrs); + continue; + + case RETURN_VALUE: + /* Get expiring frame's TOS */ + pobj2 = PM_POP(); + +#if 0 /*__DEBUG__*/ + /* #251: This safety check is disabled because it breaks ipm */ + /* #109: Check that stack should now be empty */ + /* If this is regular frame (not native and not a generator) */ + if ((PM_FP != (pPmFrame_t)(&gVmGlobal.nativeframe)) && + !(PM_FP->fo_func->f_co->co_flags & CO_GENERATOR)) + { + /* An empty stack points one past end of locals */ + t8 = PM_FP->fo_func->f_co->co_nlocals; + C_ASSERT(PM_SP == &(PM_FP->fo_locals[t8])); + } +#endif /* __DEBUG__ */ + + /* Keep ref of expiring frame */ + pobj1 = (pPmObj_t)PM_FP; + C_ASSERT(OBJ_GET_TYPE(pobj1) == OBJ_TYPE_FRM); + + /* If no previous frame, quit thread */ + if (PM_FP->fo_back == C_NULL) + { + gVmGlobal.pthread->interpctrl = INTERP_CTRL_EXIT; + retval = PM_RET_OK; + break; + } + + /* Otherwise return to previous frame */ + PM_FP = PM_FP->fo_back; + +#ifdef HAVE_GENERATORS + /* If returning function was a generator */ + if (((pPmFrame_t)pobj1)->fo_func->f_co->co_flags & CO_GENERATOR) + { + /* Raise a StopIteration exception */ + PM_RAISE(retval, PM_RET_EX_STOP); + break; + } +#endif /* HAVE_GENERATORS */ + +#ifdef HAVE_CLASSES + /* + * If returning function was class initializer + * do not push a return object + */ + if (((pPmFrame_t)pobj1)->fo_isInit) + { + /* Raise TypeError if __init__ did not return None */ + if (pobj2 != PM_NONE) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + } + else +#endif /* HAVE_CLASSES */ + + /* + * Push frame's return val, except if the expiring frame + * was due to an import statement + */ + if (!(((pPmFrame_t)pobj1)->fo_isImport)) + { + PM_PUSH(pobj2); + } + + /* Deallocate expired frame */ + PM_BREAK_IF_ERROR(heap_freeChunk(pobj1)); + continue; + +#ifdef HAVE_IMPORTS + case IMPORT_STAR: + /* #102: Implement the remaining IMPORT_ bytecodes */ + /* Expect a module on the top of the stack */ + C_ASSERT(OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD); + + /* Update PM_FP's attrs with those of the module on the stack */ + retval = dict_update((pPmObj_t)PM_FP->fo_attrs, + (pPmObj_t)((pPmFunc_t)TOS)->f_attrs, + C_TRUE); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + continue; +#endif /* HAVE_IMPORTS */ + +#ifdef HAVE_GENERATORS + case YIELD_VALUE: + /* #207: Add support for the yield keyword */ + /* Get expiring frame's TOS */ + pobj1 = PM_POP(); + + /* Raise TypeError if __init__ did not return None */ + /* (Yield means this is a generator) */ + if ((PM_FP)->fo_isInit) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + /* Return to previous frame */ + PM_FP = PM_FP->fo_back; + + /* Push yield value onto caller's TOS */ + PM_PUSH(pobj1); + continue; +#endif /* HAVE_GENERATORS */ + + case POP_BLOCK: + /* Get ptr to top block */ + pobj1 = (pPmObj_t)PM_FP->fo_blockstack; + + /* If there's no block, raise SystemError */ + C_ASSERT(pobj1 != C_NULL); + + /* Pop block */ + PM_FP->fo_blockstack = PM_FP->fo_blockstack->next; + + /* Set stack to previous level, jump to code outside block */ + PM_SP = ((pPmBlock_t)pobj1)->b_sp; + PM_IP = ((pPmBlock_t)pobj1)->b_handler; + + PM_BREAK_IF_ERROR(heap_freeChunk(pobj1)); + continue; + +#ifdef HAVE_CLASSES + case BUILD_CLASS: + /* Create and push new class */ + retval = class_new(TOS, TOS1, TOS2, &pobj2); + PM_BREAK_IF_ERROR(retval); + PM_SP -= 2; + TOS = pobj2; + continue; +#endif /* HAVE_CLASSES */ + + + /*************************************************** + * All bytecodes after 90 (0x5A) have a 2-byte arg + * that needs to be swallowed using GET_ARG(). + **************************************************/ + + case STORE_NAME: + /* Get name index */ + t16 = GET_ARG(); + + /* Get key */ + pobj2 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Set key=val in current frame's attrs dict */ + retval = dict_setItem((pPmObj_t)PM_FP->fo_attrs, pobj2, TOS); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + continue; + +#ifdef HAVE_DEL + case DELETE_NAME: + /* Get name index */ + t16 = GET_ARG(); + + /* Get key */ + pobj2 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Remove key,val pair from current frame's attrs dict */ + retval = dict_delItem((pPmObj_t)PM_FP->fo_attrs, pobj2); + PM_BREAK_IF_ERROR(retval); + continue; +#endif /* HAVE_DEL */ + + case UNPACK_SEQUENCE: + /* Get ptr to sequence */ + pobj1 = PM_POP(); + +#ifdef HAVE_BYTEARRAY + /* If object is an instance, get the thing it contains */ + if (OBJ_GET_TYPE(pobj1) == OBJ_TYPE_CLI) + { + retval = dict_getItem((pPmObj_t)((pPmInstance_t)pobj1)->cli_attrs, + PM_NONE, + &pobj2); + PM_RETURN_IF_ERROR(retval); + pobj1 = pobj2; + } +#endif /* HAVE_BYTEARRAY */ + + /* + * Get the length of the sequence; this will + * raise TypeError if obj is not a sequence. + * + * #59: Unpacking to a Dict shall not be supported + */ + retval = seq_getLength(pobj1, (uint16_t *)&t16); + if (retval != PM_RET_OK) + { + GET_ARG(); + break; + } + + /* Raise ValueError if seq length does not match num args */ + if (t16 != GET_ARG()) + { + PM_RAISE(retval, PM_RET_EX_VAL); + break; + } + + /* Push sequence's objs onto stack */ + for (; --t16 >= 0;) + { + retval = seq_getSubscript(pobj1, t16, &pobj2); + PM_BREAK_IF_ERROR(retval); + PM_PUSH(pobj2); + } + + /* Test again outside the for loop */ + PM_BREAK_IF_ERROR(retval); + continue; + + case FOR_ITER: + t16 = GET_ARG(); + +#ifdef HAVE_GENERATORS + /* If TOS is an instance, call next method */ + if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI) + { + /* Get the next() func */ + retval = class_getAttr(TOS, PM_NEXT_STR, &pobj1); + PM_BREAK_IF_ERROR(retval); + + /* Push the func and instance as an arg */ + pobj2 = TOS; + PM_PUSH(pobj1); + PM_PUSH(pobj2); + t16 = 1; + + /* Ensure pobj1 is the func */ + goto CALL_FUNC_FOR_ITER; + } + else +#endif /* HAVE_GENERATORS */ + { + /* Get the next item in the sequence iterator */ + retval = seqiter_getNext(TOS, &pobj2); + } + + /* Catch StopIteration early: pop iterator and break loop */ + if (retval == PM_RET_EX_STOP) + { + PM_SP--; + retval = PM_RET_OK; + PM_IP += t16; + continue; + } + PM_BREAK_IF_ERROR(retval); + + /* Push the next item onto the stack */ + PM_PUSH(pobj2); + continue; + + case STORE_ATTR: + /* TOS.name = TOS1 */ + /* Get names index */ + t16 = GET_ARG(); + + /* Get attrs dict from obj */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FXN) + || (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD)) + { + pobj2 = (pPmObj_t)((pPmFunc_t)TOS)->f_attrs; + } + +#ifdef HAVE_CLASSES + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLO) + { + pobj2 = (pPmObj_t)((pPmClass_t)TOS)->cl_attrs; + } + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI) + { + pobj2 = (pPmObj_t)((pPmInstance_t)TOS)->cli_attrs; + } + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MTH) + { + pobj2 = (pPmObj_t)((pPmMethod_t)TOS)->m_attrs; + } +#endif /* HAVE_CLASSES */ + + /* Other types result in an AttributeError */ + else + { + PM_RAISE(retval, PM_RET_EX_ATTR); + break; + } + + /* If attrs is not a dict, raise SystemError */ + if (OBJ_GET_TYPE(pobj2) != OBJ_TYPE_DIC) + { + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + + /* Get name/key obj */ + pobj3 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Set key=val in obj's dict */ + retval = dict_setItem(pobj2, pobj3, TOS1); + PM_BREAK_IF_ERROR(retval); + PM_SP -= 2; + continue; + +#ifdef HAVE_DEL + case DELETE_ATTR: + /* del TOS.name */ + /* Get names index */ + t16 = GET_ARG(); + + /* Get attrs dict from obj */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FXN) + || (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD)) + { + pobj2 = (pPmObj_t)((pPmFunc_t)TOS)->f_attrs; + } + +#ifdef HAVE_CLASSES + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLO) + { + pobj2 = (pPmObj_t)((pPmClass_t)TOS)->cl_attrs; + } + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI) + { + pobj2 = (pPmObj_t)((pPmInstance_t)TOS)->cli_attrs; + } + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MTH) + { + pobj2 = (pPmObj_t)((pPmMethod_t)TOS)->m_attrs; + } +#endif /* HAVE_CLASSES */ + + /* Other types result in an AttributeError */ + else + { + PM_RAISE(retval, PM_RET_EX_ATTR); + break; + } + + /* If attrs is not a dict, raise SystemError */ + if (OBJ_GET_TYPE(pobj2) != OBJ_TYPE_DIC) + { + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + + /* Get name/key obj */ + pobj3 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Remove key,val from obj's dict */ + retval = dict_delItem(pobj2, pobj3); + + /* Raise an AttributeError if key is not found */ + if (retval == PM_RET_EX_KEY) + { + PM_RAISE(retval, PM_RET_EX_ATTR); + } + + PM_BREAK_IF_ERROR(retval); + PM_SP--; + continue; +#endif /* HAVE_DEL */ + + case STORE_GLOBAL: + /* Get name index */ + t16 = GET_ARG(); + + /* Get key */ + pobj2 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Set key=val in global dict */ + retval = dict_setItem((pPmObj_t)PM_FP->fo_globals, pobj2, TOS); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + continue; + +#ifdef HAVE_DEL + case DELETE_GLOBAL: + /* Get name index */ + t16 = GET_ARG(); + + /* Get key */ + pobj2 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Remove key,val from globals */ + retval = dict_delItem((pPmObj_t)PM_FP->fo_globals, pobj2); + PM_BREAK_IF_ERROR(retval); + continue; +#endif /* HAVE_DEL */ + + case DUP_TOPX: + t16 = GET_ARG(); + C_ASSERT(t16 <= 3); + + pobj1 = TOS; + pobj2 = TOS1; + pobj3 = TOS2; + if (t16 >= 3) + PM_PUSH(pobj3); + if (t16 >= 2) + PM_PUSH(pobj2); + if (t16 >= 1) + PM_PUSH(pobj1); + continue; + + case LOAD_CONST: + /* Get const's index in CO */ + t16 = GET_ARG(); + + /* Push const on stack */ + PM_PUSH(PM_FP->fo_func->f_co->co_consts->val[t16]); + continue; + + case LOAD_NAME: + /* Get name index */ + t16 = GET_ARG(); + + /* Get name from names tuple */ + pobj1 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Get value from frame's attrs dict */ + retval = dict_getItem((pPmObj_t)PM_FP->fo_attrs, pobj1, &pobj2); + if (retval == PM_RET_EX_KEY) + { + /* Get val from globals */ + retval = dict_getItem((pPmObj_t)PM_FP->fo_globals, + pobj1, &pobj2); + + /* Check for name in the builtins module if it is loaded */ + if ((retval == PM_RET_EX_KEY) && (PM_PBUILTINS != C_NULL)) + { + /* Get val from builtins */ + retval = dict_getItem(PM_PBUILTINS, pobj1, &pobj2); + if (retval == PM_RET_EX_KEY) + { + /* Name not defined, raise NameError */ + PM_RAISE(retval, PM_RET_EX_NAME); + break; + } + } + } + PM_BREAK_IF_ERROR(retval); + PM_PUSH(pobj2); + continue; + + case BUILD_TUPLE: + /* Get num items */ + t16 = GET_ARG(); + retval = tuple_new(t16, &pobj1); + PM_BREAK_IF_ERROR(retval); + + /* Fill tuple with ptrs to objs */ + for (; --t16 >= 0;) + { + ((pPmTuple_t)pobj1)->val[t16] = PM_POP(); + } + PM_PUSH(pobj1); + continue; + + case BUILD_LIST: + t16 = GET_ARG(); + retval = list_new(&pobj1); + PM_BREAK_IF_ERROR(retval); + for (; --t16 >= 0;) + { + /* Insert obj into list */ + heap_gcPushTempRoot(pobj1, &objid); + retval = list_insert(pobj1, 0, TOS); + heap_gcPopTempRoot(objid); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + } + /* Test again outside for loop */ + PM_BREAK_IF_ERROR(retval); + + /* push list onto stack */ + PM_PUSH(pobj1); + continue; + + case BUILD_MAP: + /* Argument is ignored */ + t16 = GET_ARG(); + retval = dict_new(&pobj1); + PM_BREAK_IF_ERROR(retval); + PM_PUSH(pobj1); + continue; + + case LOAD_ATTR: + /* Implements TOS.attr */ + t16 = GET_ARG(); + +#ifdef HAVE_AUTOBOX + /* Autobox the object, if necessary */ + retval = class_autobox(&TOS); + PM_BREAK_IF_ERROR(retval); +#endif + + /* Get attrs dict from obj */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FXN) || + (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD)) + { + pobj1 = (pPmObj_t)((pPmFunc_t)TOS)->f_attrs; + } + +#ifdef HAVE_CLASSES + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLO) + { + pobj1 = (pPmObj_t)((pPmClass_t)TOS)->cl_attrs; + } + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI) + { + pobj1 = (pPmObj_t)((pPmInstance_t)TOS)->cli_attrs; + } + else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MTH) + { + pobj1 = (pPmObj_t)((pPmMethod_t)TOS)->m_attrs; + } +#endif /* HAVE_CLASSES */ + + /* Other types result in an AttributeError */ + else + { + PM_RAISE(retval, PM_RET_EX_ATTR); + break; + } + + /* If attrs is not a dict, raise SystemError */ + if (OBJ_GET_TYPE(pobj1) != OBJ_TYPE_DIC) + { + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + + /* Get name */ + pobj2 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Get attr with given name */ + retval = dict_getItem(pobj1, pobj2, &pobj3); + +#ifdef HAVE_CLASSES + /* + * If attr is not found and object is a class or instance, + * try to get the attribute from the class attrs or parent(s) + */ + if ((retval == PM_RET_EX_KEY) && + ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLO) + || (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI))) + { + retval = class_getAttr(TOS, pobj2, &pobj3); + } +#endif /* HAVE_CLASSES */ + + /* Raise an AttributeError if key is not found */ + if (retval == PM_RET_EX_KEY) + { + PM_RAISE(retval, PM_RET_EX_ATTR); + } + PM_BREAK_IF_ERROR(retval); + +#ifdef HAVE_CLASSES + /* If obj is an instance and attr is a func, create method */ + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI) && + (OBJ_GET_TYPE(pobj3) == OBJ_TYPE_FXN)) + { + pobj2 = pobj3; + retval = class_method(TOS, pobj2, &pobj3); + PM_BREAK_IF_ERROR(retval); + } +#endif /* HAVE_CLASSES */ + + /* Put attr on the stack */ + TOS = pobj3; + continue; + + case COMPARE_OP: + retval = PM_RET_OK; + t16 = GET_ARG(); + +#ifdef HAVE_FLOAT + if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT) + || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT)) + { + retval = float_compare(TOS1, TOS, &pobj3, (PmCompare_t)t16); + PM_SP--; + TOS = pobj3; + continue; + } +#endif /* HAVE_FLOAT */ + + /* Handle all integer-to-integer (or bool) comparisons */ + if (((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT) + || (OBJ_GET_TYPE(TOS) == OBJ_TYPE_BOOL)) + && ((OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT) + || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_BOOL))) + { + int32_t a = ((pPmInt_t)TOS1)->val; + int32_t b = ((pPmInt_t)TOS)->val; + + switch (t16) + { + /* *INDENT-OFF* */ + case COMP_LT: t8 = (int8_t)(a < b); break; + case COMP_LE: t8 = (int8_t)(a <= b); break; + case COMP_EQ: t8 = (int8_t)(a == b); break; + case COMP_NE: t8 = (int8_t)(a != b); break; + case COMP_GT: t8 = (int8_t)(a > b); break; + case COMP_GE: t8 = (int8_t)(a >= b); break; + case COMP_IS: t8 = (int8_t)(TOS == TOS1); break; + case COMP_IS_NOT: t8 = (int8_t)(TOS != TOS1);break; + case COMP_IN: + case COMP_NOT_IN: + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + + default: + /* Other compares are not yet supported */ + PM_RAISE(retval, PM_RET_EX_SYS); + break; + /* *INDENT-ON* */ + } + PM_BREAK_IF_ERROR(retval); + pobj3 = (t8) ? PM_TRUE : PM_FALSE; + } + + /* Handle non-integer comparisons */ + else + { + retval = PM_RET_OK; + switch (t16) + { + case COMP_EQ: + case COMP_NE: + /* Handle equality for non-int types */ + pobj3 = PM_FALSE; + t8 = obj_compare(TOS, TOS1); + if (((t8 == C_SAME) && (t16 == COMP_EQ)) + || ((t8 == C_DIFFER) && (t16 == COMP_NE))) + { + pobj3 = PM_TRUE; + } + break; + + case COMP_IN: + case COMP_NOT_IN: + /* Handle membership comparisons */ + pobj3 = PM_FALSE; + retval = obj_isIn(TOS, TOS1); + if (retval == PM_RET_OK) + { + if (t16 == COMP_IN) + { + pobj3 = PM_TRUE; + } + } + else if (retval == PM_RET_NO) + { + retval = PM_RET_OK; + if (t16 == COMP_NOT_IN) + { + pobj3 = PM_TRUE; + } + } + break; + + case COMP_IS: + pobj3 = (TOS == TOS1) ? PM_TRUE : PM_FALSE; + break; + + case COMP_IS_NOT: + pobj3 = (TOS != TOS1) ? PM_TRUE : PM_FALSE; + break; + + default: + /* Other comparisons are not implemented */ + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + PM_BREAK_IF_ERROR(retval); + } + PM_SP--; + TOS = pobj3; + continue; + + case IMPORT_NAME: + /* Get name index */ + t16 = GET_ARG(); + + /* Get name String obj */ + pobj1 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Pop unused None object */ + PM_SP--; + + /* Ensure "level" is -1; no support for relative import yet */ + C_ASSERT(obj_compare(TOS, PM_NEGONE) == C_SAME); + + /* #178: Fix import so modules are reused */ + /* Return the module if found in the modules dict (cache) */ + retval = dict_getItem(PM_PBUILTINS, PM_MD_STR, &pobj3); + PM_BREAK_IF_ERROR(retval); + retval = dict_getItem(pobj3, pobj1, &pobj2); + if (retval == PM_RET_OK) + { + TOS = pobj2; + continue; + } + if (retval != PM_RET_EX_KEY) + { + break; + } + + /* Load module from image */ + retval = mod_import(pobj1, &pobj2); + PM_BREAK_IF_ERROR(retval); + + /* #178: Fix import so modules are reused */ + /* + * Store the module's attrs/globals under the module's name + * in the global module dict (cache) + */ + heap_gcPushTempRoot(pobj2, &objid); + retval = dict_setItem(pobj3, pobj1, pobj2); + heap_gcPopTempRoot(objid); + PM_BREAK_IF_ERROR(retval); + + /* Put Module on top of stack */ + TOS = pobj2; + + /* Code after here is a duplicate of CALL_FUNCTION */ + /* Make frame object to interpret the module's root code */ + heap_gcPushTempRoot(pobj2, &objid); + retval = frame_new(pobj2, &pobj3); + heap_gcPopTempRoot(objid); + PM_BREAK_IF_ERROR(retval); + + /* No arguments to pass */ + + /* Keep ref to current frame */ + ((pPmFrame_t)pobj3)->fo_back = PM_FP; + + /* Handle to have None popped on return */ + ((pPmFrame_t)pobj3)->fo_isImport = (uint8_t)1; + + /* Set new frame */ + PM_FP = (pPmFrame_t)pobj3; + continue; + +#ifdef HAVE_IMPORTS + case IMPORT_FROM: + /* #102: Implement the remaining IMPORT_ bytecodes */ + /* Expect the module on the top of the stack */ + C_ASSERT(OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD); + pobj1 = TOS; + + /* Get the name of the object to import */ + t16 = GET_ARG(); + pobj2 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Get the object from the module's attributes */ + retval = dict_getItem((pPmObj_t)((pPmFunc_t)pobj1)->f_attrs, + pobj2, &pobj3); + PM_BREAK_IF_ERROR(retval); + + /* Push the object onto the top of the stack */ + PM_PUSH(pobj3); + continue; +#endif /* HAVE_IMPORTS */ + + case JUMP_FORWARD: + t16 = GET_ARG(); + PM_IP += t16; + continue; + + case JUMP_IF_FALSE: + t16 = GET_ARG(); + if (obj_isFalse(TOS)) + { + PM_IP += t16; + } + continue; + + case JUMP_IF_TRUE: + t16 = GET_ARG(); + if (!obj_isFalse(TOS)) + { + PM_IP += t16; + } + continue; + + case JUMP_ABSOLUTE: + case CONTINUE_LOOP: + /* Get target offset (bytes) */ + t16 = GET_ARG(); + + /* Jump to base_ip + arg */ + PM_IP = PM_FP->fo_func->f_co->co_codeaddr + t16; + continue; + + case LOAD_GLOBAL: + /* Get name */ + t16 = GET_ARG(); + pobj1 = PM_FP->fo_func->f_co->co_names->val[t16]; + + /* Try globals first */ + retval = dict_getItem((pPmObj_t)PM_FP->fo_globals, + pobj1, &pobj2); + + /* If that didn't work, try builtins */ + if (retval == PM_RET_EX_KEY) + { + retval = dict_getItem(PM_PBUILTINS, pobj1, &pobj2); + + /* No such global, raise NameError */ + if (retval == PM_RET_EX_KEY) + { + PM_RAISE(retval, PM_RET_EX_NAME); + break; + } + } + PM_BREAK_IF_ERROR(retval); + PM_PUSH(pobj2); + continue; + + case SETUP_LOOP: + { + uint8_t *pchunk; + + /* Get block span (bytes) */ + t16 = GET_ARG(); + + /* Create block */ + retval = heap_getChunk(sizeof(PmBlock_t), &pchunk); + PM_BREAK_IF_ERROR(retval); + pobj1 = (pPmObj_t)pchunk; + OBJ_SET_TYPE(pobj1, OBJ_TYPE_BLK); + + /* Store current stack pointer */ + ((pPmBlock_t)pobj1)->b_sp = PM_SP; + + /* Default handler is to exit block/loop */ + ((pPmBlock_t)pobj1)->b_handler = PM_IP + t16; + ((pPmBlock_t)pobj1)->b_type = B_LOOP; + + /* Insert block into blockstack */ + ((pPmBlock_t)pobj1)->next = PM_FP->fo_blockstack; + PM_FP->fo_blockstack = (pPmBlock_t)pobj1; + continue; + } + + case LOAD_FAST: + t16 = GET_ARG(); + PM_PUSH(PM_FP->fo_locals[t16]); + continue; + + case STORE_FAST: + t16 = GET_ARG(); + PM_FP->fo_locals[t16] = PM_POP(); + continue; + +#ifdef HAVE_DEL + case DELETE_FAST: + t16 = GET_ARG(); + PM_FP->fo_locals[t16] = PM_NONE; + continue; +#endif /* HAVE_DEL */ + +#ifdef HAVE_ASSERT + case RAISE_VARARGS: + t16 = GET_ARG(); + + /* Only supports taking 1 arg for now */ + if (t16 != 1) + { + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + + /* Load Exception class from builtins */ + retval = dict_getItem(PM_PBUILTINS, PM_EXCEPTION_STR, &pobj2); + if (retval != PM_RET_OK) + { + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + + /* Raise TypeError if TOS is not an instance of Exception */ + pobj1 = TOS; + if ((OBJ_GET_TYPE(pobj1) != OBJ_TYPE_CLO) + || !class_isSubclass(pobj1, pobj2)) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + /* Push the traceback, parameter and exception object */ + TOS = PM_NONE; + PM_PUSH(PM_NONE); + PM_PUSH(pobj1); + + /* Get the exception's code attr */ + retval = dict_getItem((pPmObj_t)((pPmClass_t)pobj1)->cl_attrs, + PM_CODE_STR, &pobj2); + PM_BREAK_IF_ERROR(retval); + + /* Raise exception by breaking with retval set to code */ + PM_RAISE(retval, (PmReturn_t)(((pPmInt_t)pobj2)->val & 0xFF)); + break; +#endif /* HAVE_ASSERT */ + + case CALL_FUNCTION: + /* Get num args */ + t16 = GET_ARG(); + + /* Ensure no keyword args */ + if ((t16 & (uint16_t)0xFF00) != 0) + { + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + + /* Get the callable */ + pobj1 = STACK(t16); + + /* Useless push to get temp-roots stack level used in cleanup */ + heap_gcPushTempRoot(pobj1, &objid); + + C_DEBUG_PRINT(VERBOSITY_LOW, + "interpret(), CALL_FUNCTION on <obj type=%d @ %p>\n", + OBJ_GET_TYPE(pobj1), pobj1); + +#ifdef HAVE_GENERATORS + /* If the callable is a generator function (can't be native) */ + if ((OBJ_GET_TYPE(pobj1) == OBJ_TYPE_FXN) + && (OBJ_GET_TYPE(((pPmFunc_t)pobj1)->f_co) == OBJ_TYPE_COB) + && (((pPmFunc_t)pobj1)->f_co->co_flags & CO_GENERATOR)) + { +#ifdef HAVE_DEFAULTARGS + /* Num required args := argcount - num default args */ + t8 = ((pPmFunc_t)pobj1)->f_co->co_argcount; + if (((pPmFunc_t)pobj1)->f_defaultargs != C_NULL) + { + t8 -= ((pPmTuple_t)((pPmFunc_t)pobj1)->f_defaultargs)-> + length; + } + + /* + * Raise a TypeError if num args passed + * is more than allowed or less than required + */ + if (((t16 & ((uint8_t)0xFF)) + > ((pPmFunc_t)pobj1)->f_co->co_argcount) + || ((t16 & ((uint8_t)0xFF)) < t8)) +#else + if ((t16 & ((uint8_t)0xFF)) != + ((pPmFunc_t)pobj1)->f_co->co_argcount) +#endif /* HAVE_DEFAULTARGS */ + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + /* Collect the function and arguments into a tuple */ + retval = tuple_new(t16 + 1, &pobj2); + heap_gcPushTempRoot(pobj2, &objid2); + PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP); + sli_memcpy((uint8_t *)&((pPmTuple_t)pobj2)->val, + (uint8_t *)&STACK(t16), + (t16 + 1) * sizeof(pPmObj_t)); + + /* Remove old args, push func/args tuple as one arg */ + PM_SP -= t16; + PM_PUSH(pobj2); + t16 = 1; + + /* Set pobj1 and stack to create an instance of Generator */ + retval = dict_getItem(PM_PBUILTINS, PM_GENERATOR_STR, + &pobj1); + C_ASSERT(retval == PM_RET_OK); + STACK(t16) = pobj1; + } +#endif /* HAVE_GENERATORS */ + +#ifdef HAVE_CLASSES + /* If the callable is a class, create an instance of it */ + if (OBJ_GET_TYPE(pobj1) == OBJ_TYPE_CLO) + { + /* This marks that the original callable was a class */ + bc = 0; + + /* Replace class with new instance */ + retval = class_instantiate(pobj1, &pobj2); + heap_gcPushTempRoot(pobj2, &objid2); + STACK(t16) = pobj2; + + /* If __init__ does not exist */ + pobj3 = C_NULL; + retval = class_getAttr(pobj1, PM_INIT_STR, &pobj3); + if (retval == PM_RET_EX_KEY) + { + /* Raise TypeError if there are args */ + if (t16 > 0) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + goto CALL_FUNC_CLEANUP; + } + + /* Otherwise, continue with instance */ + heap_gcPopTempRoot(objid); + continue; + } + else if (retval != PM_RET_OK) + { + PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP); + } + + /* Slide the arguments up 1 slot in the stack */ + PM_SP++; + for (t8 = 0; t8 < t16; t8++) + { + STACK(t8) = STACK(t8 + 1); + } + + /* Convert __init__ to method, insert it as the callable */ + retval = class_method(pobj2, pobj3, &pobj1); + PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP); + heap_gcPushTempRoot(pobj2, &objid2); + STACK(t16) = pobj1; + /* Fall through to call the method */ + } + + if (OBJ_GET_TYPE(pobj1) == OBJ_TYPE_MTH) + { + /* Set the method's func to be the callable */ + STACK(t16) = (pPmObj_t)((pPmMethod_t)pobj1)->m_func; + + /* Slide the arguments up 1 slot in the stack */ + PM_SP++; + for (t8 = 0; t8 < t16; t8++) + { + STACK(t8) = STACK(t8 + 1); + } + + /* Insert instance as "self" arg to the method */ + STACK(t16++) = (pPmObj_t)((pPmMethod_t)pobj1)->m_instance; + + /* Refresh the callable */ + pobj1 = (pPmObj_t)((pPmMethod_t)pobj1)->m_func; + } +#endif /* HAVE_CLASSES */ + +#ifdef HAVE_GENERATORS +CALL_FUNC_FOR_ITER: +#endif /* HAVE_GENERATORS */ + /* Raise a TypeError if object is not callable */ + if (OBJ_GET_TYPE(pobj1) != OBJ_TYPE_FXN) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + goto CALL_FUNC_CLEANUP; + } + + /* If it is a regular func (not native) */ + if (OBJ_GET_TYPE(((pPmFunc_t)pobj1)->f_co) == OBJ_TYPE_COB) + { + /* + * #132 Raise TypeError if num args does not match the + * code object's expected argcount + */ + +#ifdef HAVE_DEFAULTARGS + /* Num required args := argcount - num default args */ + t8 = ((pPmFunc_t)pobj1)->f_co->co_argcount; + if (((pPmFunc_t)pobj1)->f_defaultargs != C_NULL) + { + t8 -= ((pPmTuple_t)((pPmFunc_t)pobj1)->f_defaultargs)-> + length; + } + + /* + * Raise a TypeError if num args passed + * is more than allowed or less than required + */ + if (((t16 & ((uint8_t)0xFF)) + > ((pPmFunc_t)pobj1)->f_co->co_argcount) + || ((t16 & ((uint8_t)0xFF)) < t8)) +#else + if ((t16 & ((uint8_t)0xFF)) != + ((pPmFunc_t)pobj1)->f_co->co_argcount) +#endif /* HAVE_DEFAULTARGS */ + { + PM_RAISE(retval, PM_RET_EX_TYPE); + break; + } + + /* Make frame object to run the func object */ + retval = frame_new(pobj1, &pobj2); + heap_gcPushTempRoot(pobj2, &objid2); + PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP); + +#ifdef HAVE_CLASSES + /* + * If the original callable was a class, indicate that + * the frame is running the initializer so that + * its return object is checked for None and ignored. + */ + if (bc == 0) + { + ((pPmFrame_t)pobj2)->fo_isInit = C_TRUE; + } +#endif /* HAVE_CLASSES */ + +#ifdef HAVE_DEFAULTARGS + /* If this func has default arguments, put them in place */ + if (((pPmFunc_t)pobj1)->f_defaultargs != C_NULL) + { + int8_t i = 0; + + /* Copy default args into the new frame's locals */ + for ( /* t8 set above */ ; + t8 < ((pPmFunc_t)pobj1)->f_co->co_argcount; t8++) + { + ((pPmFrame_t)pobj2)->fo_locals[t8] = + ((pPmTuple_t)((pPmFunc_t)pobj1)-> + f_defaultargs)->val[i++]; + } + } +#endif /* HAVE_DEFAULTARGS */ + + /* Pass args to new frame */ + while (--t16 >= 0) + { + /* + * Pop args from stack right to left, + * since args are pushed left to right, + */ + ((pPmFrame_t)pobj2)->fo_locals[t16] = PM_POP(); + } + +#ifdef HAVE_CLOSURES + /* #256: Add support for closures */ + /* Copy arguments that become cellvars */ + if (((pPmFunc_t)pobj1)->f_co->co_cellvars != C_NULL) + { + for (t8 = 0; + t8 < ((pPmFunc_t)pobj1)->f_co->co_cellvars->length; + t8++) + { + if (((pPmInt_t)((pPmFunc_t)pobj1)-> + f_co->co_cellvars->val[t8])->val >= 0) + { + ((pPmFrame_t)pobj2)->fo_locals[ + ((pPmFunc_t)pobj1)->f_co->co_nlocals + t8] = + ((pPmFrame_t)pobj2)->fo_locals[ + ((pPmInt_t)(((pPmFunc_t)pobj1)-> + f_co->co_cellvars->val[t8]))->val + ]; + } + } + } + + /* Fill frame's freevars with references from closure */ + for (t8 = 0; + t8 < ((pPmFunc_t)pobj1)->f_co->co_nfreevars; + t8++) + { + C_ASSERT(((pPmFunc_t)pobj1)->f_closure != C_NULL); + ((pPmFrame_t)pobj2)->fo_locals[ + ((pPmFunc_t)pobj1)->f_co->co_nlocals + + ((((pPmFunc_t)pobj1)->f_co->co_cellvars == C_NULL) ? 0 : ((pPmFunc_t)pobj1)->f_co->co_cellvars->length) + + t8] = ((pPmFunc_t)pobj1)->f_closure->val[t8]; + } +#endif /* HAVE_CLOSURES */ + + /* Pop func obj */ + pobj3 = PM_POP(); + + /* Keep ref to current frame */ + ((pPmFrame_t)pobj2)->fo_back = PM_FP; + + /* Set new frame */ + PM_FP = (pPmFrame_t)pobj2; + } + + /* If it's native func */ + else if (OBJ_GET_TYPE(((pPmFunc_t)pobj1)->f_co) == + OBJ_TYPE_NOB) + { + /* Set number of locals (arguments) */ + gVmGlobal.nativeframe.nf_numlocals = (uint8_t)t16; + + /* Pop args from stack */ + while (--t16 >= 0) + { + gVmGlobal.nativeframe.nf_locals[t16] = PM_POP(); + } + + /* Set flag, so the GC knows a native session is active */ + gVmGlobal.nativeframe.nf_active = C_TRUE; + +#ifdef HAVE_GC + /* If the heap is low on memory, run the GC */ + if (heap_getAvail() < HEAP_GC_NF_THRESHOLD) + { + retval = heap_gcRun(); + PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP); + } +#endif /* HAVE_GC */ + + /* Pop the function object */ + PM_SP--; + + /* Get native function index */ + pobj2 = (pPmObj_t)((pPmFunc_t)pobj1)->f_co; + t16 = ((pPmNo_t)pobj2)->no_funcindx; + + /* + * CALL NATIVE FXN: pass caller's frame and numargs + */ + /* Positive index is a stdlib func */ + if (t16 >= 0) + { + retval = std_nat_fxn_table[t16] (&PM_FP); + } + + /* Negative index is a usrlib func */ + else + { + retval = usr_nat_fxn_table[-t16] (&PM_FP); + } + + /* + * RETURN FROM NATIVE FXN + */ + + /* Clear flag, so frame will not be marked by the GC */ + gVmGlobal.nativeframe.nf_active = C_FALSE; + +#ifdef HAVE_CLASSES + /* If class's __init__ called, do not push a return obj */ + if (bc == 0) + { + /* Raise TypeError if returned obj was not None */ + if ((retval == PM_RET_OK) + && (gVmGlobal.nativeframe.nf_stack != PM_NONE)) + { + PM_RAISE(retval, PM_RET_EX_TYPE); + goto CALL_FUNC_CLEANUP; + } + } + else +#endif /* HAVE_CLASSES */ + + /* If the frame pointer was switched, do nothing to TOS */ + if (retval == PM_RET_FRAME_SWITCH) + { + retval = PM_RET_OK; + } + + /* Otherwise, return the result from the native function */ + else + { + PM_PUSH(gVmGlobal.nativeframe.nf_stack); + } + } +CALL_FUNC_CLEANUP: + heap_gcPopTempRoot(objid); + PM_BREAK_IF_ERROR(retval); + continue; + + case MAKE_FUNCTION: + /* Get num default args to fxn */ + t16 = GET_ARG(); + + /* + * The current frame's globals become the function object's + * globals. The current frame is the container object + * of this new function object + */ + retval = func_new(TOS, (pPmObj_t)PM_FP->fo_globals, &pobj2); + PM_BREAK_IF_ERROR(retval); + + /* Put any default args in a tuple */ + if (t16 > 0) + { + +#ifdef HAVE_DEFAULTARGS + heap_gcPushTempRoot(pobj2, &objid); + retval = tuple_new(t16, &pobj3); + heap_gcPopTempRoot(objid); + PM_BREAK_IF_ERROR(retval); + PM_SP--; + while (--t16 >= 0) + { + ((pPmTuple_t)pobj3)->val[t16] = PM_POP(); + } + + /* Set func's default args */ + ((pPmFunc_t)pobj2)->f_defaultargs = (pPmTuple_t)pobj3; +#else + /* Default arguments not configured in pmfeatures.h */ + PM_RAISE(retval, PM_RET_EX_SYS); + break; +#endif /* HAVE_DEFAULTARGS */ + + } + else + { + PM_SP--; + } + + /* Push func obj */ + PM_PUSH(pobj2); + continue; + +#ifdef HAVE_CLOSURES + case MAKE_CLOSURE: + /* Get number of default args */ + t16 = GET_ARG(); + retval = func_new(TOS, (pPmObj_t)PM_FP->fo_globals, &pobj2); + PM_BREAK_IF_ERROR(retval); + + /* Set closure of the new function */ + ((pPmFunc_t)pobj2)->f_closure = (pPmTuple_t)TOS1; + PM_SP -= 2; + + /* Collect any default arguments into tuple */ + if (t16 > 0) + { + heap_gcPushTempRoot(pobj2, &objid); + retval = tuple_new(t16, &pobj3); + heap_gcPopTempRoot(objid); + PM_BREAK_IF_ERROR(retval); + + while (--t16 >= 0) + { + ((pPmTuple_t)pobj3)->val[t16] = PM_POP(); + } + ((pPmFunc_t)pobj2)->f_defaultargs = (pPmTuple_t)pobj3; + } + + /* Push new func with closure */ + PM_PUSH(pobj2); + continue; + + case LOAD_CLOSURE: + case LOAD_DEREF: + /* Loads the i'th cell of free variable storage onto TOS */ + t16 = GET_ARG(); + pobj1 = PM_FP->fo_locals[PM_FP->fo_func->f_co->co_nlocals + t16]; + if (pobj1 == C_NULL) + { + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + PM_PUSH(pobj1); + continue; + + case STORE_DEREF: + /* Stores TOS into the i'th cell of free variable storage */ + t16 = GET_ARG(); + PM_FP->fo_locals[PM_FP->fo_func->f_co->co_nlocals + t16] = PM_POP(); + continue; +#endif /* HAVE_CLOSURES */ + + + default: + /* SystemError, unknown or unimplemented opcode */ + PM_RAISE(retval, PM_RET_EX_SYS); + break; + } + +#ifdef HAVE_GENERATORS + /* If got a StopIteration exception, check for a B_LOOP block */ + if (retval == PM_RET_EX_STOP) + { + pobj1 = (pPmObj_t)PM_FP; + while ((retval == PM_RET_EX_STOP) && (pobj1 != C_NULL)) + { + pobj2 = (pPmObj_t)((pPmFrame_t)pobj1)->fo_blockstack; + while ((retval == PM_RET_EX_STOP) && (pobj2 != C_NULL)) + { + if (((pPmBlock_t)pobj2)->b_type == B_LOOP) + { + /* Resume execution where the block handler says */ + /* Set PM_FP first, so PM_SP and PM_IP are set in the frame */ + PM_FP = (pPmFrame_t)pobj1; + PM_SP = ((pPmBlock_t)pobj2)->b_sp; + PM_IP = ((pPmBlock_t)pobj2)->b_handler; + ((pPmFrame_t)pobj1)->fo_blockstack = + ((pPmFrame_t)pobj1)->fo_blockstack->next; + retval = PM_RET_OK; + break; + } + + pobj2 = (pPmObj_t)((pPmBlock_t)pobj2)->next; + } + pobj1 = (pPmObj_t)((pPmFrame_t)pobj1)->fo_back; + } + if (retval == PM_RET_OK) + { + continue; + } + } +#endif /* HAVE_GENERATORS */ + + /* + * If execution reaches this point, it is because + * a return value (from above) is not OK or we should exit the thread + * (return of the function). In any case, remove the + * current thread and reschedule. + */ + PM_REPORT_IF_ERROR(retval); + + /* If this is the last thread, return the error code */ + if ((gVmGlobal.threadList->length <= 1) && (retval != PM_RET_OK)) + { + break; + } + + retval = list_remove((pPmObj_t)gVmGlobal.threadList, + (pPmObj_t)gVmGlobal.pthread); + gVmGlobal.pthread = C_NULL; + PM_BREAK_IF_ERROR(retval); + + retval = interp_reschedule(); + PM_BREAK_IF_ERROR(retval); + } + + return retval; +} + + +PmReturn_t +interp_reschedule(void) +{ + PmReturn_t retval = PM_RET_OK; + static uint8_t threadIndex = (uint8_t)0; + pPmObj_t pobj; + + /* If there are no threads in the runnable list, null the active thread */ + if (gVmGlobal.threadList->length == 0) + { + gVmGlobal.pthread = C_NULL; + } + + /* Otherwise, get the next thread in the list (round robin) */ + else + { + if (++threadIndex >= gVmGlobal.threadList->length) + { + threadIndex = (uint8_t)0; + } + retval = list_getItem((pPmObj_t)gVmGlobal.threadList, threadIndex, + &pobj); + gVmGlobal.pthread = (pPmThread_t)pobj; + PM_RETURN_IF_ERROR(retval); + } + + /* Clear flag to indicate a reschedule has occurred */ + interp_setRescheduleFlag(0); + return retval; +} + + +PmReturn_t +interp_addThread(pPmFunc_t pfunc) +{ + PmReturn_t retval; + pPmObj_t pframe; + pPmObj_t pthread; + uint8_t objid1, objid2; + + /* Create a frame for the func */ + retval = frame_new((pPmObj_t)pfunc, &pframe); + PM_RETURN_IF_ERROR(retval); + + /* Create a thread with this new frame */ + heap_gcPushTempRoot(pframe, &objid1); + retval = thread_new(pframe, &pthread); + if (retval != PM_RET_OK) + { + heap_gcPopTempRoot(objid1); + return retval; + } + + /* Add thread to end of list */ + heap_gcPushTempRoot(pthread, &objid2); + retval = list_append((pPmObj_t)gVmGlobal.threadList, pthread); + heap_gcPopTempRoot(objid1); + return retval; +} + + +void +interp_setRescheduleFlag(uint8_t boolean) +{ + gVmGlobal.reschedule = boolean; +}