File indexing completed on 2025-01-05 05:23:46

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2010, 2011, 2012 Alex Richardson <alex.richardson@gmx.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "scripthandler.hpp"
0010 
0011 #include "scriptengineinitializer.hpp"
0012 #include "scriptlogger.hpp"
0013 #include "classes/defaultscriptclass.hpp"
0014 #include "../datatypes/datainformation.hpp"
0015 #include "../datatypes/topleveldatainformation.hpp"
0016 #include "../datatypes/array/arraydatainformation.hpp"
0017 #include "../parsers/parserutils.hpp"
0018 // Qt
0019 #include <QStringList>
0020 #include <QScriptValue>
0021 #include <QScriptEngine>
0022 
0023 ScriptHandler::ScriptHandler(QScriptEngine* engine, TopLevelDataInformation* topLevel)
0024     : mEngine(engine)
0025     , mTopLevel(topLevel)
0026     , mHandlerInfo(engine, topLevel->logger())
0027 {
0028 }
0029 
0030 ScriptHandler::~ScriptHandler() = default;
0031 
0032 QScriptEngine* ScriptHandler::engine() const
0033 {
0034     return mEngine.get();
0035 }
0036 
0037 ScriptHandlerInfo* ScriptHandler::handlerInfo()
0038 {
0039     return &mHandlerInfo;
0040 }
0041 
0042 void ScriptHandler::validateData(DataInformation* data)
0043 {
0044     Q_CHECK_PTR(data);
0045 
0046     if (data->hasBeenValidated()) {
0047         return;
0048     }
0049     // first validate the children
0050     for (uint i = 0; i < data->childCount(); ++i) {
0051         validateData(data->childAt(i));
0052     }
0053 
0054     // check if has a validation function:
0055     QScriptValue validationFunc = data->validationFunc();
0056     if (validationFunc.isValid()) {
0057         QScriptValue result = callFunction(validationFunc, data, ScriptHandlerInfo::Mode::Validating);
0058         if (result.isError()) {
0059             mTopLevel->logger()->error(data) << "Error occurred while validating element: "
0060                                              << result.toString();
0061             data->setValidationError(QStringLiteral("Error occurred in validation: ")
0062                                      + result.toString());
0063         } else if (mEngine->hasUncaughtException()) {
0064             mTopLevel->logger()->error(data) << "Error occurred while validating element:"
0065                                              << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
0066             data->setValidationError(QStringLiteral("Error occurred in validation: ")
0067                                      + result.toString());
0068             mEngine->clearExceptions();
0069         }
0070         if (result.isBool() || result.isBoolean()) {
0071             data->mValidationSuccessful = result.toBool();
0072         }
0073         if (result.isString()) {
0074             // error string
0075             QString str = result.toString();
0076             if (!str.isEmpty()) {
0077                 data->setValidationError(str);
0078             }
0079         }
0080         data->mHasBeenValidated = true;
0081     }
0082 }
0083 
0084 void ScriptHandler::updateDataInformation(DataInformation* data)
0085 {
0086     Q_CHECK_PTR(data);
0087     // check if has an update function:
0088     Q_ASSERT(!data->hasBeenUpdated());
0089     QScriptValue updateFunc = data->updateFunc();
0090     data->mHasBeenUpdated = true;
0091     if (updateFunc.isValid()) {
0092         QString context = data->fullObjectPath(); // we mustn't use data after updateFunc.call(), save context
0093         QScriptValue result = callFunction(updateFunc, data, ScriptHandlerInfo::Mode::Updating);
0094         if (result.isError()) {
0095             mTopLevel->logger()->error(context) << "Error occurred while updating element: "
0096                                                 << result.toString();
0097         }
0098         if (mEngine->hasUncaughtException()) {
0099             mTopLevel->logger()->error(context) << "Error occurred while updating element:"
0100                                                 << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
0101             mEngine->clearExceptions();
0102         }
0103     }
0104 }
0105 
0106 void ScriptHandler::updateLength(ArrayDataInformation* array)
0107 {
0108     QScriptValue lengthFunc = array->lengthFunction();
0109     if (lengthFunc.isValid()) {
0110         Q_ASSERT(lengthFunc.isFunction());
0111 
0112         QScriptValue result = callFunction(lengthFunc, array, ScriptHandlerInfo::Mode::DeterminingLength);
0113         if (mEngine->hasUncaughtException()) {
0114             mTopLevel->logger()->error(array) << "Error occurred while calculating length:"
0115                                               << result.toString() << "\nBacktrace:" << mEngine->uncaughtExceptionBacktrace();
0116             mEngine->clearExceptions();
0117         }
0118         ParsedNumber<uint> value = ParserUtils::uintFromScriptValue(result);
0119         if (value.isValid) {
0120             array->setArrayLength(value.value);
0121         } else {
0122             array->logError() << "Length function did not return a valid number! Result was: " << result.toString();
0123         }
0124     }
0125 }
0126 
0127 QString ScriptHandler::customToString(const DataInformation* data, const QScriptValue& func)
0128 {
0129     Q_ASSERT(func.isValid());
0130     Q_ASSERT(func.isFunction());
0131     Q_ASSERT(data->wasAbleToRead()); // this should never be called if EOF was reached
0132     // it is effectively const, since nothing may be modified while mode is CustomToString
0133     // const_cast is okay in this case
0134     QScriptValue result = callFunction(func, const_cast<DataInformation*>(data), ScriptHandlerInfo::Mode::CustomToString);
0135     if (result.isError()) {
0136         data->logError() << "toStringFunc caused an error:" << result.toString();
0137     }
0138     return result.toString();
0139 }
0140 
0141 QScriptValue ScriptHandler::callFunction(QScriptValue func, DataInformation* data,
0142                                          ScriptHandlerInfo::Mode mode)
0143 {
0144     Q_ASSERT(func.isFunction());
0145     // value exists, we assume it has been checked to be a function
0146     QScriptValue thisObject = data->toScriptValue(mEngine.get(), &mHandlerInfo);
0147     QScriptValue mainStruct = data->mainStructure()->toScriptValue(mEngine.get(), &mHandlerInfo);
0148     const QScriptValueList args { mainStruct };
0149     // ensure we get the right properties
0150     mHandlerInfo.setMode(mode);
0151     QScriptValue result = func.call(thisObject, args);
0152     mHandlerInfo.setMode(ScriptHandlerInfo::Mode::None);
0153     return result;
0154 }