DataInformation* OsdParser::parseNode(const QDomNode& node, const DataInformation* parent) { QDomElement elem = node.toElement(); // try to convert the node to an element. DataInformation* data = NULL; if (!elem.isNull()) { // kDebug() << "element tag: " << elem.tagName(); //e is element const QString tag = elem.tagName(); if (tag == QLatin1String("struct")) data = structFromXML(elem); else if (tag == QLatin1String("array")) data = arrayFromXML(elem, parent); else if (tag == QLatin1String("bitfield")) data = bitfieldFromXML(elem); else if (tag == QLatin1String("primitive")) data = primitiveFromXML(elem); else if (tag == QLatin1String("union")) data = unionFromXML(elem); else if (tag == QLatin1String("enum")) data = enumFromXML(elem, false); else if (tag == QLatin1String("flags")) data = enumFromXML(elem, true); else if (tag == QLatin1String("string")) data = stringFromXML(elem); } if (data) { QString byteOrder = elem.attribute(QLatin1String("byteOrder"), QLatin1String("inherit")); data->setByteOrder(byteOrderFromString(byteOrder)); } return data; }
QScriptValue::PropertyFlags DefaultScriptClass::propertyFlags(const QScriptValue& object, const QScriptString& name, uint id) { QScriptValue::PropertyFlags result; const ScriptHandlerInfo::Mode mode = mHandlerInfo->mode(); Q_ASSERT(mode != ScriptHandlerInfo::None); DataInformation* data = toDataInformation(object); if (!data) { mHandlerInfo->logger()->error() << "could not cast data from" << object.data().toString(); engine()->currentContext()->throwError(QScriptContext::ReferenceError, QStringLiteral("Attempting to access an invalid object")); return 0; } if (name == s_valid || name == s_validationError) { if (mode != ScriptHandlerInfo::Validating) result |= QScriptValue::ReadOnly; } else if (mode != ScriptHandlerInfo::Updating) { result |= QScriptValue::ReadOnly; } for (int i = 0, size = mIterableProperties.size(); i < size; ++i) { if (mIterableProperties.at(i).first == name) return result | mIterableProperties.at(i).second; } if (additionalPropertyFlags(data, name, id, &result)) return result; //is a child element else { data->logError() << "could not find flags for property with name" << name.toString(); return 0; } }
void ComplexArrayData::appendChildren(uint from, uint to) { for (uint i = from; i < to; ++i) { DataInformation* arrayElem = mChildType->clone(); arrayElem->setName(QString::number(i)); arrayElem->setParent(mParent); mChildren.append(arrayElem); } }
QScriptValue DefaultScriptClass::Default_proto_toString(QScriptContext* ctx, QScriptEngine* eng) { DataInformation* data = toDataInformation(ctx->thisObject()); if (!data) { qCWarning(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "could not cast data"; return eng->undefinedValue(); } return QString(data->typeName() + QLatin1Char(' ') + data->name()); }
QScriptString DefaultscriptClassIterator::name() const { Q_ASSERT(mCurrent >= 0 && (uint)mCurrent < mClass->mIterableProperties.size() + mData->childCount()); if (mCurrent < 0 || (uint)mCurrent >= mClass->mIterableProperties.size() + mData->childCount()) return QScriptString(); if (mCurrent < mClass->mIterableProperties.size()) return mClass->mIterableProperties.at(mCurrent).first; int index = mCurrent - mClass->mIterableProperties.size(); Q_ASSERT(index >= 0); DataInformation* child = mData->childAt(index); return mClass->engine()->toStringHandle(child->name()); }
BitCount64 ComplexArrayData::offset(const DataInformation* child) const { BitCount64 offset = 0; //sum size of elements up to index for (int i = 0; i < mChildren.size(); ++i) { DataInformation* current = mChildren.at(i); if (current == child) return offset; offset += current->size(); } Q_ASSERT(false); //should never be reached return offset; }
QScriptClass::QueryFlags DefaultScriptClass::queryProperty(const QScriptValue& object, const QScriptString& name, QScriptClass::QueryFlags flags, uint* id) { const ScriptHandlerInfo::Mode mode = mHandlerInfo->mode(); Q_ASSERT(mode != ScriptHandlerInfo::None); DataInformation* data = toDataInformation(object); if (!data) { mHandlerInfo->logger()->error() << "could not cast data from" << object.data().toString(); engine()->currentContext()->throwError(QScriptContext::ReferenceError, QStringLiteral("Attempting to access an invalid object")); return 0; } if (name == s_valid || name == s_validationError) { return mode == ScriptHandlerInfo::Validating ? flags : flags & ~HandlesWriteAccess; } if (mode != ScriptHandlerInfo::Updating) { //the only properties that are possibly writable when not updating are valid and validationError //but we checked them before so we remove handlesWriteAccess from the flags flags &= ~HandlesWriteAccess; } if (name == s_byteOrder || name == s_name || name == s_updateFunc || name == s_validationFunc || name == s_datatype || name == s_customTypeName || name == s_asStringFunc) { return flags; } else if (name == s_wasAbleToRead || name == s_parent) { return flags & ~HandlesWriteAccess; } else if (queryAdditionalProperty(data, name, &flags, id)) { return flags; } else { data->logError() << "could not find property with name" << name.toString(); engine()->currentContext()->throwError(QScriptContext::ReferenceError, QStringLiteral("Could not find property with name ") + name.toString()); return 0; } }
qint64 UnionDataInformation::readData(Okteta::AbstractByteArrayModel *input, Okteta::Address address, BitCount64 bitsRemaining, quint8* bitOffset) { Q_ASSERT(mHasBeenUpdated); //update must have been called prior to reading TopLevelDataInformation* top = topLevelDataInformation(); Q_CHECK_PTR(top); qint64 readBits = 0; const quint8 originalBitOffset = *bitOffset; quint8 bitOffsetAfterUnion = originalBitOffset; bool reachedEOF = false; for (int i = 0; i < mChildren.size(); i++) { DataInformation* next = mChildren.at(i); //first of all update the structure: top->scriptHandler()->updateDataInformation(next); DataInformation* newNext = mChildren.at(i); if (next != newNext) { logInfo() << "Child at index " << i << " was replaced."; top->setChildDataChanged(); } //bit offset always has to be reset to original value qint64 currentReadBits = newNext->readData(input, address, bitsRemaining, bitOffset); if (currentReadBits == -1) { //since this is a union, try to read all values and not abort as soon as one is too large reachedEOF = true; } else if (currentReadBits > readBits) { //this is the largest element, so the bit offset after the union is the one after this element readBits = currentReadBits; bitOffsetAfterUnion = *bitOffset; } *bitOffset = originalBitOffset; // start at beginning } *bitOffset = bitOffsetAfterUnion; mWasAbleToRead = !reachedEOF; return reachedEOF ? -1 : readBits; }
void DefaultScriptClass::setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) { const ScriptHandlerInfo::Mode mode = mHandlerInfo->mode(); Q_ASSERT(mode != ScriptHandlerInfo::None); DataInformation* data = toDataInformation(object); if (!data) { mHandlerInfo->logger()->error() << "could not cast data from" << object.data().toString(); engine()->currentContext()->throwError(QScriptContext::ReferenceError, QStringLiteral("Attempting to access an invalid object")); return; } if (mode == ScriptHandlerInfo::Validating) { //only way write access is allowed is when validating: valid and validationError if (data->hasBeenValidated()) data->logError() << "Cannot modify this object, it has already been validated!"; else if (name == s_valid) data->mValidationSuccessful = value.toBool(); else if (name == s_validationError) data->setValidationError(value.toString()); else data->logError() << "Cannot write to property" << name.toString() << "while validating!"; return; } if (mode != ScriptHandlerInfo::Updating) { data->logError() << "Writing to property" << name.toString() << "is only allowed when updating."; return; } Q_ASSERT(mode == ScriptHandlerInfo::Updating); if (name == s_byteOrder) { data->setByteOrder(ParserUtils::byteOrderFromString(value.toString(), LoggerWithContext(data->logger(), data->fullObjectPath()))); } else if (name == s_datatype) { //change the type of the underlying object setDataType(value, data); } else if (name == s_updateFunc) { data->setUpdateFunc(value); } else if (name == s_validationFunc) { data->setValidationFunc(value); } else if (name == s_name) { data->setName(value.toString()); } else if (name == s_customTypeName) { if (!value.isValid() || value.isNull() || value.isUndefined()) data->setCustomTypeName(QString()); //unset else data->setCustomTypeName(value.toString()); } else if (name == s_asStringFunc) { data->setToStringFunction(value); } else { bool setAdditional = setAdditionalProperty(data, name, id, value); if (setAdditional) return; else { data->logError() << "could not set property with name" << name.toString(); engine()->currentContext()->throwError(QScriptContext::ReferenceError, QStringLiteral("Cannot write property ") + name.toString()); } } }
void DefaultScriptClass::setDataType(const QScriptValue& value, DataInformation* data) { DataInformation* thisObj = toDataInformation(engine()->currentContext()->thisObject()); Q_CHECK_PTR(thisObj); const bool isThisObj = thisObj == data; //this object always has mHasBeenUpdated set just before calling updateFunc, so in that case it is okay if (data->hasBeenUpdated() && !isThisObj) { //this element has already been updated (and probably read, replacing it could cause crazy errors data->logError() << "Attempting to replace an already updated object. This could cause errors." "Current this object: " << (thisObj ? thisObj->fullObjectPath() : QString()); return; } //change the type of the underlying object DataInformation* newType = ScriptValueConverter::convert(value, data->name(), data->logger(), data); if (!newType) { data->logError() << "Failed to set new type, could not convert value!"; return; } DataInformationBase* parent = data->parent(); Q_CHECK_PTR(parent); TopLevelDataInformation* top = data->topLevelDataInformation(); Q_CHECK_PTR(top); //only if parent is toplevel, struct or union, can we replace bool replaced = false; if (parent->isTopLevel()) { Q_ASSERT(isThisObj); //we can only do this if we are currently at the top level element parent->asTopLevel()->setActualDataInformation(newType); replaced = true; } else if (parent->isStruct()) { StructureDataInformation* stru = parent->asStruct(); int index = stru->indexOf(data); Q_ASSERT(index != -1); Q_ASSERT(uint(index) < stru->childCount()); replaced = stru->replaceChildAt(index, newType); if (!replaced) stru->logError() << "failed to replace child at index" << index; } else if (parent->isUnion()) { UnionDataInformation* un = parent->asUnion(); int index = un->indexOf(data); Q_ASSERT(index != -1); Q_ASSERT(uint(index) < un->childCount()); replaced = un->replaceChildAt(index, newType); if (!replaced) un->logError() << "failed to replace child at index" << index; } else if (parent->isPointer()) { parent->asPointer()->setPointerTarget(newType); replaced = true; } else { data->logError() << "Failed to set data type since element is not toplevel and parent" " is neither struct nor union nor pointer."; } if (replaced) { top->setChildDataChanged(); //if the current object was "this" in javascript we have to replace it if (isThisObj) engine()->currentContext()->setThisObject(newType->toScriptValue(engine(), mHandlerInfo)); newType->mHasBeenUpdated = true; } else { delete newType; //could not set new type } }
QScriptValue DefaultScriptClass::property(const QScriptValue& object, const QScriptString& name, uint id) { Q_ASSERT(mHandlerInfo->mode() != ScriptHandlerInfo::None); DataInformation* data = toDataInformation(object); if (!data) { mHandlerInfo->logger()->error() << "could not cast data from" << object.data().toString(); return engine()->currentContext()->throwError(QScriptContext::ReferenceError, QStringLiteral("Attempting to access an invalid object")); } if (name == s_valid) { return data->validationSuccessful(); } else if (name == s_wasAbleToRead) { return data->wasAbleToRead(); } else if (name == s_parent) { Q_CHECK_PTR(data->parent()); //parent() cannot be null if (data->parent()->isTopLevel()) return engine()->nullValue(); return data->parent()->asDataInformation()->toScriptValue(engine(), mHandlerInfo); } else if (name == s_datatype) { return data->typeName(); } else if (name == s_updateFunc) { return data->updateFunc(); } else if (name == s_validationFunc) { return data->validationFunc(); } else if (name == s_validationError) { return data->validationError(); } else if (name == s_byteOrder) { return ParserUtils::byteOrderToString(data->byteOrder()); } else if (name == s_name) { return data->name(); } else if (name == s_customTypeName) { return data->typeName(); } else if (name == s_asStringFunc) { return data->toStringFunction(); } QScriptValue other = additionalProperty(data, name, id); if (other.isValid()) return other; else { data->logError() << "could not find property with name" << name.toString(); return engine()->currentContext()->throwError(QScriptContext::ReferenceError, QStringLiteral("Cannot read property ") + name.toString()); } }
QVector<TopLevelDataInformation*> OsdParser::parseStructures() const { QFileInfo fileInfo(mAbsolutePath); QVector<TopLevelDataInformation*> structures; QScopedPointer<ScriptLogger> rootLogger(new ScriptLogger()); //only needed in we get an error right now QDomDocument document = openDoc(rootLogger.data()); if (document.isNull()) { structures.append(new TopLevelDataInformation( new DummyDataInformation(0, fileInfo.fileName()), rootLogger.take(), 0, fileInfo)); return structures; } QDomElement rootElem = document.firstChildElement(QStringLiteral("data")); if (rootElem.isNull()) { rootLogger->error() << "Missing top level <data> element!"; structures.append(new TopLevelDataInformation( new DummyDataInformation(0, fileInfo.fileName()), rootLogger.take(), 0, fileInfo)); return structures; } int count = 1; for (QDomElement elem = rootElem.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement()) { if (elem.tagName() == TYPE_ENUMDEF) continue; //skip enum defs QScriptEngine* eng = ScriptEngineInitializer::newEngine(); //we need this for dynamic arrays ScriptLogger* logger = new ScriptLogger(); QVector<EnumDefinition::Ptr> enums = parseEnums(rootElem, logger); OsdParserInfo info(QString(), logger, 0, eng, enums); DataInformation* data = parseElement(elem, info); if (!data) { QString name = readProperty(elem, PROPERTY_NAME); if (name.isEmpty()) name = fileInfo.absoluteFilePath() + QStringLiteral("_element") + QString::number(count); qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "Failed to parse element" << elem.tagName() << name; qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "Parsing messages were:" << logger->messages(); data = new DummyDataInformation(0, name); } TopLevelDataInformation* topData = new TopLevelDataInformation(data, logger, eng, fileInfo); QString lockOffsetStr = readProperty(elem, PROPERTY_DEFAULT_LOCK_OFFSET); if (!lockOffsetStr.isEmpty()) { ParsedNumber<quint64> offset = ParserUtils::uint64FromString(lockOffsetStr); if (!offset.isValid) data->logError() << "Default lock offset is not a valid number:" << offset.string; else { data->logInfo() << "Default lock offset is " << offset.string; topData->setDefaultLockOffset(offset.value); } } structures.append(topData); count++; } return structures; }