Logo Search packages:      
Sourcecode: warzone2100 version File versions

interp.c

/*
      This file is part of Warzone 2100.
      Copyright (C) 1999-2004  Eidos Interactive
      Copyright (C) 2005-2007  Warzone Resurrection Project

      Warzone 2100 is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      Warzone 2100 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. See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with Warzone 2100; if not, write to the Free Software
      Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
 * Interp.c
 *
 * Execute the compiled version of a script
 *
 */

/* Control the execution trace printf's */
#include "lib/framework/frame.h"
#include "interp.h"
#include "stack.h"
#include "codeprint.h"
#include "script.h"
#include "event.h" //needed for eventGetEventID()


// the maximum number of instructions to execute before assuming
// an infinite loop
#define INTERP_MAXINSTRUCTIONS            250000
#define MAX_FUNC_CALLS 300

static INTERP_VAL *varEnvironment[MAX_FUNC_CALLS];          //environments for local variables of events/functions

typedef struct
{
      UDWORD CallerIndex;
      INTERP_VAL *ReturnAddress;
} ReturnAddressStack_t;

/**
 * Reset the return address stack
 */
static inline void retStackReset(void);

/**
 * Check whether the return address stack is empty
 *
 * \return True when empty, false otherwise
 */
static inline BOOL retStackIsEmpty(void);

/**
 * Check whether the return address stack is full
 *
 * \return True when full, false otherwise
 */
static inline BOOL retStackIsFull(void);

/**
 * Push a new address/event pair on the return address stack
 *
 * \param CallerIndex Index of the calling function
 * \param ReturnAddress Address to return to
 * \return False on failure (stack full)
 */
static BOOL retStackPush(UDWORD CallerIndex, INTERP_VAL *ReturnAddress);

/**
 * Pop an address/event pair from the return address stack
 *
 * \param CallerIndex Index of the calling function
 * \param ReturnAddress Address to return to
 * \return False on failure (stack empty)
 */
static BOOL retStackPop(UDWORD *CallerIndex, INTERP_VAL **ReturnAddress);

/* Creates a new local var environment for a new function call */
static inline void createVarEnvironment(SCRIPT_CONTEXT *psContext, UDWORD eventIndex);

/* Destroy all created variable environments */
static void cleanupVarEnvironments(void);

static inline void destroyVarEnvironment(SCRIPT_CONTEXT *psContext, UDWORD envIndex, UDWORD eventIndex);

/* The size of each opcode */
SDWORD aOpSize[] =
{
      2,    // OP_PUSH | type, value
      2,    // OP_PUSHREF | type, value
      1,    // OP_POP

      1,    // OP_PUSHGLOBAL | var_num
      1,    // OP_POPGLOBAL | var_num

      1,    // OP_PUSHARRAYGLOBAL | array_dimensions | array_base
      1,    // OP_POPARRAYGLOBAL | array_dimensions | array_base

      2,    // OP_CALL | func_pointer
      2,  // OP_VARCALL | func_pointer

      1,    // OP_JUMP | offset
      1,  // OP_JUMPTRUE | offset
      1,    // OP_JUMPFALSE | offset

      1,  // OP_BINARYOP | secondary op
      1,  // OP_UNARYOP | secondary op

      1,    // OP_EXIT
      1,  // OP_PAUSE

      -1, // OP_ADD
      -1, // OP_SUB
      -1, // OP_MUL
      -1, // OP_DIV
      -1, // OP_NEG
      -1, // OP_INC
      -1, // OP_DEC

      -1, // OP_AND
      -1, // OP_OR
      -1, // OP_NOT

      -1, // OP_CONC

      -1, // OP_EQUAL
      -1, // OP_NOTEQUAL
      -1, // OP_GREATEREQUAL
      -1, // OP_LESSEQUAL
      -1, // OP_GREATER
      -1, // OP_LESS

      2,  // OP_FUNC | func_pointer
      1,  // OP_POPLOCAL
      1,  // OP_PUSHLOCAL

      2,  // OP_PUSHLOCALREF
      1,     //OP_TO_FLOAT
      1,  //OP_TO_INT
};

/* The type equivalence table */
static TYPE_EQUIV *asInterpTypeEquiv = NULL;

// whether the interpreter is running
static BOOL       bInterpRunning = false;

/* Whether to output trace information */
static BOOL interpTrace = false;

static SCRIPT_CODE *psCurProg = NULL;
static BOOL bCurCallerIsEvent = false;

/* Print out trace info if tracing is turned on */
#define TRCPRINTF(...) do { if (interpTrace) { fprintf( stderr, __VA_ARGS__ ); } } while (false)

#define TRCPRINTVAL(x) \
      if (interpTrace) \
            cpPrintVal(x)

#define TRCPRINTOPCODE(x) \
      if (interpTrace) \
            debug( LOG_NEVER, "%s", scriptOpcodeToString(x) )

#define TRCPRINTSTACKTOP() \
      if (interpTrace) \
            stackPrintTop()

#define TRCPRINTFUNC(x) \
      if (interpTrace) \
            debug( LOG_NEVER, "%s", scriptFunctionToString(x) )

#define TRCPRINTVARFUNC(x, data) \
      if (interpTrace) \
            cpPrintVarFunc(x, data)


// true if the interpreter is currently running
BOOL interpProcessorActive(void)
{
      return bInterpRunning;
}

/* Find the value store for a global variable */
static inline INTERP_VAL *interpGetVarData(VAL_CHUNK *psGlobals, UDWORD index)
{
      VAL_CHUNK   *psChunk;

      psChunk = psGlobals;
      while (index >= CONTEXT_VALS)
      {
            index -= CONTEXT_VALS;
            psChunk = psChunk->psNext;
      }

      return psChunk->asVals + index;
}


// get the array data for an array operation
static BOOL interpGetArrayVarData(INTERP_VAL **pip, VAL_CHUNK *psGlobals, SCRIPT_CODE *psProg, INTERP_VAL **ppsVal)
{
      SDWORD            i, dimensions, vals[VAR_MAX_DIMENSIONS];
      UBYTE       *elements; //[VAR_MAX_DIMENSIONS]
      SDWORD            size, val;//, elementDWords;
//    UBYTE       *pElem;
      INTERP_VAL  *InstrPointer = *pip;
      UDWORD            base, index;

      // get the base index of the array
      base = InstrPointer->v.ival & ARRAY_BASE_MASK;

      // get the number of dimensions
      dimensions = (InstrPointer->v.ival & ARRAY_DIMENSION_MASK) >> ARRAY_DIMENSION_SHIFT;

      if (base >= psProg->numArrays)
      {
            debug( LOG_ERROR,
                  "interpGetArrayVarData: array base index out of range" );
            return false;
      }
      if (dimensions != psProg->psArrayInfo[base].dimensions)
      {
            debug( LOG_ERROR,
                  "interpGetArrayVarData: dimensions do not match" );
            return false;
      }

      // get the number of elements for each dimension
      elements = psProg->psArrayInfo[base].elements;

      // calculate the index of the array element
      size = 1;
      index = 0;
      for(i=dimensions-1; i>=0; i-=1)
      {
            if (!stackPopParams(1, VAL_INT, &val))
            {
                  return false;
            }

            if ( (val < 0) || (val >= elements[i]) )
            {
                  debug( LOG_ERROR, "interpGetArrayVarData: Array index for dimension %d out of range (passed index = %d, max index = %d)", i , val, elements[i]);
                  return false;
            }

            index += val * size;
            size *= psProg->psArrayInfo[base].elements[i];
            vals[i] = val;
      }

      // print out the debug trace
      if (interpTrace)
      {
            debug( LOG_NEVER, "%d->", base );
            for(i=0; i<dimensions; i+= 1)
            {
                  debug( LOG_NEVER, "[%d/%d]", vals[i], elements[i] );
            }
            debug( LOG_NEVER, "(%d) ", index );
      }

      // check the index is valid
      if (index > psProg->arraySize)
      {
            debug( LOG_ERROR, "interpGetArrayVarData: Array indexes out of variable space" );
            return false;
      }

      // get the variable data
      *ppsVal = interpGetVarData(psGlobals, psProg->psArrayInfo[base].base + index);

      // update the instruction pointer
      *pip += 1;// + elementDWords;

      return true;
}


// Initialise the interpreter
BOOL interpInitialise(void)
{
      asInterpTypeEquiv = NULL;

      return true;
}

/* Run a compiled script */
BOOL interpRunScript(SCRIPT_CONTEXT *psContext, INTERP_RUNTYPE runType, UDWORD index, UDWORD offset)
{
      UDWORD                  data;
      OPCODE                  opcode;
      INTERP_VAL        sVal, *psVar,*InstrPointer;
      VAL_CHUNK         *psGlobals;
      UDWORD                  numGlobals = 0;
      INTERP_VAL        *pCodeStart, *pCodeEnd, *pCodeBase;
      SCRIPT_FUNC       scriptFunc = 0;
      SCRIPT_VARFUNC    scriptVarFunc = 0;
      SCRIPT_CODE       *psProg;
      SDWORD                  instructionCount = 0;

      UDWORD                  CurEvent = 0;
      BOOL              bStop = false, bEvent = false;
      UDWORD                  callDepth = 0;
      BOOL              bTraceOn=false;         //enable to debug function/event calls

      ASSERT( psContext != NULL,
            "interpRunScript: invalid context pointer" );

      psProg = psContext->psCode;
      psCurProg = psProg;           //remember for future use

      ASSERT( psProg != NULL,
            "interpRunScript: invalid script code pointer" );

      if (bInterpRunning)
      {
            debug(LOG_ERROR,"interpRunScript: interpreter already running"
                  "                 - callback being called from within a script function?");
            goto exit_with_error;
      }

      // note that the interpreter is running to stop recursive script calls
      bInterpRunning = true;

      // Reset the stack in case another script messed up
      stackReset();

      //reset return stack
      retStackReset();

      // Turn off tracing initially
      interpTrace = false;

      /* Get the global variables */
      numGlobals = psProg->numGlobals;
      psGlobals = psContext->psGlobals;

      bEvent = false;

      // Find the code range
      switch (runType)
      {
      case IRT_TRIGGER:
            if (index > psProg->numTriggers)
            {
                  debug(LOG_ERROR,"interpRunScript: trigger index out of range");
                  ASSERT( false, "interpRunScript: trigger index out of range" );
                  return false;
            }
            pCodeBase = psProg->pCode + psProg->pTriggerTab[index];
            pCodeStart = pCodeBase;
            pCodeEnd  = psProg->pCode + psProg->pTriggerTab[index+1];

            bCurCallerIsEvent = false;

            // find the debug info for the trigger
            strcpy(last_called_script_event, eventGetTriggerID(psProg, index));

            if(bTraceOn)
                  debug(LOG_SCRIPT,"Trigger: %s", last_called_script_event);

            break;
      case IRT_EVENT:
            if (index > psProg->numEvents)
            {
                  debug(LOG_ERROR,"interpRunScript: trigger index out of range");
                  ASSERT( false, "interpRunScript: trigger index out of range" );
                  return false;
            }
            pCodeBase = psProg->pCode + psProg->pEventTab[index];
            pCodeStart = pCodeBase + offset;          //offset only used for pause() script function
            pCodeEnd  = psProg->pCode + psProg->pEventTab[index+1];

            bEvent = true; //remember it's an event
            bCurCallerIsEvent = true;

            // remember last called event/function
            strcpy(last_called_script_event, eventGetEventID(psProg, index));

            if(bTraceOn)
                  debug(LOG_SCRIPT,"Original event name: %s", last_called_script_event);

            break;
      default:
            debug(LOG_ERROR,"interpRunScript: unknown run type");
            ASSERT( false, "interpRunScript: unknown run type" );
            return false;
      }

      // Get the first opcode
      InstrPointer = pCodeStart;

      /* Make sure we start with an opcode */
      ASSERT(InstrPointer->type == VAL_PKOPCODE || InstrPointer->type == VAL_OPCODE,
            "Expected an opcode at the beginning of the interpreting process (type=%d)", InstrPointer->type);

      //opcode = InstrPointer->v.ival >> OPCODE_SHIFT;

      instructionCount = 0;

      CurEvent = index;
      bStop = false;

      // create new variable environment for this call
      if (bEvent)
      {
            createVarEnvironment(psContext, CurEvent);
      }

      while(!bStop)
      {
            // Run the code
            if (InstrPointer < pCodeEnd)// && opcode != OP_EXIT)
            {
                  if (instructionCount > INTERP_MAXINSTRUCTIONS)
                  {
                        debug( LOG_ERROR, "interpRunScript: max instruction count exceeded - infinite loop ?" );
                        goto exit_with_error;
                  }
                  instructionCount++;

                  TRCPRINTF( "%-6d  ", (int)(InstrPointer - psProg->pCode) );
                  opcode = (OPCODE)(InstrPointer->v.ival >> OPCODE_SHIFT);                //get opcode
                  data = (SDWORD)(InstrPointer->v.ival & OPCODE_DATAMASK);          //get data - only used with packed opcodes
                  switch (opcode)
                  {
                        /* Custom function call */
                        case OP_FUNC:
                              //debug( LOG_SCRIPT, "-OP_FUNC" );
                              //debug( LOG_SCRIPT, "OP_FUNC: remember event %d, ip=%d", CurEvent, (ip + 2) );

                              if(!retStackPush(CurEvent, (InstrPointer + aOpSize[opcode]))) //Remember where to jump back later
                              {
                                    debug( LOG_ERROR, "interpRunScript() - retStackPush() failed.");
                                    return false;
                              }

                              ASSERT(((INTERP_VAL *)(InstrPointer+1))->type == VAL_EVENT, "wrong value type passed for OP_FUNC: %d", ((INTERP_VAL *)(InstrPointer+1))->type);

                              // get index of the new event
                              CurEvent = ((INTERP_VAL *)(InstrPointer+1))->v.ival; //Current event = event to jump to

                              if (CurEvent > psProg->numEvents)
                              {
                                    debug( LOG_ERROR, "interpRunScript: trigger index out of range");
                                    goto exit_with_error;
                              }

                              // create new variable environment for this call
                              createVarEnvironment(psContext, CurEvent);

                              //Set new code execution boundaries
                              //----------------------------------
                              pCodeBase = psProg->pCode + psProg->pEventTab[CurEvent];
                              pCodeStart = pCodeBase;
                              pCodeEnd  = psProg->pCode + psProg->pEventTab[CurEvent+1];

                              InstrPointer = pCodeStart;                      //Start at the beginning of the new event

                              //remember last called event/index
                              strcpy(last_called_script_event, eventGetEventID(psProg, CurEvent));

                              if(bTraceOn)
                                    debug(LOG_SCRIPT,"Called: '%s'", last_called_script_event);

                              //debug( LOG_SCRIPT, "-OP_FUNC: jumped to event %d; ip=%d, numLocalVars: %d", CurEvent, ip, psContext->psCode->numLocalVars[CurEvent] );
                              //debug( LOG_SCRIPT, "-END OP_FUNC" );

                              break;

                        //handle local variables
                  case OP_PUSHLOCAL:

                        //debug( LOG_SCRIPT, "OP_PUSHLOCAL");
                        //debug( LOG_SCRIPT, "OP_PUSHLOCAL, (CurEvent=%d, data =%d) num loc vars: %d; pushing: %d", CurEvent, data, psContext->psCode->numLocalVars[CurEvent], psContext->psCode->ppsLocalVarVal[CurEvent][data].v.ival);

                        if (data >= psContext->psCode->numLocalVars[CurEvent])
                        {
                              debug(LOG_ERROR, "interpRunScript: OP_PUSHLOCAL: variable index out of range");
                              goto exit_with_error;
                        }

                        //debug(LOG_SCRIPT, "OP_PUSHLOCAL type: %d", psContext->psCode->ppsLocalVarVal[CurEvent][data].type);

                        if (!stackPush( &(varEnvironment[retStackCallDepth()][data]) ))
                        {
                              debug(LOG_ERROR, "interpRunScript: OP_PUSHLOCAL: push failed");
                              goto exit_with_error;
                        }

                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_POPLOCAL:

                        //debug( LOG_SCRIPT, "OP_POPLOCAL, event index: '%d', data: '%d'", CurEvent, data);
                        //debug( LOG_SCRIPT, "OP_POPLOCAL, numLocalVars: '%d'", psContext->psCode->numLocalVars[CurEvent]);

                        if (data >= psContext->psCode->numLocalVars[CurEvent])
                        {
                              debug(LOG_ERROR, "interpRunScript: OP_POPLOCAL: variable index out of range");
                              goto exit_with_error;
                        }

                        //DbgMsg("OP_POPLOCAL type: %d, CurEvent=%d, data=%d", psContext->psCode->ppsLocalVarVal[CurEvent][data].type, CurEvent, data);

                        if ( !stackPopType( &(varEnvironment[retStackCallDepth()][data]) ) )
                        {
                              debug(LOG_ERROR, "interpRunScript: OP_POPLOCAL: pop failed");
                              goto exit_with_error;
                        }

                        //debug(LOG_SCRIPT, "OP_POPLOCAL: type=%d, val=%d", psContext->psCode->ppsLocalVarVal[CurEvent][data].type, psContext->psCode->ppsLocalVarVal[CurEvent][data].v.ival);

                        InstrPointer += aOpSize[opcode];

                        break;

                  case OP_PUSHLOCALREF:

                        // The type of the variable is stored in with the opcode
                        sVal.type = (INTERP_TYPE)(InstrPointer->v.ival & OPCODE_DATAMASK);

                        ASSERT(  ((INTERP_VAL *)(InstrPointer + 1))->type == VAL_INT,
                              "wrong value type passed for OP_PUSHLOCALREF: %d", ((INTERP_VAL *)(InstrPointer + 1))->type);

                        /* get local var index */
                        data = ((INTERP_VAL *)(InstrPointer + 1))->v.ival;

                        if (data >= psContext->psCode->numLocalVars[CurEvent])
                        {
                              debug(LOG_ERROR, "interpRunScript: OP_PUSHLOCALREF: variable index out of range");
                              goto exit_with_error;
                        }

                        /* get local variable */
                        sVal.v.oval = &(varEnvironment[retStackCallDepth()][data]);

                        TRCPRINTOPCODE(opcode);
                        TRCPRINTVAL(sVal);
                        TRCPRINTF( "\n" );

                        if (!stackPush(&sVal))
                        {
                              debug(LOG_ERROR, "interpRunScript: OP_PUSHLOCALREF: push failed");
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;

                  case OP_PUSH:
                        // The type of the value is stored in with the opcode
                        sVal.type = (INTERP_TYPE)(InstrPointer->v.ival & OPCODE_DATAMASK);

                        //ASSERT( ((INTERP_VAL *)(InstrPointer + 1))->type  == sVal.type,
                        //    "wrong value type passed for OP_PUSH: %d, expected: %d", ((INTERP_VAL *)(InstrPointer + 1))->type, sVal.type );

                        ASSERT(interpCheckEquiv(((INTERP_VAL *)(InstrPointer + 1))->type, sVal.type),
                              "wrong value type passed for OP_PUSH: %d, expected: %d", ((INTERP_VAL *)(InstrPointer + 1))->type, sVal.type);

                        /* copy value */
                        memcpy(&sVal, (INTERP_VAL *)(InstrPointer + 1), sizeof(INTERP_VAL));

                        TRCPRINTOPCODE(opcode);
                        TRCPRINTVAL(sVal);
                        TRCPRINTF( "\n" );
                        if (!stackPush(&sVal))
                        {
                              // Eeerk, out of memory
                              debug( LOG_ERROR, "interpRunScript: out of memory!" );
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_PUSHREF:
                        // The type of the variable is stored in with the opcode
                        sVal.type = (INTERP_TYPE)(InstrPointer->v.ival & OPCODE_DATAMASK);

                        // store pointer to INTERP_VAL
                        sVal.v.oval = interpGetVarData(psGlobals, ((INTERP_VAL *)(InstrPointer + 1))->v.ival);

                        TRCPRINTOPCODE(opcode);
                        TRCPRINTVAL(sVal);
                        TRCPRINTF( "\n" );
                        if (!stackPush(&sVal))
                        {
                              // Eeerk, out of memory
                              debug( LOG_ERROR, "interpRunScript: out of memory!" );
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_POP:
                        ASSERT( InstrPointer->type == VAL_OPCODE,
                              "wrong value type passed for OP_POP: %d", InstrPointer->type);

                        TRCPRINTOPCODE(opcode);
                        if (!stackPop(&sVal))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do stack pop" );
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_BINARYOP:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_BINARYOP: %d", InstrPointer->type);

                        TRCPRINTOPCODE(data);
                        if (!stackBinaryOp((OPCODE)data))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do binary op" );
                              goto exit_with_error;
                        }
                        TRCPRINTSTACKTOP();
                        TRCPRINTF( "\n" );
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_UNARYOP:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_UNARYOP: %d", InstrPointer->type);

                        TRCPRINTOPCODE(data);
                        if (!stackUnaryOp((OPCODE)data))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do unary op" );
                              goto exit_with_error;
                        }
                        TRCPRINTSTACKTOP();
                        TRCPRINTF( "\n" );
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_PUSHGLOBAL:

                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_PUSHGLOBAL: %d", InstrPointer->type);

                        TRCPRINTF( "PUSHGLOBAL  %d\n", data );
                        if (data >= numGlobals)
                        {
                              debug( LOG_ERROR, "interpRunScript: variable index out of range" );
                              goto exit_with_error;
                        }
                        if (!stackPush(interpGetVarData(psGlobals, data)))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do stack push" );
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_POPGLOBAL:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_POPGLOBAL: %d", InstrPointer->type);

                        TRCPRINTF( "POPGLOBAL   %d ", data );
                        TRCPRINTSTACKTOP();
                        TRCPRINTF( "\n" );
                        if (data >= numGlobals)
                        {
                              debug( LOG_ERROR, "interpRunScript: variable index out of range" );
                              goto exit_with_error;
                        }
                        if (!stackPopType(interpGetVarData(psGlobals, data)))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do stack pop" );
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_PUSHARRAYGLOBAL:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_PUSHARRAYGLOBAL: %d", InstrPointer->type);

                        TRCPRINTOPCODE(opcode);
                        if (!interpGetArrayVarData(&InstrPointer, psGlobals, psProg, &psVar))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not get array var data, CurEvent=%d", CurEvent );
                              goto exit_with_error;
                        }
                        TRCPRINTF( "\n" );
                        if (!stackPush(psVar))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do stack push" );
                              goto exit_with_error;
                        }
                        break;
                  case OP_POPARRAYGLOBAL:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_POPARRAYGLOBAL: %d", InstrPointer->type);

                        TRCPRINTOPCODE(opcode);
                        if (!interpGetArrayVarData(&InstrPointer, psGlobals, psProg, &psVar))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not get array var data" );
                              goto exit_with_error;
                        }
                        TRCPRINTSTACKTOP();
                        TRCPRINTF( "\n" );
                        if (!stackPopType(psVar))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do pop stack of type" );
                              goto exit_with_error;
                        }
                        break;

                  case OP_JUMPFALSE:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_JUMPFALSE: %d", InstrPointer->type);

                        TRCPRINTF( "JUMPFALSE   %d (%d)",
                                   (SWORD)data, (int)(InstrPointer - psProg->pCode + (SWORD)data) );

                        if (!stackPop(&sVal))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do pop of stack" );
                              goto exit_with_error;
                        }
                        if (!sVal.v.bval)
                        {
                              // Do the jump
                              TRCPRINTF( " - done -\n" );
                              InstrPointer += (SWORD)data;
                              if (InstrPointer < pCodeStart || InstrPointer > pCodeEnd)
                              {
                                    debug( LOG_ERROR, "interpRunScript: jump out of range" );
                                    goto exit_with_error;
                              }
                        }
                        else
                        {
                              TRCPRINTF( "\n" );
                              InstrPointer += aOpSize[opcode];
                        }
                        break;
                  case OP_JUMP:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_JUMP: %d", InstrPointer->type);

                        TRCPRINTF( "JUMP        %d (%d)\n",
                                   (SWORD)data, (int)(InstrPointer - psProg->pCode + (SWORD)data) );
                        // Do the jump
                        InstrPointer += (SWORD)data;
                        if (InstrPointer < pCodeStart || InstrPointer > pCodeEnd)
                        {
                              debug( LOG_ERROR, "interpRunScript: jump out of range" );
                              goto exit_with_error;
                        }
                        break;
                  case OP_CALL:
                        //debug(LOG_SCRIPT, "OP_CALL");

                        ASSERT( InstrPointer->type == VAL_OPCODE,
                              "wrong value type passed for OP_CALL: %d", InstrPointer->type);

                        scriptFunc = ((INTERP_VAL *)(InstrPointer+1))->v.pFuncExtern;
                        TRCPRINTFUNC( scriptFunc );
                        TRCPRINTF( "\n" );
                        //debug(LOG_SCRIPT, "OP_CALL 1");
                        if (!scriptFunc())
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do func" );
                              goto exit_with_error;
                        }
                        //debug(LOG_SCRIPT, "OP_CALL 2");
                        InstrPointer += aOpSize[opcode];
                        //debug(LOG_SCRIPT, "OP_CALL 3");
                        break;
                  case OP_VARCALL:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_VARCALL: %d", InstrPointer->type);

                        TRCPRINTOPCODE(opcode);
                        TRCPRINTVARFUNC(  ((INTERP_VAL *)(InstrPointer+1))->v.pObjGetSet, data );
                        TRCPRINTF( "(%d)\n", data );

                        ASSERT( ((INTERP_VAL *)(InstrPointer+1))->type == VAL_OBJ_GETSET,
                              "wrong set/get function pointer type passed for OP_VARCALL: %d", ((INTERP_VAL *)(InstrPointer+1))->type);

                        scriptVarFunc =((INTERP_VAL *)(InstrPointer+1))->v.pObjGetSet;
                        if (!scriptVarFunc(data))
                        {
                              debug( LOG_ERROR, "interpRunScript: could not do var func" );
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_EXIT:     /* end of function/event, "exit" or "return" statements */
                        ASSERT( InstrPointer->type == VAL_OPCODE,
                              "wrong value type passed for OP_EXIT: %d", InstrPointer->type);

                        // jump out of the code
                        InstrPointer = pCodeEnd;
                        break;
                  case OP_PAUSE:
                        ASSERT( InstrPointer->type == VAL_PKOPCODE,
                              "wrong value type passed for OP_PAUSE: %d", InstrPointer->type);

                        TRCPRINTF( "PAUSE       %d\n", data );
                        ASSERT( stackEmpty(),
                              "interpRunScript: OP_PAUSE without empty stack" );

                        InstrPointer += aOpSize[opcode];
                        // tell the event system to reschedule this event
                        if (!eventAddPauseTrigger(psContext, index, (UDWORD)(InstrPointer - pCodeBase), data))    //only original caller can be paused since we pass index and not CurEvent (not sure if that's what we want)
                        {
                              debug( LOG_ERROR, "interpRunScript: could not add pause trigger" );
                              goto exit_with_error;
                        }
                        // now jump out of the event
                        InstrPointer = pCodeEnd;
                        break;
                  case OP_TO_FLOAT:
                        ASSERT( InstrPointer->type == VAL_OPCODE,
                              "wrong value type passed for OP_TO_FLOAT: %d", InstrPointer->type);

                        if(!stackCastTop(VAL_FLOAT))
                        {
                              debug( LOG_ERROR, "interpRunScript: OP_TO_FLOAT failed" );
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;
                  case OP_TO_INT:
                        ASSERT( InstrPointer->type == VAL_OPCODE,
                              "wrong value type passed for OP_TO_INT: %d", InstrPointer->type);

                        if(!stackCastTop(VAL_INT))
                        {
                              debug( LOG_ERROR, "interpRunScript: OP_TO_INT failed" );
                              goto exit_with_error;
                        }
                        InstrPointer += aOpSize[opcode];
                        break;
                  default:
                        debug(LOG_ERROR, "interpRunScript: unknown opcode: %d, type: %d", opcode, InstrPointer->type);
                        goto exit_with_error;
                        break;
                  }
            }
            else  //End of the event reached, see if we have to jump back to the caller function or just exit
            {
                  //debug(LOG_SCRIPT, "End of event reached");

                  if(!retStackIsEmpty())        //There was a caller function before this one
                  {
                        // destroy current variable environment
                        destroyVarEnvironment(psContext, retStackCallDepth(), CurEvent);

                        //pop caller function index and return address
                        if (!retStackPop(&CurEvent, &InstrPointer))
                        {
                              debug( LOG_ERROR, "interpRunScript() - retStackPop() failed.");
                              return false;
                        }

                        //remember last called event/index
                        strcpy(last_called_script_event, eventGetEventID(psProg, CurEvent));

                        if(bTraceOn)
                              debug(LOG_SCRIPT,"Returned to: '%s'", last_called_script_event);

                        //debug( LOG_SCRIPT, "RETURNED TO CALLER EVENT %d", CurEvent );

                        //Set new boundaries
                        //--------------------------
                        if(retStackIsEmpty())   //if we jumped back to the original caller
                        {
                              if(!bEvent)       //original caller was a trigger (is it possible at all?)
                              {
                                    pCodeBase = psProg->pCode + psProg->pTriggerTab[CurEvent];
                                    pCodeStart = pCodeBase;
                                    pCodeEnd  = psProg->pCode + psProg->pTriggerTab[CurEvent+1];
                              }
                              else              //original caller was an event
                              {
                                    pCodeBase = psProg->pCode + psProg->pEventTab[CurEvent];
                                    pCodeStart = pCodeBase + offset;    //also use the offset passed, since it's an original caller event (offset is used for pause() )
                                    pCodeEnd  = psProg->pCode + psProg->pEventTab[CurEvent+1];
                              }
                        }
                        else  //we are still jumping thru functions (this can't be a callback, since it can't/should not be called)
                        {
                              pCodeBase = psProg->pCode + psProg->pEventTab[CurEvent];
                              pCodeStart = pCodeBase;
                              pCodeEnd  = psProg->pCode + psProg->pEventTab[CurEvent+1];
                        }
                  }
                  else        //we have returned to the original caller event/function
                  {
                        //debug( LOG_SCRIPT, " *** CALL STACK EMPTY ***" );

                        //reset local vars only if original caller was an event, not a trigger
                        if(bEvent)
                        {
                              // destroy current variable environment
                              destroyVarEnvironment(psContext, retStackCallDepth(), CurEvent);
                        }

                        bStop = true;           //Stop execution of this event here, no more calling functions stored
                  }
            }

      }

      psCurProg = NULL;
      TRCPRINTF( "%-6d  EXIT\n", (int)(InstrPointer - psProg->pCode) );

      bInterpRunning = false;
      return true;

exit_with_error:
      // Deal with the script crashing or running out of memory
      debug(LOG_ERROR,"interpRunScript: *** ERROR EXIT *** (CurEvent=%d)", CurEvent);

      /* Free all memory allocated for variable environments */
      cleanupVarEnvironments();

      if(bEvent)
      {
            debug(LOG_ERROR,"Original event ID: %d (of %d)", index, psProg->numEvents);
      }
      else
      {
            debug(LOG_ERROR,"Original trigger ID: %d (of %d)", index, psProg->numTriggers);
      }

      debug(LOG_ERROR,"Current event ID: %d (of %d)", CurEvent, psProg->numEvents);
      callDepth = retStackCallDepth();
      debug(LOG_ERROR,"Call depth : %d", callDepth);

      /* Output script call trace */
      scrOutputCallTrace(LOG_ERROR);
      psCurProg = NULL;

      TRCPRINTF( "*** ERROR EXIT ***\n" );

      ASSERT(!"error while executing a script", "interpRunScript: error while executing a script");

      bInterpRunning = false;
      return false;
}


/* Set the type equivalence table */
void scriptSetTypeEquiv(TYPE_EQUIV *psTypeTab)
{
#ifdef DEBUG
      SDWORD            i;

      for(i=0; psTypeTab[i].base != 0; i++)
      {
            ASSERT( psTypeTab[i].base >= VAL_USERTYPESTART,
                  "scriptSetTypeEquiv: can only set type equivalence for user types (%d)", i );
      }
#endif

      asInterpTypeEquiv = psTypeTab;
}


/* Check if two types are equivalent
 * Means: Their data can be copied without conversion.
 * I.e. strings are NOT equivalent to anything but strings, even though they can be converted
 */
BOOL interpCheckEquiv(INTERP_TYPE to, INTERP_TYPE from)
{
      BOOL toRef = false, fromRef = false;

      // check for the VAL_REF flag
      if (to & VAL_REF)
      {
            toRef = true;
            to &= ~VAL_REF;
      }
      if (from & VAL_REF)
      {
            fromRef = true;
            from &= ~VAL_REF;
      }
      if (toRef != fromRef)
      {
            return false;
      }

      /* Void pointer is compatible with any other type */
      if (toRef == true && fromRef == true &&
            (to == VAL_VOID || from == VAL_VOID) )
      {
            return true;
      }

      if (to == from)
      {
            return true;
      }
      else if (asInterpTypeEquiv)
      {
            unsigned int i;
            for (i = 0; asInterpTypeEquiv[i].base != 0; i++)
            {
                  if (asInterpTypeEquiv[i].base == to)
                  {
                        unsigned int j;
                        for (j = 0; j < asInterpTypeEquiv[i].numEquiv; j++)
                        {
                              if (asInterpTypeEquiv[i].aEquivTypes[j] == from)
                              {
                                    return true;
                              }
                        }
                  }
            }
      }

      return false;
}


/* Instinct function to turn on tracing */
BOOL interpTraceOn(void)
{
      interpTrace = true;

      return true;
}

/* Instinct function to turn off tracing */
BOOL interpTraceOff(void)
{
      interpTrace = false;

      return true;
}


/* Call stack stuff */
static ReturnAddressStack_t retStack[MAX_FUNC_CALLS]; // Primitive stack of return addresses
static SDWORD retStackPos = -1; // Current Position, always points to the last valid element


UDWORD retStackCallDepth(void)
{
      ASSERT(retStackPos + 1 >= 0 && retStackPos + 1 < MAX_FUNC_CALLS,
            "retStackCallDepth: wrong call depth: %d", retStackPos + 1);

      return (retStackPos + 1);
}


static inline void retStackReset(void)
{
      retStackPos = -1; // Beginning of the stack
}


static inline BOOL retStackIsEmpty(void)
{
      if(retStackPos < 0) return true;
      return false;
}


static inline BOOL retStackIsFull(void)
{
      if(retStackPos >= MAX_FUNC_CALLS) return true;
      return false;
}


static BOOL retStackPush(UDWORD CallerIndex, INTERP_VAL *ReturnAddress)
{
      if (retStackIsFull())
      {
            debug( LOG_ERROR, "retStackPush(): return address stack is full");
            return false; // Stack full
      }

      retStackPos++;
      retStack[retStackPos].CallerIndex = CallerIndex;
      retStack[retStackPos].ReturnAddress = ReturnAddress;

      //debug( LOG_SCRIPT, "retStackPush: Event=%i Address=%p, ", CallerIndex, ReturnAddress);

      return true;
}


static BOOL retStackPop(UDWORD *CallerIndex, INTERP_VAL **ReturnAddress)
{
      if (retStackIsEmpty())
      {
            debug( LOG_ERROR, "retStackPop(): return address stack is empty");
            return false;
      }

      *CallerIndex = retStack[retStackPos].CallerIndex;
      *ReturnAddress = retStack[retStackPos].ReturnAddress;
      retStackPos--;

      //debug( LOG_SCRIPT, "retStackPop: Event=%i Address=%p", *EventTrigIndex, *ReturnAddress);

      return true;
}


/* Output script call stack trace */
void scrOutputCallTrace(code_part part)
{
      SDWORD i;
      const char *pEvent;

      debug(part, " *** Script call trace: ***");

      if(!bInterpRunning){
            debug(part, "<Interpreter is inactive>");
            return;
      }

      if(psCurProg == NULL){
            return;
      }

      debug(part,"%d: %s (current event)", retStackPos + 1, &(last_called_script_event[0]));

      if(psCurProg->psDebug != NULL)
      {
            for(i=retStackPos; i>=0; i--)
            {
                  if(i == 0 && !bCurCallerIsEvent){   //if original caller is a trigger
                        pEvent = eventGetTriggerID(psCurProg, retStack[i].CallerIndex);
                  }else{
                        pEvent = eventGetEventID(psCurProg, retStack[i].CallerIndex);
                  }

                  debug(part,"%d: %s (return address: %p)", i, pEvent, retStack[i].ReturnAddress);
            }
      }
      else
      {
            debug(part, "<No debug information available>");
      }
}

/* create a new local var environment for a new function call */
static inline void createVarEnvironment(SCRIPT_CONTEXT *psContext, UDWORD eventIndex)
{
      UDWORD i, callDepth = retStackCallDepth();
      UDWORD numEventVars = psContext->psCode->numLocalVars[eventIndex];

      if (numEventVars > 0)
      {
            // alloc memory
            varEnvironment[callDepth] = (INTERP_VAL *)malloc(sizeof(INTERP_VAL) * numEventVars);

            // create environment
            memcpy(varEnvironment[callDepth], psContext->psCode->ppsLocalVarVal[eventIndex], sizeof(INTERP_VAL) * numEventVars);

            // allocate new space for strings to preserve original ones
            for (i = 0; i < numEventVars; i++)
            {
                  if (varEnvironment[callDepth][i].type == VAL_STRING)
                  {
                        varEnvironment[callDepth][i].v.sval = (char*)malloc(MAXSTRLEN);
                        strcpy( varEnvironment[callDepth][i].v.sval, "" );    //initialize
                  }
            }
      }
      else
      {
            varEnvironment[callDepth] = NULL;
      }
}

static inline void destroyVarEnvironment(SCRIPT_CONTEXT *psContext, UDWORD envIndex, UDWORD eventIndex)
{
      UDWORD i;
      UDWORD numEventVars = 0;

      if(psContext != NULL)
      {
            numEventVars = psContext->psCode->numLocalVars[eventIndex];
      }

      if (varEnvironment[envIndex] != NULL)
      {
            // deallocate string space
            for (i = 0; i < numEventVars; i++)
            {
                  if (varEnvironment[envIndex][i].type == VAL_STRING)
                  {
                        free( varEnvironment[envIndex][i].v.sval );
                        varEnvironment[envIndex][i].v.sval = NULL;
                  }
            }

            free( varEnvironment[envIndex] );
            varEnvironment[envIndex] = NULL;
      }
}

/* Destroy all created variable environments */
static void cleanupVarEnvironments(void)
{
      UDWORD i;

      for (i = 0; i < retStackCallDepth(); i++)
      {
            destroyVarEnvironment(NULL, i, 0);
      }
}

Generated by  Doxygen 1.6.0   Back to index