openMSX
TclObject.cc
Go to the documentation of this file.
1 #include "TclObject.hh"
2 #include "Interpreter.hh"
3 #include "CommandException.hh"
4 #include "xrange.hh"
5 #include <cassert>
6 #include <tcl.h>
7 
8 using std::string;
9 
10 namespace openmsx {
11 
12 // class TclObject
13 
14 TclObject::TclObject(Tcl_Interp* interp_, Tcl_Obj* obj_)
15  : interp(interp_)
16 {
17  init(obj_);
18 }
19 
20 TclObject::TclObject(Tcl_Interp* interp_, string_ref value)
21  : interp(interp_)
22 {
23  init(Tcl_NewStringObj(value.data(), int(value.size())));
24 }
25 
27  : interp(nullptr)
28 {
29  init(Tcl_NewStringObj(value.data(), int(value.size())));
30 }
31 
32 TclObject::TclObject(Tcl_Interp* interp_)
33  : interp(interp_)
34 {
35  init(Tcl_NewObj());
36 }
37 
39  : interp(interp_.interp)
40 {
41  init(Tcl_NewObj());
42 }
43 
45  : interp(object.interp)
46 {
47  init(object.obj);
48 }
49 
51  : interp(nullptr)
52 {
53  init(Tcl_NewObj());
54 }
55 
56 void TclObject::init(Tcl_Obj* obj_)
57 {
58  obj = obj_;
59  Tcl_IncrRefCount(obj);
60 }
61 
63 {
64  Tcl_DecrRefCount(obj);
65 }
66 
68 {
69  if (&other != this) {
70  Tcl_DecrRefCount(obj);
71  interp = other.interp;
72  init(other.obj);
73  }
74  return *this;
75 }
76 
77 Tcl_Interp* TclObject::getInterpreter() const
78 {
79  return interp;
80 }
81 
83 {
84  return obj;
85 }
86 
87 void TclObject::throwException() const
88 {
89  string_ref message = interp ? Tcl_GetStringResult(interp)
90  : "TclObject error";
91  throw CommandException(message);
92 }
93 
95 {
96  if (Tcl_IsShared(obj)) {
97  Tcl_DecrRefCount(obj);
98  obj = Tcl_NewStringObj(value.data(), int(value.size()));
99  Tcl_IncrRefCount(obj);
100  } else {
101  Tcl_SetStringObj(obj, value.data(), int(value.size()));
102  }
103 }
104 
105 void TclObject::setInt(int value)
106 {
107  if (Tcl_IsShared(obj)) {
108  Tcl_DecrRefCount(obj);
109  obj = Tcl_NewIntObj(value);
110  Tcl_IncrRefCount(obj);
111  } else {
112  Tcl_SetIntObj(obj, value);
113  }
114 }
115 
116 void TclObject::setBoolean(bool value)
117 {
118  if (Tcl_IsShared(obj)) {
119  Tcl_DecrRefCount(obj);
120  obj = Tcl_NewBooleanObj(value);
121  Tcl_IncrRefCount(obj);
122  } else {
123  Tcl_SetBooleanObj(obj, value);
124  }
125 }
126 
127 void TclObject::setDouble(double value)
128 {
129  if (Tcl_IsShared(obj)) {
130  Tcl_DecrRefCount(obj);
131  obj = Tcl_NewDoubleObj(value);
132  Tcl_IncrRefCount(obj);
133  } else {
134  Tcl_SetDoubleObj(obj, value);
135  }
136 }
137 
138 void TclObject::setBinary(byte* buf, unsigned length)
139 {
140  if (Tcl_IsShared(obj)) {
141  Tcl_DecrRefCount(obj);
142  obj = Tcl_NewByteArrayObj(buf, length);
143  Tcl_IncrRefCount(obj);
144  } else {
145  Tcl_SetByteArrayObj(obj, buf, length);
146  }
147 }
148 
150 {
151  addListElement(Tcl_NewStringObj(element.data(), int(element.size())));
152 }
153 
155 {
156  addListElement(Tcl_NewIntObj(value));
157 }
158 
159 void TclObject::addListElement(double value)
160 {
161  addListElement(Tcl_NewDoubleObj(value));
162 }
163 
165 {
166  addListElement(element.obj);
167 }
168 
169 void TclObject::addListElement(Tcl_Obj* element)
170 {
171  if (Tcl_IsShared(obj)) {
172  Tcl_DecrRefCount(obj);
173  obj = Tcl_DuplicateObj(obj);
174  Tcl_IncrRefCount(obj);
175  }
176  if (Tcl_ListObjAppendElement(interp, obj, element) != TCL_OK) {
177  throwException();
178  }
179 }
180 
181 int TclObject::getInt() const
182 {
183  int result;
184  if (Tcl_GetIntFromObj(interp, obj, &result) != TCL_OK) {
185  throwException();
186  }
187  return result;
188 }
189 
191 {
192  int result;
193  if (Tcl_GetBooleanFromObj(interp, obj, &result) != TCL_OK) {
194  throwException();
195  }
196  return result != 0;
197 }
198 
199 double TclObject::getDouble() const
200 {
201  double result;
202  if (Tcl_GetDoubleFromObj(interp, obj, &result) != TCL_OK) {
203  throwException();
204  }
205  return result;
206 }
207 
209 {
210  int length;
211  char* buf = Tcl_GetStringFromObj(obj, &length);
212  return string_ref(buf, length);
213 }
214 
215 const byte* TclObject::getBinary(unsigned& length) const
216 {
217  return static_cast<const byte*>(Tcl_GetByteArrayFromObj(
218  obj, reinterpret_cast<int*>(&length)));
219 }
220 
221 unsigned TclObject::getListLength() const
222 {
223  int result;
224  if (Tcl_ListObjLength(interp, obj, &result) != TCL_OK) {
225  throwException();
226  }
227  return result;
228 }
229 
230 TclObject TclObject::getListIndex(unsigned index) const
231 {
232  Tcl_Obj* element;
233  if (Tcl_ListObjIndex(interp, obj, index, &element) != TCL_OK) {
234  throwException();
235  }
236  return element ? TclObject(interp, element)
237  : TclObject(interp);
238 }
239 
241 {
242  Tcl_Obj* value;
243  if (Tcl_DictObjGet(interp, obj, key.obj, &value) != TCL_OK) {
244  throwException();
245  }
246  return value ? TclObject(interp, value)
247  : TclObject(interp);
248 }
249 
251 {
252  int result;
253  if (Tcl_ExprBooleanObj(interp, obj, &result) != TCL_OK) {
254  throwException();
255  }
256  return result != 0;
257 }
258 
260 {
261  string_ref tmp = getString();
262  parse(tmp.data(), int(tmp.size()), true);
263 }
264 
266 {
267  string_ref tmp = getString();
268  parse(tmp.data(), int(tmp.size()), false);
269 }
270 
271 void TclObject::parse(const char* str, int len, bool expression) const
272 {
273  assert(interp);
274  Tcl_Parse info;
275  if (expression ?
276  Tcl_ParseExpr(interp, str, len, &info) :
277  Tcl_ParseCommand(interp, str, len, 1, &info) != TCL_OK) {
278  throw CommandException(Tcl_GetStringResult(interp));
279  }
280  struct Cleanup {
281  ~Cleanup() { Tcl_FreeParse(p); }
282  Tcl_Parse* p;
283  } cleanup = { &info };
284  (void)cleanup;
285 
286  if (!expression && (info.tokenPtr[0].type == TCL_TOKEN_SIMPLE_WORD)) {
287  // simple command name
288  Tcl_CmdInfo cmdinfo;
289  Tcl_Token& token2 = info.tokenPtr[1];
290  string procname(token2.start, token2.size);
291  if (!Tcl_GetCommandInfo(interp, procname.c_str(), &cmdinfo)) {
292  throw CommandException("invalid command name: \"" +
293  procname + '\"');
294  }
295  }
296  for (auto i : xrange(info.numTokens)) {
297  Tcl_Token& token = info.tokenPtr[i];
298  if (token.type == TCL_TOKEN_COMMAND) {
299  parse(token.start + 1, token.size - 2, false);
300  } else if ((token.type == TCL_TOKEN_VARIABLE) &&
301  (token.numComponents == 1)) {
302  // simple variable (no array)
303  Tcl_Token& token2 = info.tokenPtr[i + 1];
304  string varname(token2.start, token2.size);
305  if (!Tcl_GetVar2Ex(interp, varname.c_str(), nullptr,
306  TCL_LEAVE_ERR_MSG)) {
307  throw CommandException(Tcl_GetStringResult(interp));
308  }
309  }
310  }
311 }
312 
313 string TclObject::executeCommand(bool compile)
314 {
315  assert(interp);
316  int flags = compile ? 0 : TCL_EVAL_DIRECT;
317  int success = Tcl_EvalObjEx(interp, obj, flags);
318  string result = Tcl_GetStringResult(interp);
319  if (success != TCL_OK) {
320  throw CommandException(result);
321  }
322  return result;
323 }
324 
325 
326 } // namespace openmsx