1 /*
2     This file is part of the Okteta Kasten Framework, made within the KDE community.
3 
4     SPDX-FileCopyrightText: 2010, 2011, 2012 Alex Richardson <alex.richardson@gmx.de>
5 
6     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8 
9 #include "scripthandler.hpp"
10 #include "scriptengineinitializer.hpp"
11 #include "scriptutils.hpp"
12 #include "scriptlogger.hpp"
13 #include "classes/defaultscriptclass.hpp"
14 #include "../datatypes/datainformation.hpp"
15 #include "../datatypes/topleveldatainformation.hpp"
16 #include "../datatypes/array/arraydatainformation.hpp"
17 #include "../parsers/parserutils.hpp"
18 
19 #include <QStringList>
20 #include <QScriptValue>
21 #include <QScriptValueIterator>
22 #include <QScriptEngine>
23 
ScriptHandler(QScriptEngine * engine,TopLevelDataInformation * topLevel)24 ScriptHandler::ScriptHandler(QScriptEngine* engine, TopLevelDataInformation* topLevel)
25     : mEngine(engine)
26     , mTopLevel(topLevel)
27     , mHandlerInfo(engine, topLevel->logger())
28 {
29 }
30 
31 ScriptHandler::~ScriptHandler() = default;
32 
engine() const33 QScriptEngine* ScriptHandler::engine() const
34 {
35     return mEngine.data();
36 }
37 
handlerInfo()38 ScriptHandlerInfo* ScriptHandler::handlerInfo()
39 {
40     return &mHandlerInfo;
41 }
42 
validateData(DataInformation * data)43 void ScriptHandler::validateData(DataInformation* data)
44 {
45     Q_CHECK_PTR(data);
46 
47     if (data->hasBeenValidated()) {
48         return;
49     }
50     // first validate the children
51     for (uint i = 0; i < data->childCount(); ++i) {
52         validateData(data->childAt(i));
53     }
54 
55     // check if has a validation function:
56     QScriptValue validationFunc = data->validationFunc();
57     if (validationFunc.isValid()) {
58         QScriptValue result = callFunction(validationFunc, data, ScriptHandlerInfo::Mode::Validating);
59         if (result.isError()) {
60             mTopLevel->logger()->error(data) << "Error occurred while validating element: "
61                                              << result.toString();
62             data->setValidationError(QStringLiteral("Error occurred in validation: ")
63                                      + result.toString());
64         } else if (mEngine->hasUncaughtException()) {
65             mTopLevel->logger()->error(data) << "Error occurred while validating element:"
66                                              << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
67             data->setValidationError(QStringLiteral("Error occurred in validation: ")
68                                      + result.toString());
69             mEngine->clearExceptions();
70         }
71         if (result.isBool() || result.isBoolean()) {
72             data->mValidationSuccessful = result.toBool();
73         }
74         if (result.isString()) {
75             // error string
76             QString str = result.toString();
77             if (!str.isEmpty()) {
78                 data->setValidationError(str);
79             }
80         }
81         data->mHasBeenValidated = true;
82     }
83 }
84 
updateDataInformation(DataInformation * data)85 void ScriptHandler::updateDataInformation(DataInformation* data)
86 {
87     Q_CHECK_PTR(data);
88     // check if has an update function:
89     Q_ASSERT(!data->hasBeenUpdated());
90     QScriptValue updateFunc = data->updateFunc();
91     data->mHasBeenUpdated = true;
92     if (updateFunc.isValid()) {
93         QString context = data->fullObjectPath(); // we mustn't use data after updateFunc.call(), save context
94         QScriptValue result = callFunction(updateFunc, data, ScriptHandlerInfo::Mode::Updating);
95         if (result.isError()) {
96             mTopLevel->logger()->error(context) << "Error occurred while updating element: "
97                                                 << result.toString();
98         }
99         if (mEngine->hasUncaughtException()) {
100             mTopLevel->logger()->error(context) << "Error occurred while updating element:"
101                                                 << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
102             mEngine->clearExceptions();
103         }
104     }
105 }
106 
updateLength(ArrayDataInformation * array)107 void ScriptHandler::updateLength(ArrayDataInformation* array)
108 {
109     QScriptValue lengthFunc = array->lengthFunction();
110     if (lengthFunc.isValid()) {
111         Q_ASSERT(lengthFunc.isFunction());
112 
113         QScriptValue result = callFunction(lengthFunc, array, ScriptHandlerInfo::Mode::DeterminingLength);
114         if (mEngine->hasUncaughtException()) {
115             mTopLevel->logger()->error(array) << "Error occurred while calculating length:"
116                                               << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
117             mEngine->clearExceptions();
118         }
119         ParsedNumber<uint> value = ParserUtils::uintFromScriptValue(result);
120         if (value.isValid) {
121             array->setArrayLength(value.value);
122         } else {
123             array->logError() << "Length function did not return a valid number! Result was: " << result.toString();
124         }
125     }
126 }
127 
customToString(const DataInformation * data,const QScriptValue & func)128 QString ScriptHandler::customToString(const DataInformation* data, const QScriptValue& func)
129 {
130     Q_ASSERT(func.isValid());
131     Q_ASSERT(func.isFunction());
132     Q_ASSERT(data->wasAbleToRead()); // this should never be called if EOF was reached
133     // it is effectively const, since nothing may be modified while mode is CustomToString
134     // const_cast is okay in this case
135     QScriptValue result = callFunction(func, const_cast<DataInformation*>(data), ScriptHandlerInfo::Mode::CustomToString);
136     if (result.isError()) {
137         data->logError() << "toStringFunc caused an error:" << result.toString();
138     }
139     return result.toString();
140 }
141 
callFunction(QScriptValue func,DataInformation * data,ScriptHandlerInfo::Mode mode)142 QScriptValue ScriptHandler::callFunction(QScriptValue func, DataInformation* data,
143                                          ScriptHandlerInfo::Mode mode)
144 {
145     Q_ASSERT(func.isFunction());
146     // value exists, we assume it has been checked to be a function
147     QScriptValue thisObject = data->toScriptValue(mEngine.data(), &mHandlerInfo);
148     QScriptValue mainStruct = data->mainStructure()->toScriptValue(mEngine.data(), &mHandlerInfo);
149     const QScriptValueList args { mainStruct };
150     // ensure we get the right properties
151     mHandlerInfo.setMode(mode);
152     QScriptValue result = func.call(thisObject, args);
153     mHandlerInfo.setMode(ScriptHandlerInfo::Mode::None);
154     return result;
155 }
156