Snap for 6001391 from 4797d5b58a1ec5592b4dd37531f65d7522fc9a4a to qt-aml-resolv-release

Change-Id: Iefa9f21d046ca5959df9b84765b7cebd9d08842b
diff --git a/Android.bp b/Android.bp
index fa5d485..9a59d4d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,7 +110,6 @@
         "-Wextra",
         "-Werror",
         "-g",
-        "-DUNIT_TEST",
     ],
 
     srcs: [
@@ -134,20 +133,51 @@
         "libaidl-common",
         "libbase",
         "libcutils",
+        "libgmock",
     ],
     target: {
-        host: {
-            static_libs: ["libgmock_host"],
-        },
         android: {
             static_libs: [
-                "libgmock",
                 "liblog",
             ],
         },
     },
 }
 
+cc_fuzz {
+    name: "aidl_parser_fuzzer",
+    host_supported: true,
+    dictionary: "tests/aidl_parser_fuzzer.dict",
+
+    fuzz_config: {
+        cc: [
+            "smoreland@google.com",
+            "jiyong@google.com",
+            "jeongik@google.com",
+        ],
+    },
+
+    srcs: [
+        "tests/aidl_parser_fuzzer.cpp",
+        "tests/fake_io_delegate.cpp",
+        "tests/test_util.cpp",
+    ],
+    static_libs: [
+        "libaidl-common",
+        "libbase",
+        "libcutils",
+    ],
+    target: {
+        android: {
+            static_libs: [
+                "liblog",
+            ],
+        },
+    },
+    // Enable this to show additional information about what is being parsed during fuzzing.
+    // cflags: ["-DFUZZ_LOG"],
+}
+
 //
 // Everything below here is used for integration testing of generated AIDL code.
 //
diff --git a/aidl_const_expressions.cpp b/aidl_const_expressions.cpp
index 8214ac8..dbd1f8b 100644
--- a/aidl_const_expressions.cpp
+++ b/aidl_const_expressions.cpp
@@ -306,19 +306,23 @@
   return new AidlConstantValue(location, Type::STRING, value);
 }
 
-AidlConstantValue* AidlConstantValue::ShallowCopy(const AidlConstantValue& other) {
-  switch (other.GetType()) {
-    case Type::INT8:   // fall-through
-    case Type::INT32:  // fall-through
-    case Type::INT64:
-      return Integral(
-          AIDL_LOCATION_HERE,
-          other.ValueString(AidlTypeSpecifier(AIDL_LOCATION_HERE, "long", false, nullptr, ""),
-                            AidlConstantValueDecorator));
-    default:
-      AIDL_FATAL(other) << "Unsupported type for ShallowCopy: " << ToString(other.GetType());
-      return nullptr;
+AidlConstantValue* AidlConstantValue::ShallowIntegralCopy(const AidlConstantValue& other) {
+  // TODO(b/142894901): Perform full proper copy
+  AidlTypeSpecifier type = AidlTypeSpecifier(AIDL_LOCATION_HERE, "long", false, nullptr, "");
+  // TODO(b/142722772) CheckValid() should be called before ValueString()
+  if (!other.CheckValid()) {
+    AIDL_FATAL(other) << "Invalid value for ShallowIntegralCopy";
   }
+  if (!other.evaluate(type)) {
+    AIDL_FATAL(other) << "Unsupported type for ShallowIntegralCopy: " << ToString(other.GetType());
+  }
+
+  AidlConstantValue* result =
+      Integral(AIDL_LOCATION_HERE, other.ValueString(type, AidlConstantValueDecorator));
+  if (result == nullptr) {
+    AIDL_FATAL(other) << "Unable to perform ShallowIntegralCopy.";
+  }
+  return result;
 }
 
 string AidlConstantValue::ValueString(const AidlTypeSpecifier& type,
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 945abc4..e27dbbe 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -769,7 +769,7 @@
         enumerator->SetValue(std::make_unique<AidlBinaryConstExpression>(
             AIDL_LOCATION_HERE,
             std::unique_ptr<AidlConstantValue>(
-                AidlConstantValue::ShallowCopy(*previous->GetValue())),
+                AidlConstantValue::ShallowIntegralCopy(*previous->GetValue())),
             "+",
             std::unique_ptr<AidlConstantValue>(
                 AidlConstantValue::Integral(AIDL_LOCATION_HERE, "1"))));
diff --git a/aidl_language.h b/aidl_language.h
index 6df25eb..ca2e359 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -399,9 +399,9 @@
   // example: "\"asdf\""
   static AidlConstantValue* String(const AidlLocation& location, const string& value);
 
-  // Construct an AidlConstantValue by evaluating the other constant's value
-  // string. This does not preserve the structure of the copied constant.
-  static AidlConstantValue* ShallowCopy(const AidlConstantValue& other);
+  // Construct an AidlConstantValue by evaluating the other integral constant's
+  // value string. This does not preserve the structure of the copied constant.
+  static AidlConstantValue* ShallowIntegralCopy(const AidlConstantValue& other);
 
   Type GetType() const { return final_type_; }
 
diff --git a/aidl_language_y.yy b/aidl_language_y.yy
index 79eb1a0..c9df5e8 100644
--- a/aidl_language_y.yy
+++ b/aidl_language_y.yy
@@ -18,6 +18,7 @@
 #include "aidl_language.h"
 #include "aidl_language_y.h"
 #include "logging.h"
+#include <android-base/parseint.h>
 #include <set>
 #include <map>
 #include <stdio.h>
@@ -158,7 +159,7 @@
 %type<annotation_list>annotation_list
 %type<type> type
 %type<type> unannotated_type
-%type<arg_list> arg_list
+%type<arg_list> arg_list arg_non_empty_list
 %type<arg> arg
 %type<direction> direction
 %type<type_args> type_args
@@ -212,10 +213,14 @@
 
 decls
  : decl {
-    ps->AddDefinedType(unique_ptr<AidlDefinedType>($1));
+    if ($1 != nullptr) {
+      ps->AddDefinedType(unique_ptr<AidlDefinedType>($1));
+    }
   }
  | decls decl {
-    ps->AddDefinedType(unique_ptr<AidlDefinedType>($2));
+    if ($2 != nullptr) {
+      ps->AddDefinedType(unique_ptr<AidlDefinedType>($2));
+    }
   };
 
 decl
@@ -223,12 +228,12 @@
    {
     $$ = $2;
 
-    if ($1->size() > 0) {
+    if ($1->size() > 0 && $$ != nullptr) {
       // copy comments from annotation to decl
-      $2->SetComments($1->begin()->GetComments());
+      $$->SetComments($1->begin()->GetComments());
+      $$->Annotate(std::move(*$1));
     }
 
-    $$->Annotate(std::move(*$1));
     delete $1;
    }
  ;
@@ -307,7 +312,6 @@
     ps->AddError();
     $$ = nullptr;
     delete $1;
-    delete $2;
     delete $4;
   };
 
@@ -430,10 +434,9 @@
   }
  | '(' error ')'
    {
-     std::cerr << "ERROR: invalid const expression within parenthesis: "
-               << $2->GetText() << " at " << @1 << ".\n";
-     // to avoid segfaults
+     std::cerr << "ERROR: invalid const expression within parenthesis at " << @1 << ".\n";
      ps->AddError();
+     // to avoid segfaults
      $$ = AidlConstantValue::Integral(loc(@1), "0");
    }
  ;
@@ -515,13 +518,23 @@
     delete $4;
   }
  | type identifier '(' arg_list ')' '=' INTVALUE ';' {
-    $$ = new AidlMethod(loc(@2), false, $1, $2->GetText(), $4, $1->GetComments(), std::stoi($7->GetText()));
+    int32_t serial;
+    if (!android::base::ParseInt($7->GetText(), &serial)) {
+        AIDL_ERROR(loc(@7)) << "Could not parse int value: " << $7->GetText();
+        ps->AddError();
+    }
+    $$ = new AidlMethod(loc(@2), false, $1, $2->GetText(), $4, $1->GetComments(), serial);
     delete $2;
     delete $7;
   }
  | annotation_list ONEWAY type identifier '(' arg_list ')' '=' INTVALUE ';' {
     const std::string& comments = ($1->size() > 0) ? $1->begin()->GetComments() : $2->GetComments();
-    $$ = new AidlMethod(loc(@4), true, $3, $4->GetText(), $6, comments, std::stoi($9->GetText()));
+    int32_t serial;
+    if (!android::base::ParseInt($9->GetText(), &serial)) {
+        AIDL_ERROR(loc(@9)) << "Could not parse int value: " << $9->GetText();
+        ps->AddError();
+    }
+    $$ = new AidlMethod(loc(@4), true, $3, $4->GetText(), $6, comments, serial);
     $3->Annotate(std::move(*$1));
     delete $1;
     delete $2;
@@ -529,18 +542,22 @@
     delete $9;
   };
 
-arg_list
- :
-  { $$ = new std::vector<std::unique_ptr<AidlArgument>>(); }
- | arg {
+arg_non_empty_list
+ : arg {
     $$ = new std::vector<std::unique_ptr<AidlArgument>>();
     $$->push_back(std::unique_ptr<AidlArgument>($1));
   }
- | arg_list ',' arg {
+ | arg_non_empty_list ',' arg {
     $$ = $1;
     $$->push_back(std::unique_ptr<AidlArgument>($3));
   };
 
+arg_list
+ : /*empty*/
+   { $$ = new std::vector<std::unique_ptr<AidlArgument>>(); }
+ | arg_non_empty_list { $$ = $1; }
+ ;
+
 arg
  : direction type identifier {
     $$ = new AidlArgument(loc(@3), $1, $2, $3->GetText());
@@ -636,18 +653,20 @@
  : ANNOTATION
   {
     $$ = AidlAnnotation::Parse(loc(@1), $1->GetText(), nullptr);
-    if ($$ == nullptr) {
+    if ($$) {
+      $$->SetComments($1->GetComments());
+    } else {
       ps->AddError();
     }
-    $$->SetComments($1->GetComments());
     delete $1;
   };
  | ANNOTATION '(' parameter_list ')' {
     $$ = AidlAnnotation::Parse(loc(@1), $1->GetText(), $3);
-    if ($$ == nullptr) {
+    if ($$) {
+      $$->SetComments($1->GetComments());
+    } else {
       ps->AddError();
     }
-    $$->SetComments($1->GetComments());
     delete $1;
     delete $3;
  }
diff --git a/build/aidl_interface.go b/build/aidl_interface.go
index 6cca62b..56534e7 100644
--- a/build/aidl_interface.go
+++ b/build/aidl_interface.go
@@ -713,12 +713,10 @@
 }
 
 func (i *aidlInterface) currentVersion(ctx android.BaseModuleContext) string {
-	if len(i.properties.Versions) == 0 {
+	if !i.hasVersion() {
 		return ""
 	} else {
-		latestVersion := i.properties.Versions[len(i.properties.Versions)-1]
-
-		i, err := strconv.Atoi(latestVersion)
+		i, err := strconv.Atoi(i.latestVersion())
 		if err != nil {
 			ctx.PropertyErrorf("versions", "must be integers")
 			return ""
@@ -728,16 +726,51 @@
 	}
 }
 
+func (i *aidlInterface) latestVersion() string {
+	return i.properties.Versions[len(i.properties.Versions)-1]
+}
+func (i *aidlInterface) isLatestVersion(version string) bool {
+	if !i.hasVersion() {
+		return true
+	}
+	return version == i.latestVersion()
+}
+func (i *aidlInterface) hasVersion() bool {
+	return len(i.properties.Versions) > 0
+}
+
 func (i *aidlInterface) isCurrentVersion(ctx android.BaseModuleContext, version string) bool {
 	return version == i.currentVersion(ctx)
 }
 
+// This function returns module name with version. Assume that there is foo of which latest version is 2
+// Version -> Module name
+// "1"->foo-V1
+// "2"->foo-V2
+// "3"(unfrozen)->foo-unstable
+// ""-> foo
 func (i *aidlInterface) versionedName(ctx android.BaseModuleContext, version string) string {
 	name := i.ModuleBase.Name()
-	if !i.isCurrentVersion(ctx, version) {
-		name = name + "-V" + version
+	if version == "" {
+		return name
 	}
-	return name
+	if i.isCurrentVersion(ctx, version) {
+		return name + "-unstable"
+	}
+	return name + "-V" + version
+}
+
+// The only difference between versionedName and cppOutputName is about unstable version
+// foo-unstable -> foo-V3
+func (i *aidlInterface) cppOutputName(version string) string {
+	name := i.ModuleBase.Name()
+	if !i.hasVersion() {
+		return name
+	}
+	if version == "" {
+		version = i.latestVersion()
+	}
+	return name + "-V" + version
 }
 
 func (i *aidlInterface) srcsForVersion(mctx android.LoadHookContext, version string) (srcs []string, base string) {
@@ -784,9 +817,16 @@
 
 	currentVersion := i.currentVersion(mctx)
 
+	versionsForCpp := make([]string, len(i.properties.Versions))
+	copy(versionsForCpp, i.properties.Versions)
+	if i.hasVersion() {
+		// In C++ library, AIDL doesn't create the module of which name is with latest version,
+		// instead of it, there is a module without version.
+		versionsForCpp[len(i.properties.Versions)-1] = ""
+	}
 	if i.shouldGenerateCppBackend() {
 		libs = append(libs, addCppLibrary(mctx, i, currentVersion, langCpp))
-		for _, version := range i.properties.Versions {
+		for _, version := range versionsForCpp {
 			addCppLibrary(mctx, i, version, langCpp)
 		}
 	}
@@ -795,20 +835,23 @@
 		// TODO(b/119771576): inherit properties and export 'is vendor' computation from cc.go
 		if !proptools.Bool(i.properties.Vendor_available) {
 			libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdk))
-			for _, version := range i.properties.Versions {
+			for _, version := range versionsForCpp {
 				addCppLibrary(mctx, i, version, langNdk)
 			}
 		}
 		// TODO(b/121157555): combine with '-ndk' variant
 		libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdkPlatform))
-		for _, version := range i.properties.Versions {
+		for _, version := range versionsForCpp {
 			addCppLibrary(mctx, i, version, langNdkPlatform)
 		}
 	}
-
+	versionsForJava := i.properties.Versions
+	if i.hasVersion() {
+		versionsForJava = append(i.properties.Versions, "")
+	}
 	if i.shouldGenerateJavaBackend() {
 		libs = append(libs, addJavaLibrary(mctx, i, currentVersion))
-		for _, version := range i.properties.Versions {
+		for _, version := range versionsForJava {
 			addJavaLibrary(mctx, i, version)
 		}
 	}
@@ -825,7 +868,10 @@
 func addCppLibrary(mctx android.LoadHookContext, i *aidlInterface, version string, lang string) string {
 	cppSourceGen := i.versionedName(mctx, version) + "-" + lang + "-source"
 	cppModuleGen := i.versionedName(mctx, version) + "-" + lang
-
+	cppOutputGen := i.cppOutputName(version) + "-" + lang
+	if i.hasVersion() && version == "" {
+		version = i.latestVersion()
+	}
 	srcs, base := i.srcsForVersion(mctx, version)
 	if len(srcs) == 0 {
 		// This can happen when the version is about to be frozen; the version
@@ -904,6 +950,7 @@
 		Stl:                       stl,
 		Cpp_std:                   cpp_std,
 		Cflags:                    append(addCflags, "-Wextra", "-Wall", "-Werror"),
+		Stem:                      proptools.StringPtr(cppOutputGen),
 	}, &i.properties.VndkProperties)
 
 	return cppModuleGen
@@ -912,7 +959,9 @@
 func addJavaLibrary(mctx android.LoadHookContext, i *aidlInterface, version string) string {
 	javaSourceGen := i.versionedName(mctx, version) + "-java-source"
 	javaModuleGen := i.versionedName(mctx, version) + "-java"
-
+	if i.hasVersion() && version == "" {
+		version = i.latestVersion()
+	}
 	srcs, base := i.srcsForVersion(mctx, version)
 	if len(srcs) == 0 {
 		// This can happen when the version is about to be frozen; the version
diff --git a/build/properties.go b/build/properties.go
index b59db61..a1b3489 100644
--- a/build/properties.go
+++ b/build/properties.go
@@ -43,6 +43,7 @@
 	Stl                       *string
 	Cpp_std                   *string
 	Cflags                    []string
+	Stem                      *string
 }
 
 type javaProperties struct {
diff --git a/generate_cpp_unittest.cpp b/generate_cpp_unittest.cpp
index 4d8ed1e..fd6c9f0 100644
--- a/generate_cpp_unittest.cpp
+++ b/generate_cpp_unittest.cpp
@@ -1383,8 +1383,13 @@
   ZERO,
   ONE,
   THREE = 3,
-  FOUR = 4,
+  FOUR = 3 + 1,
   FIVE,
+  SIX,
+  SEVEN,
+  EIGHT = 16 / 2,
+  NINE,
+  TEN,
 })";
 
 const char kExpectedEnumHeaderOutput[] =
@@ -1403,6 +1408,11 @@
   THREE = 3,
   FOUR = 4,
   FIVE = 5,
+  SIX = 6,
+  SEVEN = 7,
+  EIGHT = 8,
+  NINE = 9,
+  TEN = 10,
 };
 
 }  // namespace os
diff --git a/generate_ndk.cpp b/generate_ndk.cpp
index 60c93d6..fc9cc20 100644
--- a/generate_ndk.cpp
+++ b/generate_ndk.cpp
@@ -170,7 +170,11 @@
 
 static void GenerateHeaderIncludes(CodeWriter& out, const AidlTypenames& types,
                                    const AidlDefinedType& defined_type) {
-  out << "#include <android/binder_parcel_utils.h>\n";
+  out << "#include <cstdint>\n";
+  out << "#include <memory>\n";
+  out << "#include <optional>\n";
+  out << "#include <string>\n";
+  out << "#include <vector>\n";
   out << "#ifdef BINDER_STABILITY_SUPPORT\n";
   out << "#include <android/binder_stability.h>\n";
   out << "#endif  // BINDER_STABILITY_SUPPORT\n";
@@ -196,6 +200,8 @@
 }
 static void GenerateSourceIncludes(CodeWriter& out, const AidlTypenames& types,
                                    const AidlDefinedType& /*defined_type*/) {
+  out << "#include <android/binder_parcel_utils.h>\n";
+
   types.IterateTypes([&](const AidlDefinedType& a_defined_type) {
     if (a_defined_type.AsInterface() != nullptr) {
       out << "#include <" << NdkHeaderFile(a_defined_type, ClassNames::CLIENT, false /*use_os_sep*/)
diff --git a/options.cpp b/options.cpp
index 8bd2887..fa34a9d 100644
--- a/options.cpp
+++ b/options.cpp
@@ -39,7 +39,7 @@
 string Options::GetUsage() const {
   std::ostringstream sstr;
   sstr << "usage:" << endl
-       << myname_ << " --lang={java|cpp} [OPTION]... INPUT..." << endl
+       << myname_ << " --lang={java|cpp|ndk} [OPTION]... INPUT..." << endl
        << "   Generate Java or C++ files for AIDL file(s)." << endl
        << endl
        << myname_ << " --preprocess OUTPUT INPUT..." << endl
diff --git a/tests/aidl_parser_fuzzer.cpp b/tests/aidl_parser_fuzzer.cpp
new file mode 100644
index 0000000..a3269ce
--- /dev/null
+++ b/tests/aidl_parser_fuzzer.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 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 "aidl.h"
+#include "fake_io_delegate.h"
+#include "options.h"
+
+#include <iostream>
+
+#ifdef FUZZ_LOG
+constexpr bool kFuzzLog = true;
+#else
+constexpr bool kFuzzLog = false;
+#endif
+
+using android::aidl::test::FakeIoDelegate;
+
+void fuzz(uint8_t options, const std::string& content) {
+  uint8_t lang = options & 0x3;
+  std::string langOpt;
+  switch (lang) {
+    case 1:
+      langOpt = "cpp";
+      break;
+    case 2:
+      langOpt = "ndk";
+      break;
+    case 3:
+      langOpt = "java";
+      break;
+    default:
+      return;
+  }
+
+  // TODO: fuzz multiple files
+  // TODO: fuzz arguments
+  FakeIoDelegate io;
+  io.SetFileContents("a/path/Foo.aidl", content);
+
+  std::vector<std::string> args;
+  args.emplace_back("aidl");
+  args.emplace_back("--lang=" + langOpt);
+  args.emplace_back("-b");
+  args.emplace_back("-I .");
+  args.emplace_back("-o out");
+  // corresponding items also in aidl_parser_fuzzer.dict
+  args.emplace_back("a/path/Foo.aidl");
+
+  if (kFuzzLog) {
+    std::cout << "lang: " << langOpt << " content: " << content << std::endl;
+  }
+
+  int ret = android::aidl::compile_aidl(Options::From(args), io);
+  if (ret != 0) return;
+
+  if (kFuzzLog) {
+    for (const std::string& f : io.ListOutputFiles()) {
+      std::string output;
+      if (io.GetWrittenContents(f, &output)) {
+        std::cout << "OUTPUT " << f << ": " << std::endl;
+        std::cout << output << std::endl;
+      }
+    }
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size <= 1) return 0;  // no use
+
+  uint8_t options = *data;
+  data++;
+  size--;
+
+  std::string content(reinterpret_cast<const char*>(data), size);
+  fuzz(options, content);
+
+  return 0;
+}
diff --git a/tests/aidl_parser_fuzzer.dict b/tests/aidl_parser_fuzzer.dict
new file mode 100644
index 0000000..1f882a0
--- /dev/null
+++ b/tests/aidl_parser_fuzzer.dict
@@ -0,0 +1,62 @@
+# keywords
+" parcelable "
+" import "
+" package "
+" in "
+" out "
+" inout "
+" cpp_header "
+" const "
+" true "
+" false "
+" interface "
+" oneway "
+" enum "
+
+# types
+" void "
+" boolean "
+" byte "
+" char "
+" int "
+" long "
+" float "
+" double "
+" String "
+" List "
+" Map "
+" IBinder "
+" FileDescriptor "
+" CharSequence "
+" ParcelFileDescriptor "
+" java.util.List "
+" java.util.Map "
+" android.os.ParcelFileDescriptor "
+
+# annotations
+" @nullable "
+" @utf8InCpp "
+" @VintfStability "
+" @UnsupportedAppUsage "
+" @SystemApi "
+" @JavaOnlyStableParcelable "
+" @Backing "
+" expectedSignature "
+" implicitMember "
+" maxTargetSdk "
+" publicAlternatives "
+" trackingBug "
+" type "
+
+# ignorable imports
+" android.os.IInterface "
+" android.os.IBinder "
+" android.os.Parcelable "
+" android.os.Parcel "
+" android.content.Context "
+" java.lang.String "
+
+# specific to fuzzer
+" a.path "
+" Foo "
+
diff --git a/tests/fake_io_delegate.cpp b/tests/fake_io_delegate.cpp
index 682ea57..dc35c4d 100644
--- a/tests/fake_io_delegate.cpp
+++ b/tests/fake_io_delegate.cpp
@@ -151,6 +151,14 @@
   return true;
 }
 
+std::vector<std::string> FakeIoDelegate::ListOutputFiles() {
+  std::vector<std::string> out;
+  for (const auto& [file, contents] : written_file_contents_) {
+    out.push_back(file);
+  }
+  return out;
+}
+
 bool FakeIoDelegate::PathWasRemoved(const std::string& path) {
   if (removed_files_.count(path) > 0) {
     return true;
diff --git a/tests/fake_io_delegate.h b/tests/fake_io_delegate.h
index 68a9626..f077c0f 100644
--- a/tests/fake_io_delegate.h
+++ b/tests/fake_io_delegate.h
@@ -60,6 +60,7 @@
   // Returns true iff we've previously written to |path|.
   // When we return true, we'll set *contents to the written string.
   bool GetWrittenContents(const std::string& path, std::string* content);
+  std::vector<std::string> ListOutputFiles();
 
   bool PathWasRemoved(const std::string& path);