Logo Search packages:      
Sourcecode: falconpl version File versions  Download package

void Falcon::VMachine::run (  ) 

Virtual machine main loop. This is the method that is responsible for the main virtual machine loop. The VM can be configured to run a limited number of steps (or even one); there are also several other settings that may affect the run behavior.

Usually, the user program won't call this; the launch() method sets up the execution environment and then calls run(). Calling this method is useful to continue a pending execution (including single step execution).

If a symbol name is provided, then the symbol is executed retaining all the current status. The symbol is searched in the locals of the top module, and then in the global of the runtime eaxtly as for launch(). This is useful, in example, to provide callback entry points in scripts that must maintain their execution status between calls. In this way, each run maintain the previous status but grants execution of different entry points; also this is quite faster than having to enter the realization phase.

An error will be risen if launch has not been previously called and the routine name is not provided.

Definition at line 121 of file vm_run.cpp.

References Falcon::Item::asObject(), Falcon::ExtFuncDef::call(), callReturn(), Falcon::Error::catchable(), Falcon::MemPool::checkForGarbage(), Falcon::Error::className(), Falcon::Error::decref(), electContext(), fillErrorContext(), Falcon::Symbol::getExtFuncDef(), Falcon::ErrorHandler::handleError(), Falcon::Error::incref(), Falcon::Item::isOfClass(), m_allowYield, m_atomicMode, m_bSingleStep, m_code, m_currentContext, m_errhand, m_error, m_event, m_loopsCallback, m_loopsContext, m_loopsGC, m_opCount, m_opHandlers, m_opLimit, m_opNextCheck, m_pc, m_pc_next, m_sleepAsRequests, m_sleepingContexts, m_stackBase, m_symbol, m_tryFrame, periodicCallback(), popTry(), Falcon::Error::raised(), raiseError(), Falcon::VMContext::save(), Falcon::Error::scriptize(), Falcon::Item::setNil(), and Falcon::Item::setObject().

Referenced by callItem().

{
   tOpcodeHandler *ops = m_opHandlers;
   m_event = eventNone;

   while( 1 )
   {
      // move m_pc_next to the end of instruction and beginning of parameters.
      // in case of opcode in the call_request range, this will move next pc to return.
      m_pc_next = m_pc + sizeof( uint32 );

      // external call required?
      if ( m_pc >= i_pc_call_request )
      {
         switch( m_pc )
         {
            case i_pc_call_external_ctor_return:
               m_regA = m_regS1;
               //fallthrough
            case i_pc_call_external_return:
               callReturn();
            break;

            // the request was just to ignore opcode
            case i_pc_redo_request:
               if ( m_opCount )
                  m_opCount--; // prevent hitting oplimit
            break;

            default:
               m_regA.setNil();
               m_symbol->getExtFuncDef()->call( this );
         }
      }
      else
      {
         // execute the opcode.
         ops[ m_code[ m_pc ] ]( this );
      }

      m_opCount ++;

      //=========================
      // Executes periodic checks
      if ( m_opCount > m_opNextCheck )
      {
         if ( m_opCount > m_opNextGC )
         {
            m_memPool->checkForGarbage();
            m_opNextGC = m_opCount + m_loopsGC;
            m_opNextCheck = m_opNextGC;
         }

         if ( m_opLimit > 0 )
         {
            // Bail out???
            if ( m_opCount > m_opLimit )
               return;
            else
               if ( m_opNextCheck > m_opLimit )
                  m_opNextCheck = m_opLimit;
         }

         if( ! m_atomicMode )
         {
            if( m_allowYield && ! m_sleepingContexts.empty() && m_opCount > m_opNextContext ) {
               rotateContext();
               m_opNextContext = m_opCount + m_loopsContext;
               if( m_opNextContext < m_opNextCheck )
                  m_opNextCheck = m_opNextContext;
            }

            // Periodic Callback
            if( m_loopsCallback > 0 && m_opCount > m_opNextCallback )
            {
               periodicCallback();
               m_opNextCallback = m_opCount + m_loopsCallback;
               if( m_opNextCallback < m_opNextCheck )
                  m_opNextCheck = m_opNextCallback;
            }

            // in case of single step:
            if( m_bSingleStep )
            {
               // stop also next op
               m_opNextCheck = m_opCount + 1;
               return; // maintain the event we have, but exit now.
            }
         }
      }

      //===============================
      // consider requests.
      //
      switch( m_event )
      {

         // This events leads to VM main loop exit.
         case eventInterrupt:
         case eventSuspend:
            if ( m_atomicMode )
            {
               raiseError( new InterruptedError( ErrorParam( e_interrupted ).origin( e_orig_vm ).
                     symbol( "itemToString" ).
                     module( "core.vm" ).
                     line( __LINE__ ).
                     hard() ) );
               // just re-parse the event
               m_pc = i_pc_redo_request;
               continue;
            }

            m_pc = m_pc_next;
         return;

         // event return is used to return from a temporary frame,
         // so it is better to reset it on exit so that the calling function
         // do not need this burden.
         case eventReturn:
            m_pc = m_pc_next;
            resetEvent();
         return;

         // manage try/catch
         case eventRisen:
            // While the try frame is not in the current frame, we should return.
            // this unless m_stackBase is zero; in that case, the VM must take some action.

            // However, before proceding we have to create a correct stack frame report for the error.
            // If the error is internally generated, a frame has been already created. We should
            // create here only error data about uncaught raises from the scripts.

            if( m_tryFrame == i_noTryFrame && m_error == 0 )  // uncaught error raised from scripts...
            {
               // create the error that the external application will see.
               Error *err;
               if ( m_regB.isOfClass( "Error" ) )
               {
                  // in case of an error of class Error, we have already a good error inside of it.
                  ErrorCarrier *car = (ErrorCarrier *)m_regB.asObject()->getUserData();
                  err = car->error();
                  err->incref();
               }
               else {
                  // else incapsulate the item in an error.
                  err = new GenericError( ErrorParam( e_uncaught ).origin( e_orig_vm ) );
                  err->raised( m_regB );
                  fillErrorContext( err );
               }
               m_error = err;
            }

            // Enter the stack frame that should handle the error (or raise to the top if uncaught)
            while( m_stackBase != 0 && ( m_stackBase > m_tryFrame || m_tryFrame == i_noTryFrame ) )
            {
               callReturn();
               if ( m_event == eventReturn )
               {
                  // yes, exit but of course, maintain the error status
                  m_event = eventRisen;

                  // we must return only if the stackbase is not zero; otherwise, we return to a
                  // base callItem, and we must manage internally that case.
                  if ( m_stackBase != 0 )
                      return;
               }
               // call return may raise eventQuit, but only when m_stackBase is zero,
               // so we don't consider it.
            }

            // We are in the frame that should handle the error, in one way or another
            // should we catch it?
            // If the error is zero, we know we have a script exception ready to be caught
            // as we have filtered it before
            if ( m_error == 0 )
            {
               popTry( true );
               m_event = eventNone;
               continue;
            }
            // else catch it only if allowed.
            else if( m_error->catchable() && m_tryFrame != i_noTryFrame )
            {
               CoreObject *obj = m_error->scriptize( this );

               if ( obj != 0 )
               {
                  // we'll manage the error throuhg the obj, so we release the ref.
                  m_error->decref();
                  m_error = 0;
                  m_regB.setObject( obj );
                  popTry( true );
                  m_event = eventNone;
                  continue;
               }
               else {
                  // panic. Should not happen
                  if( m_errhand != 0 ) {
                     Error *err = new CodeError( ErrorParam( e_undef_sym, __LINE__ ).
                        module( "core.vm" ).symbol( "vm_run" ).extra( m_error->className() ) );
                     m_error->decref();
                     m_error = err;
                     m_errhand->handleError( err );
                  }
                  return;
               }
            }
            // we couldn't catch the error (this also means we're at m_stackBase zero)
            // we should handle it then exit
            else {
               // we should manage the error; if we're here, m_stackBase is zero,
               // so we are the last in charge
               if( m_errhand != 0 )
                  m_errhand->handleError( m_error );
               // we're out of business.
               return;
            }
         break;

         case eventYield:
            m_pc = m_pc_next;
            m_event = eventNone;
            yield( m_yieldTime );
            if ( m_event == eventSleep )
               return;
            else if ( m_event == eventRisen )
            {
               m_pc = i_pc_redo_request;
               continue;
            }
            m_event = eventNone;
         continue;

         // this can only be generated by an electContext or rotateContext that finds there's the need to sleep
         // as contexts cannot be elected in atomic mode, and as wait event is
         // already guarded against atomic mode breaks, we let this through
         case eventSleep:
            return;

         case eventWait:
            if ( m_atomicMode )
            {
               raiseError( new InterruptedError( ErrorParam( e_interrupted ).origin( e_orig_vm ).
                     symbol( "vm_run" ).
                     module( "core.vm" ).
                     line( __LINE__ ).
                     hard() ) );
               // just re-parse the event
               m_pc = i_pc_redo_request;
               continue;
            }
            if ( m_sleepingContexts.empty() && !m_sleepAsRequests && m_yieldTime < 0.0 )
            {
               m_error = new GenericError( ErrorParam( e_deadlock ).origin( e_orig_vm ) );
               fillErrorContext( m_error );
               m_event = eventRisen;
               if( m_errhand != 0 )
                     m_errhand->handleError( m_error );
               return;
            }

            m_pc = m_pc_next;

            m_currentContext->save( this );

                  // if wait time is > 0, put at sleep
                  if( m_yieldTime > 0.0 )
                        putAtSleep( m_currentContext, m_yieldTime );

                  electContext();

            if ( m_event == eventSleep )
               return;

            m_event = eventNone;
         continue;

         case eventQuit:
            m_pc = m_pc_next;
            // quit the machine
         return;

         default:
            // switch to next instruction
            m_pc = m_pc_next;
      }

   } // end while -- VM LOOP
}


Generated by  Doxygen 1.6.0   Back to index