Preprocessed uses the same syntax of AIDL
Preprocessed file has been using a similar but different syntax. Now,
it is using the same syntax with AIDL files with a couple of small
changes.
- Type decl can have qualified name.
(interestinly, I only changed this for interface/enum)
- Interface can omit body(members) blocks.
interface IFoo; // is simply an empty interface
Bug: 25479378
Test: aidl_unittests
Test: m
Change-Id: Icf5e4c321c469af00506c5ff14782251018d9d57
diff --git a/Android.bp b/Android.bp
index 077be21..73dc78f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -100,11 +100,11 @@
"generate_rust.cpp",
"import_resolver.cpp",
"io_delegate.cpp",
- "line_reader.cpp",
"location.cpp",
"logging.cpp",
"options.cpp",
"parser.cpp",
+ "preprocess.cpp",
],
yacc: {
gen_location_hh: true,
diff --git a/aidl.cpp b/aidl.cpp
index 8501c55..18eb2f5 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -50,6 +50,7 @@
#include "options.h"
#include "os.h"
#include "parser.h"
+#include "preprocess.h"
#ifndef O_BINARY
# define O_BINARY 0
@@ -302,51 +303,6 @@
return true;
}
-// TODO: Remove this in favor of using the YACC parser b/25479378
-bool ParsePreprocessedLine(const string& line, string* decl, std::string* package,
- string* class_name) {
- // erase all trailing whitespace and semicolons
- const size_t end = line.find_last_not_of(" ;\t");
- if (end == string::npos) {
- return false;
- }
- if (line.rfind(';', end) != string::npos) {
- return false;
- }
-
- decl->clear();
- string type;
- vector<string> pieces = Split(line.substr(0, end + 1), " \t");
- for (const string& piece : pieces) {
- if (piece.empty()) {
- continue;
- }
- if (decl->empty()) {
- *decl = std::move(piece);
- } else if (type.empty()) {
- type = std::move(piece);
- } else {
- return false;
- }
- }
-
- // Note that this logic is absolutely wrong. Given a parcelable
- // org.some.Foo.Bar, the class name is Foo.Bar, but this code will claim that
- // the class is just Bar. However, this was the way it was done in the past.
- //
- // See b/17415692
- size_t dot_pos = type.rfind('.');
- if (dot_pos != string::npos) {
- *class_name = type.substr(dot_pos + 1);
- *package = type.substr(0, dot_pos);
- } else {
- *class_name = type;
- package->clear();
- }
-
- return true;
-}
-
bool ValidateAnnotationContext(const AidlDocument& doc) {
struct AnnotationValidator : AidlVisitor {
bool success = true;
@@ -402,65 +358,6 @@
namespace internals {
-bool parse_preprocessed_file(const IoDelegate& io_delegate, const string& filename,
- AidlTypenames* typenames) {
- bool success = true;
- unique_ptr<LineReader> line_reader = io_delegate.GetLineReader(filename);
- if (!line_reader) {
- AIDL_ERROR(filename) << "cannot open preprocessed file";
- success = false;
- return success;
- }
-
- string line;
- int lineno = 1;
- for ( ; line_reader->ReadLine(&line); ++lineno) {
- if (line.empty() || line.compare(0, 2, "//") == 0) {
- // skip comments and empty lines
- continue;
- }
-
- string decl;
- std::string package;
- string class_name;
- if (!ParsePreprocessedLine(line, &decl, &package, &class_name)) {
- success = false;
- break;
- }
-
- AidlLocation::Point point = {.line = lineno, .column = 0 /*column*/};
- AidlLocation location = AidlLocation(filename, point, point, AidlLocation::Source::EXTERNAL);
-
- if (decl == "parcelable") {
- // ParcelFileDescriptor is treated as a built-in type, but it's also in the framework.aidl.
- // So aidl should ignore built-in types in framework.aidl to prevent duplication.
- // (b/130899491)
- if (AidlTypenames::IsBuiltinTypename(class_name)) {
- continue;
- }
- AidlParcelable* doc = new AidlParcelable(location, class_name, package, Comments{});
- typenames->AddPreprocessedType(unique_ptr<AidlParcelable>(doc));
- } else if (decl == "structured_parcelable") {
- AidlStructuredParcelable* doc =
- new AidlStructuredParcelable(location, class_name, package, Comments{}, nullptr, nullptr);
- typenames->AddPreprocessedType(unique_ptr<AidlStructuredParcelable>(doc));
- } else if (decl == "interface") {
- AidlInterface* doc =
- new AidlInterface(location, class_name, Comments{}, false, package, nullptr);
- typenames->AddPreprocessedType(unique_ptr<AidlInterface>(doc));
- } else {
- success = false;
- break;
- }
- }
- if (!success) {
- AIDL_ERROR(filename) << " on line " << lineno << " malformed preprocessed file line: '" << line
- << "'";
- }
-
- return success;
-}
-
AidlError load_and_validate_aidl(const std::string& input_file_name, const Options& options,
const IoDelegate& io_delegate, AidlTypenames* typenames,
vector<string>* imported_files) {
@@ -487,14 +384,12 @@
}
// Import the preprocessed file
- for (const string& s : options.PreprocessedFiles()) {
- if (!parse_preprocessed_file(io_delegate, s, typenames)) {
- err = AidlError::BAD_PRE_PROCESSED_FILE;
+ for (const string& filename : options.PreprocessedFiles()) {
+ auto preprocessed = Parser::Parse(filename, io_delegate, *typenames, /*is_preprocessed=*/true);
+ if (!preprocessed) {
+ return AidlError::BAD_PRE_PROCESSED_FILE;
}
}
- if (err != AidlError::OK) {
- return err;
- }
// Find files to import and parse them
vector<string> import_paths;
@@ -526,7 +421,8 @@
auto imported_doc = Parser::Parse(import_path, io_delegate, *typenames);
if (imported_doc == nullptr) {
- AIDL_ERROR(import_path) << "error while importing " << import_path << " for " << import;
+ AIDL_ERROR(import_path) << "error while importing " << import_path << " for "
+ << import->GetNeededClass();
err = AidlError::BAD_IMPORT;
continue;
}
@@ -862,25 +758,6 @@
return true;
}
-bool preprocess_aidl(const Options& options, const IoDelegate& io_delegate) {
- unique_ptr<CodeWriter> writer = io_delegate.GetCodeWriter(options.OutputFile());
-
- for (const auto& file : options.InputFiles()) {
- AidlTypenames typenames;
- const AidlDocument* document = Parser::Parse(file, io_delegate, typenames);
- if (document == nullptr) return false;
-
- for (const auto& defined_type : document->DefinedTypes()) {
- if (!writer->Write("%s %s;\n", defined_type->GetPreprocessDeclarationName().c_str(),
- defined_type->GetCanonicalName().c_str())) {
- return false;
- }
- }
- }
-
- return writer->Close();
-}
-
int aidl_entry(const Options& options, const IoDelegate& io_delegate) {
AidlErrorLog::clearError();
@@ -890,7 +767,7 @@
ret = android::aidl::compile_aidl(options, io_delegate);
break;
case Options::Task::PREPROCESS:
- ret = android::aidl::preprocess_aidl(options, io_delegate) ? 0 : 1;
+ ret = android::aidl::Preprocess(options, io_delegate) ? 0 : 1;
break;
case Options::Task::DUMP_API:
ret = android::aidl::dump_api(options, io_delegate) ? 0 : 1;
diff --git a/aidl.h b/aidl.h
index 1ff8d46..2ac0e19 100644
--- a/aidl.h
+++ b/aidl.h
@@ -46,7 +46,6 @@
};
int compile_aidl(const Options& options, const IoDelegate& io_delegate);
-bool preprocess_aidl(const Options& options, const IoDelegate& io_delegate);
bool dump_mappings(const Options& options, const IoDelegate& io_delegate);
// main entry point to AIDL
@@ -82,9 +81,6 @@
const IoDelegate& io_delegate, AidlTypenames* typenames,
vector<string>* imported_files);
-bool parse_preprocessed_file(const IoDelegate& io_delegate, const std::string& filename,
- AidlTypenames* typenames);
-
} // namespace internals
} // namespace aidl
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 5bff95e..8f0c255 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -900,11 +900,20 @@
AidlDefinedType::AidlDefinedType(const AidlLocation& location, const std::string& name,
const Comments& comments, const std::string& package,
std::vector<std::unique_ptr<AidlMember>>* members)
- : AidlAnnotatable(location, comments),
- name_(name),
- package_(package),
- split_package_(package.empty() ? std::vector<std::string>()
- : android::base::Split(package, ".")) {
+ : AidlAnnotatable(location, comments), name_(name), package_(package) {
+ // adjust name/package when name is fully qualified (for preprocessed files)
+ if (package_.empty() && name_.find('.') != std::string::npos) {
+ // Note that this logic is absolutely wrong. Given a parcelable
+ // org.some.Foo.Bar, the class name is Foo.Bar, but this code will claim that
+ // the class is just Bar. However, this was the way it was done in the past.
+ //
+ // See b/17415692
+ auto pos = name.rfind('.');
+ // name is the last part.
+ name_ = name.substr(pos + 1);
+ // package is the initial parts (except the last).
+ package_ = name.substr(0, pos);
+ }
if (members) {
for (auto& m : *members) {
if (auto constant = m->AsConstantDeclaration(); constant) {
diff --git a/aidl_language.h b/aidl_language.h
index fcc592a..deb530a 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -908,7 +908,10 @@
std::string GetPackage() const { return package_; }
/* dot joined package and name, example: "android.package.foo.IBar" */
std::string GetCanonicalName() const;
- const std::vector<std::string>& GetSplitPackage() const { return split_package_; }
+ std::vector<std::string> GetSplitPackage() const {
+ if (package_.empty()) return std::vector<std::string>();
+ return android::base::Split(package_, ".");
+ }
virtual std::string GetPreprocessDeclarationName() const = 0;
@@ -979,8 +982,7 @@
bool CheckValidWithMembers(const AidlTypenames& typenames) const;
std::string name_;
- const std::string package_;
- const std::vector<std::string> split_package_;
+ std::string package_;
std::vector<std::unique_ptr<AidlVariableDeclaration>> variables_;
std::vector<std::unique_ptr<AidlConstantDeclaration>> constants_;
std::vector<std::unique_ptr<AidlMethod>> methods_;
@@ -1166,11 +1168,16 @@
class AidlPackage : public AidlNode {
public:
- AidlPackage(const AidlLocation& location, const Comments& comments)
- : AidlNode(location, comments) {}
+ AidlPackage(const AidlLocation& location, const std::string& name, const Comments& comments)
+ : AidlNode(location, comments), name_(name) {}
virtual ~AidlPackage() = default;
void TraverseChildren(std::function<void(const AidlNode&)>) const {}
void DispatchVisit(AidlVisitor& v) const { v.Visit(*this); }
+
+ const std::string& GetName() const { return name_; }
+
+ private:
+ std::string name_;
};
class AidlImport : public AidlNode {
diff --git a/aidl_language_y.yy b/aidl_language_y.yy
index 19b1262..7dc1f94 100644
--- a/aidl_language_y.yy
+++ b/aidl_language_y.yy
@@ -214,8 +214,8 @@
$$ = nullptr;
}
| PACKAGE qualified_name ';' {
- $$ = new AidlPackage(loc(@1, @3), $1->GetComments());
- ps->SetPackage($2->GetText());
+ $$ = new AidlPackage(loc(@1, @3), $2->GetText(), $1->GetComments());
+ ps->SetPackage(*$$);
delete $1;
delete $2;
}
@@ -310,16 +310,19 @@
parcelable_decl
: PARCELABLE qualified_name optional_type_params ';' {
+ // No check for type name here. We allow nested types for unstructured parcelables.
$$ = new AidlParcelable(loc(@2), $2->GetText(), ps->Package(), $1->GetComments(), "", $3);
delete $1;
delete $2;
}
| PARCELABLE qualified_name optional_type_params '{' parcelable_members '}' {
+ ps->CheckValidTypeName(*$2, loc(@2));
$$ = new AidlStructuredParcelable(loc(@2), $2->GetText(), ps->Package(), $1->GetComments(), $3, $5);
delete $1;
delete $2;
}
| PARCELABLE qualified_name CPP_HEADER C_STR ';' {
+ // No check for type name here. We allow nested types for unstructured parcelables.
$$ = new AidlParcelable(loc(@2), $2->GetText(), ps->Package(), $1->GetComments(), $4->GetText());
delete $1;
delete $2;
@@ -362,13 +365,21 @@
;
interface_decl
- : INTERFACE identifier '{' interface_members '}' {
+ : INTERFACE qualified_name ';' {
+ ps->CheckValidTypeName(*$2, loc(@2));
+ $$ = new AidlInterface(loc(@1), $2->GetText(), $1->GetComments(), false, ps->Package(), nullptr);
+ delete $1;
+ delete $2;
+ }
+ | INTERFACE qualified_name '{' interface_members '}' {
+ ps->CheckValidTypeName(*$2, loc(@2));
$$ = new AidlInterface(loc(@1), $2->GetText(), $1->GetComments(), false, ps->Package(), $4);
delete $1;
delete $2;
}
- | ONEWAY INTERFACE identifier '{' interface_members '}' {
- $$ = new AidlInterface(loc(@2), $3->GetText(), $1->GetComments(), true, ps->Package(), $5);
+ | ONEWAY INTERFACE qualified_name '{' interface_members '}' {
+ ps->CheckValidTypeName(*$3, loc(@3));
+ $$ = new AidlInterface(loc(@2), $3->GetText(), $1->GetComments(), true, ps->Package(), $5);
delete $1;
delete $2;
delete $3;
@@ -577,7 +588,8 @@
;
enum_decl
- : ENUM identifier enum_decl_body {
+ : ENUM qualified_name enum_decl_body {
+ ps->CheckValidTypeName(*$2, loc(@2));
$$ = new AidlEnumDeclaration(loc(@2), $2->GetText(), $3, ps->Package(), $1->GetComments());
delete $1;
delete $2;
@@ -587,6 +599,7 @@
union_decl
: UNION qualified_name optional_type_params '{' parcelable_members '}' {
+ ps->CheckValidTypeName(*$2, loc(@2));
$$ = new AidlUnionDecl(loc(@2), $2->GetText(), ps->Package(), $1->GetComments(), $3, $5);
delete $1;
delete $2;
diff --git a/aidl_typenames.cpp b/aidl_typenames.cpp
index 8440463..fe87023 100644
--- a/aidl_typenames.cpp
+++ b/aidl_typenames.cpp
@@ -115,9 +115,20 @@
return in_ignore_import || defined_type_not_from_preprocessed;
}
-bool AidlTypenames::AddDocument(std::unique_ptr<AidlDocument> doc) {
+bool AidlTypenames::AddDocument(std::unique_ptr<AidlDocument> doc, bool is_preprocessed) {
+ auto& types = is_preprocessed ? preprocessed_types_ : defined_types_;
for (const auto& type : doc->DefinedTypes()) {
- if (defined_types_.find(type->GetCanonicalName()) != defined_types_.end()) {
+ // ParcelFileDescriptor is treated as a built-in type, but it's also in the framework.aidl.
+ // So aidl should ignore built-in types in framework.aidl to prevent duplication.
+ // (b/130899491)
+ if (is_preprocessed && IsBuiltinTypename(type->GetName())) {
+ continue;
+ }
+
+ if (auto prev_definition = types.find(type->GetCanonicalName());
+ prev_definition != types.end()) {
+ AIDL_ERROR(type) << "redefinition:" << type->GetCanonicalName() << " is defined "
+ << prev_definition->second->GetLocation();
return false;
}
if (!HasValidNameComponents(*type)) {
@@ -126,7 +137,7 @@
}
documents_.push_back(std::move(doc));
for (const auto& type : documents_.back()->DefinedTypes()) {
- defined_types_.emplace(type->GetCanonicalName(), type.get());
+ types.emplace(type->GetCanonicalName(), type.get());
}
return true;
}
@@ -147,18 +158,6 @@
return *(documents_[0]);
}
-bool AidlTypenames::AddPreprocessedType(unique_ptr<AidlDefinedType> type) {
- const string name = type->GetCanonicalName();
- if (preprocessed_types_.find(name) != preprocessed_types_.end()) {
- return false;
- }
- if (!HasValidNameComponents(*type)) {
- return false;
- }
- preprocessed_types_.insert(make_pair(name, std::move(type)));
- return true;
-}
-
bool AidlTypenames::IsBuiltinTypename(const string& type_name) {
return kBuiltinTypes.find(type_name) != kBuiltinTypes.end() ||
kJavaLikeTypeToAidlType.find(type_name) != kJavaLikeTypeToAidlType.end();
@@ -192,7 +191,7 @@
auto found_prep = preprocessed_types_.find(type_name);
if (found_prep != preprocessed_types_.end()) {
- return DefinedImplResult(found_prep->second.get(), true);
+ return DefinedImplResult(found_prep->second, true);
}
// Then match with the class name. Defined types has higher priority than
@@ -205,7 +204,7 @@
for (auto it = preprocessed_types_.begin(); it != preprocessed_types_.end(); it++) {
if (it->second->GetName() == type_name) {
- return DefinedImplResult(it->second.get(), true);
+ return DefinedImplResult(it->second, true);
}
}
diff --git a/aidl_typenames.h b/aidl_typenames.h
index f86f158..a379abb 100644
--- a/aidl_typenames.h
+++ b/aidl_typenames.h
@@ -57,11 +57,10 @@
class AidlTypenames final {
public:
AidlTypenames() = default;
- bool AddDocument(std::unique_ptr<AidlDocument> doc);
+ bool AddDocument(std::unique_ptr<AidlDocument> doc, bool is_preprocessed);
const AidlDocument* GetDocumentFor(const AidlDefinedType* type) const;
const std::vector<std::unique_ptr<AidlDocument>>& AllDocuments() const { return documents_; }
const AidlDocument& MainDocument() const;
- bool AddPreprocessedType(unique_ptr<AidlDefinedType> type);
static bool IsBuiltinTypename(const string& type_name);
static bool IsPrimitiveTypename(const string& type_name);
bool IsParcelable(const string& type_name) const;
@@ -103,7 +102,7 @@
};
DefinedImplResult TryGetDefinedTypeImpl(const string& type_name) const;
map<string, AidlDefinedType*> defined_types_;
- map<string, unique_ptr<AidlDefinedType>> preprocessed_types_;
+ map<string, AidlDefinedType*> preprocessed_types_;
std::vector<std::unique_ptr<AidlDocument>> documents_;
};
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 81931a1..b3e07bd 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -35,9 +35,10 @@
#include "comments.h"
#include "logging.h"
#include "options.h"
+#include "parser.h"
+#include "preprocess.h"
#include "tests/fake_io_delegate.h"
-using android::aidl::internals::parse_preprocessed_file;
using android::aidl::test::FakeIoDelegate;
using android::base::StringPrintf;
using std::map;
@@ -675,7 +676,7 @@
string simple_content = "parcelable a.Foo;\ninterface b.IBar;";
io_delegate_.SetFileContents("path", simple_content);
EXPECT_FALSE(typenames_.ResolveTypename("a.Foo").is_resolved);
- EXPECT_TRUE(parse_preprocessed_file(io_delegate_, "path", &typenames_));
+ EXPECT_TRUE(Parser::Parse("path", io_delegate_, typenames_, /*is_preprocessed=*/true));
EXPECT_TRUE(typenames_.ResolveTypename("a.Foo").is_resolved);
EXPECT_TRUE(typenames_.ResolveTypename("b.IBar").is_resolved);
}
@@ -685,7 +686,7 @@
io_delegate_.SetFileContents("path", simple_content);
EXPECT_FALSE(typenames_.ResolveTypename("a.Foo").is_resolved);
- EXPECT_TRUE(parse_preprocessed_file(io_delegate_, "path", &typenames_));
+ EXPECT_TRUE(Parser::Parse("path", io_delegate_, typenames_, /*is_preprocessed=*/true));
EXPECT_TRUE(typenames_.ResolveTypename("a.Foo").is_resolved);
EXPECT_TRUE(typenames_.ResolveTypename("b.IBar").is_resolved);
}
@@ -737,18 +738,101 @@
io_delegate_.SetFileContents("one/IBar.aidl", "package one; import p.Outer;"
"interface IBar {}");
- vector<string> args {
- "aidl",
- "--preprocess",
- "preprocessed",
- "p/Outer.aidl",
- "one/IBar.aidl"};
+ vector<string> args{"aidl", "--preprocess", "preprocessed",
+ "-I.", "p/Outer.aidl", "one/IBar.aidl"};
Options options = Options::From(args);
- EXPECT_TRUE(::android::aidl::preprocess_aidl(options, io_delegate_));
+ EXPECT_TRUE(::android::aidl::Preprocess(options, io_delegate_));
- string output;
- EXPECT_TRUE(io_delegate_.GetWrittenContents("preprocessed", &output));
- EXPECT_EQ("parcelable p.Outer.Inner;\ninterface one.IBar;\n", output);
+ std::map<std::string, std::string> expected = {{"preprocessed",
+ "parcelable p.Outer.Inner;\n"
+ "interface one.IBar {\n"
+ "}\n"}};
+ EXPECT_THAT(io_delegate_.OutputFiles(), testing::Eq(expected));
+}
+
+TEST_F(AidlTest, PreprocessVariousThings) {
+ io_delegate_.SetFileContents("foo/bar/IFoo.aidl",
+ "package foo.bar;\n"
+ "interface IFoo {\n"
+ " int foo();\n"
+ " const int FOO = foo.bar.Bar.BAR + 1; // should be 44\n"
+ "}\n");
+ io_delegate_.SetFileContents("foo/bar/Bar.aidl",
+ "package foo.bar;\n"
+ "parcelable Bar {\n"
+ " const int BAR = imported.Foo.FOO + 1; // should be 43\n"
+ " imported.Foo foo;\n"
+ "}\n");
+ io_delegate_.SetFileContents("foo/bar/Gen.aidl",
+ "package foo.bar;\n"
+ "parcelable Gen<T> {\n"
+ "}\n");
+ io_delegate_.SetFileContents("foo/bar/Enum.aidl",
+ "package foo.bar;\n"
+ "enum Enum {\n"
+ " FOO = 3, BAR = FOO + 3, // should be 3, 6\n"
+ "}\n");
+ io_delegate_.SetFileContents("sub/imported/Foo.aidl",
+ "package imported;\n"
+ "parcelable Foo {\n"
+ " const int FOO = 42;\n"
+ "}\n");
+
+ vector<string> args = {
+ "aidl",
+ "--preprocess",
+ "preprocessed",
+ "-Isub",
+ "-I.",
+ "foo/bar/IFoo.aidl",
+ "foo/bar/Bar.aidl",
+ "foo/bar/Gen.aidl",
+ "foo/bar/Enum.aidl",
+ };
+ ASSERT_TRUE(Preprocess(Options::From(args), io_delegate_));
+ std::string preprocessed =
+ "interface foo.bar.IFoo {\n"
+ " const int FOO = 44;\n"
+ "}\n"
+ "parcelable foo.bar.Bar {\n"
+ " const int BAR = 43;\n"
+ "}\n"
+ "parcelable foo.bar.Gen<T> {\n"
+ "}\n"
+ "enum foo.bar.Enum {\n"
+ " FOO = 3,\n"
+ " BAR = 6,\n"
+ "}\n";
+ std::map<std::string, std::string> expected = {{"preprocessed", preprocessed}};
+ EXPECT_THAT(io_delegate_.OutputFiles(), testing::Eq(expected));
+
+ // use preprocessed
+ io_delegate_.SetFileContents("a/Foo.aidl",
+ "package a; parcelable Foo { const int y = foo.bar.Bar.BAR; }");
+ io_delegate_.SetFileContents("preprocessed", preprocessed);
+ CaptureStderr();
+ auto options = Options::From("aidl --lang java -o out a/Foo.aidl -ppreprocessed");
+ EXPECT_EQ(0, aidl::compile_aidl(options, io_delegate_));
+ EXPECT_EQ("", GetCapturedStderr());
+ string code;
+ EXPECT_TRUE(io_delegate_.GetWrittenContents("out/a/Foo.java", &code));
+ EXPECT_THAT(code, testing::HasSubstr("public static final int y = 43;"));
+}
+
+TEST_F(AidlTest, PreprocessedFileCantDeclarePackage) {
+ string simple_content = "package xxx; parcelable a.Foo;";
+ io_delegate_.SetFileContents("path", simple_content);
+ CaptureStderr();
+ EXPECT_FALSE(Parser::Parse("path", io_delegate_, typenames_, /*is_preprocessed=*/true));
+ EXPECT_THAT(GetCapturedStderr(), HasSubstr("Preprocessed file can't declare package."));
+}
+
+TEST_F(AidlTest, RejectQualifiedTypeNameUnlessPreprocessed) {
+ string simple_content = "parcelable a.Foo {}";
+ io_delegate_.SetFileContents("path", simple_content);
+ CaptureStderr();
+ EXPECT_FALSE(Parser::Parse("path", io_delegate_, typenames_, /*is_preprocessed=*/false));
+ EXPECT_THAT(GetCapturedStderr(), HasSubstr("Type name can't be qualified"));
}
TEST_P(AidlTest, SupportDeprecated) {
diff --git a/io_delegate.cpp b/io_delegate.cpp
index c05f4b1..e054a6b 100644
--- a/io_delegate.cpp
+++ b/io_delegate.cpp
@@ -108,11 +108,6 @@
return contents;
}
-unique_ptr<LineReader> IoDelegate::GetLineReader(
- const string& file_path) const {
- return LineReader::ReadFromFile(file_path);
-}
-
bool IoDelegate::FileIsReadable(const string& path) const {
#ifdef _WIN32
// check that the file exists and is not write-only
diff --git a/io_delegate.h b/io_delegate.h
index e0b7252..48d2807 100644
--- a/io_delegate.h
+++ b/io_delegate.h
@@ -23,7 +23,6 @@
#include <android-base/result.h>
#include "code_writer.h"
-#include "line_reader.h"
namespace android {
namespace aidl {
@@ -53,9 +52,6 @@
const std::string& filename,
const std::string& content_suffix = "") const;
- virtual std::unique_ptr<LineReader> GetLineReader(
- const std::string& file_path) const;
-
virtual bool FileIsReadable(const std::string& path) const;
virtual std::unique_ptr<CodeWriter> GetCodeWriter(
diff --git a/line_reader.cpp b/line_reader.cpp
deleted file mode 100644
index dedca20..0000000
--- a/line_reader.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "line_reader.h"
-
-#include <fstream>
-#include <sstream>
-
-using std::istringstream;
-using std::ifstream;
-using std::string;
-using std::unique_ptr;
-
-namespace android {
-namespace aidl {
-
-class FileLineReader : public LineReader {
- public:
- FileLineReader() = default;
- ~FileLineReader() override {
- input_stream_.close();
- }
-
- // non-copyable, non-movable
- FileLineReader(const FileLineReader&) = delete;
- FileLineReader(FileLineReader&&) = delete;
- FileLineReader& operator=(const FileLineReader&) = delete;
- FileLineReader& operator=(FileLineReader&&) = delete;
-
- bool Init(const std::string& file_path) {
- input_stream_.open(file_path, ifstream::in | ifstream::binary);
- return input_stream_.is_open() && input_stream_.good();
- }
-
- bool ReadLine(string* line) override {
- if (!input_stream_.good()) {
- return false;
- }
- line->clear();
- std::getline(input_stream_, *line);
- return true;
- }
-
- private:
- ifstream input_stream_;
-}; // class FileLineReader
-
-class MemoryLineReader : public LineReader {
- public:
- explicit MemoryLineReader(const string& contents) : input_stream_(contents) {}
- ~MemoryLineReader() override = default;
-
- // non-copyable, non-movable
- MemoryLineReader(const MemoryLineReader&) = delete;
- MemoryLineReader(MemoryLineReader&&) = delete;
- MemoryLineReader& operator=(const MemoryLineReader&) = delete;
- MemoryLineReader& operator=(MemoryLineReader&&) = delete;
-
- bool ReadLine(string* line) override {
- if (!input_stream_.good()) {
- return false;
- }
- line->clear();
- std::getline(input_stream_, *line);
- return true;
- }
-
- private:
- istringstream input_stream_;
-}; // class MemoryLineReader
-
-unique_ptr<LineReader> LineReader::ReadFromFile(const string& file_path) {
- unique_ptr<FileLineReader> file_reader(new FileLineReader());
- unique_ptr<LineReader> ret;
- if (file_reader->Init(file_path)) {
- ret.reset(file_reader.release());
- }
- return ret;
-}
-
-unique_ptr<LineReader> LineReader::ReadFromMemory(const string& contents) {
- return unique_ptr<LineReader>(new MemoryLineReader(contents));
-}
-
-} // namespace aidl
-} // namespace android
diff --git a/line_reader.h b/line_reader.h
deleted file mode 100644
index 0b81a60..0000000
--- a/line_reader.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-#include <string>
-
-namespace android {
-namespace aidl {
-
-class LineReader {
- public:
- LineReader() = default;
- virtual ~LineReader() = default;
-
- LineReader(const LineReader&) = delete;
- LineReader(LineReader&&) = delete;
- LineReader& operator=(const LineReader&) = delete;
- LineReader& operator=(LineReader&&) = delete;
-
- virtual bool ReadLine(std::string* line) = 0;
-
- static std::unique_ptr<LineReader> ReadFromFile(
- const std::string& file_path);
- static std::unique_ptr<LineReader> ReadFromMemory(
- const std::string& contents);
-}; // class LineReader
-
-} // namespace aidl
-} // namespace android
diff --git a/parser.cpp b/parser.cpp
index abe1dbf..d2fe362 100644
--- a/parser.cpp
+++ b/parser.cpp
@@ -27,7 +27,7 @@
const AidlDocument* Parser::Parse(const std::string& filename,
const android::aidl::IoDelegate& io_delegate,
- AidlTypenames& typenames) {
+ AidlTypenames& typenames, bool is_preprocessed) {
auto clean_path = android::aidl::IoDelegate::CleanPath(filename);
// reuse pre-parsed document from typenames
for (auto& doc : typenames.AllDocuments()) {
@@ -46,13 +46,18 @@
// nulls at the end.
raw_buffer->append(2u, '\0');
- Parser parser(clean_path, *raw_buffer, typenames);
+ Parser parser(clean_path, *raw_buffer, is_preprocessed);
if (yy::parser(&parser).parse() != 0 || parser.HasError()) {
return nullptr;
}
- return parser.document_;
+ // transfer ownership to AidlTypenames and return the raw pointer
+ const AidlDocument* result = parser.document_.get();
+ if (!typenames.AddDocument(std::move(parser.document_), is_preprocessed)) {
+ return nullptr;
+ }
+ return result;
}
void Parser::SetTypeParameters(AidlTypeSpecifier* type,
@@ -68,6 +73,21 @@
}
}
+void Parser::CheckValidTypeName(const AidlToken& token, const AidlLocation& loc) {
+ if (!is_preprocessed_ && token.GetText().find('.') != std::string::npos) {
+ AIDL_ERROR(loc) << "Type name can't be qualified. Use `package`.";
+ AddError();
+ }
+}
+
+void Parser::SetPackage(const AidlPackage& package) {
+ if (is_preprocessed_) {
+ AIDL_ERROR(package) << "Preprocessed file can't declare package.";
+ AddError();
+ }
+ package_ = package.GetName();
+}
+
class ReferenceResolver : public AidlVisitor {
public:
ReferenceResolver(const AidlDefinedType* scope, TypeResolver& resolver, bool* success)
@@ -162,9 +182,8 @@
return success;
}
-Parser::Parser(const std::string& filename, std::string& raw_buffer,
- android::aidl::AidlTypenames& typenames)
- : filename_(filename), typenames_(typenames) {
+Parser::Parser(const std::string& filename, std::string& raw_buffer, bool is_preprocessed)
+ : filename_(filename), is_preprocessed_(is_preprocessed) {
yylex_init(&scanner_);
buffer_ = yy_scan_buffer(&raw_buffer[0], raw_buffer.length(), scanner_);
}
diff --git a/parser.h b/parser.h
index 67668a0..f286de2 100644
--- a/parser.h
+++ b/parser.h
@@ -68,7 +68,7 @@
// Parse contents of file |filename|. Should only be called once.
static const AidlDocument* Parse(const std::string& filename,
const android::aidl::IoDelegate& io_delegate,
- AidlTypenames& typenames);
+ AidlTypenames& typenames, bool is_preprocessed = false);
void AddError() { error_++; }
bool HasError() const { return error_ != 0; }
@@ -85,30 +85,23 @@
void SetTypeParameters(AidlTypeSpecifier* type,
std::vector<std::unique_ptr<AidlTypeSpecifier>>* type_args);
- void SetPackage(const std::string& package) { package_ = package; }
+ // fully-qualified type names are allowed only in preprocessed files
+ void CheckValidTypeName(const AidlToken& token, const AidlLocation& loc);
+
+ void SetPackage(const AidlPackage& package);
const std::string& Package() const { return package_; }
- void SetDocument(std::unique_ptr<AidlDocument>&& document) {
- // The parsed document is owned by typenames_. This parser object only has
- // a reference to it.
- document_ = document.get();
- if (!typenames_.AddDocument(std::move(document))) {
- document_ = nullptr;
- AddError();
- }
- }
+ void SetDocument(std::unique_ptr<AidlDocument> document) { document_ = std::move(document); }
private:
- explicit Parser(const std::string& filename, std::string& raw_buffer,
- android::aidl::AidlTypenames& typenames);
+ explicit Parser(const std::string& filename, std::string& raw_buffer, bool is_preprocessed);
std::string filename_;
+ bool is_preprocessed_;
std::string package_;
- AidlTypenames& typenames_;
-
void* scanner_ = nullptr;
YY_BUFFER_STATE buffer_;
int error_ = 0;
- const AidlDocument* document_;
+ std::unique_ptr<AidlDocument> document_;
};
diff --git a/preprocess.cpp b/preprocess.cpp
new file mode 100644
index 0000000..643c871
--- /dev/null
+++ b/preprocess.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "preprocess.h"
+
+#include <android-base/strings.h>
+
+#include "aidl.h"
+
+using android::base::Join;
+
+namespace android {
+namespace aidl {
+
+namespace {
+// PreprocessVisitor emits
+// - type including comments(hide/deprecated) and annotations
+// - constant delcarations for interface/parcelable/unions
+// - enumerators for enums
+struct PreprocessVisitor : AidlVisitor {
+ CodeWriter& out;
+ PreprocessVisitor(CodeWriter& out) : out(out) {}
+
+ void DumpType(const AidlDefinedType& dt, const string& type) {
+ DumpComments(dt);
+ DumpAnnotations(dt);
+ out << type << " " << dt.GetCanonicalName();
+ if (auto generic_type = dt.AsParameterizable(); generic_type && generic_type->IsGeneric()) {
+ out << "<" << Join(generic_type->GetTypeParameters(), ", ") << ">";
+ }
+ }
+ void DumpMembers(const AidlDefinedType& dt) {
+ out << " {\n";
+ out.Indent();
+ for (const auto& constdecl : dt.GetConstantDeclarations()) {
+ constdecl->DispatchVisit(*this);
+ }
+ out.Dedent();
+ out << "}\n";
+ }
+ void DumpComments(const AidlCommentable& c) {
+ const auto hidden = c.IsHidden();
+ const auto deprecated = FindDeprecated(c.GetComments());
+ if (hidden || deprecated) {
+ out << "/**\n";
+ if (hidden) {
+ out << " * @hide\n";
+ }
+ if (deprecated) {
+ out << " * @deprecated " << deprecated->note << "\n";
+ }
+ out << " */\n";
+ }
+ }
+ void DumpAnnotations(const AidlAnnotatable& a) {
+ auto annotations = a.ToString();
+ if (!annotations.empty()) {
+ out << annotations << "\n";
+ }
+ }
+ void DumpConstantValue(const AidlTypeSpecifier& type, const AidlConstantValue& c) {
+ out << c.ValueString(type, AidlConstantValueDecorator);
+ }
+ void Visit(const AidlInterface& t) override {
+ DumpType(t, "interface");
+ DumpMembers(t);
+ }
+ void Visit(const AidlParcelable& t) override {
+ DumpType(t, "parcelable");
+ if (const auto& cpp_header = t.GetCppHeader(); !cpp_header.empty()) {
+ out << " cpp_header " << cpp_header;
+ }
+ out << ";\n";
+ }
+ void Visit(const AidlStructuredParcelable& t) override {
+ DumpType(t, "parcelable");
+ DumpMembers(t);
+ }
+ void Visit(const AidlUnionDecl& t) override {
+ DumpType(t, "union");
+ DumpMembers(t);
+ }
+ void Visit(const AidlEnumDeclaration& t) override {
+ DumpType(t, "enum");
+ out << " {\n";
+ out.Indent();
+ for (const auto& e : t.GetEnumerators()) {
+ out << e->GetName() << " = ";
+ DumpConstantValue(t.GetBackingType(), *e->GetValue());
+ out << ",\n";
+ }
+ out.Dedent();
+ out << "}\n";
+ }
+ void Visit(const AidlConstantDeclaration& c) override {
+ DumpComments(c);
+ out << "const ";
+ Visit(c.GetType());
+ out << " " << c.GetName() << " = ";
+ DumpConstantValue(c.GetType(), c.GetValue());
+ out << ";\n";
+ }
+ void Visit(const AidlTypeSpecifier& t) override { out << t.ToString(); }
+};
+
+} // namespace
+
+bool Preprocess(const Options& options, const IoDelegate& io_delegate) {
+ unique_ptr<CodeWriter> writer = io_delegate.GetCodeWriter(options.OutputFile());
+ PreprocessVisitor visitor(*writer);
+
+ for (const auto& file : options.InputFiles()) {
+ AidlTypenames typenames;
+ auto result =
+ internals::load_and_validate_aidl(file, options, io_delegate, &typenames, nullptr);
+ if (result == AidlError::OK || result == AidlError::FOUND_PARCELABLE) {
+ const auto& doc = typenames.MainDocument();
+ for (const auto& t : doc.DefinedTypes()) {
+ t->DispatchVisit(visitor);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ return writer->Close();
+}
+
+} // namespace aidl
+} // namespace android
\ No newline at end of file
diff --git a/preprocess.h b/preprocess.h
new file mode 100644
index 0000000..d734a68
--- /dev/null
+++ b/preprocess.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "aidl_language.h"
+#include "code_writer.h"
+
+namespace android {
+namespace aidl {
+
+bool Preprocess(const Options& options, const IoDelegate& io_delegate);
+
+} // namespace aidl
+} // namespace android
diff --git a/tests/fake_io_delegate.cpp b/tests/fake_io_delegate.cpp
index 5e912ce..4633282 100644
--- a/tests/fake_io_delegate.cpp
+++ b/tests/fake_io_delegate.cpp
@@ -57,16 +57,6 @@
return contents;
}
-unique_ptr<LineReader> FakeIoDelegate::GetLineReader(
- const string& file_path) const {
- unique_ptr<LineReader> ret;
- const auto& it = file_contents_.find(CleanPath(file_path));
- if (it != file_contents_.cend()) {
- ret = LineReader::ReadFromMemory(it->second);
- }
- return ret;
-}
-
bool FakeIoDelegate::FileIsReadable(const string& path) const {
return file_contents_.find(CleanPath(path)) != file_contents_.end();
}
diff --git a/tests/fake_io_delegate.h b/tests/fake_io_delegate.h
index 0e278f4..951c32e 100644
--- a/tests/fake_io_delegate.h
+++ b/tests/fake_io_delegate.h
@@ -42,8 +42,6 @@
std::unique_ptr<std::string> GetFileContents(
const std::string& filename,
const std::string& append_content_suffix = "") const override;
- std::unique_ptr<LineReader> GetLineReader(
- const std::string& file_path) const override;
bool FileIsReadable(const std::string& path) const override;
std::unique_ptr<CodeWriter> GetCodeWriter(
const std::string& file_path) const override;