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) {
QScriptValue value = script_engine->evaluate(script, filename);
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) {
QTextStream err(stderr);
err << "Qt Script exception:\n"
<< script_engine->uncaughtException().toString()
<< js_exception.toString()
<< "\nBacktrace:\n";
Q_FOREACH(QString line, script_engine->uncaughtExceptionBacktrace()) {
Q_FOREACH(QString line, bt) {
err << " " << line << "\n";
}
}
throw CGAL::Three::Script_exception
(script_engine->uncaughtException().toString().toStdString());
(script_engine->uncaughtException().toString(), bt);
}
else if(!quiet && !value.isNull() && !value.isUndefined()) {
QTextStream(stderr) << "Qt Script evaluated to \""
@ -468,6 +476,10 @@ void MainWindow::enableScriptDebugger(bool b /* = true */)
if(b) {
debugger->action(QScriptEngineDebugger::InterruptAction)->trigger();
}
else {
std::cerr << "Detach the script debugger\n";
debugger->detach();
}
}
return;
# endif
@ -1389,6 +1401,13 @@ bool MainWindow::loadScript(QFileInfo info)
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()
{
#if defined(QT_SCRIPT_LIB)

View File

@ -210,6 +210,8 @@ public Q_SLOTS:
*/
void enableScriptDebugger(bool = true);
/// This slot is used to test exception handling in Qt Scripts.
void throw_exception();
protected Q_SLOTS:
//!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/exceptions.h>
using namespace CGAL::Three;
class Polyhedron_demo_trivial_plugin :
public QObject,
public Polyhedron_demo_plugin_interface,
protected QScriptable
public Polyhedron_demo_plugin_interface
{
Q_OBJECT
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
@ -163,13 +161,6 @@ public:
return false;}
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 enableAction();

View File

@ -47,11 +47,12 @@ Polyhedron_demo::Polyhedron_demo(int& argc, char **argv,
QCommandLineOption no_try_catch("no-try-catch",
tr("Do not catch uncaught exceptions."));
parser.addOption(no_try_catch);
#ifdef QT_SCRIPTTOOLS_LIB
QCommandLineOption debug_scripts("debug-scripts",
tr("Use the scripts debugger."));
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",
tr("Ignore the autostart.js file, if any."));
parser.addOption(no_autostart);
@ -74,7 +75,16 @@ Polyhedron_demo::Polyhedron_demo(int& argc, char **argv,
if(parser.isSet(debug_scripts)) {
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
mainWindow.loadScript(":/cgal/Polyhedron_3/javascript/lib.js");
QFileInfo autostart_js("autostart.js");
if(!parser.isSet(no_autostart) && autostart_js.exists()) {

View File

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