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 }