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