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;