00001 /** @file tcl/interp.cc c++ wrapper for Tcl_Interp, translates between 00002 tcl error codes and c++ exceptions */ 00003 /////////////////////////////////////////////////////////////////////// 00004 // 00005 // Copyright (c) 2000-2004 California Institute of Technology 00006 // Copyright (c) 2004-2007 University of Southern California 00007 // Rob Peters <rjpeters at usc dot edu> 00008 // 00009 // created: Wed Oct 11 10:27:35 2000 00010 // commit: $Id: interp.cc 11876 2009-10-22 15:53:06Z icore $ 00011 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/tcl/interp.cc $ 00012 // 00013 // -------------------------------------------------------------------- 00014 // 00015 // This file is part of GroovX 00016 // [http://ilab.usc.edu/rjpeters/groovx/] 00017 // 00018 // GroovX is free software; you can redistribute it and/or modify it 00019 // under the terms of the GNU General Public License as published by 00020 // the Free Software Foundation; either version 2 of the License, or 00021 // (at your option) any later version. 00022 // 00023 // GroovX is distributed in the hope that it will be useful, but 00024 // WITHOUT ANY WARRANTY; without even the implied warranty of 00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00026 // General Public License for more details. 00027 // 00028 // You should have received a copy of the GNU General Public License 00029 // along with GroovX; if not, write to the Free Software Foundation, 00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 00031 // 00032 /////////////////////////////////////////////////////////////////////// 00033 00034 #ifndef GROOVX_TCL_INTERP_CC_UTC20050628162421_DEFINED 00035 #define GROOVX_TCL_INTERP_CC_UTC20050628162421_DEFINED 00036 00037 #include "tcl/interp.h" 00038 00039 #include "rutz/demangle.h" 00040 #include "rutz/error.h" 00041 #include "rutz/fstring.h" 00042 #include "rutz/sfmt.h" 00043 00044 #include "tcl/list.h" // for eval_objv() 00045 00046 #include <exception> 00047 #include <tcl.h> 00048 #include <typeinfo> 00049 00050 #include "rutz/trace.h" 00051 #include "rutz/debug.h" 00052 GVX_DBG_REGISTER 00053 00054 using rutz::fstring; 00055 00056 namespace 00057 { 00058 void c_interp_delete_proc(void* clientdata, Tcl_Interp*) throw() 00059 { 00060 tcl::interpreter* intp = static_cast<tcl::interpreter*>(clientdata); 00061 intp->forget_interp(); 00062 } 00063 00064 bool report_error(tcl::interpreter& interp, const tcl::obj& code, 00065 tcl::error_strategy strategy, 00066 const rutz::file_pos& where) 00067 { 00068 switch (strategy) 00069 { 00070 case tcl::THROW_ERROR: 00071 { 00072 const fstring msg = 00073 rutz::sfmt("error while evaluating %s:\n%s", 00074 Tcl_GetString(code.get()), 00075 interp.get_result<const char*>()); 00076 00077 // Now clear the interpreter's result string, since we've 00078 // already incorporated that message into our error message: 00079 interp.reset_result(); 00080 00081 throw rutz::error(msg, where); 00082 } 00083 break; 00084 case tcl::IGNORE_ERROR: 00085 return false; 00086 } 00087 00088 return false; 00089 } 00090 } 00091 00092 /////////////////////////////////////////////////////////////////////// 00093 // 00094 // tcl::interpreter member definitions 00095 // 00096 /////////////////////////////////////////////////////////////////////// 00097 00098 tcl::interpreter::interpreter(Tcl_Interp* interp) : 00099 m_interp(interp) 00100 { 00101 GVX_TRACE("tcl::interpreter::interpreter"); 00102 if (interp == 0) 00103 throw rutz::error("tried to make tcl::interpreter " 00104 "with a null Tcl_Interp*", SRC_POS); 00105 00106 Tcl_CallWhenDeleted(m_interp, c_interp_delete_proc, 00107 static_cast<void*>(this)); 00108 } 00109 00110 tcl::interpreter::interpreter(const tcl::interpreter& other) throw() : 00111 m_interp(other.m_interp) 00112 { 00113 GVX_TRACE("tcl::interpreter::interpreter(const interpreter&)"); 00114 00115 if (m_interp != 0) 00116 { 00117 Tcl_CallWhenDeleted(m_interp, c_interp_delete_proc, 00118 static_cast<void*>(this)); 00119 } 00120 } 00121 00122 tcl::interpreter::~interpreter() throw() 00123 { 00124 GVX_TRACE("tcl::interpreter::~interpreter"); 00125 00126 if (m_interp != 0) 00127 Tcl_DontCallWhenDeleted(m_interp, c_interp_delete_proc, 00128 static_cast<void*>(this)); 00129 } 00130 00131 /////////////////////////////////////////////////////////////////////// 00132 // 00133 // tcl::interpreter -- Tcl_Interp management 00134 // 00135 /////////////////////////////////////////////////////////////////////// 00136 00137 Tcl_Interp* tcl::interpreter::intp() const 00138 { 00139 if (m_interp == 0) 00140 throw rutz::error("tcl::interpreter doesn't have a valid Tcl_Interp*", 00141 SRC_POS); 00142 00143 return m_interp; 00144 } 00145 00146 bool tcl::interpreter::is_deleted() const throw() 00147 { 00148 GVX_TRACE("tcl::interpreter::is_deleted"); 00149 00150 return (m_interp == 0) || Tcl_InterpDeleted(m_interp); 00151 } 00152 00153 void tcl::interpreter::forget_interp() throw() 00154 { 00155 GVX_TRACE("tcl::interpreter::forget_interp"); 00156 m_interp = 0; 00157 } 00158 00159 void tcl::interpreter::destroy() throw() 00160 { 00161 GVX_TRACE("tcl::interpreter::destroy"); 00162 00163 if (m_interp != 0) 00164 { 00165 Tcl_DeleteInterp(m_interp); 00166 m_interp = 0; 00167 } 00168 } 00169 00170 /////////////////////////////////////////////////////////////////////// 00171 // 00172 // tcl::interpreter -- Packages 00173 // 00174 /////////////////////////////////////////////////////////////////////// 00175 00176 void tcl::interpreter::pkg_provide(const char* name, const char* version) 00177 { 00178 GVX_TRACE("tcl::interpreter::pkg_provide"); 00179 Tcl_PkgProvide(intp(), name, version); 00180 } 00181 00182 /////////////////////////////////////////////////////////////////////// 00183 // 00184 // tcl::interpreter -- Expressions 00185 // 00186 /////////////////////////////////////////////////////////////////////// 00187 00188 bool tcl::interpreter::eval_boolean_expr(const tcl::obj& obj) const 00189 { 00190 GVX_TRACE("tcl::interpreter::eval_boolean_expr"); 00191 00192 int expr_result; 00193 00194 if (Tcl_ExprBooleanObj(intp(), obj.get(), &expr_result) != TCL_OK) 00195 { 00196 throw rutz::error("error evaluating boolean expression", SRC_POS); 00197 } 00198 00199 return bool(expr_result); 00200 } 00201 00202 /////////////////////////////////////////////////////////////////////// 00203 // 00204 // tcl::interpreter -- Evaluating code 00205 // 00206 /////////////////////////////////////////////////////////////////////// 00207 00208 bool tcl::interpreter::eval(const char* code, 00209 tcl::error_strategy strategy) 00210 { 00211 tcl::obj obj(tcl::convert_from(code)); 00212 return eval(obj, strategy); 00213 } 00214 00215 bool tcl::interpreter::eval(const fstring& code, 00216 tcl::error_strategy strategy) 00217 { 00218 tcl::obj obj(tcl::convert_from(code)); 00219 return eval(obj, strategy); 00220 } 00221 00222 bool tcl::interpreter::eval(const tcl::obj& code, 00223 tcl::error_strategy strategy) 00224 { 00225 GVX_TRACE("tcl::interpreter::eval"); 00226 00227 if (!is_valid()) 00228 throw rutz::error("Tcl_Interp* was null " 00229 "in tcl::interpreter::eval", SRC_POS); 00230 00231 // We want to use TCL_EVAL_DIRECT here because that will avoid a 00232 // string conversion cycle -- that may be important if we have 00233 // objects with fragile representations (i.e., objects that can't 00234 // survive a object->string->object cycle because their string 00235 // representations don't represent the full object value). 00236 00237 if ( Tcl_EvalObjEx(intp(), code.get(), 00238 TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL) == TCL_OK ) 00239 return true; 00240 00241 // else, there was some error during the Tcl eval... 00242 00243 return report_error(*this, code, strategy, SRC_POS); 00244 } 00245 00246 bool tcl::interpreter::eval_objv(const tcl::list& objv, 00247 tcl::error_strategy strategy) 00248 { 00249 GVX_TRACE("tcl::interpreter::eval_objv"); 00250 00251 if (!this->is_valid()) 00252 throw rutz::error("Tcl_Interp* was null " 00253 "in tcl::interpreter::eval", SRC_POS); 00254 00255 if ( Tcl_EvalObjv(this->intp(), objv.length(), objv.elements(), 00256 TCL_EVAL_GLOBAL) == TCL_OK ) 00257 return true; 00258 00259 // else, there was some error during the Tcl eval... 00260 00261 return report_error(*this, objv.as_obj(), strategy, SRC_POS); 00262 } 00263 00264 bool tcl::interpreter::eval_file(const char* fname) 00265 { 00266 GVX_TRACE("tcl::interpreter::eval_file"); 00267 return (Tcl_EvalFile(intp(), fname) == TCL_OK); 00268 } 00269 00270 void tcl::interpreter::source_rc_file() 00271 { 00272 GVX_TRACE("tcl::interpreter::source_rc_file"); 00273 Tcl_SourceRCFile(intp()); 00274 } 00275 00276 /////////////////////////////////////////////////////////////////////// 00277 // 00278 // tcl::interpreter -- Result 00279 // 00280 /////////////////////////////////////////////////////////////////////// 00281 00282 void tcl::interpreter::reset_result() const 00283 { 00284 GVX_TRACE("tcl::interpreter::reset_result"); 00285 00286 Tcl_ResetResult(intp()); 00287 } 00288 00289 void tcl::interpreter::append_result(const char* msg) const 00290 { 00291 GVX_TRACE("tcl::interpreter::append_result(const char*)"); 00292 00293 Tcl_AppendResult(intp(), msg, static_cast<char*>(0)); 00294 } 00295 00296 void tcl::interpreter::append_result(const fstring& msg) const 00297 { 00298 GVX_TRACE("tcl::interpreter::append_result(const fstring&)"); 00299 00300 Tcl_AppendResult(intp(), msg.c_str(), static_cast<char*>(0)); 00301 } 00302 00303 Tcl_Obj* tcl::interpreter::get_obj_result() const 00304 { 00305 GVX_TRACE("tcl::interpreter::get_obj_result"); 00306 00307 return Tcl_GetObjResult(intp()); 00308 } 00309 00310 void tcl::interpreter::set_obj_result(Tcl_Obj* obj) 00311 { 00312 GVX_TRACE("tcl::interpreter::set_obj_result"); 00313 00314 Tcl_SetObjResult(intp(), obj); 00315 } 00316 00317 /////////////////////////////////////////////////////////////////////// 00318 // 00319 // tcl::interpreter -- Variables 00320 // 00321 /////////////////////////////////////////////////////////////////////// 00322 00323 void tcl::interpreter::set_global_var(const char* var_name, 00324 const tcl::obj& var) const 00325 { 00326 GVX_TRACE("tcl::interpreter::set_global_var"); 00327 00328 if (Tcl_SetVar2Ex(intp(), const_cast<char*>(var_name), /*name2*/0, 00329 var.get(), TCL_GLOBAL_ONLY) == 0) 00330 { 00331 throw rutz::error(rutz::sfmt("couldn't set global variable '%s'", 00332 var_name), SRC_POS); 00333 } 00334 } 00335 00336 void tcl::interpreter::unset_global_var(const char* var_name) const 00337 { 00338 GVX_TRACE("tcl::interpreter::unset_global_var"); 00339 00340 if (Tcl_UnsetVar(intp(), const_cast<char*>(var_name), 00341 TCL_GLOBAL_ONLY) != TCL_OK) 00342 { 00343 throw rutz::error(rutz::sfmt("couldn't unset global variable '%s'", 00344 var_name), SRC_POS); 00345 } 00346 } 00347 00348 Tcl_Obj* tcl::interpreter::get_obj_global_var(const char* name1, 00349 const char* name2) const 00350 { 00351 GVX_TRACE("tcl::interpreter::get_obj_global_var"); 00352 Tcl_Obj* obj = Tcl_GetVar2Ex(intp(), 00353 const_cast<char*>(name1), 00354 const_cast<char*>(name2), 00355 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); 00356 00357 if (obj == 0) 00358 { 00359 throw rutz::error(rutz::sfmt("couldn't get global variable '%s'", 00360 name1), SRC_POS); 00361 } 00362 00363 return obj; 00364 } 00365 00366 void tcl::interpreter::link_int(const char* var_name, int* addr, 00367 bool read_only) 00368 { 00369 GVX_TRACE("tcl::interpreter::link_int"); 00370 dbg_eval_nl(3, var_name); 00371 00372 int flag = TCL_LINK_INT; 00373 if (read_only) flag |= TCL_LINK_READ_ONLY; 00374 00375 if ( Tcl_LinkVar(intp(), var_name, 00376 reinterpret_cast<char *>(addr), flag) 00377 != TCL_OK ) 00378 throw rutz::error("error while linking int variable", SRC_POS); 00379 } 00380 00381 void tcl::interpreter::link_double(const char* var_name, double* addr, 00382 bool read_only) 00383 { 00384 GVX_TRACE("tcl::interpreter::link_double"); 00385 dbg_eval_nl(3, var_name); 00386 00387 int flag = TCL_LINK_DOUBLE; 00388 if (read_only) flag |= TCL_LINK_READ_ONLY; 00389 00390 if ( Tcl_LinkVar(intp(), var_name, 00391 reinterpret_cast<char *>(addr), flag) 00392 != TCL_OK ) 00393 throw rutz::error("error while linking double variable", SRC_POS); 00394 } 00395 00396 void tcl::interpreter::link_boolean(const char* var_name, int* addr, 00397 bool read_only) 00398 { 00399 GVX_TRACE("tcl::interpreter::link_boolean"); 00400 dbg_eval_nl(3, var_name); 00401 00402 int flag = TCL_LINK_BOOLEAN; 00403 if (read_only) flag |= TCL_LINK_READ_ONLY; 00404 00405 if ( Tcl_LinkVar(intp(), var_name, 00406 reinterpret_cast<char *>(addr), flag) 00407 != TCL_OK ) 00408 throw rutz::error("error while linking boolean variable", SRC_POS); 00409 } 00410 00411 void tcl::interpreter::handle_live_exception(const char* where, 00412 const rutz::file_pos& pos) throw() 00413 { 00414 GVX_TRACE("tcl::interpreter::handle_live_exception"); 00415 00416 try 00417 { 00418 throw; 00419 } 00420 catch (std::exception& err) 00421 { 00422 dbg_print_nl(3, "caught (std::exception&)"); 00423 00424 if (is_valid()) 00425 { 00426 const char* what = err.what(); 00427 00428 const fstring msg = 00429 rutz::sfmt("%s caught at %s:%d:\n%s%s", 00430 rutz::demangled_name(typeid(err)), 00431 pos.m_file_name, pos.m_line_no, 00432 ((where != 0 && where[0] != '\0') 00433 ? rutz::sfmt("%s: ", where).c_str() 00434 : ""), 00435 ((what != 0 && what[0] != '\0') 00436 ? rutz::sfmt("%s ", what).c_str() 00437 : "")); 00438 00439 append_result(msg); 00440 } 00441 } 00442 catch (...) 00443 { 00444 dbg_print_nl(3, "caught (...)"); 00445 00446 if (is_valid()) 00447 { 00448 const fstring msg = 00449 rutz::sfmt("exception of unknown type caught at %s:%d\n%s", 00450 pos.m_file_name, pos.m_line_no, 00451 ((where != 0 && where[0] != '\0') 00452 ? where 00453 : "")); 00454 00455 append_result(msg); 00456 } 00457 } 00458 } 00459 00460 void tcl::interpreter::background_error() throw() 00461 { 00462 GVX_TRACE("tcl::interpreter::background_error"); 00463 00464 if (is_valid()) 00465 Tcl_BackgroundError(m_interp); 00466 } 00467 00468 void tcl::interpreter::add_error_info(const char* info) 00469 { 00470 GVX_TRACE("tcl::interpreter::add_error_info"); 00471 00472 Tcl_AddErrorInfo(intp(), info); 00473 } 00474 00475 void tcl::interpreter::clear_event_queue() 00476 { 00477 GVX_TRACE("tcl::interpreter::clear_event_queue"); 00478 while (Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) != 0) 00479 { /* Empty loop body */ } 00480 } 00481 00482 bool tcl::interpreter::has_command(const char* cmd_name) const 00483 { 00484 GVX_TRACE("tcl::interpreter::has_command"); 00485 Tcl_CmdInfo info; 00486 int result = Tcl_GetCommandInfo(intp(), cmd_name, &info); 00487 return (result != 0); 00488 } 00489 00490 void tcl::interpreter::delete_command(const char* cmd_name) 00491 { 00492 GVX_TRACE("tcl::interpreter::delete_command"); 00493 00494 // We must check if the interp has been tagged for deletion already, 00495 // since if it is then we must not attempt to use it to delete a Tcl 00496 // command (this results in "called Tcl_HashEntry on deleted 00497 // table"). Not deleting the command in that case does not cause a 00498 // resource leak, however, since the Tcl_Interp as part if its own 00499 // destruction will delete all commands associated with it. 00500 if ( !is_deleted() ) 00501 { 00502 Tcl_DeleteCommand(intp(), cmd_name); 00503 } 00504 } 00505 00506 fstring tcl::interpreter::get_proc_body(const char* proc_name) 00507 { 00508 GVX_TRACE("tcl::interpreter::get_proc_body"); 00509 if (has_command(proc_name)) 00510 { 00511 reset_result(); 00512 00513 if (eval(rutz::sfmt("info body %s", proc_name))) 00514 { 00515 fstring result = get_result<const char*>(); 00516 reset_result(); 00517 return result; 00518 } 00519 } 00520 00521 return ""; 00522 } 00523 00524 void tcl::interpreter::create_proc(const char* namesp, const char* proc_name, 00525 const char* args, const char* body) 00526 { 00527 GVX_TRACE("tcl::interpreter::create_proc"); 00528 00529 if (namesp == 0 || (*namesp == '\0')) 00530 { 00531 namesp = "::"; 00532 } 00533 00534 const fstring proc_cmd = 00535 rutz::sfmt("namespace eval %s { proc %s {%s} {%s} }", 00536 namesp, proc_name, args ? args : "", body); 00537 00538 eval(proc_cmd); 00539 } 00540 00541 void tcl::interpreter::delete_proc(const char* namesp, const char* proc_name) 00542 { 00543 GVX_TRACE("tcl::interpreter::delete_proc"); 00544 00545 // by renaming to the empty string "", we delete the Tcl proc 00546 const fstring cmd_str = 00547 rutz::sfmt("rename %s::%s \"\"", 00548 ((namesp != 0) && (*namesp != '\0')) ? namesp : "", 00549 proc_name); 00550 00551 eval(cmd_str); 00552 } 00553 00554 static const char __attribute__((used)) vcid_groovx_tcl_interp_cc_utc20050628162421[] = "$Id: interp.cc 11876 2009-10-22 15:53:06Z icore $ $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/tcl/interp.cc $"; 00555 #endif // !GROOVX_TCL_INTERP_CC_UTC20050628162421_DEFINED