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

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 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 "scriptvalueconverter.hpp"
0010 #include "scriptvalueconverter_p.hpp"
0011 
0012 #include "datainformationfactory.hpp"
0013 #include "parserutils.hpp"
0014 #include "../datatypes/uniondatainformation.hpp"
0015 #include "../datatypes/structuredatainformation.hpp"
0016 #include "../datatypes/strings/stringdata.hpp"
0017 #include "../datatypes/strings/stringdatainformation.hpp"
0018 #include "../script/scriptlogger.hpp"
0019 
0020 using namespace ParserStrings;
0021 
0022 namespace ScriptValueConverter {
0023 
0024 DataInformation* toDataInformation(const QScriptValue& value, const ParserInfo& oldInfo)
0025 {
0026     if (!value.isValid()) {
0027         oldInfo.error() << "invalid value passed!";
0028         return nullptr;
0029     }
0030     ParserInfo info(oldInfo);
0031     QString nameOverride = value.property(PROPERTY_NAME()).toString();
0032     if (!nameOverride.isEmpty()) {
0033         info.name = nameOverride;
0034     }
0035 
0036     // check function array and date, since they are objects too
0037     if (value.isRegExp()) {
0038         // apparently regexp is a function
0039         info.error() << "Cannot convert a RegExp object to DataInformation!";
0040         return nullptr;
0041     }
0042     if (value.isFunction()) {
0043         info.error() << "Cannot convert a Function object to DataInformation!";
0044         return nullptr;
0045     }
0046     if (value.isArray()) {
0047         info.error() << "Cannot convert a Array object to DataInformation!";
0048         return nullptr;
0049     }
0050     if (value.isDate()) {
0051         info.error() << "Cannot convert a Date object to DataInformation!";
0052         return nullptr;
0053     }
0054     if (value.isError()) {
0055         info.error() << "Cannot convert a Error object to DataInformation!";
0056         return nullptr;
0057     }
0058     // variant and qobject are also object types, however they cannot appear from user code, no need to check
0059 
0060     // if it is a string, we convert to primitive type, if not it has to be an object
0061     if (value.isString()) {
0062         return toPrimitive(value, info); // a type string is also okay
0063 
0064     }
0065     if (!value.isObject()) {
0066         if (value.isBool()) {
0067             info.error() << "Cannot convert Boolean to DataInformation!";
0068         } else if (value.isNull()) {
0069             info.error() << "Cannot convert null to DataInformation!";
0070         } else if (value.isUndefined()) {
0071             info.error() << "Cannot convert undefined to DataInformation!";
0072         } else if (value.isNumber()) {
0073             info.error() << "Cannot convert Number to DataInformation!";
0074         } else {
0075             info.error() << "Cannot convert object of unknown type to DataInformation!";
0076         }
0077 
0078         return nullptr; // no point trying to convert
0079     }
0080 
0081     QString type = value.property(PROPERTY_INTERNAL_TYPE()).toString();
0082     if (type.isEmpty()) {
0083         info.error() << "Cannot convert object since type of object could not be determined!";
0084         return nullptr;
0085     }
0086     DataInformation* returnVal = nullptr;
0087 
0088     if (type == TYPE_ARRAY()) {
0089         returnVal = toArray(value, info);
0090     } else if (type == TYPE_STRUCT()) {
0091         returnVal = toStruct(value, info);
0092     } else if (type == TYPE_UNION()) {
0093         returnVal = toUnion(value, info);
0094     } else if (type == TYPE_BITFIELD()) {
0095         returnVal = toBitfield(value, info);
0096     } else if (type == TYPE_ENUM()) {
0097         returnVal = toEnum(value, false, info);
0098     } else if (type == TYPE_FLAGS()) {
0099         returnVal = toEnum(value, true, info);
0100     } else if (type == TYPE_STRING()) {
0101         returnVal = toString(value, info);
0102     } else if (type == TYPE_POINTER()) {
0103         returnVal = toPointer(value, info);
0104     } else if (type == TYPE_TAGGED_UNION()) {
0105         returnVal = toTaggedUnion(value, info);
0106     } else if (type == TYPE_PRIMITIVE()) {
0107         returnVal = toPrimitive(value, info);
0108     } else {
0109         info.error() << "Unknown type:" << type;
0110     }
0111 
0112     if (returnVal) {
0113         CommonParsedData cpd(info);
0114         QString byteOrderStr = value.property(PROPERTY_BYTEORDER()).toString();
0115         if (!byteOrderStr.isEmpty()) {
0116             cpd.endianess = ParserUtils::byteOrderFromString(byteOrderStr,
0117                                                              LoggerWithContext(info.logger, info.context()));
0118         }
0119         cpd.updateFunc = value.property(PROPERTY_UPDATE_FUNC());
0120         cpd.validationFunc = value.property(PROPERTY_VALIDATION_FUNC());
0121         cpd.toStringFunc = value.property(PROPERTY_TO_STRING_FUNC());
0122         cpd.customTypeName = value.property(PROPERTY_CUSTOM_TYPE_NAME()).toString();
0123         if (!DataInformationFactory::commonInitialization(returnVal, cpd)) {
0124             delete returnVal; // error message has already been logged
0125             return nullptr;
0126         }
0127     }
0128     return returnVal;
0129 }
0130 
0131 ArrayDataInformation* toArray(const QScriptValue& value, const ParserInfo& info)
0132 {
0133     ArrayParsedData apd(info);
0134     apd.length = value.property(PROPERTY_LENGTH());
0135     QScriptValue childType = value.property(PROPERTY_TYPE());
0136     ParserInfo childInfo(info);
0137     DummyDataInformation dummy(info.parent, info.name + QLatin1Char('.') + NAME_ARRAY_TYPE());
0138     childInfo.parent = &dummy;
0139     apd.arrayType = toDataInformation(childType, childInfo);
0140 
0141     return DataInformationFactory::newArray(apd);
0142 }
0143 
0144 AbstractBitfieldDataInformation* toBitfield(const QScriptValue& value, const ParserInfo& info)
0145 {
0146     BitfieldParsedData bpd(info);
0147     bpd.type = value.property(PROPERTY_TYPE()).toString();
0148     bpd.width = ParserUtils::intFromScriptValue(value.property(PROPERTY_WIDTH()));
0149     return DataInformationFactory::newBitfield(bpd);
0150 }
0151 
0152 PrimitiveDataInformation* toPrimitive(const QScriptValue& value, const ParserInfo& info)
0153 {
0154     PrimitiveParsedData ppd(info);
0155     ppd.type = value.isString() ? value.toString() : value.property(PROPERTY_TYPE()).toString();
0156     return DataInformationFactory::newPrimitive(ppd);
0157 }
0158 
0159 StructureDataInformation* toStruct(const QScriptValue& value, const ParserInfo& info)
0160 {
0161     StructOrUnionParsedData supd(info);
0162     supd.children.reset(new ScriptValueChildrenParser(info, value.property(PROPERTY_CHILDREN())));
0163     return DataInformationFactory::newStruct(supd);
0164 }
0165 
0166 UnionDataInformation* toUnion(const QScriptValue& value, const ParserInfo& info)
0167 {
0168     StructOrUnionParsedData supd(info);
0169     supd.children.reset(new ScriptValueChildrenParser(info, value.property(PROPERTY_CHILDREN())));
0170     return DataInformationFactory::newUnion(supd);
0171 }
0172 
0173 PointerDataInformation* toPointer(const QScriptValue& value, const ParserInfo& info)
0174 {
0175     PointerParsedData ppd(info);
0176     QScriptValue pointerScale = value.property(PROPERTY_SCALE());
0177     if (pointerScale.isNumber()) {
0178         // A pointer scale of magnitude > 2^53 is extremely unlikely.
0179         ppd.pointerScale = pointerScale.toInteger();
0180     }
0181     ppd.interpretFunc = value.property(PROPERTY_INTERPRET_FUNC());
0182 
0183     ParserInfo childInfo(info);
0184     DummyDataInformation dummy(info.parent, info.name);
0185     childInfo.parent = &dummy;
0186     childInfo.name = NAME_POINTER_TARGET();
0187     ppd.pointerTarget = toDataInformation(value.property(PROPERTY_TARGET()), childInfo);
0188     childInfo.name = NAME_POINTER_VALUE_TYPE();
0189     ppd.valueType = toDataInformation(value.property(PROPERTY_TYPE()), childInfo);
0190 
0191     return DataInformationFactory::newPointer(ppd);
0192 }
0193 
0194 EnumDataInformation* toEnum(const QScriptValue& value, bool flags, const ParserInfo& info)
0195 {
0196     EnumParsedData epd(info);
0197     QScriptValue enumType = value.property(PROPERTY_TYPE());
0198     if (enumType.isString()) {
0199         epd.type = enumType.toString();
0200     } else if (enumType.isObject()) {
0201         epd.type = enumType.property(PROPERTY_TYPE()).toString();
0202     }
0203     // else it stays empty
0204     epd.enumName = value.property(PROPERTY_ENUM_NAME()).toString();
0205     epd.enumValuesObject = value.property(PROPERTY_ENUM_VALUES());
0206 
0207     if (flags) {
0208         return DataInformationFactory::newFlags(epd);
0209     }
0210 
0211     return DataInformationFactory::newEnum(epd);
0212 }
0213 
0214 StringDataInformation* toString(const QScriptValue& value, const ParserInfo& info)
0215 {
0216     StringParsedData spd(info);
0217     spd.encoding = value.property(PROPERTY_ENCODING()).toString();
0218     spd.termination = ParserUtils::uintFromScriptValue(value.property(PROPERTY_TERMINATED_BY()));
0219     spd.maxByteCount = ParserUtils::uintFromScriptValue(value.property(PROPERTY_MAX_BYTE_COUNT()));
0220     spd.maxCharCount = ParserUtils::uintFromScriptValue(value.property(PROPERTY_MAX_CHAR_COUNT()));
0221     return DataInformationFactory::newString(spd);
0222 }
0223 
0224 TaggedUnionDataInformation* toTaggedUnion(const QScriptValue& value, const ParserInfo& info)
0225 {
0226     TaggedUnionParsedData tpd(info);
0227     QScriptValue alternatives = value.property(PROPERTY_ALTERNATIVES());
0228     if (!alternatives.isArray()) {
0229         info.error() << "Alternatives must be an array!";
0230         return nullptr;
0231     }
0232     int length = alternatives.property(PROPERTY_LENGTH()).toInt32();
0233     tpd.alternatives.reserve(length);
0234     for (int i = 0; i < length; ++i) {
0235         TaggedUnionParsedData::Alternatives alt;
0236         QScriptValue current = alternatives.property(i);
0237         alt.name = current.property(PROPERTY_STRUCT_NAME()).toString();
0238         alt.selectIf = current.property(PROPERTY_SELECT_IF());
0239         alt.fields = QSharedPointer<ChildrenParser>(
0240             new ScriptValueChildrenParser(info, current.property(PROPERTY_CHILDREN())));
0241         tpd.alternatives.append(alt);
0242     }
0243 
0244     tpd.children.reset(new ScriptValueChildrenParser(info, value.property(PROPERTY_CHILDREN())));
0245     tpd.defaultFields.reset(new ScriptValueChildrenParser(info, value.property(PROPERTY_DEFAULT_CHILDREN())));
0246     return DataInformationFactory::newTaggedUnion(tpd);
0247 }
0248 
0249 } // namespace ScriptValueConverter
0250 
0251 ScriptValueConverter::ScriptValueChildrenParser::ScriptValueChildrenParser(const ParserInfo& info,
0252                                                                            const QScriptValue& children)
0253     : mValue(children)
0254     , mIter(children)
0255     , mInfo(info)
0256 {
0257 }
0258 
0259 ScriptValueConverter::ScriptValueChildrenParser::~ScriptValueChildrenParser() = default;
0260 
0261 DataInformation* ScriptValueConverter::ScriptValueChildrenParser::next()
0262 {
0263     Q_ASSERT(hasNext());
0264     mIter.next();
0265     if (mValue.isArray() && mIter.name() == QLatin1String("length")) {
0266         mIter.next(); // skip length property
0267     }
0268     mInfo.name = mIter.name();
0269     return toDataInformation(mIter.value(), mInfo);
0270 }
0271 
0272 bool ScriptValueConverter::ScriptValueChildrenParser::hasNext()
0273 {
0274     if (!mIter.hasNext()) {
0275         return false;
0276     }
0277     if (mValue.isArray()) {
0278         // check if next element is length property
0279         mIter.next();
0280         if (mIter.name() != QLatin1String("length")) {
0281             mIter.previous(); // go back and return true
0282             return true;
0283         }
0284         return mIter.hasNext(); // skipped length
0285     }
0286 
0287     return true;
0288 }
0289 
0290 void ScriptValueConverter::ScriptValueChildrenParser::setParent(DataInformation* newParent)
0291 {
0292     mInfo.parent = newParent;
0293 }