File indexing completed on 2024-06-30 05:51:31

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 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 "structunionscriptclass.hpp"
0010 #include "../../datatypes/datainformationwithchildren.hpp"
0011 #include "../../datatypes/topleveldatainformation.hpp"
0012 #include "../../parsers/parserutils.hpp"
0013 #include "../scriptlogger.hpp"
0014 #include <structureslogging.hpp>
0015 
0016 StructUnionScriptClass::StructUnionScriptClass(QScriptEngine* engine, ScriptHandlerInfo* handlerInfo)
0017     : DefaultScriptClass(engine, handlerInfo)
0018 {
0019     s_childCount = engine->toStringHandle(ParserStrings::PROPERTY_CHILD_COUNT()); // read-only
0020     mIterableProperties.append(qMakePair(s_childCount, QScriptValue::ReadOnly | QScriptValue::Undeletable));
0021     s_children = engine->toStringHandle(ParserStrings::PROPERTY_CHILDREN()); // write-only
0022 
0023     mStructUnionPrototype = engine->newObject();
0024     mStructUnionPrototype.setProperty(QStringLiteral("toString"),
0025                                       engine->newFunction(StructUnion_proto_toString));
0026     mStructUnionPrototype.setProperty(QStringLiteral("setChildren"),
0027                                       engine->newFunction(StructUnion_proto_setChildren));
0028     mStructUnionPrototype.setProperty(QStringLiteral("child"),
0029                                       engine->newFunction(StructUnion_proto_child));
0030 }
0031 
0032 StructUnionScriptClass::~StructUnionScriptClass() = default;
0033 
0034 bool StructUnionScriptClass::queryAdditionalProperty(const DataInformation* data, const QScriptString& name, QScriptClass::QueryFlags* flags, uint* id)
0035 {
0036     Q_UNUSED(data)
0037     // no need to modify flags since both read and write are handled
0038     if (name == s_childCount) {
0039         *flags &= ~HandlesWriteAccess;
0040         return true;
0041     }
0042     if (name == s_children) {
0043         *flags &= ~HandlesReadAccess;
0044         return true;
0045     }
0046 
0047     bool isArrayIndex;
0048     quint32 pos = name.toArrayIndex(&isArrayIndex);
0049     uint count = data->childCount();
0050     bool isValidChild = false;
0051     if (isArrayIndex && pos < count) {
0052         isValidChild = true;
0053     } else {
0054         // compare name, names that match special properties/functions will be
0055         // hidden since these were checked before
0056         QString objName = name.toString();
0057         for (uint i = 0; i < count; ++i) {
0058             if (objName == data->childAt(i)->name()) {
0059                 isValidChild = true;
0060                 pos = i;
0061                 break;
0062             }
0063         }
0064     }
0065     if (isValidChild) {
0066         *id = pos + 1; // add 1 to distinguish from the default value of 0
0067         *flags &= ~HandlesWriteAccess; // writing is not yet supported
0068         return true;
0069     }
0070     return false; // not found
0071 }
0072 
0073 bool StructUnionScriptClass::additionalPropertyFlags(const DataInformation* data, const QScriptString& name, uint id, QScriptValue::PropertyFlags* flags)
0074 {
0075     Q_UNUSED(data)
0076     // no need to modify flags since both read and write are handled
0077     if (id != 0) {
0078         *flags |= QScriptValue::ReadOnly;
0079         return true;
0080     }
0081     // TODO is this necessary, will there be any way a child has no id set?
0082     // check named children
0083     QString objName = name.toString();
0084     uint count = data->childCount();
0085     for (uint i = 0; i < count; ++i) {
0086         DataInformation* child = data->childAt(i);
0087         Q_CHECK_PTR(child);
0088         if (objName == child->name()) {
0089             *flags |= QScriptValue::ReadOnly;
0090             return true;
0091         }
0092     }
0093 
0094     return false;
0095 }
0096 
0097 QScriptValue StructUnionScriptClass::additionalProperty(const DataInformation* data, const QScriptString& name, uint id)
0098 {
0099     const auto* dataW = static_cast<const DataInformationWithChildren*>(data);
0100     // do a dynamic cast in debug mode to ensure the static cast was valid
0101     Q_CHECK_PTR(dynamic_cast<const DataInformationWithChildren*>(dataW));
0102 
0103     if (id != 0) {
0104         quint32 pos = id - 1;
0105         if (pos >= data->childCount()) {
0106             dataW->logError() << "attempting to access out of bounds child: index was" << pos
0107                               << ", maximum is" << (data->childCount() - 1);
0108             return engine()->currentContext()->throwError(QScriptContext::RangeError,
0109                                                           QStringLiteral("Attempting to access struct index %1, but length is %2").arg(
0110                                                               QString::number(pos), QString::number(data->childCount())));
0111         }
0112         Q_CHECK_PTR(data->childAt(pos));
0113         return data->childAt(pos)->toScriptValue(engine(), mHandlerInfo);
0114     }
0115     if (name == s_childCount) {
0116         return dataW->childCount();
0117     }
0118     if (name == s_children) {
0119         dataW->logError() << "attempting to read read-only property" << s_children.toString();
0120         return engine()->undefinedValue();
0121     }
0122     // TODO is this necessary, will there be any way a child has no id set?
0123     // TODO testing seems to indicate this is not necessary, will leave it thought until I'm sure
0124     // check named children
0125     QString objName = name.toString();
0126     uint count = data->childCount();
0127     for (uint i = 0; i < count; ++i) {
0128         DataInformation* child = data->childAt(i);
0129         Q_CHECK_PTR(child);
0130         if (objName == child->name()) {
0131             return child->toScriptValue(engine(), mHandlerInfo);
0132         }
0133     }
0134     return {};
0135 }
0136 
0137 bool StructUnionScriptClass::setAdditionalProperty(DataInformation* data, const QScriptString& name, uint, const QScriptValue& value)
0138 {
0139     auto* dataW = static_cast<DataInformationWithChildren*>(data);
0140     // do a dynamic cast in debug mode to ensure the static cast was valid
0141     Q_CHECK_PTR(dynamic_cast<DataInformationWithChildren*>(dataW));
0142 
0143     if (name == s_children) {
0144         dataW->setChildren(value);
0145         return true;
0146     }
0147     // TODO set children!!
0148     return false;
0149 }
0150 
0151 QScriptValue StructUnionScriptClass::prototype() const
0152 {
0153     return mStructUnionPrototype;
0154 }
0155 
0156 QScriptValue StructUnionScriptClass::StructUnion_proto_toString(QScriptContext* ctx, QScriptEngine* eng)
0157 {
0158     DataInformation* data = toDataInformation(ctx->thisObject());
0159     if (!data) {
0160         qCWarning(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "could not cast data";
0161         return eng->undefinedValue();
0162     }
0163     return data->typeName(); // TODO better toString
0164 }
0165 
0166 QScriptValue StructUnionScriptClass::StructUnion_proto_child(QScriptContext* ctx, QScriptEngine* eng)
0167 {
0168     if (ctx->argumentCount() < 1) {
0169         ctx->throwError(QScriptContext::RangeError,
0170                         QStringLiteral("(struct/union).child(name) needs at least one argument"));
0171         return eng->undefinedValue();
0172     }
0173     QScriptValue arg = ctx->argument(0);
0174     if (!arg.isString()) {
0175         ctx->throwError(QScriptContext::TypeError,
0176                         QStringLiteral("(struct/union).child(name) argument has to be a string"));
0177         return QScriptValue::UndefinedValue;
0178     }
0179     DataInformation* data = toDataInformation(ctx->thisObject());
0180     if (!data) {
0181         qCWarning(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "could not cast data";
0182         return eng->undefinedValue();
0183     }
0184     uint count = data->childCount();
0185     QString name = arg.toString();
0186     for (uint i = 0; i < count; i++) {
0187         DataInformation* child = data->childAt(i);
0188         if (child->name() == name) {
0189             return child->toScriptValue(eng, data->topLevelDataInformation()->scriptHandler()->handlerInfo());
0190         }
0191     }
0192 
0193     return eng->nullValue();
0194 }
0195 
0196 QScriptValue StructUnionScriptClass::StructUnion_proto_setChildren(QScriptContext* ctx, QScriptEngine* eng)
0197 {
0198     if (ctx->argumentCount() < 1) {
0199         return ctx->throwError(QScriptContext::RangeError,
0200                                QStringLiteral("(struct/union).child(children) needs one argument"));
0201     }
0202     DataInformation* data = toDataInformation(ctx->thisObject());
0203     if (!data) {
0204         qCWarning(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "could not cast data";
0205         return eng->undefinedValue();
0206     }
0207     auto* dataW = static_cast<DataInformationWithChildren*>(data);
0208     // do a dynamic cast in debug mode to ensure the static cast was valid
0209     Q_CHECK_PTR(dynamic_cast<DataInformationWithChildren*>(dataW));
0210 
0211     dataW->setChildren(ctx->argument(0));
0212     return eng->undefinedValue();
0213 }