1 #include "scriptmanagerimp.hpp" 2 3 #include <cassert> 4 #include <sstream> 5 #include <exception> 6 #include <algorithm> 7 8 #include <components/debug/debuglog.hpp> 9 10 #include <components/esm/loadscpt.hpp> 11 12 #include <components/misc/stringops.hpp> 13 14 #include <components/compiler/scanner.hpp> 15 #include <components/compiler/context.hpp> 16 #include <components/compiler/exception.hpp> 17 #include <components/compiler/quickfileparser.hpp> 18 19 #include "../mwworld/esmstore.hpp" 20 21 #include "extensions.hpp" 22 #include "interpretercontext.hpp" 23 24 namespace MWScript 25 { ScriptManager(const MWWorld::ESMStore & store,Compiler::Context & compilerContext,int warningsMode,const std::vector<std::string> & scriptBlacklist)26 ScriptManager::ScriptManager (const MWWorld::ESMStore& store, 27 Compiler::Context& compilerContext, int warningsMode, 28 const std::vector<std::string>& scriptBlacklist) 29 : mErrorHandler(), mStore (store), 30 mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), 31 mOpcodesInstalled (false), mGlobalScripts (store) 32 { 33 mErrorHandler.setWarningsMode (warningsMode); 34 35 mScriptBlacklist.resize (scriptBlacklist.size()); 36 37 std::transform (scriptBlacklist.begin(), scriptBlacklist.end(), 38 mScriptBlacklist.begin(), Misc::StringUtils::lowerCase); 39 std::sort (mScriptBlacklist.begin(), mScriptBlacklist.end()); 40 } 41 compile(const std::string & name)42 bool ScriptManager::compile (const std::string& name) 43 { 44 mParser.reset(); 45 mErrorHandler.reset(); 46 47 if (const ESM::Script *script = mStore.get<ESM::Script>().find (name)) 48 { 49 mErrorHandler.setContext(name); 50 51 bool Success = true; 52 try 53 { 54 std::istringstream input (script->mScriptText); 55 56 Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions()); 57 58 scanner.scan (mParser); 59 60 if (!mErrorHandler.isGood()) 61 Success = false; 62 } 63 catch (const Compiler::SourceException&) 64 { 65 // error has already been reported via error handler 66 Success = false; 67 } 68 catch (const std::exception& error) 69 { 70 Log(Debug::Error) << "Error: An exception has been thrown: " << error.what(); 71 Success = false; 72 } 73 74 if (!Success) 75 { 76 Log(Debug::Error) << "Error: script compiling failed: " << name; 77 } 78 79 if (Success) 80 { 81 std::vector<Interpreter::Type_Code> code; 82 mParser.getCode(code); 83 mScripts.emplace(name, CompiledScript(code, mParser.getLocals())); 84 85 return true; 86 } 87 } 88 89 return false; 90 } 91 run(const std::string & name,Interpreter::Context & interpreterContext)92 bool ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext) 93 { 94 // compile script 95 ScriptCollection::iterator iter = mScripts.find (name); 96 97 if (iter==mScripts.end()) 98 { 99 if (!compile (name)) 100 { 101 // failed -> ignore script from now on. 102 std::vector<Interpreter::Type_Code> empty; 103 mScripts.emplace(name, CompiledScript(empty, Compiler::Locals())); 104 return false; 105 } 106 107 iter = mScripts.find (name); 108 assert (iter!=mScripts.end()); 109 } 110 111 // execute script 112 if (!iter->second.mByteCode.empty() && iter->second.mActive) 113 try 114 { 115 if (!mOpcodesInstalled) 116 { 117 installOpcodes (mInterpreter); 118 mOpcodesInstalled = true; 119 } 120 121 mInterpreter.run (&iter->second.mByteCode[0], iter->second.mByteCode.size(), interpreterContext); 122 return true; 123 } 124 catch (const MissingImplicitRefError& e) 125 { 126 Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what(); 127 } 128 catch (const std::exception& e) 129 { 130 Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what(); 131 132 iter->second.mActive = false; // don't execute again. 133 } 134 return false; 135 } 136 clear()137 void ScriptManager::clear() 138 { 139 for (auto& script : mScripts) 140 { 141 script.second.mActive = true; 142 } 143 144 mGlobalScripts.clear(); 145 } 146 compileAll()147 std::pair<int, int> ScriptManager::compileAll() 148 { 149 int count = 0; 150 int success = 0; 151 152 for (auto& script : mStore.get<ESM::Script>()) 153 { 154 if (!std::binary_search (mScriptBlacklist.begin(), mScriptBlacklist.end(), 155 Misc::StringUtils::lowerCase(script.mId))) 156 { 157 ++count; 158 159 if (compile(script.mId)) 160 ++success; 161 } 162 } 163 164 return std::make_pair (count, success); 165 } 166 getLocals(const std::string & name)167 const Compiler::Locals& ScriptManager::getLocals (const std::string& name) 168 { 169 std::string name2 = Misc::StringUtils::lowerCase (name); 170 171 { 172 ScriptCollection::iterator iter = mScripts.find (name2); 173 174 if (iter!=mScripts.end()) 175 return iter->second.mLocals; 176 } 177 178 { 179 std::map<std::string, Compiler::Locals>::iterator iter = mOtherLocals.find (name2); 180 181 if (iter!=mOtherLocals.end()) 182 return iter->second; 183 } 184 185 if (const ESM::Script *script = mStore.get<ESM::Script>().search (name2)) 186 { 187 Compiler::Locals locals; 188 189 const Compiler::ContextOverride override(mErrorHandler, name2 + "[local variables]"); 190 191 std::istringstream stream (script->mScriptText); 192 Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals); 193 Compiler::Scanner scanner (mErrorHandler, stream, mCompilerContext.getExtensions()); 194 scanner.scan (parser); 195 196 std::map<std::string, Compiler::Locals>::iterator iter = 197 mOtherLocals.emplace(name2, locals).first; 198 199 return iter->second; 200 } 201 202 throw std::logic_error ("script " + name + " does not exist"); 203 } 204 getGlobalScripts()205 GlobalScripts& ScriptManager::getGlobalScripts() 206 { 207 return mGlobalScripts; 208 } 209 } 210