Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++ // Qt-Security score:significant
++
++ #include "qqmljsstreamwriter_p.h"
++ #include "qanystringviewutils_p.h"
++
++ #include <QtCore/QBuffer>
++ #include <QtCore/QStringList>
++
++ QT_BEGIN_NAMESPACE
++
++ // TODO: All of this could be improved by using and re-using one buffer for all the writing.
++ // We don't really need to allocate any temporary byte arrays.
++
++ static QByteArray enquoteByteArray(QByteArrayView string)
++ {
++ const qsizetype length = string.length();
++ QByteArray buffer;
++ buffer.reserve(length + 2);
++ buffer.append(' ');
++ buffer.append(string);
++ buffer.append(' ');
++ buffer.replace('\\', "\\\\").replace('"', "\\\"");
++ buffer[0] = '"';
++ buffer[buffer.length() - 1] = '"';
++ return buffer;
++ }
++
++ static QByteArray enquoteAnyString(QAnyStringView string)
++ {
++ return string.visit([](auto data) {
++ return QAnyStringViewUtils::processAsUtf8(data, [](QByteArrayView view) {
++ return enquoteByteArray(view);
++ });
++ });
++ }
++
++ QQmlJSStreamWriter::QQmlJSStreamWriter(QByteArray *array)
++ : m_indentDepth(0)
++ , m_pendingLineLength(0)
++ , m_maybeOneline(false)
++ , m_stream(new QBuffer(array))
++ {
++ m_stream->open(QIODevice::WriteOnly);
++ }
++
++ void QQmlJSStreamWriter::writeStartDocument()
++ {
++ }
++
++ void QQmlJSStreamWriter::writeEndDocument()
++ {
++ }
++
++ void QQmlJSStreamWriter::writeLibraryImport(
++ QByteArrayView uri, int majorVersion, int minorVersion, QByteArrayView as)
++ {
++ m_stream->write("import ");
++ m_stream->write(uri.data(), uri.length());
++ m_stream->write(" ");
++ m_stream->write(QByteArray::number(majorVersion));
++ m_stream->write(".");
++ m_stream->write(QByteArray::number(minorVersion));
++ if (!as.isEmpty()) {
++ m_stream->write(" as ");
++ m_stream->write(as.data(), as.length());
++ }
++ m_stream->write("\n");
++ }
++
++ void QQmlJSStreamWriter::writeStartObject(QByteArrayView component)
++ {
++ flushPotentialLinesWithNewlines();
++ writeIndent();
++ m_stream->write(component.data(), component.length());
++ m_stream->write(" {");
++ ++m_indentDepth;
++ m_maybeOneline = true;
++ }
++
++ void QQmlJSStreamWriter::writeEndObject()
++ {
++ if (m_maybeOneline) {
++ --m_indentDepth;
++ for (int i = 0; i < m_pendingLines.size(); ++i) {
++ m_stream->write(" ");
++ m_stream->write(m_pendingLines.at(i).trimmed());
++ if (i != m_pendingLines.size() - 1)
++ m_stream->write(";");
++ }
++
++ if (!m_pendingLines.isEmpty())
++ m_stream->write(" }\n");
++ else
++ m_stream->write("}\n");
++
++ m_pendingLines.clear();
++ m_pendingLineLength = 0;
++ m_maybeOneline = false;
++ } else {
++ flushPotentialLinesWithNewlines();
++ --m_indentDepth;
++ writeIndent();
++ m_stream->write("}\n");
++ }
++ }
++
++ void QQmlJSStreamWriter::writeScriptBinding(QByteArrayView name, QByteArrayView rhs)
++ {
++ QByteArray buffer;
++ buffer.reserve(name.length() + 2 + rhs.length());
++ buffer.append(name);
++ buffer.append(": ");
++ buffer.append(rhs);
++ writePotentialLine(buffer);
++ }
++
++ void QQmlJSStreamWriter::writeStringBinding(QByteArrayView name, QAnyStringView value)
++ {
++ writeScriptBinding(name, enquoteAnyString(value));
++ }
++
++ void QQmlJSStreamWriter::writeNumberBinding(QByteArrayView name, qint64 value)
++ {
++ writeScriptBinding(name, QByteArray::number(value));
++ }
++
++ void QQmlJSStreamWriter::writeBooleanBinding(QByteArrayView name, bool value)
++ {
++ writeScriptBinding(name, value ? "true" : "false");
++ }
++
++ template<typename String, typename ElementHandler>
++ void QQmlJSStreamWriter::doWriteArrayBinding(
++ QByteArrayView name, const QList<String> &elements, ElementHandler &&handler)
++ {
++ flushPotentialLinesWithNewlines();
++ writeIndent();
++
++ // try to use a single line
++ QByteArray singleLine(name.data(), name.length());
++ singleLine += ": [";
++ for (int i = 0; i < elements.size(); ++i) {
++ QAnyStringViewUtils::processAsUtf8(elements.at(i), [&](QByteArrayView element) {
++ singleLine += handler(element);
++ });
++ if (i != elements.size() - 1)
++ singleLine += ", ";
++ }
++ singleLine += "]\n";
++ if (singleLine.size() + m_indentDepth * 4 < 80) {
++ m_stream->write(singleLine);
++ return;
++ }
++
++ // write multi-line
++ m_stream->write(name.data(), name.length());
++ m_stream->write(": [\n");
++ ++m_indentDepth;
++ for (int i = 0; i < elements.size(); ++i) {
++ writeIndent();
++ QAnyStringViewUtils::processAsUtf8(elements.at(i), [&](QByteArrayView element) {
++ const auto handled = handler(element);
++ m_stream->write(handled.data(), handled.length());
++ });
++ if (i != elements.size() - 1) {
++ m_stream->write(",\n");
++ } else {
++ m_stream->write("\n");
++ }
++ }
++ --m_indentDepth;
++ writeIndent();
++ m_stream->write("]\n");
++ }
++
++ void QQmlJSStreamWriter::writeArrayBinding(QByteArrayView name, const QByteArrayList &elements)
++ {
++ doWriteArrayBinding(name, elements, [](QByteArrayView view) { return view; });
++ }
++
++ void QQmlJSStreamWriter::writeStringListBinding(
++ QByteArrayView name, const QList<QAnyStringView> &elements)
++ {
++ doWriteArrayBinding(name, elements, enquoteByteArray);
++ }
++
++ void QQmlJSStreamWriter::write(QByteArrayView data)
++ {
++ flushPotentialLinesWithNewlines();
++ m_stream->write(data.data(), data.length());
++ }
++
++ void QQmlJSStreamWriter::writeEnumObjectLiteralBinding(
++ QByteArrayView name, const QList<std::pair<QAnyStringView, int> > &keyValue)
++ {
++ flushPotentialLinesWithNewlines();
++ writeIndent();
++ m_stream->write(name.data(), name.length());
++ m_stream->write(": {\n");
++ ++m_indentDepth;
++ for (int i = 0, end = keyValue.size(); i != end; ++i) {
++ writeIndent();
++ const auto &entry = keyValue[i];
++ m_stream->write(enquoteAnyString(entry.first));
++ m_stream->write(": ");
++ m_stream->write(QByteArray::number(entry.second));
++ if (i != end - 1)
++ m_stream->write(",\n");
++ else
++ m_stream->write("\n");
++ }
++ --m_indentDepth;
++ writeIndent();
++ m_stream->write("}\n");
++ }
++
++ void QQmlJSStreamWriter::writeIndent()
++ {
++ for (int i = 0; i < m_indentDepth; ++i)
++ m_stream->write(" ");
++ }
++
++ void QQmlJSStreamWriter::writePotentialLine(const QByteArray &line)
++ {
++ m_pendingLines.append(line);
++ m_pendingLineLength += line.size();
++ if (m_pendingLineLength >= 80) {
++ flushPotentialLinesWithNewlines();
++ }
++ }
++
++ void QQmlJSStreamWriter::flushPotentialLinesWithNewlines()
++ {
++ if (m_maybeOneline)
++ m_stream->write("\n");
++ for (const QByteArray &line : std::as_const(m_pendingLines)) {
++ writeIndent();
++ m_stream->write(line);
++ m_stream->write("\n");
++ }
++ m_pendingLines.clear();
++ m_pendingLineLength = 0;
++ m_maybeOneline = false;
++ }
++
++ QT_END_NAMESPACE