1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2006, 2007 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27 
28 #include "machine/rloperation.h"
29 
30 #include <sstream>
31 #include <string>
32 #include <utility>
33 #include <vector>
34 
35 #include "libreallive/bytecode.h"
36 #include "machine/rloperation/references.h"
37 #include "machine/rlmachine.h"
38 #include "machine/rlmodule.h"
39 #include "utilities/exception.h"
40 
41 // -----------------------------------------------------------------------
42 // RLOperation
43 // -----------------------------------------------------------------------
44 
RLOperation()45 RLOperation::RLOperation() : name_() {}
46 
~RLOperation()47 RLOperation::~RLOperation() {}
48 
SetProperty(int property,int value)49 RLOperation* RLOperation::SetProperty(int property, int value) {
50   if (!property_list_) {
51     property_list_.reset(new std::vector<std::pair<int, int>>);
52   }
53 
54   // Modify the property if it already exists
55   PropertyList::iterator it = FindProperty(property);
56   if (it != property_list_->end()) {
57     it->second = value;
58   } else {
59     property_list_->emplace_back(property, value);
60   }
61 
62   return this;
63 }
64 
GetProperty(int property,int & value) const65 bool RLOperation::GetProperty(int property, int& value) const {
66   if (property_list_) {
67     PropertyList::iterator it = FindProperty(property);
68     if (it != property_list_->end()) {
69       value = it->second;
70       return true;
71     }
72   }
73 
74   if (module_) {
75     // If we don't have a property, ask our module if it has one.
76     return module_->GetProperty(property, value);
77   }
78 
79   return false;
80 }
81 
FindProperty(int property) const82 RLOperation::PropertyList::iterator RLOperation::FindProperty(int property)
83     const {
84   return find_if(property_list_->begin(),
85                  property_list_->end(),
86                  [&](Property& p) { return p.first == property; });
87 }
88 
AdvanceInstructionPointer()89 bool RLOperation::AdvanceInstructionPointer() { return true; }
90 
DispatchFunction(RLMachine & machine,const libreallive::CommandElement & ff)91 void RLOperation::DispatchFunction(RLMachine& machine,
92                                    const libreallive::CommandElement& ff) {
93   if (!ff.AreParametersParsed()) {
94     std::vector<std::string> unparsed = ff.GetUnparsedParameters();
95     libreallive::ExpressionPiecesVector output;
96     ParseParameters(unparsed, output);
97     ff.SetParsedParameters(std::move(output));
98   }
99 
100   const libreallive::ExpressionPiecesVector& parameter_pieces =
101       ff.GetParsedParameters();
102 
103   // Now Dispatch based on these parameters.
104   Dispatch(machine, parameter_pieces);
105 
106   // By default, we advacne the instruction pointer on any instruction we
107   // perform. Weird special cases all derive from RLOp_SpecialCase, which
108   // redefines the Dispatcher, so this is ok.
109   if (AdvanceInstructionPointer())
110     machine.AdvanceInstructionPointer();
111 }
112 
113 // Implementation for IntConstant_T
getData(RLMachine & machine,const libreallive::ExpressionPiecesVector & p,unsigned int & position)114 IntConstant_T::type IntConstant_T::getData(
115     RLMachine& machine,
116     const libreallive::ExpressionPiecesVector& p,
117     unsigned int& position) {
118   return p[position++].GetIntegerValue(machine);
119 }
120 
121 // Was working to change the verify_type to parse_parameters.
ParseParameters(unsigned int & position,const std::vector<std::string> & input,libreallive::ExpressionPiecesVector & output)122 void IntConstant_T::ParseParameters(
123     unsigned int& position,
124     const std::vector<std::string>& input,
125     libreallive::ExpressionPiecesVector& output) {
126   const char* data = input.at(position).c_str();
127   libreallive::ExpressionPiece ep(libreallive::GetData(data));
128 
129   if (ep.GetExpressionValueType() != libreallive::ValueTypeInteger) {
130     std::ostringstream oss;
131     oss << "IntConstant_T parse error. Expected type string, but actually "
132         << "contained \"" << ep.GetDebugString() << "\"";
133     throw rlvm::Exception(oss.str());
134   }
135 
136   output.push_back(std::move(ep));
137   position++;
138 }
139 
getData(RLMachine & machine,const libreallive::ExpressionPiecesVector & p,unsigned int & position)140 IntReference_T::type IntReference_T::getData(
141     RLMachine& machine,
142     const libreallive::ExpressionPiecesVector& p,
143     unsigned int& position) {
144   return p[position++].GetIntegerReferenceIterator(machine);
145 }
146 
ParseParameters(unsigned int & position,const std::vector<std::string> & input,libreallive::ExpressionPiecesVector & output)147 void IntReference_T::ParseParameters(
148     unsigned int& position,
149     const std::vector<std::string>& input,
150     libreallive::ExpressionPiecesVector& output) {
151   const char* data = input.at(position).c_str();
152   libreallive::ExpressionPiece ep(libreallive::GetData(data));
153 
154   if (ep.GetExpressionValueType() != libreallive::ValueTypeInteger) {
155     std::ostringstream oss;
156     oss << "IntReference_T parse error. Expected type string, but actually "
157         << "contained \"" << ep.GetDebugString() << "\"";
158     throw rlvm::Exception(oss.str());
159   }
160 
161   output.push_back(std::move(ep));
162   position++;
163 }
164 
getData(RLMachine & machine,const libreallive::ExpressionPiecesVector & p,unsigned int & position)165 StrConstant_T::type StrConstant_T::getData(
166     RLMachine& machine,
167     const libreallive::ExpressionPiecesVector& p,
168     unsigned int& position) {
169   // When I was trying to get P_BRIDE running in rlvm, I noticed that when
170   // loading a game, I would often crash with invalid iterators in the LRUCache
171   // library, in SDLGraphicsSystem where I cached recently used images. After
172   // adding some verification methods to it, it was obvious that it's internal
173   // state was entirely screwed up. There were duplicates in the std::list<>
174   // (it's a precondition that its elements are unique), there were entries in
175   // the std::list<> that didn't exist in the corresponding std::map<>; the
176   // data structure was a complete mess. I verified the datastructure before
177   // and after each operation...and found that it would almost always be
178   // corrupted in the check that happened at the beginning of each
179   // function. Printing out the c_str() pointers showed there was no change
180   // between the consistent data structure and the corrupted one; the
181   // underlying pointers were the same.
182   //
183   // Setting a few watch points, the internal c_str() buffers were being
184   // overwritten by a memcpy in libstdc++ which was called by boost::serialize
185   // during the loading of the RLMachine's local memory.  But that makes
186   // sense. Think about what happens when we execute the following kepago:
187   //
188   //   strS[0] = 'SOMEFILE'
189   //   grpOpenBg(0, strS[0])
190   //
191   // We are assigning a value into the RLMachine's string memory. We are then
192   // passing a copy-on-write string, backed by one memory buffer that contains
193   // 'SOMEFILE' around. LRUCache and string memory now point to the same char*
194   // buffer.
195   //
196   // boost::serialization appears to ignore the COW semantics of std::string
197   // and just writes into it during a serialization::load() no matter how many
198   // people hold references to the inner buffer. Now we have one screwed up
199   // LRUCache data structure, and probably screwed up other places.
200   //
201   // So to fix this, we break the COW semantics here by forcing a copy. I'd
202   // prefer to do this in RLMachine or Memory, but I can't because they return
203   // references.
204   string tmp = p[position++].GetStringValue(machine);
205   return string(tmp.data(), tmp.size());
206 }
207 
ParseParameters(unsigned int & position,const std::vector<std::string> & input,libreallive::ExpressionPiecesVector & output)208 void StrConstant_T::ParseParameters(
209     unsigned int& position,
210     const std::vector<std::string>& input,
211     libreallive::ExpressionPiecesVector& output) {
212   const char* data = input.at(position).c_str();
213   libreallive::ExpressionPiece ep(libreallive::GetData(data));
214 
215   if (ep.GetExpressionValueType() != libreallive::ValueTypeString) {
216     std::ostringstream oss;
217     oss << "StrConstant_T parse error. Expected type string, but actually "
218         << "contained \"" << ep.GetDebugString() << "\"";
219     throw rlvm::Exception(oss.str());
220   }
221 
222   output.push_back(std::move(ep));
223   position++;
224 }
225 
getData(RLMachine & machine,const libreallive::ExpressionPiecesVector & p,unsigned int & position)226 StrReference_T::type StrReference_T::getData(
227     RLMachine& machine,
228     const libreallive::ExpressionPiecesVector& p,
229     unsigned int& position) {
230   return p[position++].GetStringReferenceIterator(machine);
231 }
232 
ParseParameters(unsigned int & position,const std::vector<std::string> & input,libreallive::ExpressionPiecesVector & output)233 void StrReference_T::ParseParameters(
234     unsigned int& position,
235     const std::vector<std::string>& input,
236     libreallive::ExpressionPiecesVector& output) {
237   const char* data = input.at(position).c_str();
238   libreallive::ExpressionPiece ep(libreallive::GetData(data));
239 
240   if (ep.GetExpressionValueType() != libreallive::ValueTypeString) {
241     std::ostringstream oss;
242     oss << "StrReference_T parse error. Expected type string, but actually "
243         << "contained \"" << ep.GetDebugString() << "\"";
244     throw rlvm::Exception(oss.str());
245   }
246 
247   output.push_back(std::move(ep));
248   position++;
249 }
250 
Dispatch(RLMachine & machine,const libreallive::ExpressionPiecesVector & parameters)251 void RLOp_SpecialCase::Dispatch(
252     RLMachine& machine,
253     const libreallive::ExpressionPiecesVector& parameters) {
254   throw rlvm::Exception("Tried to call empty RLOp_SpecialCase::Dispatch().");
255 }
256 
ParseParameters(const std::vector<std::string> & input,libreallive::ExpressionPiecesVector & output)257 void RLOp_SpecialCase::ParseParameters(
258     const std::vector<std::string>& input,
259     libreallive::ExpressionPiecesVector& output) {
260   for (auto const& parameter : input) {
261     const char* src = parameter.c_str();
262     output.push_back(libreallive::GetData(src));
263   }
264 }
265 
DispatchFunction(RLMachine & machine,const libreallive::CommandElement & ff)266 void RLOp_SpecialCase::DispatchFunction(RLMachine& machine,
267                                         const libreallive::CommandElement& ff) {
268   // First try to run the default parse_parameters if we can.
269   if (!ff.AreParametersParsed()) {
270     std::vector<std::string> unparsed = ff.GetUnparsedParameters();
271     libreallive::ExpressionPiecesVector output;
272     ParseParameters(unparsed, output);
273     ff.SetParsedParameters(std::move(output));
274   }
275 
276   // Pass this on to the implementation of this functor.
277   operator()(machine, ff);
278 }
279 
280 template <>
ParseParameters(const std::vector<std::string> & input,libreallive::ExpressionPiecesVector & output)281 void RLNormalOpcode<>::ParseParameters(
282     const std::vector<std::string>& input,
283     libreallive::ExpressionPiecesVector& output) {
284 }
285 
286 template <>
Dispatch(RLMachine & machine,const libreallive::ExpressionPiecesVector & parameters)287 void RLOpcode<>::Dispatch(
288     RLMachine& machine,
289     const libreallive::ExpressionPiecesVector& parameters) {
290   operator()(machine);
291 }
292 
293 // Default instantiations.
294 template class RLNormalOpcode<>;
295 template class RLNormalOpcode<IntConstant_T>;
296 template class RLNormalOpcode<IntConstant_T, IntConstant_T>;
297 template class RLNormalOpcode<IntConstant_T, StrConstant_T>;
298 template class RLNormalOpcode<IntConstant_T, IntConstant_T, IntConstant_T>;
299 template class RLNormalOpcode<IntConstant_T, IntConstant_T, IntConstant_T,
300                               IntConstant_T>;
301 template class RLNormalOpcode<IntReference_T>;
302 template class RLNormalOpcode<IntReference_T, IntReference_T>;
303 template class RLNormalOpcode<StrConstant_T>;
304 template class RLNormalOpcode<StrConstant_T, IntConstant_T>;
305 template class RLNormalOpcode<StrConstant_T, StrConstant_T>;
306 template class RLNormalOpcode<StrReference_T>;
307 
308 template class RLOpcode<>;
309 template class RLOpcode<IntConstant_T>;
310 template class RLOpcode<IntConstant_T, IntConstant_T>;
311 template class RLOpcode<IntConstant_T, StrConstant_T>;
312 template class RLOpcode<IntConstant_T, IntConstant_T, IntConstant_T>;
313 template class RLOpcode<IntConstant_T, IntConstant_T, IntConstant_T,
314                         IntConstant_T>;
315 template class RLOpcode<IntReference_T>;
316 template class RLOpcode<IntReference_T, IntReference_T>;
317 template class RLOpcode<StrConstant_T>;
318 template class RLOpcode<StrConstant_T, IntConstant_T>;
319 template class RLOpcode<StrConstant_T, StrConstant_T>;
320 template class RLOpcode<StrReference_T>;
321