runtime.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. #include "runtime.h"
  2. #include "codeBlock.h"
  3. #include "console/script.h"
  4. #include "console/runtime.h"
  5. #include "core/volume.h"
  6. #include "core/stream/fileStream.h"
  7. #include "core/util/timeClass.h"
  8. namespace TorqueScript
  9. {
  10. // Buffer for expanding script filenames.
  11. static char scriptFilenameBuffer[1024];
  12. TorqueScriptRuntime::TorqueScriptRuntime()
  13. {
  14. Con::registerRuntime(0, this);
  15. }
  16. TorqueScriptRuntime::~TorqueScriptRuntime()
  17. {
  18. }
  19. Con::EvalResult TorqueScriptRuntime::evaluate(const char* string, bool echo, const char* fileName)
  20. {
  21. ConsoleStackFrameSaver stackSaver;
  22. stackSaver.save();
  23. if (echo)
  24. {
  25. if (string[0] == '%')
  26. Con::printf("%s", string);
  27. else
  28. Con::printf("%s%s", Con::getVariable("$Con::Prompt"), string);
  29. }
  30. if (fileName)
  31. fileName = StringTable->insert(fileName);
  32. CodeBlock* newCodeBlock = new CodeBlock();
  33. return (newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0));
  34. }
  35. Con::EvalResult TorqueScriptRuntime::evaluate(const char* script, S32 frame, bool echo, const char* fileName)
  36. {
  37. // Make sure we're passing a valid frame to the eval.
  38. if (frame > Script::gEvalState.getStackDepth())
  39. frame = Script::gEvalState.getStackDepth() - 1;
  40. if (frame < 0)
  41. frame = 0;
  42. // Local variables use their own memory management and can't be queried by just executing
  43. // TorqueScript, we have to go digging into the interpreter.
  44. S32 evalBufferLen = dStrlen(script);
  45. bool isEvaluatingLocalVariable = evalBufferLen > 0 && script[0] == '%';
  46. if (isEvaluatingLocalVariable)
  47. {
  48. // See calculation of current frame in pushing a reference frame for console exec, we need access
  49. // to the proper scope.
  50. //frame = gEvalState.getTopOfStack() - frame - 1;
  51. S32 stackIndex = Script::gEvalState.getStackDepth() - frame - 1;
  52. Script::gEvalState.pushDebugFrame(stackIndex);
  53. Dictionary& stackFrame = Script::gEvalState.getCurrentFrame();
  54. StringTableEntry functionName = stackFrame.scopeName;
  55. StringTableEntry namespaceName = stackFrame.scopeNamespace->mName;
  56. StringTableEntry varToLookup = StringTable->insert(script);
  57. S32 registerId = ((CodeBlock*)stackFrame.module)->variableRegisterTable.lookup(namespaceName, functionName, varToLookup);
  58. if (registerId == -1)
  59. {
  60. // ERROR, can't read the variable!
  61. return Con::EvalResult("variable not found");
  62. }
  63. const char* varResult = Script::gEvalState.getLocalStringVariable(registerId);
  64. Script::gEvalState.popFrame();
  65. ConsoleValue val;
  66. val.setString(varResult);
  67. return Con::EvalResult("variable not found");
  68. }
  69. // Execute the eval.
  70. CodeBlock* newCodeBlock = new CodeBlock();
  71. Con::EvalResult result = newCodeBlock->compileExec(NULL, script, false, frame);
  72. return result;
  73. }
  74. //------------------------------------------------------------------------------
  75. Con::EvalResult TorqueScriptRuntime::evaluatef(const char* string, ...)
  76. {
  77. char buffer[4096];
  78. va_list args;
  79. va_start(args, string);
  80. dVsprintf(buffer, sizeof(buffer), string, args);
  81. va_end(args);
  82. return evaluate(buffer);
  83. }
  84. bool TorqueScriptRuntime::executeFile(const char* fileName, bool noCalls, bool journalScript)
  85. {
  86. bool journal = false;
  87. U32 execDepth = 0;
  88. U32 journalDepth = 1;
  89. execDepth++;
  90. if (journalDepth >= execDepth)
  91. journalDepth = execDepth + 1;
  92. else
  93. journal = true;
  94. bool ret = false;
  95. if (journalScript && !journal)
  96. {
  97. journal = true;
  98. journalDepth = execDepth;
  99. }
  100. // Determine the filename we actually want...
  101. Con::expandScriptFilename(scriptFilenameBuffer, sizeof(scriptFilenameBuffer), fileName);
  102. // since this function expects a script file reference, if it's a .dso
  103. // lets terminate the string before the dso so it will act like a .tscript
  104. if (dStrEndsWith(scriptFilenameBuffer, ".dso"))
  105. {
  106. scriptFilenameBuffer[dStrlen(scriptFilenameBuffer) - dStrlen(".dso")] = '\0';
  107. }
  108. // Figure out where to put DSOs
  109. StringTableEntry dsoPath = Con::getDSOPath(scriptFilenameBuffer);
  110. const char* ext = dStrrchr(scriptFilenameBuffer, '.');
  111. if (!ext)
  112. {
  113. // Try appending the default script extension and see if that succeeds
  114. if (executeFile(fileName + String("." TORQUE_SCRIPT_EXTENSION), noCalls, journalScript))
  115. {
  116. return true;
  117. }
  118. // We need an extension!
  119. Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file name %s.", scriptFilenameBuffer);
  120. execDepth--;
  121. return false;
  122. }
  123. // Check Editor Extensions
  124. bool isEditorScript = false;
  125. // If the script file extension is '.ed.tscript' then compile it to a different compiled extension
  126. if (dStricmp(ext, "." TORQUE_SCRIPT_EXTENSION) == 0)
  127. {
  128. const char* ext2 = ext - 3;
  129. if (dStricmp(ext2, ".ed." TORQUE_SCRIPT_EXTENSION) == 0)
  130. isEditorScript = true;
  131. }
  132. else if (dStricmp(ext, ".gui") == 0)
  133. {
  134. const char* ext2 = ext - 3;
  135. if (dStricmp(ext2, ".ed.gui") == 0)
  136. isEditorScript = true;
  137. }
  138. StringTableEntry scriptFileName = StringTable->insert(scriptFilenameBuffer);
  139. // Is this a file we should compile? (anything in the prefs path should not be compiled)
  140. StringTableEntry prefsPath = Platform::getPrefsPath();
  141. bool compiled = dStricmp(ext, ".mis") && !journal && !Con::getBoolVariable("Scripts::ignoreDSOs");
  142. // [tom, 12/5/2006] stripBasePath() fucks up if the filename is not in the exe
  143. // path, current directory or prefs path. Thus, getDSOFilename() will also screw
  144. // up and so this allows the scripts to still load but without a DSO.
  145. if (Platform::isFullPath(Platform::stripBasePath(scriptFilenameBuffer)))
  146. compiled = false;
  147. // [tom, 11/17/2006] It seems to make sense to not compile scripts that are in the
  148. // prefs directory. However, getDSOPath() can handle this situation and will put
  149. // the dso along with the script to avoid name clashes with tools/game dsos.
  150. if ((dsoPath && *dsoPath == 0) || (prefsPath && prefsPath[0] && dStrnicmp(
  151. scriptFileName, prefsPath, dStrlen(prefsPath)) == 0))
  152. compiled = false;
  153. // If we're in a journaling mode, then we will read the script
  154. // from the journal file.
  155. if (journal && Journal::IsPlaying())
  156. {
  157. char fileNameBuf[256];
  158. bool fileRead = false;
  159. U32 fileSize;
  160. Journal::ReadString(fileNameBuf);
  161. Journal::Read(&fileRead);
  162. if (!fileRead)
  163. {
  164. Con::errorf(ConsoleLogEntry::Script, "Journal script read (failed) for %s", fileNameBuf);
  165. execDepth--;
  166. return false;
  167. }
  168. Journal::Read(&fileSize);
  169. char* script = new char[fileSize + 1];
  170. Journal::Read(fileSize, script);
  171. script[fileSize] = 0;
  172. Con::printf("Executing (journal-read) %s.", scriptFileName);
  173. CodeBlock *newCodeBlock = new CodeBlock();
  174. newCodeBlock->compileExec(scriptFileName, script, noCalls, 0);
  175. delete newCodeBlock;
  176. delete[] script;
  177. execDepth--;
  178. return true;
  179. }
  180. // Ok, we let's try to load and compile the script.
  181. Torque::FS::FileNodeRef scriptFile = Torque::FS::GetFileNode(scriptFileName);
  182. Torque::FS::FileNodeRef dsoFile;
  183. // ResourceObject *rScr = gResourceManager->find(scriptFileName);
  184. // ResourceObject *rCom = NULL;
  185. char nameBuffer[512];
  186. char* script = NULL;
  187. U32 version;
  188. Stream* compiledStream = NULL;
  189. Torque::Time scriptModifiedTime, dsoModifiedTime;
  190. // Check here for .edso
  191. bool edso = false;
  192. if (dStricmp(ext, ".edso") == 0 && scriptFile != NULL)
  193. {
  194. edso = true;
  195. dsoFile = scriptFile;
  196. scriptFile = NULL;
  197. dsoModifiedTime = dsoFile->getModifiedTime();
  198. dStrcpy(nameBuffer, scriptFileName, 512);
  199. }
  200. // If we're supposed to be compiling this file, check to see if there's a DSO
  201. if (compiled && !edso)
  202. {
  203. const char* filenameOnly = dStrrchr(scriptFileName, '/');
  204. if (filenameOnly)
  205. ++filenameOnly;
  206. else
  207. filenameOnly = scriptFileName;
  208. char pathAndFilename[1024];
  209. Platform::makeFullPathName(filenameOnly, pathAndFilename, sizeof(pathAndFilename), dsoPath);
  210. if (isEditorScript)
  211. dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".edso", NULL);
  212. else
  213. dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".dso", NULL);
  214. dsoFile = Torque::FS::GetFileNode(nameBuffer);
  215. if (scriptFile != NULL)
  216. scriptModifiedTime = scriptFile->getModifiedTime();
  217. if (dsoFile != NULL)
  218. dsoModifiedTime = dsoFile->getModifiedTime();
  219. }
  220. // Let's do a sanity check to complain about DSOs in the future.
  221. //
  222. // MM: This doesn't seem to be working correctly for now so let's just not issue
  223. // the warning until someone knows how to resolve it.
  224. //
  225. //if(compiled && rCom && rScr && Platform::compareFileTimes(comModifyTime, scrModifyTime) < 0)
  226. //{
  227. //Con::warnf("exec: Warning! Found a DSO from the future! (%s)", nameBuffer);
  228. //}
  229. // If we had a DSO, let's check to see if we should be reading from it.
  230. //MGT: fixed bug with dsos not getting recompiled correctly
  231. //Note: Using Nathan Martin's version from the forums since its easier to read and understand
  232. if (compiled && dsoFile != NULL && (scriptFile == NULL || (dsoModifiedTime >= scriptModifiedTime)))
  233. {
  234. //MGT: end
  235. compiledStream = FileStream::createAndOpen(nameBuffer, Torque::FS::File::Read);
  236. if (compiledStream)
  237. {
  238. // Check the version!
  239. compiledStream->read(&version);
  240. if (version != Con::DSOVersion)
  241. {
  242. Con::warnf("exec: Found an old DSO (%s, ver %d < %d), ignoring.", nameBuffer, version, Con::DSOVersion);
  243. delete compiledStream;
  244. compiledStream = NULL;
  245. }
  246. }
  247. }
  248. // If we're journalling, let's write some info out.
  249. if (journal && Journal::IsRecording())
  250. Journal::WriteString(scriptFileName);
  251. if (scriptFile != NULL && !compiledStream)
  252. {
  253. // If we have source but no compiled version, then we need to compile
  254. // (and journal as we do so, if that's required).
  255. void* data;
  256. U32 dataSize = 0;
  257. Torque::FS::ReadFile(scriptFileName, data, dataSize, true);
  258. if (journal && Journal::IsRecording())
  259. Journal::Write(bool(data != NULL));
  260. if (data == NULL)
  261. {
  262. Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file %s.", scriptFileName);
  263. execDepth--;
  264. return false;
  265. }
  266. else
  267. {
  268. if (!dataSize)
  269. {
  270. execDepth--;
  271. return false;
  272. }
  273. script = (char*)data;
  274. if (journal && Journal::IsRecording())
  275. {
  276. Journal::Write(dataSize);
  277. Journal::Write(dataSize, data);
  278. }
  279. }
  280. #ifndef TORQUE_NO_DSO_GENERATION
  281. if (compiled)
  282. {
  283. // compile this baddie.
  284. #ifdef TORQUE_DEBUG
  285. Con::printf("Compiling %s...", scriptFileName);
  286. #endif
  287. CodeBlock *code = new CodeBlock();
  288. code->compile(nameBuffer, scriptFileName, script);
  289. delete code;
  290. code = NULL;
  291. compiledStream = FileStream::createAndOpen(nameBuffer, Torque::FS::File::Read);
  292. if (compiledStream)
  293. {
  294. compiledStream->read(&version);
  295. }
  296. else
  297. {
  298. // We have to exit out here, as otherwise we get double error reports.
  299. delete[] script;
  300. execDepth--;
  301. return false;
  302. }
  303. }
  304. #endif
  305. }
  306. else
  307. {
  308. if (journal && Journal::IsRecording())
  309. Journal::Write(bool(false));
  310. }
  311. if (compiledStream)
  312. {
  313. // Delete the script object first to limit memory used
  314. // during recursive execs.
  315. delete[] script;
  316. script = 0;
  317. // We're all compiled, so let's run it.
  318. #ifdef TORQUE_DEBUG
  319. Con::printf("Loading compiled script %s.", nameBuffer);
  320. #endif
  321. CodeBlock* code = new CodeBlock();
  322. code->read(scriptFileName, *compiledStream);
  323. delete compiledStream;
  324. code->exec(0, scriptFileName, NULL, 0, NULL, noCalls, NULL, 0);
  325. ret = true;
  326. }
  327. else if (scriptFile)
  328. {
  329. // No compiled script, let's just try executing it
  330. // directly... this is either a mission file, or maybe
  331. // we're on a readonly volume.
  332. #ifdef TORQUE_DEBUG
  333. Con::printf("Executing %s.", scriptFileName);
  334. #endif
  335. CodeBlock *newCodeBlock = new CodeBlock();
  336. StringTableEntry name = StringTable->insert(scriptFileName);
  337. newCodeBlock->compileExec(name, script, noCalls, 0);
  338. ret = true;
  339. }
  340. else
  341. {
  342. // Don't have anything.
  343. Con::warnf(ConsoleLogEntry::Script, "Missing file: %s!", scriptFileName);
  344. ret = false;
  345. }
  346. delete[] script;
  347. execDepth--;
  348. return ret;
  349. }
  350. bool TorqueScriptRuntime::compile(const char* fileName, bool overrideNoDso)
  351. {
  352. Con::expandScriptFilename( scriptFilenameBuffer, sizeof( scriptFilenameBuffer ), fileName );
  353. // Figure out where to put DSOs
  354. StringTableEntry dsoPath = Con::getDSOPath(scriptFilenameBuffer);
  355. if(dsoPath && *dsoPath == 0)
  356. return false;
  357. // If the script file extention is '.ed.tscript' then compile it to a different compiled extention
  358. bool isEditorScript = false;
  359. const char *ext = dStrrchr( scriptFilenameBuffer, '.' );
  360. if( ext && ( dStricmp( ext, "." TORQUE_SCRIPT_EXTENSION) == 0 ) )
  361. {
  362. const char* ext2 = ext - 3;
  363. if( dStricmp( ext2, ".ed." TORQUE_SCRIPT_EXTENSION) == 0 )
  364. isEditorScript = true;
  365. }
  366. else if( ext && ( dStricmp( ext, ".gui" ) == 0 ) )
  367. {
  368. const char* ext2 = ext - 3;
  369. if( dStricmp( ext2, ".ed.gui" ) == 0 )
  370. isEditorScript = true;
  371. }
  372. const char *filenameOnly = dStrrchr(scriptFilenameBuffer, '/');
  373. if(filenameOnly)
  374. ++filenameOnly;
  375. else
  376. filenameOnly = scriptFilenameBuffer;
  377. char nameBuffer[512];
  378. if( isEditorScript )
  379. dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".edso", NULL);
  380. else
  381. dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".dso", NULL);
  382. void *data = NULL;
  383. U32 dataSize = 0;
  384. Torque::FS::ReadFile(scriptFilenameBuffer, data, dataSize, true);
  385. if(data == NULL)
  386. {
  387. Con::errorf(ConsoleLogEntry::Script, "compile: invalid script file %s.", scriptFilenameBuffer);
  388. return false;
  389. }
  390. const char *script = static_cast<const char *>(data);
  391. #ifdef TORQUE_DEBUG
  392. Con::printf("Compiling %s...", scriptFilenameBuffer);
  393. #endif
  394. CodeBlock *code = new CodeBlock();
  395. code->compile(nameBuffer, scriptFilenameBuffer, script, overrideNoDso);
  396. delete code;
  397. delete[] script;
  398. return true;
  399. }
  400. }