fixup! Better exception handling (with Qt Script)

This commit is contained in:
Laurent Rineau 2016-10-27 17:55:58 +02:00
parent ed2eae8879
commit 54dbab9d3c
5 changed files with 81 additions and 26 deletions

View File

@ -433,17 +433,25 @@ void MainWindow::evaluate_script(QString script,
const bool quiet) { const bool quiet) {
QScriptValue value = script_engine->evaluate(script, filename); QScriptValue value = script_engine->evaluate(script, filename);
if(script_engine->hasUncaughtException()) { if(script_engine->hasUncaughtException()) {
QScriptValue js_exception = script_engine->uncaughtException();
QScriptValue js_bt =js_exception.property("backtrace");
QStringList bt = script_engine->uncaughtExceptionBacktrace();
if(js_bt.isValid()) {
QStringList other_bt;
qScriptValueToSequence(js_bt, other_bt);
if(!other_bt.isEmpty()) bt = other_bt;
}
if(!quiet) { if(!quiet) {
QTextStream err(stderr); QTextStream err(stderr);
err << "Qt Script exception:\n" err << "Qt Script exception:\n"
<< script_engine->uncaughtException().toString() << js_exception.toString()
<< "\nBacktrace:\n"; << "\nBacktrace:\n";
Q_FOREACH(QString line, script_engine->uncaughtExceptionBacktrace()) { Q_FOREACH(QString line, bt) {
err << " " << line << "\n"; err << " " << line << "\n";
} }
} }
throw CGAL::Three::Script_exception throw CGAL::Three::Script_exception
(script_engine->uncaughtException().toString().toStdString()); (script_engine->uncaughtException().toString(), bt);
} }
else if(!quiet && !value.isNull() && !value.isUndefined()) { else if(!quiet && !value.isNull() && !value.isUndefined()) {
QTextStream(stderr) << "Qt Script evaluated to \"" QTextStream(stderr) << "Qt Script evaluated to \""
@ -468,6 +476,10 @@ void MainWindow::enableScriptDebugger(bool b /* = true */)
if(b) { if(b) {
debugger->action(QScriptEngineDebugger::InterruptAction)->trigger(); debugger->action(QScriptEngineDebugger::InterruptAction)->trigger();
} }
else {
std::cerr << "Detach the script debugger\n";
debugger->detach();
}
} }
return; return;
# endif # endif
@ -1389,6 +1401,13 @@ bool MainWindow::loadScript(QFileInfo info)
return false; return false;
} }
void MainWindow::throw_exception() {
wrap_a_call_to_cpp([]() {
throw std::runtime_error("Exception thrown in "
"MainWindow::throw_exception()");
}, this, __FILE__, __LINE__);
}
void MainWindow::on_actionLoadScript_triggered() void MainWindow::on_actionLoadScript_triggered()
{ {
#if defined(QT_SCRIPT_LIB) #if defined(QT_SCRIPT_LIB)

View File

@ -210,6 +210,8 @@ public Q_SLOTS:
*/ */
void enableScriptDebugger(bool = true); void enableScriptDebugger(bool = true);
/// This slot is used to test exception handling in Qt Scripts.
void throw_exception();
protected Q_SLOTS: protected Q_SLOTS:
//!Gets the new selected item(s) from the sceneView and updates the scene //!Gets the new selected item(s) from the sceneView and updates the scene

View File

@ -139,13 +139,11 @@ private:
}; };
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h> #include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include <CGAL/Three/exceptions.h>
using namespace CGAL::Three; using namespace CGAL::Three;
class Polyhedron_demo_trivial_plugin : class Polyhedron_demo_trivial_plugin :
public QObject, public QObject,
public Polyhedron_demo_plugin_interface, public Polyhedron_demo_plugin_interface
protected QScriptable
{ {
Q_OBJECT Q_OBJECT
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
@ -163,13 +161,6 @@ public:
return false;} return false;}
public Q_SLOTS: public Q_SLOTS:
/// This slot is used to test exception handling in Qt Scripts.
void throw_exception() {
wrap_a_call_to_cpp([]() {
throw std::runtime_error("Exception thrown in "
"Trivial_plugin::throw_exception()");
}, this, __FILE__, __LINE__);
}
void bbox(); void bbox();
void enableAction(); void enableAction();

View File

@ -47,11 +47,12 @@ Polyhedron_demo::Polyhedron_demo(int& argc, char **argv,
QCommandLineOption no_try_catch("no-try-catch", QCommandLineOption no_try_catch("no-try-catch",
tr("Do not catch uncaught exceptions.")); tr("Do not catch uncaught exceptions."));
parser.addOption(no_try_catch); parser.addOption(no_try_catch);
#ifdef QT_SCRIPTTOOLS_LIB
QCommandLineOption debug_scripts("debug-scripts", QCommandLineOption debug_scripts("debug-scripts",
tr("Use the scripts debugger.")); tr("Use the scripts debugger."));
parser.addOption(debug_scripts); parser.addOption(debug_scripts);
#endif QCommandLineOption no_debug_scripts("no-debug-scripts",
tr("Do not use the scripts debugger."));
parser.addOption(no_debug_scripts);
QCommandLineOption no_autostart("no-autostart", QCommandLineOption no_autostart("no-autostart",
tr("Ignore the autostart.js file, if any.")); tr("Ignore the autostart.js file, if any."));
parser.addOption(no_autostart); parser.addOption(no_autostart);
@ -74,7 +75,16 @@ Polyhedron_demo::Polyhedron_demo(int& argc, char **argv,
if(parser.isSet(debug_scripts)) { if(parser.isSet(debug_scripts)) {
mainWindow.enableScriptDebugger(); mainWindow.enableScriptDebugger();
} }
if(parser.isSet(no_debug_scripts)) {
mainWindow.enableScriptDebugger(false);
}
#else
if(parser.isSet(debug_scripts)) {
std::cerr << "Qt Script Tools have not been configured!";
abort();
}
#endif #endif
mainWindow.loadScript(":/cgal/Polyhedron_3/javascript/lib.js"); mainWindow.loadScript(":/cgal/Polyhedron_3/javascript/lib.js");
QFileInfo autostart_js("autostart.js"); QFileInfo autostart_js("autostart.js");
if(!parser.isSet(no_autostart) && autostart_js.exists()) { if(!parser.isSet(no_autostart) && autostart_js.exists()) {

View File

@ -26,17 +26,23 @@
#include <QScriptable> #include <QScriptable>
#include <QScriptContext> #include <QScriptContext>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <QStringList>
namespace CGAL{ namespace CGAL{
namespace Three{ namespace Three{
struct Script_exception : public std::runtime_error { class Script_exception : public std::runtime_error {
Script_exception(const std::string& what_arg) QStringList bt;
: std::runtime_error(what_arg) public:
{} Script_exception(QString what_arg,
Script_exception(QString what_arg) QStringList backtrace)
: std::runtime_error(what_arg.toStdString()) : std::runtime_error(what_arg.toStdString())
, bt(backtrace)
{} {}
QStringList backtrace() const {
return bt;
}
}; };
template <typename T> template <typename T>
@ -73,14 +79,41 @@ wrap_a_call_to_cpp(Callable f,
return f(); return f();
} }
catch(const std::exception& e) { catch(const std::exception& e) {
const Script_exception* se = dynamic_cast<const Script_exception*>(&e);
QScriptContext* context = qs->context(); QScriptContext* context = qs->context();
if(c == PARENT_CONTEXT) context = context->parentContext(); QStringList qt_bt = context->backtrace();
QString error = QObject::tr("Error"); if(se) qt_bt = se->backtrace();
if(file != 0) error += QObject::tr(" at file %1").arg(file); std::cerr << "Backtrace:\n";
if(line != -1) error += QString(":%1").arg(line); Q_FOREACH(QString s, qt_bt)
error += QString(":\n%1").arg(e.what()); {
std::cerr << " " << qPrintable(s) << std::endl;
}
context = context->parentContext();
if(c == PARENT_CONTEXT) {
std::cerr << "> parent";
context = context->parentContext();
} else {
std::cerr << "> current";
}
std::cerr << " context: "
<< qPrintable(context->toString()) << std::endl;
QString error;
if(se) {
error = se->what();
} else {
error = QObject::tr("Error");
QString context;
if(file != 0) context += QObject::tr(" at file %1").arg(file);
if(line != -1) context += QString(":%1").arg(line);
if(!context.isNull()) {
error += context;
qt_bt.push_front(QObject::tr("<cpp>") + context);
}
error += QString(": %1").arg(e.what());
}
QScriptValue v = context->throwError(error); QScriptValue v = context->throwError(error);
v.setProperty("backtrace", context->backtrace().join(";")); v.setProperty("backtrace",
qScriptValueFromSequence(context->engine(), qt_bt));
std::cerr << "result after throwError: " std::cerr << "result after throwError: "
<< qPrintable(v.toString()) << std::endl; << qPrintable(v.toString()) << std::endl;
return Return_type(); return Return_type();