openMSX
RecordedCommand.cc
Go to the documentation of this file.
1 #include "RecordedCommand.hh"
3 #include "TclObject.hh"
4 #include "Scheduler.hh"
5 #include "StateChange.hh"
6 #include "ScopedAssign.hh"
7 #include "serialize.hh"
8 #include "serialize_stl.hh"
9 #include "stl.hh"
10 #include "view.hh"
11 #include "xrange.hh"
12 #include <string>
13 #include <vector>
14 
15 namespace openmsx {
16 
18  StateChangeDistributor& stateChangeDistributor_,
19  Scheduler& scheduler_,
20  std::string_view name_)
21  : Command(commandController_, name_)
22  , stateChangeDistributor(stateChangeDistributor_)
23  , scheduler(scheduler_)
24  , currentResultObject(&dummyResultObject)
25 {
26  stateChangeDistributor.registerListener(*this);
27 }
28 
30 {
31  stateChangeDistributor.unregisterListener(*this);
32 }
33 
35 {
36  auto time = scheduler.getCurrentTime();
37  if (needRecord(tokens)) {
38  ScopedAssign sa(currentResultObject, &result);
39  stateChangeDistributor.distributeNew<MSXCommandEvent>(time, tokens);
40  } else {
41  execute(tokens, result, time);
42  }
43 }
44 
46 {
47  return true;
48 }
49 
50 [[nodiscard]] static std::string_view getBaseName(std::string_view str)
51 {
52  auto pos = str.rfind("::");
53  return (pos == std::string_view::npos) ? str : str.substr(pos + 2);
54 }
55 
56 void RecordedCommand::signalStateChange(const StateChange& event)
57 {
58  const auto* commandEvent = dynamic_cast<const MSXCommandEvent*>(&event);
59  if (!commandEvent) return;
60 
61  const auto& tokens = commandEvent->getTokens();
62  if (getBaseName(tokens[0].getString()) != getName()) return;
63 
64  if (needRecord(tokens)) {
65  execute(tokens, *currentResultObject, commandEvent->getTime());
66  } else {
67  // Normally this shouldn't happen. But it's possible in case
68  // we're replaying a replay file that has manual edits in the
69  // event log. It's crucial for security that we don't blindly
70  // execute such commands. We already only execute
71  // RecordedCommands, but we also need a strict check that
72  // only commands that would be recorded are also replayed.
73  // For example:
74  // debug set_bp 0x0038 true {<some-arbitrary-Tcl-command>}
75  // The debug write/write_block commands should be recorded and
76  // replayed, but via the set_bp it would be possible to
77  // execute arbitrary Tcl code.
78  }
79 }
80 
81 void RecordedCommand::stopReplay(EmuTime::param /*time*/) noexcept
82 {
83  // nothing
84 }
85 
86 
87 // class MSXCommandEvent
88 
90  : StateChange(time_)
91  , tokens(tokens_.size())
92 {
93  for (auto i : xrange(tokens.size())) {
94  tokens[i] = tokens_[i];
95  }
96 }
97 
98 template<typename Archive>
99 void MSXCommandEvent::serialize(Archive& ar, unsigned /*version*/)
100 {
101  ar.template serializeBase<StateChange>(*this);
102 
103  // serialize vector<TclObject> as vector<string>
104  std::vector<std::string> str;
105  if constexpr (!Archive::IS_LOADER) {
107  tokens, [](auto& t) { return std::string(t.getString()); }));
108  }
109  ar.serialize("tokens", str);
110  if constexpr (Archive::IS_LOADER) {
111  assert(tokens.empty());
112  size_t n = str.size();
113  tokens = dynarray<TclObject>(n);
114  for (auto i : xrange(n)) {
115  tokens[i] = TclObject(str[i]);
116  }
117  }
118 }
120 
121 } // namespace openmsx
TclObject t
Assign new value to some variable and restore the original value when this object goes out of scope.
Definition: ScopedAssign.hh:8
const std::string & getName() const
Definition: Completer.hh:23
This class is used to for Tcl commands that directly influence the MSX state (e.g.
void serialize(Archive &ar, unsigned version)
virtual void execute(span< const TclObject > tokens, TclObject &result, EmuTime::param time)=0
This is like the execute() method of the Command class, it only has an extra time parameter.
virtual bool needRecord(span< const TclObject > tokens) const
It's possible that in some cases the command doesn't need to be recorded after all (e....
RecordedCommand(CommandController &commandController, StateChangeDistributor &stateChangeDistributor, Scheduler &scheduler, std::string_view name)
EmuTime::param getCurrentTime() const
Get the current scheduler time.
Definition: Scheduler.cc:84
void registerListener(StateChangeListener &listener)
(Un)registers the given object to receive state change events.
void distributeNew(EmuTime::param time, Args &&...args)
Deliver the event to all registered listeners MSX input devices should call the distributeNew() versi...
void unregisterListener(StateChangeListener &listener)
Base class for all external MSX state changing events.
Definition: StateChange.hh:20
Definition: span.hh:126
constexpr index_type size() const noexcept
Definition: span.hh:296
constexpr bool empty() const noexcept
Definition: span.hh:300
This file implemented 3 utility functions:
Definition: Autofire.cc:9
REGISTER_POLYMORPHIC_CLASS(StateChange, AutofireStateChange, "AutofireStateChange")
size_t size(std::string_view utf8)
constexpr auto transform(Range &&range, UnaryOp op)
Definition: view.hh:392
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))>>
Definition: stl.hh:275
constexpr auto xrange(T e)
Definition: xrange.hh:155