Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ad32cac authored by Adam Lesinski's avatar Adam Lesinski Committed by Android (Google) Code Review
Browse files

Merge "AAPT2: Verify min/max attr fields"

parents 805578d8 a5870657
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@
#define AAPT_DIAGNOSTICS_H

#include "Source.h"

#include "util/StringPiece.h"
#include "util/Util.h"

+47 −0
Original line number Diff line number Diff line
@@ -711,6 +711,46 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o
        }
    }

    Maybe<int32_t> maybeMin, maybeMax;

    if (Maybe<StringPiece16> maybeMinStr = xml::findAttribute(parser, u"min")) {
        StringPiece16 minStr = util::trimWhitespace(maybeMinStr.value());
        if (!minStr.empty()) {
            android::Res_value value;
            if (android::ResTable::stringToInt(minStr.data(), minStr.size(), &value)) {
                maybeMin = static_cast<int32_t>(value.data);
            }
        }

        if (!maybeMin) {
            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
                         << "invalid 'min' value '" << minStr << "'");
            return false;
        }
    }

    if (Maybe<StringPiece16> maybeMaxStr = xml::findAttribute(parser, u"max")) {
        StringPiece16 maxStr = util::trimWhitespace(maybeMaxStr.value());
        if (!maxStr.empty()) {
            android::Res_value value;
            if (android::ResTable::stringToInt(maxStr.data(), maxStr.size(), &value)) {
                maybeMax = static_cast<int32_t>(value.data);
            }
        }

        if (!maybeMax) {
            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
                         << "invalid 'max' value '" << maxStr << "'");
            return false;
        }
    }

    if ((maybeMin || maybeMax) && (typeMask & android::ResTable_map::TYPE_INTEGER) == 0) {
        mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
                     << "'min' and 'max' can only be used when format='integer'");
        return false;
    }

    struct SymbolComparator {
        bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
            return a.symbol.name.value() < b.symbol.name.value();
@@ -794,6 +834,13 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o
    std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
    attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
    attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
    if (maybeMin) {
        attr->minInt = maybeMin.value();
    }

    if (maybeMax) {
        attr->maxInt = maybeMax.value();
    }
    outResource->value = std::move(attr);
    return true;
}
+16 −0
Original line number Diff line number Diff line
@@ -138,6 +138,22 @@ TEST_F(ResourceParserTest, ParseAttr) {
    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
}

TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
    std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
    ASSERT_TRUE(testParse(input));

    Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
    ASSERT_NE(nullptr, attr);
    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->typeMask);
    EXPECT_EQ(10, attr->minInt);
    EXPECT_EQ(23, attr->maxInt);
}

TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
    std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
    ASSERT_FALSE(testParse(input));
}

TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
    std::string input = "<declare-styleable name=\"Styleable\">\n"
                        "  <attr name=\"foo\" />\n"
+81 −3
Original line number Diff line number Diff line
@@ -15,9 +15,9 @@
 */

#include "Resource.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"

#include "util/Util.h"
#include "flatten/ResourceTypeExtensions.h"

@@ -216,7 +216,7 @@ void BinaryPrimitive::print(std::ostream* out) const {
            *out << "(null)";
            break;
        case android::Res_value::TYPE_INT_DEC:
            *out << "(integer) " << value.data;
            *out << "(integer) " << static_cast<int32_t>(value.data);
            break;
        case android::Res_value::TYPE_INT_HEX:
            *out << "(integer) " << std::hex << value.data << std::dec;
@@ -237,7 +237,10 @@ void BinaryPrimitive::print(std::ostream* out) const {
    }
}

Attribute::Attribute(bool w, uint32_t t) : weak(w), typeMask(t) {
Attribute::Attribute(bool w, uint32_t t) :
        weak(w), typeMask(t),
        minInt(std::numeric_limits<int32_t>::min()),
        maxInt(std::numeric_limits<int32_t>::max()) {
}

bool Attribute::isWeak() const {
@@ -361,6 +364,81 @@ void Attribute::print(std::ostream* out) const {
    }
}

static void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
                                          const Item* value) {
    *msg << "expected";
    if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
        *msg << " boolean";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
        *msg << " color";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
        *msg << " dimension";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
        *msg << " enum";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
        *msg << " flags";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
        *msg << " float";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
        *msg << " fraction";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
        *msg << " integer";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
        *msg << " reference";
    }

    if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
        *msg << " string";
    }

    *msg << " but got " << *value;
}

bool Attribute::matches(const Item* item, DiagMessage* outMsg) const {
    android::Res_value val = {};
    item->flatten(&val);

    // Always allow references.
    const uint32_t mask = typeMask | android::ResTable_map::TYPE_REFERENCE;
    if (!(mask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
        if (outMsg) {
            buildAttributeMismatchMessage(outMsg, this, item);
        }
        return false;

    } else if (ResourceUtils::androidTypeToAttributeTypeMask(val.dataType) &
            android::ResTable_map::TYPE_INTEGER) {
        if (static_cast<int32_t>(util::deviceToHost32(val.data)) < minInt) {
            if (outMsg) {
                *outMsg << *item << " is less than minimum integer " << minInt;
            }
            return false;
        } else if (static_cast<int32_t>(util::deviceToHost32(val.data)) > maxInt) {
            if (outMsg) {
                *outMsg << *item << " is greater than maximum integer " << maxInt;
            }
            return false;
        }
    }
    return true;
}

Style* Style::clone(StringPool* newPool) const {
    Style* style = new Style();
    style->parent = parent;
+5 −3
Original line number Diff line number Diff line
@@ -17,9 +17,10 @@
#ifndef AAPT_RESOURCE_VALUES_H
#define AAPT_RESOURCE_VALUES_H

#include "util/Maybe.h"
#include "Diagnostics.h"
#include "Resource.h"
#include "StringPool.h"
#include "util/Maybe.h"

#include <array>
#include <androidfw/ResourceTypes.h>
@@ -233,8 +234,8 @@ struct Attribute : public BaseValue<Attribute> {

	bool weak;
    uint32_t typeMask;
    uint32_t minInt;
    uint32_t maxInt;
    int32_t minInt;
    int32_t maxInt;
    std::vector<Symbol> symbols;

    Attribute(bool w, uint32_t t = 0u);
@@ -243,6 +244,7 @@ struct Attribute : public BaseValue<Attribute> {
    Attribute* clone(StringPool* newPool) const override;
    void printMask(std::ostream* out) const;
    void print(std::ostream* out) const override;
    bool matches(const Item* item, DiagMessage* outMsg) const;
};

struct Style : public BaseValue<Style> {
Loading