mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-10 18:24:11 +01:00
2299 lines
78 KiB
C
2299 lines
78 KiB
C
/*
|
|
# This file is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall.
|
|
#
|
|
# This file is part of the PyMite VM.
|
|
# The PyMite VM is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU GENERAL PUBLIC LICENSE Version 2.
|
|
#
|
|
# The PyMite VM is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
# A copy of the GNU GENERAL PUBLIC LICENSE Version 2
|
|
# is seen in the file COPYING in this directory.
|
|
*/
|
|
|
|
|
|
#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 */
|
|
|
|
/* Raise TypeError if args aren't ints */
|
|
if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT)
|
|
|| (OBJ_GET_TYPE(TOS1) != OBJ_TYPE_INT))
|
|
{
|
|
PM_RAISE(retval, PM_RET_EX_TYPE);
|
|
break;
|
|
}
|
|
|
|
/* Raise ZeroDivisionError if denominator is zero */
|
|
if (((pPmInt_t)TOS)->val == 0)
|
|
{
|
|
PM_RAISE(retval, PM_RET_EX_ZDIV);
|
|
break;
|
|
}
|
|
|
|
/* Otherwise perform operation */
|
|
retval = int_new(((pPmInt_t)TOS1)->val /
|
|
((pPmInt_t)TOS)->val, &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 */
|
|
|
|
/* Raise TypeError if args aren't ints */
|
|
if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT)
|
|
|| (OBJ_GET_TYPE(TOS1) != OBJ_TYPE_INT))
|
|
{
|
|
PM_RAISE(retval, PM_RET_EX_TYPE);
|
|
break;
|
|
}
|
|
|
|
/* Raise ZeroDivisionError if denominator is zero */
|
|
if (((pPmInt_t)TOS)->val == 0)
|
|
{
|
|
PM_RAISE(retval, PM_RET_EX_ZDIV);
|
|
break;
|
|
}
|
|
|
|
/* Otherwise perform operation */
|
|
retval = int_new(((pPmInt_t)TOS1)->val %
|
|
((pPmInt_t)TOS)->val, &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);
|
|
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, &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();
|
|
|
|
/* 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;
|
|
|
|
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);
|
|
|
|
/* #110: Prevent importing previously-loaded module */
|
|
/* If the named module is in globals, put it on the stack */
|
|
retval =
|
|
dict_getItem((pPmObj_t)PM_FP->fo_globals, pobj1, &pobj2);
|
|
if ((retval == PM_RET_OK)
|
|
&& (OBJ_GET_TYPE(pobj2) == OBJ_TYPE_MOD))
|
|
{
|
|
TOS = pobj2;
|
|
continue;
|
|
}
|
|
|
|
/* Load module from image */
|
|
retval = mod_import(pobj1, &pobj2);
|
|
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();
|
|
}
|
|
|
|
#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;
|
|
|
|
/* Set flag, so the GC knows a native session is active */
|
|
gVmGlobal.nativeframe.nf_active = C_TRUE;
|
|
|
|
/*
|
|
* 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;
|
|
}
|