Support @nullable(heap) for recursive types
@nullable(heap=true) marks a parcelable field to be held in a heap
allocated wrapper like unique_ptr<T> in C++ and Box<T> in Rust.
This enables recursive parcelable types like following:
parcelable Recursive {
@nullable(heap=true) Recursive r;
}
Note `r` is represented in Option<Box<Recursive>> in Rust because Box<T>
should hold a value always.
Bug: 188690695
Test: aidl_integration_test
Change-Id: Ic9b209da73c1395785c078aeecd34fda74e42e7d
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 6484fd0..cfa5d4a 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -257,6 +257,63 @@
EXPECT_EQ(expected_stderr, GetCapturedStderr());
}
+TEST_P(AidlTest, RejectRecursiveParcelable) {
+ CaptureStderr();
+ EXPECT_EQ(nullptr, Parse("Foo.aidl", "parcelable Foo { Foo foo; }", typenames_, GetLanguage()));
+ EXPECT_THAT(GetCapturedStderr(), HasSubstr("Foo is a recursive parcelable"));
+}
+
+TEST_P(AidlTest, RejectIndirectRecursiveParcelable) {
+ io_delegate_.SetFileContents("Bar.aidl", "parcelable Bar { Foo foo; }");
+ import_paths_.emplace("");
+ CaptureStderr();
+ EXPECT_EQ(nullptr, Parse("Foo.aidl", "parcelable Foo { Bar bar; }", typenames_, GetLanguage()));
+ EXPECT_THAT(GetCapturedStderr(), HasSubstr("Foo is a recursive parcelable"));
+}
+
+TEST_P(AidlTest, RejectRecursiveTypeEvenIfNullable) {
+ // Note: in native backends @nullable is mapped to non-heap wrapper like std::optional/Option<T>
+ io_delegate_.SetFileContents("Bar.aidl", "parcelable Bar { @nullable Foo foo; }");
+ import_paths_.emplace("");
+ CaptureStderr();
+ EXPECT_EQ(nullptr, Parse("Foo.aidl", "parcelable Foo { Bar bar; }", typenames_, GetLanguage()));
+ EXPECT_THAT(GetCapturedStderr(), HasSubstr("Foo is a recursive parcelable"));
+}
+
+TEST_P(AidlTest, OkayIfRecursionInvolvesHeapType) {
+ CaptureStderr();
+ std::string java_only_map_field;
+ if (GetLanguage() == Options::Language::JAVA) {
+ java_only_map_field = " Map<String, Foo> map;\n";
+ }
+ EXPECT_NE(nullptr, Parse("Foo.aidl",
+ "parcelable Foo {\n"
+ " List<Foo> list;\n" +
+ java_only_map_field +
+ " Foo[] arr;\n"
+ " @nullable(heap=true) Foo heap_nullable;\n"
+ "}\n",
+ typenames_, GetLanguage()));
+ EXPECT_THAT(GetCapturedStderr(), "");
+}
+
+TEST_P(AidlTest, InterfaceCanReferenceItself) {
+ CaptureStderr();
+ EXPECT_NE(nullptr, Parse("IFoo.aidl", "interface IFoo { void foo(in IFoo self); }", typenames_,
+ GetLanguage()));
+ EXPECT_THAT(GetCapturedStderr(), "");
+}
+
+TEST_P(AidlTest, HeapNullableCantApplyToOtherThanParcelables) {
+ CaptureStderr();
+ EXPECT_EQ(nullptr, Parse("Foo.aidl",
+ "parcelable Foo {\n"
+ " @nullable(heap=true) String s;\n"
+ "}",
+ typenames_, GetLanguage()));
+ EXPECT_THAT(GetCapturedStderr(), HasSubstr("@nullable(heap=true) is available to parcelables"));
+}
+
TEST_P(AidlTest, RejectsDuplicatedArgumentNames) {
const string method = "package a; interface IFoo { void f(int a, int a); }";
const string expected_stderr =
@@ -1371,15 +1428,8 @@
}
TEST_F(AidlTest, CppNameOf_GenericType) {
- io_delegate_.SetFileContents("p/Wrapper.aidl", "package p; parcelable Wrapper<T> { T wrapped; }");
- import_paths_.emplace("");
- // Since we don't support compilation of Wrapper directly (due to "T" reference),
- // prepare Holder so that Wrapper gets parsed into AidlTypenames
- const string input_path = "p/Holder.aidl";
- const string input =
- "package p; import p.Wrapper; parcelable Holder {\n"
- " @nullable Wrapper<String> value;\n"
- "}";
+ const string input_path = "p/Wrapper.aidl";
+ const string input = "package p; parcelable Wrapper<T> {}";
auto parse_result = Parse(input_path, input, typenames_, Options::Language::CPP);
EXPECT_NE(nullptr, parse_result);