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

Commit 9e10ac70 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AAPT2: Process <java-symbols> and private symbol package

Need to introduce the idea of multiple levels of visibility to support <java-symbol>.

Public, Private, Undefined.

Public means it is accessible from outside and requires an ID assigned.
Private means that we explicitly want this to be a symbol (show up in R.java), but not visible
to other packages. No ID required.

Undefined is any normal resource. When --private-symbols is specified in the link phase,
these resources will not show up in R.java.

Change-Id: Icba89221e08e685dee7683786aa7112baf28c856
parent 5359893e
Loading
Loading
Loading
Loading
+9 −8
Original line number Diff line number Diff line
@@ -124,17 +124,18 @@ void Debug::printTable(ResourceTable* table) {
            }

            for (const ResourceEntry* entry : sortedEntries) {
                ResourceId id = {
                        package->id ? package->id.value() : uint8_t(0),
                ResourceId id(package->id ? package->id.value() : uint8_t(0),
                              type->id ? type->id.value() : uint8_t(0),
                        entry->id ? entry->id.value() : uint16_t(0)
                };
                              entry->id ? entry->id.value() : uint16_t(0));
                ResourceName name(package->name, type->type, entry->name);

                ResourceName name = { package->name, type->type, entry->name };
                std::cout << "    spec resource " << id << " " << name;
                if (entry->publicStatus.isPublic) {
                    std::cout << " PUBLIC";
                switch (entry->symbolStatus.state) {
                case SymbolState::kPublic: std::cout << " PUBLIC"; break;
                case SymbolState::kPrivate: std::cout << " _PRIVATE_"; break;
                default: break;
                }

                std::cout << std::endl;

                PrintVisitor visitor;
+34 −5
Original line number Diff line number Diff line
@@ -77,6 +77,18 @@ static std::u16string transform(const StringPiece16& symbol) {
    return output;
}

bool JavaClassGenerator::skipSymbol(SymbolState state) {
    switch (mOptions.types) {
    case JavaClassGeneratorOptions::SymbolTypes::kAll:
        return false;
    case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
        return state == SymbolState::kUndefined;
    case JavaClassGeneratorOptions::SymbolTypes::kPublic:
        return state != SymbolState::kPublic;
    }
    return true;
}

void JavaClassGenerator::generateStyleable(const StringPiece16& packageNameToGenerate,
                                           const std::u16string& entryName,
                                           const Styleable* styleable,
@@ -121,7 +133,7 @@ void JavaClassGenerator::generateStyleable(const StringPiece16& packageNameToGen
        // We may reference IDs from other packages, so prefix the entry name with
        // the package.
        const ResourceNameRef& itemName = sortedAttributes[i].second;
        if (packageNameToGenerate != itemName.package) {
        if (!itemName.package.empty() && packageNameToGenerate != itemName.package) {
            *out << "_" << transform(itemName.package);
        }
        *out << "_" << transform(itemName.entry) << " = " << i << ";\n";
@@ -137,7 +149,11 @@ bool JavaClassGenerator::generateType(const StringPiece16& packageNameToGenerate
    std::u16string unmangledPackage;
    std::u16string unmangledName;
    for (const auto& entry : type->entries) {
        ResourceId id = { package->id.value(), type->id.value(), entry->id.value() };
        if (skipSymbol(entry->symbolStatus.state)) {
            continue;
        }

        ResourceId id(package->id.value(), type->id.value(), entry->id.value());
        assert(id.isValid());

        unmangledName = entry->name;
@@ -157,7 +173,7 @@ bool JavaClassGenerator::generateType(const StringPiece16& packageNameToGenerate
        }

        if (!isValidSymbol(unmangledName)) {
            ResourceNameRef resourceName = { packageNameToGenerate, type->type, unmangledName };
            ResourceNameRef resourceName(packageNameToGenerate, type->type, unmangledName);
            std::stringstream err;
            err << "invalid symbol name '" << resourceName << "'";
            mError = err.str();
@@ -177,13 +193,24 @@ bool JavaClassGenerator::generateType(const StringPiece16& packageNameToGenerate
}

bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, std::ostream* out) {
    generateHeader(packageNameToGenerate, out);
    return generate(packageNameToGenerate, packageNameToGenerate, out);
}

bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
                                  const StringPiece16& outPackageName, std::ostream* out) {
    generateHeader(outPackageName, out);

    *out << "public final class R {\n";

    for (const auto& package : mTable->packages) {
        for (const auto& type : package->types) {
            *out << "    public static final class " << type->type << " {\n";
            StringPiece16 typeStr;
            if (type->type == ResourceType::kAttrPrivate) {
                typeStr = toString(ResourceType::kAttr);
            } else {
                typeStr = toString(type->type);
            }
            *out << "    public static final class " << typeStr << " {\n";
            if (!generateType(packageNameToGenerate, package.get(), type.get(), out)) {
                return false;
            }
@@ -196,4 +223,6 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, st
    return true;
}



} // namespace aapt
+18 −1
Original line number Diff line number Diff line
@@ -33,6 +33,17 @@ struct JavaClassGeneratorOptions {
     * on resource entries. Default is true.
     */
    bool useFinal = true;

    enum class SymbolTypes {
        kAll,
        kPublicPrivate,
        kPublic,
    };

    /*
     *
     */
    SymbolTypes types = SymbolTypes::kAll;
};

/*
@@ -49,7 +60,11 @@ public:
     * We need to generate these symbols in a separate file.
     * Returns true on success.
     */
    bool generate(const StringPiece16& package, std::ostream* out);
    bool generate(const StringPiece16& packageNameToGenerate, std::ostream* out);

    bool generate(const StringPiece16& packageNameToGenerate,
                  const StringPiece16& outputPackageName,
                  std::ostream* out);

    const std::string& getError() const;

@@ -64,6 +79,8 @@ private:
                           const Styleable* styleable,
                           std::ostream* out);

    bool skipSymbol(SymbolState state);

    ResourceTable* mTable;
    JavaClassGeneratorOptions mOptions;
    std::string mError;
+81 −0
Original line number Diff line number Diff line
@@ -65,6 +65,87 @@ TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
              output.find("public static final int hey_dude_cool_attr = 0;"));
}

TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
            .setPackageId(u"android", 0x01)
            .addSimple(u"@android:id/one", ResourceId(0x01020000))
            .addSimple(u"@android:id/com.foo$two", ResourceId(0x01020001))
            .build();

    JavaClassGenerator generator(table.get(), {});
    std::stringstream out;
    ASSERT_TRUE(generator.generate(u"android", u"com.android.internal", &out));

    std::string output = out.str();
    EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
    EXPECT_NE(std::string::npos, output.find("public static final int one = 0x01020000;"));
    EXPECT_EQ(std::string::npos, output.find("two"));
    EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
}

TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
            .setPackageId(u"android", 0x01)
            .addSimple(u"@android:^attr-private/one", ResourceId(0x01010000))
            .build();

    JavaClassGenerator generator(table.get(), {});
    std::stringstream out;
    ASSERT_TRUE(generator.generate(u"android", &out));

    std::string output = out.str();
    EXPECT_NE(std::string::npos, output.find("public static final class attr"));
    EXPECT_EQ(std::string::npos, output.find("public static final class ^attr-private"));
}

TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
    StdErrDiagnostics diag;
    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
            .setPackageId(u"android", 0x01)
            .addSimple(u"@android:id/one", ResourceId(0x01020000))
            .addSimple(u"@android:id/two", ResourceId(0x01020001))
            .addSimple(u"@android:id/three", ResourceId(0x01020002))
            .build();
    ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie(u"@android:id/one"), {}, {},
                                      SymbolState::kPublic, &diag));
    ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie(u"@android:id/two"), {}, {},
                                      SymbolState::kPrivate, &diag));

    JavaClassGeneratorOptions options;
    options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
    {
        JavaClassGenerator generator(table.get(), options);
        std::stringstream out;
        ASSERT_TRUE(generator.generate(u"android", &out));
        std::string output = out.str();
        EXPECT_NE(std::string::npos, output.find("public static final int one = 0x01020000;"));
        EXPECT_EQ(std::string::npos, output.find("two"));
        EXPECT_EQ(std::string::npos, output.find("three"));
    }

    options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
    {
        JavaClassGenerator generator(table.get(), options);
        std::stringstream out;
        ASSERT_TRUE(generator.generate(u"android", &out));
        std::string output = out.str();
        EXPECT_NE(std::string::npos, output.find("public static final int one = 0x01020000;"));
        EXPECT_NE(std::string::npos, output.find("public static final int two = 0x01020001;"));
        EXPECT_EQ(std::string::npos, output.find("three"));
    }

    options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
    {
        JavaClassGenerator generator(table.get(), options);
        std::stringstream out;
        ASSERT_TRUE(generator.generate(u"android", &out));
        std::string output = out.str();
        EXPECT_NE(std::string::npos, output.find("public static final int one = 0x01020000;"));
        EXPECT_NE(std::string::npos, output.find("public static final int two = 0x01020001;"));
        EXPECT_NE(std::string::npos, output.find("public static final int three = 0x01020002;"));
    }
}

/*
 * TODO(adamlesinski): Re-enable this once we get merging working again.
 * TEST(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
+32 −5
Original line number Diff line number Diff line
@@ -18,10 +18,11 @@
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "util/Util.h"
#include "ValueVisitor.h"
#include "XmlPullParser.h"

#include "util/Util.h"

#include <sstream>

namespace aapt {
@@ -184,7 +185,7 @@ struct ParsedResource {
    ResourceName name;
    Source source;
    ResourceId id;
    bool markPublic = false;
    SymbolState symbolState = SymbolState::kUndefined;
    std::unique_ptr<Value> value;
    std::list<ParsedResource> childResources;
};
@@ -192,9 +193,11 @@ struct ParsedResource {
// Recursively adds resources to the ResourceTable.
static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config,
                                IDiagnostics* diag, ParsedResource* res) {
    if (res->markPublic && !table->markPublic(res->name, res->id, res->source, diag)) {
    if (res->symbolState != SymbolState::kUndefined) {
        if (!table->setSymbolState(res->name, res->id, res->source, res->symbolState, diag)) {
            return false;
        }
    }

    if (!res->value) {
        return true;
@@ -318,6 +321,8 @@ bool ResourceParser::parseResources(XmlPullParser* parser) {
            result = parseAttr(parser, &parsedResource);
        } else if (elementName == u"public") {
            result = parsePublic(parser, &parsedResource);
        } else if (elementName == u"java-symbol" || elementName == u"symbol") {
            result = parseSymbol(parser, &parsedResource);
        } else {
            mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
                        << "unknown resource type '" << elementName << "'");
@@ -506,7 +511,29 @@ bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResou
        outResource->value = util::make_unique<Id>();
    }

    outResource->markPublic = true;
    outResource->symbolState = SymbolState::kPublic;
    return true;
}

bool ResourceParser::parseSymbol(XmlPullParser* parser, ParsedResource* outResource) {
    const Source source = mSource.withLine(parser->getLineNumber());

    Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
    if (!maybeType) {
        mDiag->error(DiagMessage(source) << "<" << parser->getElementName() << "> must have a "
                     "'type' attribute");
        return false;
    }

    const ResourceType* parsedType = parseResourceType(maybeType.value());
    if (!parsedType) {
        mDiag->error(DiagMessage(source) << "invalid resource type '" << maybeType.value()
                     << "' in <" << parser->getElementName() << ">");
        return false;
    }

    outResource->name.type = *parsedType;
    outResource->symbolState = SymbolState::kPrivate;
    return true;
}

Loading