PCMSqVM.cpp

Go to the documentation of this file.
00001 
00002 //
00003 // This source file is a part of ParCompMark
00004 // Parallel Compositing Benchmark Framework
00005 //
00006 // for latest info see http://parcompmark.sourceforge.net
00007 
00008 //
00009 // Copyright (C) 2006 IT2 ParCompMark Dev. Team
00010 // 
00011 // This program is free software; you can redistribute it and/or
00012 // modify it under the terms of the GNU General Public License
00013 // as published by the Free Software Foundation; either version 2
00014 // of the License, or (at your option) any later version.
00015 // 
00016 // This program is distributed in the hope that it will be useful,
00017 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00019 // GNU General Public License for more details.
00020 // 
00021 // You should have received a copy of the GNU General Public License
00022 // along with this program; if not, write to the Free Software
00023 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00024 
00025 //
00026 // Inner includes
00027 //
00028 
00029 #include "../include/PCMSqVM.h"
00030 
00031 #include "../include/PCMScriptRegistration.h"
00032 
00033 //
00034 // Outer includes
00035 //
00036 
00037 #include <stdarg.h>
00038 
00039 namespace ParCompMark
00040 {
00041 
00042   //
00043   // Class constants
00044   //
00045 
00046   const std::string SqVM::NOMAINMETHOD = "null";
00047   const u32 SqVM::mStringBufferSize = 32768;
00048 
00049   //
00050   // Class variables
00051   //
00052 
00053    SqVM::Pointer SqVM::mCurrentVM;
00054   Mutex SqVM::mCurrentVMLock;
00055    SqVM::ScriptOutput SqVM::mScriptOutput = SqVM::LOG;
00056   char SqVM::mStringBuffer[32768] = "";
00057 
00058   //
00059   // Constructors & destructor
00060   //
00061 
00062    SqVM::SqVM(const std::string & name):
00063         // Parent initializer 
00064    Name(name                    /* Name of the virtual machine */
00065   ),
00066         // Smart pointer initializer 
00067    mThis(this           /* This pointer */
00068          , false                /* Do not take ownership of itself */
00069         )
00070         // You have to initialize the following attributes:
00071         // - mThis
00072         // - mSquirrelVMSys
00073         // - mCurrentVM
00074         // - mCurrentVMLock
00075         // - mError
00076         // - mInitialized
00077         // - mScripts
00078         // - mScriptOutput
00079         // - mStringBuffer[32768]
00080   {
00081         // mThis, mCurrentVM is autoinitialized
00082 
00083         mSquirrelVMSys = 0;
00084 
00085         mScripts = new Container < SqVM::Script, Mutex >;
00086 
00087         mError = false;
00088         mInitialized = false;
00089 
00090         Logger::getInstance()->log(Logger::NOTICE, "Squirrel virtual machine `" + mName + "\' has been created.");
00091   }
00092 
00093  /*----------------------------------------------------------------------*/
00094 
00095   SqVM::~SqVM()
00096   {
00097         if(mInitialized)
00098          finalize();
00099 
00100         Logger::getInstance()->log(Logger::NOTICE, "Squirrel virtual machine `" + mName + "\' has been destroyed.");
00101   }
00102 
00103  /*----------------------------------------------------------------------*/
00104 
00105   //
00106   // Class methods
00107   //
00108 
00109   void SqVM::printFunction(::HSQUIRRELVM sqVM, const SQChar * s, ...)
00110   {
00111         va_list arglist;
00112 
00113         va_start(arglist, s);
00114 #ifdef SQUNICODE
00115         vwsprintf(mStringBuffer, s, arglist);
00116 #else
00117         vsprintf(mStringBuffer, s, arglist);
00118 #endif
00119         va_end(arglist);
00120 
00121         if(mCurrentVM->mError || strcasestr(mStringBuffer, " error "))
00122         {
00123          switch (mScriptOutput)
00124          {
00125                 case SqVM::LOG:
00126                  Logger::getInstance()->logMultiLine(Logger::ERROR,
00127                                                         "Squirrel error on VM(" + mCurrentVM->getName() + ") : " +
00128                                                         mStringBuffer);
00129                  break;
00130                 case SqVM::STD:
00131                  std::cerr << mStringBuffer;
00132                  break;
00133          }
00134 
00135          // Error string in output causes error state
00136          mCurrentVM->mError = true;
00137         } else
00138          switch (mScriptOutput)
00139          {
00140                 case SqVM::LOG:
00141                  Logger::getInstance()->logMultiLine(Logger::NOTICE,
00142                                                         "Squirrel message on VM(" + mCurrentVM->getName() + ") : " +
00143                                                         mStringBuffer);
00144                  break;
00145                 case SqVM::STD:
00146                  std::cout << mStringBuffer;
00147                  break;
00148          }
00149   }
00150 
00151  /*----------------------------------------------------------------------*/
00152 
00153   //
00154   // Methods
00155   //
00156 
00157   std::string SqVM::runScriptFromFile(const std::string & filename, const std::string & mainMethod,
00158                                         const std::list < std::string > &parameters, const bool & hasReturn)
00159   {
00160         activate();
00161 
00162         // Find or add script
00163         SqVM::Script::Pointer script = findOrAddScript(filename, false, mainMethod, hasReturn);
00164 
00165         // Copy parameters
00166         setParameters(script, parameters);
00167 
00168         // Compile the script (if needed) and then execute it
00169         compileAndExecuteScript(script);
00170 
00171         // Copy return value
00172         std::string returnValue = script->returnValue;
00173 
00174         // Unlock the script
00175         script.unlock();
00176 
00177         deactivate();
00178 
00179         return returnValue;
00180   }
00181 
00182  /*----------------------------------------------------------------------*/
00183 
00184   std::string SqVM::runScriptFromString(const std::string & scriptString, const std::string & scriptName,
00185                                         const std::string & mainMethod,
00186                                         const std::list < std::string > &parameters,
00187                                         const bool & hasReturn)
00188   {
00189         SqVM::Script::Pointer script;
00190 
00191         try
00192         {
00193          activate();
00194 
00195          // The script has a name
00196          if(scriptName.size())
00197                 script = findOrAddScript(scriptName, true, mainMethod, hasReturn);
00198 
00199          // The script is unnamed
00200          else
00201                 script = createScript("", true, mainMethod, hasReturn);
00202 
00203          // Set script string
00204          script->scriptString = scriptString;
00205 
00206          // Copy parameters
00207          setParameters(script, parameters);
00208 
00209          // Compile the script (if needed) and then execute it
00210          compileAndExecuteScript(script);
00211 
00212          // Copy return value
00213          std::string returnValue = script->returnValue;
00214 
00215          // Unlock the script
00216          script.unlock();
00217 
00218          deactivate();
00219 
00220          return returnValue;
00221         }
00222         catch(const Exception & e)
00223         {
00224          // Unlock the script and deactivate the VM
00225          if(script.getLocked())
00226                 script.unlock();
00227 
00228          deactivate();
00229 
00230          // Throw forward
00231          throw Exception(e);
00232         }
00233 
00234         return "";
00235   }
00236 
00237  /*----------------------------------------------------------------------*/
00238 
00239   std::string SqVM::runScriptByName(const std::string & scriptName,
00240                                   const std::list < std::string > &parameters)
00241   {
00242         activate();
00243 
00244         SqVM::Script::Pointer script = findScript(scriptName);
00245 
00246         // Compile the script (if needed) and then execute it
00247         compileAndExecuteScript(script);
00248 
00249         // Copy return value
00250         std::string returnValue = script->returnValue;
00251 
00252         // Unlock the script
00253         script.unlock();
00254 
00255         deactivate();
00256 
00257         return returnValue;
00258   }
00259 
00260  /*----------------------------------------------------------------------*/
00261 
00262   void SqVM::finalize()
00263   {
00264 
00265         Assert(mInitialized, INVALID_OPERATION_ERROR, "SqVM::finalize()");
00266 
00267         activate();
00268 
00269         // Shut down VM
00270         SquirrelVM::Shutdown();
00271         delete mSquirrelVMSys;
00272 
00273         mSquirrelVMSys = 0;
00274 
00275         deactivate();
00276 
00277         mInitialized = false;
00278 
00279         Logger::getInstance()->log(Logger::DEBUG, "Squirrel virtual machine `" + mName + "\' has been finalized.");
00280   }
00281 
00282  /*----------------------------------------------------------------------*/
00283 
00284   void SqVM::activate()
00285   {
00286         // Lock static attribute and assign current VM
00287         mCurrentVMLock.lock();
00288         mCurrentVM = mThis;
00289         //mCurrentVM.assignWithLock(mThis);
00290 
00291         // Initialize VM if needed
00292         // (providing that the current VM is activated)
00293         if(!mInitialized)
00294          initialize();
00295 
00296         // Activate VM
00297         if(mSquirrelVMSys)
00298          SquirrelVM::SetVMSys(*mSquirrelVMSys);
00299   }
00300 
00301  /*----------------------------------------------------------------------*/
00302 
00303   void SqVM::deactivate()
00304   {
00305         // Unlock static attribute
00306         //mCurrentVM.unlock();
00307 
00308         // Set Null pointer as current VM
00309         mCurrentVM = 0;
00310 
00311         mCurrentVMLock.unlock();
00312   }
00313 
00314  /*----------------------------------------------------------------------*/
00315 
00316   void SqVM::initialize()
00317   {
00318         Assert(!mInitialized, INVALID_OPERATION_ERROR, "SqVM::initialize()");
00319         // Assume that the current VM is activated
00320 
00321         // Create virtual machine
00322         SquirrelVM::Init();
00323         mSquirrelVMSys = new SquirrelVMSys();
00324         SquirrelVM::GetVMSys(*mSquirrelVMSys);
00325 
00326         // The handlers use the print function set through(sq_setprintfunc)
00327         // to output the error.
00328         sq_setprintfunc(mSquirrelVMSys->_VM, SqVM::printFunction);
00329 
00330         mInitialized = true;
00331 
00332         // Call autogenerated Squirrel class bindings method
00333         squirrelClassBindings();
00334 
00335         Logger::getInstance()->log(Logger::DEBUG, "Squirrel virtual machine `" + mName + "\' has been initialized.");
00336   }
00337 
00338  /*----------------------------------------------------------------------*/
00339 
00340   SqVM::Script::Pointer SqVM::createScript(const std::string & scriptName, const bool & dynamic,
00341                                           const std::string & mainMethod, const bool & hasReturn)
00342   {
00343         SqVM::Script::Pointer script(new SqVM::Script());
00344         script->compiled = false;
00345         script->name = scriptName;
00346         script->dynamic = dynamic;
00347         script->mainMethod = mainMethod;
00348         script->scriptString = "";
00349         script->hasReturn = hasReturn;
00350         script.lock();
00351 
00352         return script;
00353   }
00354 
00355  /*----------------------------------------------------------------------*/
00356 
00357   SqVM::Script::Pointer SqVM::findScript(const std::string & scriptName)
00358   {
00359         SqVM::Script::Pointer script;
00360 
00361         // Add script if does not exists
00362         mScripts.lock();
00363 
00364         if(!mScripts->has(scriptName))
00365         {
00366          mScripts.unlock();
00367          deactivate();
00368          Except(INVALID_NAME_ERROR, "SqVM::findScript()", "The script `" + scriptName + "\' is not found.");
00369         }
00370 
00371         script = mScripts->get(scriptName);
00372         script.lock();
00373         mScripts.unlock();
00374 
00375         return script;
00376   }
00377 
00378  /*----------------------------------------------------------------------*/
00379 
00380   SqVM::Script::Pointer SqVM::findOrAddScript(const std::string & scriptName, const bool & dynamic,
00381                                                 const std::string & mainMethod, const bool & hasReturn)
00382   {
00383         SqVM::Script::Pointer script;
00384 
00385         // Add script if does not exists
00386         mScripts.lock();
00387         if(mScripts->has(scriptName))
00388         {
00389          // The script is already exists
00390          // (lock, because we will need it)
00391          script = mScripts->get(scriptName);
00392          script.lock();
00393 
00394          // Test main method and dynamic flag
00395          if(script->mainMethod != mainMethod)
00396          {
00397                 script.unlock();
00398                 mScripts.unlock();
00399                 deactivate();
00400                 Except(INVALID_NAME_ERROR, "SqVM::findOrAddScript()",
00401                    "The script `" + scriptName + "\' is already exists but with another entry point.");
00402          }
00403 
00404          else if(script->dynamic != dynamic)
00405          {
00406                 script.unlock();
00407                 mScripts.unlock();
00408                 deactivate();
00409                 Except(INVALID_VALUE_ERROR, "SqVM::findOrAddScript()",
00410                    "The script `" + scriptName + "\' is already exists but with different dynamic flag.");
00411          }
00412 
00413          else if(script->hasReturn != hasReturn)
00414          {
00415                 script.unlock();
00416                 mScripts.unlock();
00417                 deactivate();
00418                 Except(INVALID_VALUE_ERROR, "SqVM::findOrAddScript()",
00419                    "The script `" + scriptName + "\' is already exists but with different return flag.");
00420          }
00421 
00422         } else
00423         {
00424          // New script (lock, because we will need it)
00425          script = createScript(scriptName, dynamic, mainMethod, hasReturn);
00426 
00427          mScripts->add(scriptName, script);
00428         }
00429 
00430         mScripts.unlock();
00431 
00432         return script;
00433   }
00434 
00435  /*----------------------------------------------------------------------*/
00436 
00437   void SqVM::compileAndExecuteScript(SqVM::Script::Pointer & script)
00438   {
00439         // Setup script state for error checking
00440         ScriptState scriptState = UNCOMPILED;
00441 
00442         // Readable script name
00443         std::string scriptName = script->name.size()? script->name : "(unnamed)";
00444         scriptName = script->dynamic ? "(dynamic) script `" + scriptName + "\'" : "script `" + scriptName + "\'";
00445 
00446         try
00447         {
00448 
00449          // Compile the script if needed
00450          if(!script->compiled)
00451          {
00452                 // Compile the Squirrel script from source
00453                 if(script->dynamic)
00454                  script->scriptObject = SquirrelVM::CompileBuffer(_SC(script->scriptString.c_str()));
00455                 else
00456                  script->scriptObject = SquirrelVM::CompileScript(_SC(script->name.c_str()));
00457 
00458                 // Run the script (this creates functions etc.)
00459                 SquirrelVM::RunScript(script->scriptObject);
00460                 script->compiled = true;
00461                 Logger::getInstance()->log(Logger::NOTICE, "Squirrel script `" + script->name + "\' compiled.");
00462          }
00463 
00464          scriptState = COMPILED;
00465 
00466          // Execute the script (call entry method if given)
00467          if(script->mainMethod != SqVM::NOMAINMETHOD)
00468          {
00469                 switch (script->hasReturn)
00470                 {
00471                  case true:
00472                         switch (script->parameterCount)
00473                         {
00474                          case 0:
00475                                 script->returnValue =::SqPlus::SquirrelFunction <
00476                                  const SQChar * >(_T(script->mainMethod.c_str()))();
00477                                 break;
00478                          case 1:
00479                                 script->returnValue =::SqPlus::SquirrelFunction <
00480                                  const SQChar * >(_T(script->mainMethod.c_str()))(script->parameters[0]);
00481                                 break;
00482                          case 2:
00483                                 script->returnValue =::SqPlus::SquirrelFunction <
00484                                  const SQChar * >(_T(script->mainMethod.c_str()))(script->parameters[0],
00485                                                                                          script->parameters[1]);
00486                                 break;
00487                          case 3:
00488                                 script->returnValue =::SqPlus::SquirrelFunction <
00489                                  const SQChar * >(_T(script->mainMethod.c_str()))(script->parameters[0],
00490                                                                                          script->parameters[1],
00491                                                                                          script->parameters[2]);
00492                                 break;
00493                          case 4:
00494                                 script->returnValue =::SqPlus::SquirrelFunction <
00495                                  const SQChar * >(_T(script->mainMethod.c_str()))(script->parameters[0],
00496                                                                                          script->parameters[1],
00497                                                                                          script->parameters[2],
00498                                                                                          script->parameters[3]);
00499                                 break;
00500                          case 5:
00501                                 script->returnValue =::SqPlus::SquirrelFunction <
00502                                  const SQChar * >(_T(script->mainMethod.c_str()))(script->parameters[0],
00503                                                                                          script->parameters[1],
00504                                                                                          script->parameters[2],
00505                                                                                          script->parameters[3],
00506                                                                                          script->parameters[4]);
00507                                 break;
00508                          case 6:
00509                                 script->returnValue =::SqPlus::SquirrelFunction <
00510                                  const SQChar * >(_T(script->mainMethod.c_str()))(script->parameters[0],
00511                                                                                          script->parameters[1],
00512                                                                                          script->parameters[2],
00513                                                                                          script->parameters[3],
00514                                                                                          script->parameters[4],
00515                                                                                          script->parameters[5]);
00516                                 break;
00517                          default:
00518                                 Except(SCRIPT_ERROR, "SqVM::compileAndExecuteScript",
00519                                    "Too many parameters passed to `" + script->mainMethod + "\' in script `" +
00520                                    scriptName + "\'");
00521                         }
00522                         break;
00523                  case false:
00524                         switch (script->parameterCount)
00525                         {
00526                          case 0:
00527                                 ::SqPlus::SquirrelFunction < void >(_T(script->mainMethod.c_str())) ();
00528 
00529                                 break;
00530                          case 1:
00531                                 ::SqPlus::SquirrelFunction <
00532                                  void >(_T(script->mainMethod.c_str())) (script->parameters[0]);
00533                                 break;
00534                          case 2:
00535                                 ::SqPlus::SquirrelFunction <
00536                                  void >(_T(script->mainMethod.c_str())) (script->parameters[0],
00537                                                                          script->parameters[1]);
00538                                 break;
00539                          case 3:
00540                                 ::SqPlus::SquirrelFunction <
00541                                  void >(_T(script->mainMethod.c_str())) (script->parameters[0],
00542                                                                          script->parameters[1],
00543                                                                          script->parameters[2]);
00544                                 break;
00545                          case 4:
00546                                 ::SqPlus::SquirrelFunction <
00547                                  void >(_T(script->mainMethod.c_str())) (script->parameters[0],
00548                                                                          script->parameters[1],
00549                                                                          script->parameters[2],
00550                                                                          script->parameters[3]);
00551                                 break;
00552                          case 5:
00553                                 ::SqPlus::SquirrelFunction <
00554                                  void >(_T(script->mainMethod.c_str())) (script->parameters[0],
00555                                                                          script->parameters[1],
00556                                                                          script->parameters[2],
00557                                                                          script->parameters[3],
00558                                                                          script->parameters[4]);
00559                                 break;
00560                          case 6:
00561                                 ::SqPlus::SquirrelFunction <
00562                                  void >(_T(script->mainMethod.c_str())) (script->parameters[0],
00563                                                                          script->parameters[1],
00564                                                                          script->parameters[2],
00565                                                                          script->parameters[3],
00566                                                                          script->parameters[4],
00567                                                                          script->parameters[5]);
00568                                 break;
00569                          default:
00570                                 Except(SCRIPT_ERROR, "SqVM::compileAndExecuteScript",
00571                                    "Too many parameters passed to `" + script->mainMethod + "\' in script `" +
00572                                    scriptName + "\'");
00573                         }
00574                 }
00575          }
00576 
00577          scriptState = EXECUTED;
00578 
00579         }
00580         catch(SquirrelError & e)
00581         {
00582          // Turn on error flag
00583          mError = true;
00584 
00585          // Unlock the script
00586          script.unlock();
00587 
00588          deactivate();
00589 
00590          // Report error to logfile
00591          switch (scriptState)
00592          {
00593                 case UNCOMPILED:
00594                  if(script->dynamic)
00595                         Logger::getInstance()->log(Logger::ERROR,
00596                                                  "Squirrel " + scriptName +
00597                                                  " cannot be compiled, since it has errors.");
00598                  else
00599                         Logger::getInstance()->log(Logger::ERROR,
00600                                                  "Squirrel " + scriptName +
00601                                                  " cannot be loaded or compiled, since it has errors or the file does not exist.");
00602                  break;
00603                 case COMPILED:
00604                  Logger::getInstance()->log(Logger::ERROR,
00605                                            "An error occured after executing method `" + script->mainMethod +
00606                                            "\' of " + scriptName);
00607                  break;
00608                 case EXECUTED:
00609                  Logger::getInstance()->log(Logger::FATAL,
00610                                            "Some internal error occured after the execution of " + scriptName +
00611                                            ". It should not be happened.");
00612                  break;
00613          }
00614 
00615          // Log dynamic source
00616          if(script->dynamic)
00617          {
00618                 Logger::getInstance()->logMultiLine(Logger::ERROR,
00619                                                  "Here is the source of the dynamic script for debugging:\n ===[ code starts ] =========== \n"
00620                                                  + script->scriptString + "\n ===[ code ends ] ============= ");
00621          }
00622 
00623          Except(SCRIPT_ERROR, "SqVM::compileAndExecuteScript", "Squirrel error: `" + std::string(e.desc) + "\'");
00624         }
00625   }
00626 
00627  /*----------------------------------------------------------------------*/
00628 
00629   void SqVM::setParameters(SqVM::Script::Pointer & script, const std::list < std::string > &parameters)
00630   {
00631         u32 _i = 0;
00632 
00633         for(std::list < std::string >::const_iterator i = parameters.begin(); i != parameters.end(); i++, _i++)
00634          script->parameters[_i] = (*i).c_str();
00635 
00636         script->parameterCount = _i;
00637   }
00638 
00639  /*----------------------------------------------------------------------*/
00640 
00641 }