From 7999468bb9b861fe703c421d713dd3a92b6dc84f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 18 Jul 2022 00:18:10 +0200 Subject: [PATCH 01/81] Issue #1122 - avoid error messages which are confusing. --- src/lym/lym/lymMacroCollection.cc | 20 ++++++++++---------- src/tl/tl/tlFileUtils.cc | 13 +++++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/lym/lym/lymMacroCollection.cc b/src/lym/lym/lymMacroCollection.cc index 5f9a1e12c..3316ef036 100644 --- a/src/lym/lym/lymMacroCollection.cc +++ b/src/lym/lym/lymMacroCollection.cc @@ -281,18 +281,18 @@ MacroCollection::add_folder (const std::string &description, const std::string & if (! force_create) { if (tl::verbosity () >= 20) { - tl::log << "Folder does not exist - skipping: " << fp; + tl::log << tl::to_string (tr ("Folder does not exist - skipping: ")) << fp; } return 0; } else { if (tl::verbosity () >= 20) { - tl::log << "Folder does not exist yet - trying to create it: " << fp; + tl::log << tl::to_string (tr ("Folder does not exist yet - trying to create it: ")) << fp; } if (! tl::mkpath (fp)) { - if (tl::verbosity () >= 10) { - tl::error << "Unable to create folder path: " << fp; + if (tl::verbosity () >= 20) { + tl::error << tl::to_string (tr ("Unable to create folder path: ")) << fp; } return 0; } @@ -301,8 +301,8 @@ MacroCollection::add_folder (const std::string &description, const std::string & } if (! tl::is_dir (fp)) { - if (tl::verbosity () >= 10) { - tl::error << "Folder is not a directory: " << fp; + if (tl::verbosity () >= 20) { + tl::error << tl::to_string (tr ("Folder is not a directory - skipping: ")) << fp; } return 0; } @@ -317,7 +317,7 @@ MacroCollection::add_folder (const std::string &description, const std::string & if (! readonly && ! tl::is_writable (fp)) { readonly = true; if (tl::verbosity () >= 20) { - tl::log << "Folder is read-only: " << fp; + tl::log << tl::to_string (tr ("Folder is read-only: ")) << fp; } } @@ -368,7 +368,7 @@ void MacroCollection::scan () std::string p = path (); if (tl::verbosity () >= 20) { - tl::info << "Scanning macro path " << p << " (readonly=" << m_readonly << ")"; + tl::info << tl::to_string (tr ("Scanning macro path ")) << p << " (readonly=" << m_readonly << ")"; } if (! p.empty () && p[0] == ':') { @@ -501,7 +501,7 @@ MacroCollection::create_entry (const std::string &path) } } catch (tl::Exception &ex) { - tl::error << "Reading " << path << ": " << ex.msg (); + tl::error << tl::to_string (tr ("Reading ")) << path << ": " << ex.msg (); } } @@ -578,7 +578,7 @@ void MacroCollection::save () bool MacroCollection::rename (const std::string &n) { if (tl::verbosity () >= 20) { - tl::info << "Renaming macro folder " << path () << " to " << n; + tl::info << tl::to_string (tr ("Renaming macro folder ")) << path () << " to " << n; } begin_changes (); if (! tl::rename_file (path (), n)) { diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index 19cce3c2e..a084c6b51 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -27,6 +27,9 @@ #include +// Use this define to print debug output +// #define FILE_UTILS_VERBOSE + #if defined(_MSC_VER) # include @@ -448,7 +451,9 @@ bool mkpath (const std::string &p) front += parts[i++]; if (! file_exists (front)) { if (! mkdir (front)) { +#if defined(FILE_UTILS_VERBOSE) tl::error << tr ("Unable to create directory: ") << front; +#endif return false; } } @@ -511,13 +516,17 @@ bool rm_dir_recursive (const std::string &p) for (std::vector::const_iterator e = entries.begin (); e != entries.end (); ++e) { std::string tc = tl::combine_path (path, *e); if (! rm_file (tc)) { +#if defined(FILE_UTILS_VERBOSE) tl::error << tr ("Unable to remove file: ") << tc; +#endif return false; } } if (! rm_dir (path)) { +#if defined(FILE_UTILS_VERBOSE) tl::error << tr ("Unable to remove directory: ") << path; +#endif return false; } @@ -535,7 +544,9 @@ cp_dir_recursive (const std::string &source, const std::string &target) for (std::vector::const_iterator e = entries.begin (); e != entries.end (); ++e) { std::string tc = tl::combine_path (path_to, *e); if (! mkpath (tc)) { +#if defined(FILE_UTILS_VERBOSE) tl::error << tr ("Unable to create target directory: ") << tc; +#endif return false; } if (! cp_dir_recursive (tl::combine_path (path, *e), tc)) { @@ -558,8 +569,10 @@ cp_dir_recursive (const std::string &source, const std::string &target) is.copy_to (os); } catch (tl::Exception &ex) { +#if defined(FILE_UTILS_VERBOSE) tl::error << tr ("Unable to copy file ") << tl::combine_path (path_to, *e) << tr (" to ") << tl::combine_path (path, *e) << tr ("(Error ") << ex.msg () << ")"; +#endif return false; } From 7d078ed0414da6a1e6ad7d2d0731a755faa487c3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 29 Jul 2022 23:58:19 +0200 Subject: [PATCH 02/81] Express argument details in Python call errors --- src/pya/pya/pya.cc | 33 +++++++++++++++++ src/pya/pya/pya.h | 25 ++++++++----- src/pya/pya/pyaConvert.cc | 20 +++++----- src/pya/pya/pyaModule.cc | 77 +++++++++++++++++++-------------------- 4 files changed, 97 insertions(+), 58 deletions(-) diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc index 9a39c7704..3092eb8e4 100644 --- a/src/pya/pya/pya.cc +++ b/src/pya/pya/pya.cc @@ -38,6 +38,7 @@ #include "tlTimer.h" #include "tlFileUtils.h" #include "tlString.h" +#include "tlInternational.h" #if defined(HAVE_QT) # include @@ -51,6 +52,38 @@ namespace pya { +// -------------------------------------------------------------------------- +// PythonError implementation + +PythonError::PythonError (const char *msg, const char *cls, const std::vector &backtrace) + : tl::ScriptError (msg, cls, backtrace) +{ } + +PythonError::PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector &backtrace) + : tl::ScriptError (msg, sourcefile, line, cls, backtrace) +{ } + +PythonError::PythonError (const PythonError &d) + : tl::ScriptError (d) +{ } + +// -------------------------------------------------------------------------- +// PythonArgumentError implementation + +PythonArgumentError::PythonArgumentError (const tl::Exception &ex, int index, const std::string &name) + : tl::Exception (ex.msg ()), m_index (index), m_name (name) +{ } + +std::string +PythonArgumentError::msg () const +{ + if (! m_name.empty ()) { + return tl::sprintf (tl::to_string (tr ("%s for argument #%d ('%s')")), tl::Exception::msg (), m_index + 1, m_name); + } else { + return tl::sprintf (tl::to_string (tr ("%s for argument #%d")), tl::Exception::msg (), m_index + 1); + } +} + // -------------------------------------------------------------------------- /** diff --git a/src/pya/pya/pya.h b/src/pya/pya/pya.h index 6ca3c4ca7..11cdcd9f0 100644 --- a/src/pya/pya/pya.h +++ b/src/pya/pya/pya.h @@ -77,17 +77,24 @@ class PYA_PUBLIC PythonError : public tl::ScriptError { public: - PythonError (const char *msg, const char *cls, const std::vector &backtrace) - : tl::ScriptError (msg, cls, backtrace) - { } + PythonError (const char *msg, const char *cls, const std::vector &backtrace); + PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector &backtrace); + PythonError (const PythonError &d); +}; - PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector &backtrace) - : tl::ScriptError (msg, sourcefile, line, cls, backtrace) - { } +/** + * @brief A class encapsulating an argument error + */ +class PYA_PUBLIC PythonArgumentError + : public tl::Exception +{ +public: + PythonArgumentError (const tl::Exception &ex, int index, const std::string &name); + virtual std::string msg () const; - PythonError (const PythonError &d) - : tl::ScriptError (d) - { } +private: + int m_index; + std::string m_name; }; class PythonModule; diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index 09fccb5e7..fb24e5732 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -52,7 +52,7 @@ long python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return (long) (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } @@ -75,7 +75,7 @@ char python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return char (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a character"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a character"))); } } @@ -92,7 +92,7 @@ unsigned long python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return (unsigned long) (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } @@ -109,7 +109,7 @@ long long python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return (long long) (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } @@ -126,7 +126,7 @@ unsigned long long python2c_func::operator() (PyObject *rval } else if (PyFloat_Check (rval)) { return (unsigned long long) (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } @@ -145,7 +145,7 @@ __int128 python2c_func<__int128>::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return PyFloat_AsDouble (rval); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } #endif @@ -163,7 +163,7 @@ double python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return PyFloat_AsDouble (rval); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a floating-point value"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a floating-point value"))); } } @@ -188,7 +188,7 @@ std::string python2c_func::operator() (PyObject *rval) } else if (PyByteArray_Check (rval)) { return std::string (PyByteArray_AsString (rval), PyByteArray_Size (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a string"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a string"))); } } @@ -224,7 +224,7 @@ std::vector python2c_func >::operator() (PyObject *rval) Py_ssize_t sz = PyByteArray_Size (rval); return std::vector (cp, cp + sz); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a byte array"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a byte array"))); } } @@ -250,7 +250,7 @@ QByteArray python2c_func::operator() (PyObject *rval) } else if (PyByteArray_Check (rval)) { return QByteArray (PyByteArray_AsString (rval), PyByteArray_Size (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a byte array"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a byte array"))); } } diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 5eff6c85d..37d7dfab0 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -1175,6 +1175,42 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P } } +static void +push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap) +{ + int i = 0; + int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + + try { + + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { + push_arg (*a, arglist, PyTuple_GetItem (args, i), heap); + } + + } catch (tl::Exception &ex) { + + // In case of an error upon write, pop the arguments to clean them up. + // Without this, there is a risk to keep dead objects on the stack. + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { + pop_arg (*a, arglist, 0, heap); + } + + const gsi::ArgSpecBase *arg_spec = meth->begin_arguments () [i].spec (); + throw PythonArgumentError (ex, i, arg_spec ? arg_spec->name () : std::string ()); + + } catch (...) { + + // In case of an error upon write, pop the arguments to clean them up. + // Without this, there is a risk to keep dead objects on the stack. + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { + pop_arg (*a, arglist, 0, heap); + } + + throw; + + } +} + static PyObject * method_adaptor (int mid, PyObject *self, PyObject *args) { @@ -1203,8 +1239,6 @@ method_adaptor (int mid, PyObject *self, PyObject *args) throw tl::Exception (tl::to_string (tr ("Cannot call non-const method on a const reference"))); } - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); - void *obj = 0; if (p) { // Hint: this potentially instantiates the object @@ -1214,24 +1248,7 @@ method_adaptor (int mid, PyObject *self, PyObject *args) gsi::SerialArgs retlist (meth->retsize ()); gsi::SerialArgs arglist (meth->argsize ()); - try { - - int i = 0; - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { - push_arg (*a, arglist, PyTuple_GetItem (args, i), heap); - } - - } catch (...) { - - // In case of an error upon write, pop the arguments to clean them up. - // Without this, there is a risk to keep dead objects on the stack. - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, arglist, 0, heap); - } - - throw; - - } + push_args (arglist, meth, args, heap); meth->call (obj, arglist, retlist); @@ -1764,25 +1781,7 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args) gsi::SerialArgs retlist (meth->retsize ()); gsi::SerialArgs arglist (meth->argsize ()); - try { - - int i = 0; - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { - push_arg (*a, arglist, PyTuple_GetItem (args, i), heap); - } - - } catch (...) { - - // In case of an error upon write, pop the arguments to clean them up. - // Without this, there is a risk to keep dead objects on the stack. - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, arglist, 0, heap); - } - - throw; - - } + push_args (arglist, meth, args, heap); meth->call (0, arglist, retlist); From 590c4a6b31353fda4f87fb5e6c6b42cd1bb613fa Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Jul 2022 01:32:27 +0200 Subject: [PATCH 03/81] Argument numbers/names for Ruby too --- src/pya/pya/pya.cc | 17 -------- src/pya/pya/pya.h | 15 ------- src/pya/pya/pyaModule.cc | 11 ++++- src/rba/rba/rba.cc | 89 ++++++++++++++++++++------------------ src/tl/tl/tlException.h | 20 +++++++++ src/tl/tl/tlScriptError.cc | 6 --- src/tl/tl/tlScriptError.h | 2 - 7 files changed, 77 insertions(+), 83 deletions(-) diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc index 3092eb8e4..15456c0dc 100644 --- a/src/pya/pya/pya.cc +++ b/src/pya/pya/pya.cc @@ -67,23 +67,6 @@ PythonError::PythonError (const PythonError &d) : tl::ScriptError (d) { } -// -------------------------------------------------------------------------- -// PythonArgumentError implementation - -PythonArgumentError::PythonArgumentError (const tl::Exception &ex, int index, const std::string &name) - : tl::Exception (ex.msg ()), m_index (index), m_name (name) -{ } - -std::string -PythonArgumentError::msg () const -{ - if (! m_name.empty ()) { - return tl::sprintf (tl::to_string (tr ("%s for argument #%d ('%s')")), tl::Exception::msg (), m_index + 1, m_name); - } else { - return tl::sprintf (tl::to_string (tr ("%s for argument #%d")), tl::Exception::msg (), m_index + 1); - } -} - // -------------------------------------------------------------------------- /** diff --git a/src/pya/pya/pya.h b/src/pya/pya/pya.h index 11cdcd9f0..fe1337b40 100644 --- a/src/pya/pya/pya.h +++ b/src/pya/pya/pya.h @@ -82,21 +82,6 @@ public: PythonError (const PythonError &d); }; -/** - * @brief A class encapsulating an argument error - */ -class PYA_PUBLIC PythonArgumentError - : public tl::Exception -{ -public: - PythonArgumentError (const tl::Exception &ex, int index, const std::string &name); - virtual std::string msg () const; - -private: - int m_index; - std::string m_name; -}; - class PythonModule; /** diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 37d7dfab0..fc4a7ee29 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -1195,8 +1195,17 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args pop_arg (*a, arglist, 0, heap); } + std::string msg; const gsi::ArgSpecBase *arg_spec = meth->begin_arguments () [i].spec (); - throw PythonArgumentError (ex, i, arg_spec ? arg_spec->name () : std::string ()); + + if (arg_spec && ! arg_spec->name ().empty ()) { + msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d ('%s')")), ex.basic_msg (), i + 1, arg_spec->name ()); + } else { + msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d")), ex.basic_msg (), i + 1); + } + + ex.set_basic_msg (msg); + throw ex; } catch (...) { diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 2a0c1b8f5..ab323b222 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -914,6 +914,51 @@ static gsi::ArgType create_void_type () static gsi::ArgType s_void_type = create_void_type (); +static void +push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, VALUE *argv, int argc, tl::Heap &heap) +{ + int i = 0; + + try { + + VALUE *av = argv; + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && av < argv + argc; ++a, ++av, ++i) { + push_arg (*a, arglist, *av, heap); + } + + } catch (tl::Exception &ex) { + + // In case of an error upon write, pop the arguments to clean them up. + // Without this, there is a risk to keep dead objects on the stack. + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { + pop_arg (*a, 0, arglist, heap); + } + + const gsi::ArgSpecBase *arg_spec = meth->begin_arguments () [i].spec (); + + std::string msg; + if (arg_spec && ! arg_spec->name ().empty ()) { + msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d ('%s')")), ex.basic_msg (), i + 1, arg_spec->name ()); + } else { + msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d")), ex.basic_msg (), i + 1); + } + + ex.set_basic_msg (msg); + throw ex; + + } catch (...) { + + // In case of an error upon write, pop the arguments to clean them up. + // Without this, there is a risk to keep dead objects on the stack. + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { + pop_arg (*a, 0, arglist, heap); + } + + throw; + + } +} + VALUE method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) { @@ -991,28 +1036,8 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) { gsi::SerialArgs arglist (meth->argsize ()); - - try { - - VALUE *av = argv; - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && av < argv + argc; ++a, ++av) { - push_arg (*a, arglist, *av, heap); - } - - } catch (...) { - - // In case of an error upon write, pop the arguments to clean them up. - // Without this, there is a risk to keep dead objects on the stack. - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, 0, arglist, heap); - } - - throw; - - } - + push_args (arglist, meth, argv, argc, heap); meth->call (0, arglist, retlist); - } void *obj = retlist.read (heap); @@ -1065,28 +1090,8 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) { gsi::SerialArgs arglist (meth->argsize ()); - - try { - - VALUE *av = argv; - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && av < argv + argc; ++a, ++av) { - push_arg (*a, arglist, *av, heap); - } - - } catch (...) { - - // In case of an error upon write, pop the arguments to clean them up. - // Without this, there is a risk to keep dead objects on the stack. - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, 0, arglist, heap); - } - - throw; - - } - + push_args (arglist, meth, argv, argc, heap); meth->call (obj, arglist, retlist); - } if (meth->ret_type ().is_iter ()) { diff --git a/src/tl/tl/tlException.h b/src/tl/tl/tlException.h index 4c5181aef..0eb93543f 100644 --- a/src/tl/tl/tlException.h +++ b/src/tl/tl/tlException.h @@ -126,8 +126,28 @@ public: virtual ~Exception () { } + /** + * @brief Gets the full message text + * Derived classes may dynamically build error messages. + * "basic_msg" is the core message. Derived classes may + * ignore the core message or modify the latter to build + * the full message. + */ virtual std::string msg () const { return m_msg; } + /** + * @brief Gets the basic message + * The basic message is the actual error text. Derived classes + * may decide to deliver a more elaborate version of the message + * through "msg". + */ + std::string basic_msg () const { return m_msg; } + + /** + * @brief Exchanges the basic message + */ + void set_basic_msg (const std::string &msg) { m_msg = msg; } + private: std::string m_msg; void init (const std::string &fmt, const std::vector &a); diff --git a/src/tl/tl/tlScriptError.cc b/src/tl/tl/tlScriptError.cc index 39a985833..9444db1d3 100644 --- a/src/tl/tl/tlScriptError.cc +++ b/src/tl/tl/tlScriptError.cc @@ -97,12 +97,6 @@ ScriptError::ScriptError (const ScriptError &d) // .. nothing yet .. } -std::string -ScriptError::basic_msg () const -{ - return tl::Exception::msg (); -} - std::string ScriptError::msg () const { diff --git a/src/tl/tl/tlScriptError.h b/src/tl/tl/tlScriptError.h index 937ce9d23..312dd4a6b 100644 --- a/src/tl/tl/tlScriptError.h +++ b/src/tl/tl/tlScriptError.h @@ -128,8 +128,6 @@ public: virtual std::string msg () const; - std::string basic_msg () const; - private: std::string m_sourcefile; int m_line; From 43101ded7f90a803282223204b89a40ccb8894ae Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Jul 2022 19:28:14 +0200 Subject: [PATCH 04/81] More consistent handling of exceptions and their display in the Ruby debugger - without triggering too many breakpoints on rethrow and showing the reason for exceptions arising from argument errors --- src/rba/rba/rba.cc | 126 +++++++++++++++++++++++++------------- src/rba/rba/rba.h | 10 +++ src/rba/rba/rbaUtils.cc | 12 ++++ src/rba/rba/rbaUtils.h | 8 +++ src/tl/tl/tlException.cc | 1 + src/tl/tl/tlException.h | 18 +++++- src/tl/tl/tlScriptError.h | 15 ++++- 7 files changed, 145 insertions(+), 45 deletions(-) diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index ab323b222..051ddac7a 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -675,49 +675,78 @@ struct RubyInterpreterPrivateData // ------------------------------------------------------------------- // Ruby API +static void +handle_exception (VALUE exc, bool first_chance) +{ + if (! first_chance) { + // Re-raise the exception without blocking in the debugger + block_exceptions (true); + } + + rb_exc_raise (exc); +} + +static void +handle_exception (const std::string &where, std::exception &ex) +{ + VALUE error_msg = rb_str_new2 ((std::string(ex.what ()) + tl::to_string (tr (" in ")) + where).c_str ()); + VALUE args [1]; + args [0] = error_msg; + VALUE exc = rb_class_new_instance(1, args, rb_eRuntimeError); + handle_exception (exc, true); +} + +static void +handle_exception (const std::string &where, tl::ExitException &ex) +{ + VALUE error_msg = rb_str_new2 ((ex.msg () + tl::to_string (tr (" in ")) + where).c_str ()); + VALUE args [2]; + args [0] = INT2NUM (ex.status ()); + args [1] = error_msg; + VALUE exc = rb_class_new_instance (2, args, rb_eSystemExit); + handle_exception (exc, ex.first_chance ()); +} + +static void +handle_exception (const std::string & /*where*/, rba::RubyError &ex) +{ + handle_exception (ex.exc (), ex.first_chance ()); +} + +static void +handle_exception (const std::string &where, tl::Exception &ex) +{ + VALUE error_msg = rb_str_new2 ((ex.msg () + tl::to_string (tr (" in ")) + where).c_str ()); \ + VALUE args [1]; + args [0] = error_msg; + VALUE exc = rb_class_new_instance(1, args, rb_eRuntimeError); + handle_exception (exc, ex.first_chance ()); +} + +static void +handle_exception (const std::string &where) +{ + VALUE error_msg = rb_str_new2 ((tl::to_string (tr ("Unspecific exception in ")) + where).c_str ()); \ + VALUE args [1]; + args [0] = error_msg; + VALUE exc = rb_class_new_instance(1, args, rb_eRuntimeError); + handle_exception (exc, true); +} + #define RBA_TRY \ - VALUE __error_msg = Qnil; \ - int __estatus = 0; \ - VALUE __exc = Qnil; \ - VALUE __eclass = Qnil; \ - { \ - try { + try { #define RBA_CATCH(where) \ - } catch (std::exception &ex) { \ - __eclass = rb_eRuntimeError; \ - __error_msg = rb_str_new2 ((std::string(ex.what ()) + tl::to_string (tr (" in ")) + (where)).c_str ()); \ - } catch (tl::ExitException &ex) { \ - __estatus = ex.status (); \ - __eclass = rb_eSystemExit; \ - __error_msg = rb_str_new2 ((ex.msg () + tl::to_string (tr (" in ")) + (where)).c_str ()); \ - } catch (rba::RubyError &ex) { \ - __eclass = rb_eRuntimeError; \ - __exc = ex.exc (); \ - } catch (tl::Exception &ex) { \ - __eclass = rb_eRuntimeError; \ - __error_msg = rb_str_new2 ((ex.msg () + tl::to_string (tr (" in ")) + (where)).c_str ()); \ - } catch (...) { \ - __eclass = rb_eRuntimeError; \ - __error_msg = rb_str_new2 ((tl::to_string (tr ("Unspecific exception in ")) + (where)).c_str ()); \ - } \ - } \ - if (__exc != Qnil) { \ - /* Re-raise the exception without blocking in the debugger */ \ - /* TODO: should not access private data */ \ - RubyInterpreter::instance ()->d->block_exceptions = true; \ - rb_exc_raise (__exc); \ - } else if (__eclass == rb_eSystemExit) { \ - /* HINT: we do the rb_raise outside any destructor code - sometimes this longjmp seems not to work properly */ \ - VALUE args [2]; \ - args [0] = INT2NUM (__estatus); \ - args [1] = __error_msg; \ - rb_exc_raise (rb_class_new_instance(2, args, __eclass)); \ - } else if (__eclass != Qnil) { \ - /* HINT: we do the rb_raise outside any destructor code - sometimes this longjmp seems not to work properly */ \ - VALUE args [1]; \ - args [0] = __error_msg; \ - rb_exc_raise (rb_class_new_instance(1, args, __eclass)); \ + } catch (std::exception &ex) { \ + handle_exception ((where), ex); \ + } catch (tl::ExitException &ex) { \ + handle_exception ((where), ex); \ + } catch (rba::RubyError &ex) { \ + handle_exception ((where), ex); \ + } catch (tl::Exception &ex) { \ + handle_exception ((where), ex); \ + } catch (...) { \ + handle_exception ((where)); \ } static VALUE @@ -943,8 +972,9 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, VALUE *argv, i msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d")), ex.basic_msg (), i + 1); } - ex.set_basic_msg (msg); - throw ex; + tl::Exception new_ex (msg); + new_ex.set_first_chance (ex.first_chance ()); + throw new_ex; } catch (...) { @@ -2225,6 +2255,18 @@ RubyInterpreter::remove_console (gsi::Console *console) } } +void +RubyInterpreter::block_exceptions (bool f) +{ + d->block_exceptions = f; +} + +bool +RubyInterpreter::exceptions_blocked () +{ + return d->block_exceptions; +} + static size_t prepare_trace (RubyInterpreter *interp, const char *fn) { diff --git a/src/rba/rba/rba.h b/src/rba/rba/rba.h index 1b204e186..fa4e39d3b 100644 --- a/src/rba/rba/rba.h +++ b/src/rba/rba/rba.h @@ -178,6 +178,16 @@ public: */ void end_exec (); + /** + * @brief Gets a flag indicating whether exceptions are blocked from being seen in the debugger + */ + bool exceptions_blocked (); + + /** + * @brief Sets a flag indicating whether exceptions are blocked from being seen in the debugger + */ + void block_exceptions (bool f); + /** * @brief Fetch the version string * diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc index ccc071d2a..439fe97bb 100644 --- a/src/rba/rba/rbaUtils.cc +++ b/src/rba/rba/rbaUtils.cc @@ -112,6 +112,18 @@ rba_get_backtrace_from_array (VALUE backtrace, std::vector } } +void +block_exceptions (bool f) +{ + RubyInterpreter::instance ()->block_exceptions (f); +} + +bool +exceptions_blocked () +{ + return RubyInterpreter::instance ()->exceptions_blocked (); +} + void rba_check_error () { diff --git a/src/rba/rba/rbaUtils.h b/src/rba/rba/rbaUtils.h index bcb514351..436841b50 100644 --- a/src/rba/rba/rbaUtils.h +++ b/src/rba/rba/rbaUtils.h @@ -224,6 +224,9 @@ VALUE rba_f_eval_checked (int argc, VALUE *argv, VALUE self); void rba_yield_checked (VALUE value); VALUE rba_eval_string_in_context (const char *expr, const char *file, int line, int context); +bool exceptions_blocked (); +void block_exceptions (bool f); + /** * @brief A struct encapsulating the call parameters for a function */ @@ -261,7 +264,12 @@ R rba_safe_func (R (*f) (A), A arg) int error = 0; RUBY_BEGIN_EXEC + // NOTE: we do not want exceptions to be seen in the debugger here - later they are rethrown after + // being annotated. This is when we want to see them. + bool eb = exceptions_blocked (); + block_exceptions (true); rb_protect (&rba_safe_func_caller, (VALUE) &cp, &error); + block_exceptions (eb); RUBY_END_EXEC if (error) { diff --git a/src/tl/tl/tlException.cc b/src/tl/tl/tlException.cc index 4c66acd1e..614836a03 100644 --- a/src/tl/tl/tlException.cc +++ b/src/tl/tl/tlException.cc @@ -31,6 +31,7 @@ namespace tl void Exception::init (const std::string &fmt, const std::vector &a) { + m_first_chance = true; m_msg = tl::sprintf (fmt, a); } diff --git a/src/tl/tl/tlException.h b/src/tl/tl/tlException.h index 0eb93543f..ecde47d9b 100644 --- a/src/tl/tl/tlException.h +++ b/src/tl/tl/tlException.h @@ -46,7 +46,7 @@ class TL_PUBLIC Exception { public: Exception (const std::string &msg) - : m_msg (msg) + : m_msg (msg), m_first_chance (true) { } Exception (const std::string &fmt, const std::vector &a) @@ -148,8 +148,24 @@ public: */ void set_basic_msg (const std::string &msg) { m_msg = msg; } + /** + * @brief Sets a flag indicating whether this exception is a first-chance one + * + * "first chance" means it has not been seen in the debugger. + * Set this flag to false to indicate that it already got seen. + * By default the flag is true, indicating it has not been handled + * by the debugger. + */ + void set_first_chance (bool f) { m_first_chance = f; } + + /** + * @brief Gets a flag indicating that is this a first-chance exception + */ + bool first_chance () const { return m_first_chance; } + private: std::string m_msg; + bool m_first_chance; void init (const std::string &fmt, const std::vector &a); }; diff --git a/src/tl/tl/tlScriptError.h b/src/tl/tl/tlScriptError.h index 312dd4a6b..3e00a4f60 100644 --- a/src/tl/tl/tlScriptError.h +++ b/src/tl/tl/tlScriptError.h @@ -148,8 +148,19 @@ class TL_PUBLIC ExitException : public tl::Exception { public: - ExitException () : tl::Exception ("exit"), m_status (1) { } - ExitException (int status) : tl::Exception ("exit"), m_status (status) { } + ExitException () + : tl::Exception ("exit"), m_status (1) + { + // do not catch in debugger + set_first_chance (false); + } + + ExitException (int status) + : tl::Exception ("exit"), m_status (status) + { + // do not catch in debugger + set_first_chance (false); + } int status() const { return m_status; } From 3c5ca36081e672d3d8f3cdaf494317a511ab0317 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Jul 2022 00:10:36 +0200 Subject: [PATCH 05/81] [Consider merging] fixed a potential race condition with invalid Shapes container after clear --- src/db/db/dbShapes.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index 250918349..962234ee1 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -998,6 +998,8 @@ Shapes::clear () { if (!m_layers.empty ()) { + invalidate_state (); // HINT: must come before the change is done! + for (tl::vector::const_iterator l = m_layers.end (); l != m_layers.begin (); ) { // because the undo stack will do a push, we need to remove layers from the back (this is the last undo // element to be executed) @@ -1010,7 +1012,6 @@ Shapes::clear () } } - invalidate_state (); // HINT: must come before the change is done! m_layers.clear (); } From 429e43fe374b49d32662f4e6c2eb3ae8b29e66ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Jul 2022 00:11:20 +0200 Subject: [PATCH 06/81] Fixed Qt-less LayoutView (removed QWidget as base class which is no longer true) --- src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc | 13 +------------ src/layview/layview/gsiDeclLayLayoutView_qt.cc | 5 +++++ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc index fbbbb4ed9..290fdb66e 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc @@ -33,13 +33,6 @@ #include "dbLayoutVsSchematic.h" #include "tlStream.h" -#if defined(HAVE_QTBINDINGS) -# include "gsiQtGuiExternals.h" -# include "gsiQtWidgetsExternals.h" // for Qt5 -#else -# define QT_EXTERNAL_BASE(x) -#endif - namespace gsi { @@ -466,11 +459,7 @@ static lay::AbstractMenu *menu (lay::LayoutViewBase *view) return view->menu (); } -#if defined(HAVE_QT) -LAYBASIC_PUBLIC Class decl_LayoutViewBase (QT_EXTERNAL_BASE (QWidget) "lay", "LayoutViewBase", -#else -LAYBASIC_PUBLIC Class decl_LayoutViewBase (QT_EXTERNAL_BASE (QWidget) "lay", "LayoutViewBase", -#endif +LAYBASIC_PUBLIC Class decl_LayoutViewBase ("lay", "LayoutViewBase", gsi::constant ("LV_NoLayers", (unsigned int) lay::LayoutViewBase::LV_NoLayers, "@brief With this option, no layers view will be provided (see \\layer_control_frame)\n" "Use this value with the constructor's 'options' argument.\n" diff --git a/src/layview/layview/gsiDeclLayLayoutView_qt.cc b/src/layview/layview/gsiDeclLayLayoutView_qt.cc index 8f104d62f..a5a95d6c3 100644 --- a/src/layview/layview/gsiDeclLayLayoutView_qt.cc +++ b/src/layview/layview/gsiDeclLayLayoutView_qt.cc @@ -116,6 +116,11 @@ Class decl_LayoutView (decl_LayoutViewBase, "lay", "LayoutView" "\n" "This method has been introduced in version 0.27\n" ) + + gsi::method ("widget", &lay::LayoutView::widget, + "@brief Gets the QWidget object for the layout view\n" + "\n" + "This method has been introduced in version 0.28 where LayoutView is no longer derived from QWidget directly.\n" + ) + #endif gsi::method ("current", &lay::LayoutView::current, "@brief Returns the current view\n" From 40b4b22f029a7f39fddcbbbb7819bc00e6b8d1bd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 1 Aug 2022 19:01:41 +0200 Subject: [PATCH 07/81] Added variants for CellInstArray constructor that take a Cell reference --- src/db/db/gsiDeclDbCell.cc | 123 +++++++++++++++++++++++++++ testdata/ruby/dbCellInstArrayTest.rb | 29 +++++++ 2 files changed, 152 insertions(+) diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 634716370..a03c27290 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -141,6 +141,55 @@ struct cell_inst_array_defs } } + // Cell-based constructors + + static C * + new_cell_inst_vector2 (const db::Cell *cell, const vector_type &v) + { + tl_assert (cell != 0); + return new_cell_inst_vector (cell->cell_index (), v); + } + + static C * + new_cell_inst2 (const db::Cell *cell, const trans_type &t) + { + tl_assert (cell != 0); + return new_cell_inst (cell->cell_index (), t); + } + + static C * + new_cell_inst_cplx2 (const db::Cell *cell, const complex_trans_type &t) + { + tl_assert (cell != 0); + return new_cell_inst_cplx (cell->cell_index (), t); + } + + static C * + new_cell_inst_array_vector2 (const db::Cell *cell, const vector_type &v, + const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb) + { + tl_assert (cell != 0); + return new_cell_inst_array_vector (cell->cell_index (), v, a, b, na, nb); + } + + static C * + new_cell_inst_array2 (const db::Cell *cell, const trans_type &t, + const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb) + { + tl_assert (cell != 0); + return new_cell_inst_array (cell->cell_index (), t, a, b, na, nb); + } + + static C * + new_cell_inst_array_cplx2 (const db::Cell *cell, const complex_trans_type &t, + const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb) + { + tl_assert (cell != 0); + return new_cell_inst_array_cplx (cell->cell_index (), t, a, b, na, nb); + } + + // Methods + static db::cell_index_type cell_index (const C *a) { return a->object ().cell_index (); @@ -151,6 +200,12 @@ struct cell_inst_array_defs a->object ().cell_index (cell_index); } + static void set_cell (C *a, db::Cell *cell) + { + tl_assert (cell != 0); + a->object ().cell_index (cell->cell_index ()); + } + static C transformed_simple (const C *arr, const coord_trans_type &t) { return arr->transformed (t); @@ -421,17 +476,41 @@ struct cell_inst_array_defs "@param cell_index The cell to instantiate\n" "@param trans The transformation by which to instantiate the cell\n" ) + + gsi::constructor ("new", &new_cell_inst2, gsi::arg ("cell"), gsi::arg ("trans"), + "@brief Creates a single cell instance\n" + "@param cell The cell to instantiate\n" + "@param trans The transformation by which to instantiate the cell\n" + "\n" + "This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It " + "has been introduced in version 0.28." + ) + gsi::constructor ("new", &new_cell_inst_vector, gsi::arg ("cell_index"), gsi::arg ("disp"), "@brief Creates a single cell instance\n" "@param cell_index The cell to instantiate\n" "@param disp The displacement\n" "This convenience initializer has been introduced in version 0.28." ) + + gsi::constructor ("new", &new_cell_inst_vector2, gsi::arg ("cell"), gsi::arg ("disp"), + "@brief Creates a single cell instance\n" + "@param cell The cell to instantiate\n" + "@param disp The displacement\n" + "\n" + "This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It " + "has been introduced in version 0.28." + ) + gsi::constructor ("new", &new_cell_inst_cplx, gsi::arg ("cell_index"), gsi::arg ("trans"), "@brief Creates a single cell instance with a complex transformation\n" "@param cell_index The cell to instantiate\n" "@param trans The complex transformation by which to instantiate the cell\n" ) + + gsi::constructor ("new", &new_cell_inst_cplx2, gsi::arg ("cell"), gsi::arg ("trans"), + "@brief Creates a single cell instance with a complex transformation\n" + "@param cell The cell to instantiate\n" + "@param trans The complex transformation by which to instantiate the cell\n" + "\n" + "This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It " + "has been introduced in version 0.28." + ) + gsi::constructor ("new", &new_cell_inst_array, gsi::arg ("cell_index"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"), "@brief Creates a single cell instance\n" "@param cell_index The cell to instantiate\n" @@ -445,6 +524,18 @@ struct cell_inst_array_defs "Starting with version 0.25 the displacements are of vector type." ) ) + + gsi::constructor ("new", &new_cell_inst_array2, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"), + "@brief Creates a single cell instance\n" + "@param cell The cell to instantiate\n" + "@param trans The transformation by which to instantiate the cell\n" + "@param a The displacement vector of the array in the 'a' axis\n" + "@param b The displacement vector of the array in the 'b' axis\n" + "@param na The number of placements in the 'a' axis\n" + "@param nb The number of placements in the 'b' axis\n" + "\n" + "This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It " + "has been introduced in version 0.28." + ) + gsi::constructor ("new", &new_cell_inst_array_vector, gsi::arg ("cell_index"), gsi::arg ("disp"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"), "@brief Creates a single cell instance\n" "@param cell_index The cell to instantiate\n" @@ -456,6 +547,18 @@ struct cell_inst_array_defs "\n" "This convenience initializer has been introduced in version 0.28." ) + + gsi::constructor ("new", &new_cell_inst_array_vector2, gsi::arg ("cell"), gsi::arg ("disp"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"), + "@brief Creates a single cell instance\n" + "@param cell The cell to instantiate\n" + "@param disp The basic displacement of the first instance\n" + "@param a The displacement vector of the array in the 'a' axis\n" + "@param b The displacement vector of the array in the 'b' axis\n" + "@param na The number of placements in the 'a' axis\n" + "@param nb The number of placements in the 'b' axis\n" + "\n" + "This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It " + "has been introduced in version 0.28." + ) + gsi::constructor ("new", &new_cell_inst_array_cplx, gsi::arg ("cell_index"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"), "@brief Creates a single cell instance with a complex transformation\n" "@param cell_index The cell to instantiate\n" @@ -469,6 +572,18 @@ struct cell_inst_array_defs "Starting with version 0.25 the displacements are of vector type." ) ) + + gsi::constructor ("new", &new_cell_inst_array_cplx2, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"), + "@brief Creates a single cell instance with a complex transformation\n" + "@param cell The cell to instantiate\n" + "@param trans The complex transformation by which to instantiate the cell\n" + "@param a The displacement vector of the array in the 'a' axis\n" + "@param b The displacement vector of the array in the 'b' axis\n" + "@param na The number of placements in the 'a' axis\n" + "@param nb The number of placements in the 'b' axis\n" + "\n" + "This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It " + "has been introduced in version 0.28." + ) + gsi::iterator ("each_trans", (typename C::iterator (C::*) () const) &C::begin, "@brief Gets the simple transformations represented by this instance\n" "For a single instance, this iterator will deliver the single, simple transformation. " @@ -500,10 +615,18 @@ struct cell_inst_array_defs ) + gsi::method_ext ("cell_index", &cell_index, "@brief Gets the cell index of the cell instantiated \n" + "Use \\Layout#cell to get the \\Cell object from the cell index." ) + method_ext ("cell_index=", &set_cell_index, gsi::arg ("index"), "@brief Sets the index of the cell this instance refers to\n" ) + + method_ext ("cell=", &set_cell, gsi::arg ("cell"), + "@brief Sets the cell this instance refers to\n" + "This is a convenience method and equivalent to 'cell_index = cell.cell_index()'. There is no getter for " + "the cell pointer because the \\CellInstArray object only knows about cell indexes.\n" + "\n" + "This convenience method has been introduced in version 0.28.\n" + ) + gsi::method ("cplx_trans", (complex_trans_type (C::*) () const) &C::complex_trans, "@brief Gets the complex transformation of the first instance in the array\n" "This method is always applicable, compared to \\trans, since simple transformations can be expressed as complex transformations as well." diff --git a/testdata/ruby/dbCellInstArrayTest.rb b/testdata/ruby/dbCellInstArrayTest.rb index b61aece95..9d936c787 100644 --- a/testdata/ruby/dbCellInstArrayTest.rb +++ b/testdata/ruby/dbCellInstArrayTest.rb @@ -631,6 +631,35 @@ class DBCellInst_TestClass < TestBase end + # Cell * variants + def test_5_CellPointer + + ly = RBA::Layout::new + cell = ly.create_cell("TOP") + + a = RBA::CellInstArray::new(cell, RBA::Trans::new(RBA::Trans::R90)) + assert_equal(a.cell_index, cell.cell_index) + + a = RBA::CellInstArray::new(cell, RBA::Vector::new(42, -17)) + assert_equal(a.cell_index, cell.cell_index) + + a = RBA::CellInstArray::new(cell, RBA::ICplxTrans::new(1.5)) + assert_equal(a.cell_index, cell.cell_index) + + a = RBA::CellInstArray::new(cell, RBA::ICplxTrans::new(1.0, 45, false, RBA::Vector::new)) + assert_equal(a.cell_index, cell.cell_index) + + a = RBA::CellInstArray::new(cell, RBA::Trans::new(RBA::Trans::R90), RBA::Vector::new(10, 20), RBA::Vector::new(30, 40), 3, 5) + assert_equal(a.cell_index, cell.cell_index) + + a = RBA::CellInstArray::new(cell, RBA::Vector::new(42, -17), RBA::Vector::new(10, 20), RBA::Vector::new(30, 40), 3, 5) + assert_equal(a.cell_index, cell.cell_index) + + a = RBA::CellInstArray::new(cell, RBA::ICplxTrans::new(1.5), RBA::Vector::new(10, 20), RBA::Vector::new(30, 40), 3, 5) + assert_equal(a.cell_index, cell.cell_index) + + end + end load("test_epilogue.rb") From 42ddd07bfe117c052252ecc304876c0142e88302 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 2 Aug 2022 22:51:32 +0200 Subject: [PATCH 08/81] Introducing 'close all' and 'close all except' for macro editor tabs, avoid one additional entry on the edit trace stack when raising an exception --- src/lay/lay/layMacroEditorDialog.cc | 83 +++++++++++++++++++++++++++++ src/lay/lay/layMacroEditorDialog.h | 2 + 2 files changed, 85 insertions(+) diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc index 90a0b1f27..dd1f2725f 100644 --- a/src/lay/lay/layMacroEditorDialog.cc +++ b/src/lay/lay/layMacroEditorDialog.cc @@ -392,6 +392,15 @@ MacroEditorDialog::MacroEditorDialog (lay::Dispatcher *pr, lym::MacroCollection connect (tabWidget, SIGNAL (tabCloseRequested (int)), this, SLOT (tab_close_requested (int))); #endif + tabWidget->setContextMenuPolicy (Qt::ActionsContextMenu); + + QAction *action = new QAction (tr ("Close All"), this); + connect (action, SIGNAL (triggered ()), this, SLOT (close_all ())); + tabWidget->addAction (action); + action = new QAction (tr ("Close All Except Current"), this); + connect (action, SIGNAL (triggered ()), this, SLOT (close_all_but_current ())); + tabWidget->addAction (action); + dbgOn->setEnabled (true); runButton->setEnabled (true); runThisButton->setEnabled (true); @@ -2228,6 +2237,75 @@ MacroEditorDialog::new_macro() return m; } +void +MacroEditorDialog::close_all () +{ + if (m_in_exec) { + return; + } + +BEGIN_PROTECTED + + tabWidget->clear (); + + for (std::map ::iterator p = m_tab_widgets.begin (); p != m_tab_widgets.end (); ++p) { + if (p->second) { + p->second->connect_macro (0); + } + delete p->second; + } + + m_tab_widgets.clear (); + + refresh_file_watcher (); + +END_PROTECTED +} + +void +MacroEditorDialog::close_all_but_current () +{ + if (m_in_exec) { + return; + } + +BEGIN_PROTECTED + + QWidget *cw = tabWidget->currentWidget (); + int ci = tabWidget->currentIndex (); + + if (ci < 0) { + close_all (); + return; + } + + for (int i = tabWidget->count (); i > 0; ) { + --i; + if (i != ci) { + tabWidget->removeTab (i); + } + } + + std::map new_widgets; + + for (std::map ::iterator p = m_tab_widgets.begin (); p != m_tab_widgets.end (); ++p) { + if (cw && p->second == cw) { + new_widgets.insert (*p); + } else { + if (p->second) { + p->second->connect_macro (0); + } + delete p->second; + } + } + + m_tab_widgets.swap (new_widgets); + + refresh_file_watcher (); + +END_PROTECTED +} + void MacroEditorDialog::tab_close_requested (int index) { @@ -3405,7 +3483,12 @@ MacroEditorDialog::editor_for_macro (lym::Macro *macro) if (macro == mp_run_macro) { tabWidget->setTabIcon (index, QIcon (QString::fromUtf8 (m_in_exec ? (m_in_breakpoint ? ":/pause.png" : ":/stop.png") : ":/run.png"))); } + + bool f = m_add_edit_trace_enabled; + m_add_edit_trace_enabled = false; tabWidget->setCurrentWidget (editor); + m_add_edit_trace_enabled = f; + m_tab_widgets.insert (std::make_pair (macro, editor)); refresh_file_watcher (); diff --git a/src/lay/lay/layMacroEditorDialog.h b/src/lay/lay/layMacroEditorDialog.h index 7e6e363e0..e3c14c68d 100644 --- a/src/lay/lay/layMacroEditorDialog.h +++ b/src/lay/lay/layMacroEditorDialog.h @@ -212,6 +212,8 @@ private slots: void search_editing (); void search_finished (); void tab_close_requested (int); + void close_all (); + void close_all_but_current (); void replace_mode_button_clicked (); void replace_next_button_clicked (); void replace_all_button_clicked (); From 008302122071d0314664659019875f0a205f219f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Aug 2022 23:44:06 +0200 Subject: [PATCH 09/81] [Consider merging] Some refactoring of L2N and LVSDB readers for more future compatibility --- src/db/db/dbLayoutToNetlistFormatDefs.h | 105 +-- src/db/db/dbLayoutToNetlistReader.cc | 209 ++++-- src/db/db/dbLayoutToNetlistReader.h | 12 +- src/db/db/dbLayoutVsSchematicFormatDefs.cc | 14 + src/db/db/dbLayoutVsSchematicFormatDefs.h | 117 +-- src/db/db/dbLayoutVsSchematicReader.cc | 32 +- .../dbLayoutToNetlistReaderTests.cc | 24 + src/db/unit_tests/dbLayoutVsSchematicTests.cc | 16 + testdata/algo/l2n_reader_5.l2n | 452 ++++++++++++ testdata/algo/l2n_reader_au_5.l2n | 487 +++++++++++++ testdata/algo/lvs_test3.lvsdb | 687 ++++++++++++++++++ testdata/algo/lvs_test3_au.lvsdb | 673 +++++++++++++++++ 12 files changed, 2651 insertions(+), 177 deletions(-) create mode 100644 testdata/algo/l2n_reader_5.l2n create mode 100644 testdata/algo/l2n_reader_au_5.l2n create mode 100644 testdata/algo/lvs_test3.lvsdb create mode 100644 testdata/algo/lvs_test3_au.lvsdb diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index d77386a2d..428eaaeaa 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -46,55 +46,76 @@ namespace db * The file follows the declaration-before-use principle * (circuits before subcircuits, nets before use ...) * - * Global statements: + * Main body: + * [version|description|unit|top|layer|connect|global|circuit|class|device|any]* * + * [version]: * version() - file format version [short key: V] + * + * [description]: * description() - an arbitrary description text [short key: B] + * + * [unit]: * unit() - specifies the database unit [short key: U] + * + * [top]: * top() - specifies the name of the top circuit [short key: W] + * + * [layer]: * layer( ?) - define a layer [short key: L] + * + * [connect]: * connect( ...) - connects layer1 with the following layers [short key: C] + * + * [global]: * global( ...) * - connects the shapes of the layer with the given global * nets [short key: G] + * + * [circuit]: * circuit( [circuit-def]) - circuit (cell) [short key: X] + * + * [class]: * class(