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

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 "datainformationfactory.hpp"
0010 
0011 #include "../datatypes/primitive/bitfield/boolbitfielddatainformation.hpp"
0012 #include "../datatypes/primitive/bitfield/unsignedbitfielddatainformation.hpp"
0013 #include "../datatypes/primitive/bitfield/signedbitfielddatainformation.hpp"
0014 #include "../datatypes/primitivefactory.hpp"
0015 
0016 #include "../script/scriptlogger.hpp"
0017 #include <QScriptEngine>
0018 // Std
0019 #include <utility>
0020 
0021 AbstractBitfieldDataInformation* DataInformationFactory::newBitfield(const BitfieldParsedData& pd)
0022 {
0023     if (!pd.width.isValid) {
0024         if (pd.width.string.isEmpty()) {
0025             pd.error() << "Bitfield is missing width.";
0026         } else {
0027             pd.error() << "Width of bitfield is not a valid number: " << pd.width.string;
0028         }
0029         return nullptr;
0030     }
0031     if (pd.width.value <= 0 || pd.width.value > 64) {
0032         pd.error() << "Width of bitfield is not a value from 1-64:" << pd.width.value;
0033         return nullptr;
0034     }
0035     AbstractBitfieldDataInformation* bitf = nullptr;
0036     const QString type = pd.type.toLower();
0037     if (type.isEmpty()) {
0038         pd.info() << "No bitfield type specified, defaulting to unsigned.";
0039         bitf = new UnsignedBitfieldDataInformation(pd.name, pd.width.value, pd.parent);
0040     } else if (type == QLatin1String("bool")) {
0041         bitf = new BoolBitfieldDataInformation(pd.name, pd.width.value, pd.parent);
0042     } else if (type == QLatin1String("unsigned")) {
0043         bitf = new UnsignedBitfieldDataInformation(pd.name, pd.width.value, pd.parent);
0044     } else if (type == QLatin1String("signed")) {
0045         bitf = new SignedBitfieldDataInformation(pd.name, pd.width.value, pd.parent);
0046     } else {
0047         pd.error() << "invalid bitfield type attribute given:" << type;
0048         return nullptr;
0049     }
0050     return bitf;
0051 }
0052 
0053 PrimitiveDataInformation* DataInformationFactory::newPrimitive(const PrimitiveParsedData& pd)
0054 {
0055     if (pd.type.isEmpty()) {
0056         pd.error() << "Type of primitive not specified, cannot create it!";
0057         return nullptr;
0058     }
0059     LoggerWithContext lwc(pd.logger, pd.context());
0060     PrimitiveDataType primitiveType = PrimitiveFactory::typeStringToType(pd.type, lwc);
0061     if (primitiveType == PrimitiveDataType::Invalid || primitiveType == PrimitiveDataType::Bitfield) {
0062         pd.error() << "Unrecognized primitive type: " << pd.type;
0063         return nullptr;
0064     }
0065     return PrimitiveFactory::newInstance(pd.name, primitiveType, lwc, pd.parent);
0066 }
0067 
0068 namespace {
0069 template <class T>
0070 T* newEnumOrFlags(const EnumParsedData& pd)
0071 {
0072     LoggerWithContext lwc(pd.logger, pd.context() + QLatin1String(" (type)"));
0073     const PrimitiveDataType primitiveType = PrimitiveFactory::typeStringToType(pd.type, lwc);
0074     if (primitiveType == PrimitiveDataType::Invalid || primitiveType == PrimitiveDataType::Bitfield) {
0075         pd.error() << "Unrecognized enum type: " << pd.type;
0076         return nullptr;
0077     }
0078     if (primitiveType == PrimitiveDataType::Float || primitiveType == PrimitiveDataType::Double) {
0079         pd.error() << "Floating-point enums are not allowed since they make little sense.";
0080         return nullptr;
0081     }
0082     EnumDefinition::Ptr definition = pd.enumDef;
0083     if (!definition) {
0084         QMap<AllPrimitiveTypes, QString> enumValues =
0085             EnumDefinition::parseEnumValues(pd.enumValuesObject, lwc, primitiveType);
0086         if (enumValues.isEmpty()) {
0087             pd.error() << "No enum values specified!";
0088             return nullptr;
0089         }
0090         definition = EnumDefinition::Ptr(new EnumDefinition(enumValues, pd.enumName, primitiveType));
0091     }
0092     if (definition->type() != primitiveType) {
0093         pd.error().nospace() << "Enum type (" << definition->type() << ") and value type (" << primitiveType
0094                              << ") do not match!";
0095         return nullptr;
0096     }
0097     PrimitiveDataInformation* primData = PrimitiveFactory::newInstance(pd.name, primitiveType, lwc);
0098     // TODO allow bitfields?
0099     if (!primData) {
0100         pd.error() << "Could not create a value object for this enum!";
0101         return nullptr;
0102     }
0103     return new T(pd.name, primData, definition, pd.parent);
0104 }
0105 
0106 template <class T>
0107 T* newStructOrUnion(const StructOrUnionParsedData& supd)
0108 {
0109     auto* structOrUnion = new T(supd.name, QVector<DataInformation*>(), supd.parent);
0110     supd.children->setParent(structOrUnion);
0111     while (supd.children->hasNext()) {
0112         DataInformation* data = supd.children->next();
0113         if (data) {
0114             structOrUnion->appendChild(data, false);
0115         } else {
0116             return nullptr; // error message should be logged already
0117         }
0118     }
0119     if (structOrUnion->childCount() == 0) {
0120         supd.info() << "No children were found, this is probably a mistake.";
0121     }
0122     return structOrUnion;
0123 }
0124 
0125 QString generateLengthFunction(DataInformation* current, DataInformation* last, const QString& elemName,
0126                                const QString& currentString, const ParserInfo& info)
0127 {
0128     if (!current) { // reached top
0129         return {};
0130     }
0131     for (int i = current->childCount() - 1; i >= 0; --i) {
0132         DataInformation* child = current->childAt(i);
0133         if (child == last) {
0134             return {}; // don't go down again after going up one level
0135 
0136         }
0137         QString childName = child->name();
0138         if (childName == elemName) {
0139             QString function = QLatin1String("function() { return this.parent.") + currentString
0140                                + elemName + QLatin1String(".value; }");
0141             info.info() << "Found element for dynamic array length: " << child->fullObjectPath()
0142                         << ", resulting function is: " << function;
0143             return function;
0144         }
0145         if (child->childCount() > 0) {
0146             QString func = generateLengthFunction(child, current, elemName,
0147                                                   currentString + childName + QLatin1Char('.'), info);
0148             // if func is empty no valid child was found, just continue
0149             if (!func.isEmpty()) {
0150                 return func;
0151             }
0152         }
0153         // has no children and was not the desired element -> continue loop
0154     }
0155 
0156     // now check parents
0157     DataInformationBase* nextBase = current->parent();
0158     if (!nextBase) {
0159         return {};
0160     }
0161 
0162     DataInformation* next = nextBase->asDataInformation();
0163     if (next == last) {
0164         return {}; // we moved one level down previously, don't move up again
0165     }
0166 
0167     return generateLengthFunction(current->parent()->asDataInformation(), current, elemName,
0168                                   currentString + QLatin1String("parent."), info);
0169 }
0170 
0171 }
0172 
0173 EnumDataInformation* DataInformationFactory::newEnum(const EnumParsedData& pd)
0174 {
0175     return newEnumOrFlags<EnumDataInformation>(pd);
0176 }
0177 
0178 FlagDataInformation* DataInformationFactory::newFlags(const EnumParsedData& pd)
0179 {
0180     return newEnumOrFlags<FlagDataInformation>(pd);
0181 }
0182 
0183 ArrayDataInformation* DataInformationFactory::newArray(const ArrayParsedData& pd)
0184 {
0185     if (!pd.arrayType) {
0186         pd.error() << "Failed to parse array type!";
0187         return nullptr;
0188     }
0189     if (!pd.length.isValid()) {
0190         pd.error() << "No array length specified!";
0191         return nullptr;
0192     }
0193     const ParsedNumber<uint> fixedLength = ParserUtils::uintFromScriptValue(pd.length);
0194     if (fixedLength.isValid) {
0195         return new ArrayDataInformation(pd.name, fixedLength.value, pd.arrayType, pd.parent, QScriptValue());
0196     }
0197     if (pd.length.isFunction()) {
0198         return new ArrayDataInformation(pd.name, 0, pd.arrayType, pd.parent, pd.length);
0199     }
0200 
0201     // neither integer nor function, must be a string containing the name of another element.
0202     const QString lengthStr = pd.length.toString();
0203     if (!pd.parent) {
0204         pd.error() << "Toplevel array has length depending on other field (" << lengthStr
0205                     << "). This is not possible.";
0206         return nullptr;
0207     }
0208     if (lengthStr.contains(QLatin1Char('.'))) {
0209         pd.error() << "Referenced array length element (" << lengthStr << ") contains '.', this is not allowed!";
0210         return nullptr; // TODO maybe add possible shorthand length="this.parent.length"
0211     }
0212     QString lengthFunctionString = generateLengthFunction(pd.parent, nullptr, lengthStr, QString(), pd);
0213     if (lengthFunctionString.isEmpty()) {
0214         pd.error() << "Could not find element " << lengthStr << " referenced as array length!";
0215         return nullptr;
0216     }
0217     QScriptValue lengthFunction = ParserUtils::functionSafeEval(pd.engine, lengthFunctionString);
0218     return new ArrayDataInformation(pd.name, 0, pd.arrayType, pd.parent, lengthFunction);
0219 }
0220 
0221 StringDataInformation* DataInformationFactory::newString(const StringParsedData& pd)
0222 {
0223     if (pd.maxByteCount.isValid && pd.maxCharCount.isValid) {
0224         pd.error() << "Both maxCharCount and maxByteCount are set, only one is allowed.";
0225         return nullptr;
0226     }
0227     StringDataInformation::StringType encoding = ParserUtils::toStringEncoding(pd.encoding,
0228                                                                                LoggerWithContext(pd.logger, pd.context()));
0229     if (encoding == StringDataInformation::StringType::InvalidEncoding) {
0230         pd.error() << "Bad string encoding given:" << pd.encoding;
0231         return nullptr;
0232     }
0233     auto* data = new StringDataInformation(pd.name, encoding, pd.parent);
0234     bool modeSet = false;
0235     if (pd.termination.isValid) {
0236         data->setTerminationCodePoint(pd.termination.value);
0237         modeSet = true;
0238     }
0239     if (pd.maxByteCount.isValid) {
0240         data->setMaxByteCount(pd.maxByteCount.value);
0241         modeSet = true;
0242     }
0243     if (pd.maxCharCount.isValid) {
0244         data->setMaxCharCount(pd.maxCharCount.value);
0245         modeSet = true;
0246     }
0247     // if mode is None, we assume zero terminated strings
0248     if (!modeSet) {
0249         pd.info() << "No string termination mode set, assuming null terminated strings.";
0250         data->setTerminationCodePoint(0);
0251         Q_ASSERT(data->terminationMode() == StringData::Sequence);
0252     }
0253     return data;
0254 }
0255 
0256 UnionDataInformation* DataInformationFactory::newUnion(const StructOrUnionParsedData& pd)
0257 {
0258     return newStructOrUnion<UnionDataInformation>(pd);
0259 }
0260 
0261 StructureDataInformation* DataInformationFactory::newStruct(const StructOrUnionParsedData& pd)
0262 {
0263     return newStructOrUnion<StructureDataInformation>(pd);
0264 }
0265 
0266 bool DataInformationFactory::commonInitialization(DataInformation* data, const CommonParsedData& pd)
0267 {
0268     data->setByteOrder(pd.endianess);
0269 
0270     if (data->name().isEmpty()) {
0271         pd.warn() << "Name is empty!";
0272     }
0273     if (pd.updateFunc.isValid()) {
0274         if (!pd.updateFunc.isFunction()) {
0275             pd.error() << "Update function is not a function: " << pd.updateFunc.toString();
0276             return false;
0277         }
0278 
0279         data->setUpdateFunc(pd.updateFunc);
0280     }
0281     if (pd.validationFunc.isValid()) {
0282         if (!pd.validationFunc.isFunction()) {
0283             pd.error() << "Validation function is not a function: " << pd.validationFunc.toString();
0284             return false;
0285         }
0286 
0287         data->setValidationFunc(pd.validationFunc);
0288     }
0289     if (pd.toStringFunc.isValid()) {
0290         if (!pd.toStringFunc.isFunction()) {
0291             pd.error() << "To string function is not a function: " << pd.toStringFunc.toString();
0292             return false;
0293         }
0294 
0295         data->setToStringFunction(pd.toStringFunc);
0296     }
0297     if (!pd.customTypeName.isEmpty()) {
0298         data->setCustomTypeName(pd.customTypeName);
0299     }
0300     return true;
0301 
0302 }
0303 
0304 PointerDataInformation* DataInformationFactory::newPointer(const PointerParsedData& pd)
0305 {
0306     if (!pd.pointerTarget) {
0307         pd.error() << "Missing pointer target";
0308         return nullptr;
0309     }
0310     if (!pd.valueType) {
0311         pd.error() << "Missing pointer type";
0312         return nullptr;
0313     }
0314     if (!pd.valueType->isPrimitive()) {
0315         pd.error() << "Bad pointer type, only unsigned integers are allowed";
0316         return nullptr;
0317     }
0318     if (pd.interpretFunc.isValid() && !pd.interpretFunc.isFunction()) {
0319         pd.error() << "Bad pointer interpretation, only functions are allowed";
0320         return nullptr;
0321     }
0322     PrimitiveDataInformation* primValue = pd.valueType->asPrimitive();
0323     if (!(primValue->type() == PrimitiveDataType::UInt8 || primValue->type() == PrimitiveDataType::UInt16
0324           || primValue->type() == PrimitiveDataType::UInt32 || primValue->type() == PrimitiveDataType::UInt64)) {
0325         pd.error() << "Bad pointer type, only unsigned integers are allowed"; // TODO offsets (signed int + bitfields)
0326         return nullptr;
0327     }
0328     return new PointerDataInformation(pd.name, pd.pointerTarget, primValue, pd.parent,
0329                                       pd.pointerScale, pd.interpretFunc);
0330 }
0331 
0332 TaggedUnionDataInformation* DataInformationFactory::newTaggedUnion(const TaggedUnionParsedData& pd)
0333 {
0334     std::unique_ptr<TaggedUnionDataInformation> tagged(new TaggedUnionDataInformation(pd.name, pd.parent));
0335     pd.children->setParent(tagged.get());
0336     while (pd.children->hasNext()) {
0337         DataInformation* data = pd.children->next();
0338         if (data) {
0339             tagged->appendChild(data, false);
0340         } else {
0341             return nullptr; // error message should be logged already
0342         }
0343     }
0344     if (tagged->childCount() == 0) {
0345         pd.error() << "No children in tagged union. At least one is required so that tag can be evaluated.";
0346         return nullptr;
0347     }
0348     // verify alternatives
0349     bool alternativesValid = true;
0350     QVector<TaggedUnionDataInformation::FieldInfo> altInfo;
0351     altInfo.reserve(pd.alternatives.size());
0352     for (int i = 0; i < pd.alternatives.size(); ++i) {
0353         const TaggedUnionParsedData::Alternatives& fi = pd.alternatives.at(i);
0354         if (!fi.selectIf.isFunction()) {
0355             ParsedNumber<quint64> number = ParserUtils::uint64FromScriptValue(fi.selectIf);
0356             if (!number.isValid) {
0357                 pd.error() << "Alternative number" << i << "is not valid. SelectIf is neither function nor number!";
0358                 alternativesValid = false;
0359             }
0360             // number is valid -> there must be exactly one field
0361             if (tagged->childCount() != 1) {
0362                 pd.error() << "Alternative number" << i << "is not valid. SelectIf is number,"
0363                     " but there is not exactly one child!";
0364                 alternativesValid = false;
0365             }
0366         }
0367         QVector<DataInformation*> children;
0368         while (fi.fields->hasNext()) {
0369             DataInformation* next = fi.fields->next();
0370             if (next) {
0371                 children.append(next);
0372             } else {
0373                 pd.error() << "Alternative number" << i << "has an invalid field!";
0374                 alternativesValid = false;
0375             }
0376         }
0377         altInfo.append(TaggedUnionDataInformation::FieldInfo(fi.name, fi.selectIf, children));
0378 
0379     }
0380 
0381     if (!alternativesValid) {
0382         for (const auto& info : std::as_const(altInfo)) {
0383             qDeleteAll(info.fields);
0384         }
0385 
0386         return nullptr;
0387     }
0388     tagged->setAlternatives(altInfo, false);
0389 
0390     pd.defaultFields->setParent(tagged.get());
0391     while (pd.defaultFields->hasNext()) {
0392         DataInformation* data = pd.defaultFields->next();
0393         if (data) {
0394             tagged->appendDefaultField(data, false);
0395         } else {
0396             return nullptr; // error message should be logged already
0397         }
0398     }
0399     return tagged.release();
0400 }