union: java backend
union is a parcelable which can hold only a single field with a tag.
Example:
union Union {
int n;
String s;
}
In Java, you can instantiate it with
- default constructor: init with first field
- value constructor: Union.n(42) or Union.s("abc")
You can query "tag" before getting the contents from it.
It also supports getter/setter.
Example:
void foo(Union u) {
if (u.getTag() == Union.n) { // query
int n = u.getN(); // getter
...
}
u.setS("abc"); // setter
}
Bug: 150948558
Test: atest aidl_integration_test
Change-Id: I5c2d87e09462c0d3c6617d73fdf0e49c281d551e
diff --git a/aidl.cpp b/aidl.cpp
index 9250ba1..2f00a12 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -424,11 +424,11 @@
if (main_parser == nullptr) {
return AidlError::PARSE_ERROR;
}
- int num_interfaces_or_structured_parcelables = 0;
+ int num_top_level_decls = 0;
for (const auto& type : main_parser->ParsedDocument().DefinedTypes()) {
- if (type->AsInterface() != nullptr || type->AsStructuredParcelable() != nullptr) {
- num_interfaces_or_structured_parcelables++;
- if (num_interfaces_or_structured_parcelables > 1) {
+ if (type->AsUnstructuredParcelable() == nullptr) {
+ num_top_level_decls++;
+ if (num_top_level_decls > 1) {
AIDL_ERROR(*type) << "You must declare only one type per file.";
return AidlError::BAD_TYPE;
}
@@ -578,8 +578,10 @@
AidlStructuredParcelable* parcelable = defined_type->AsStructuredParcelable();
AidlParcelable* unstructured_parcelable = defined_type->AsUnstructuredParcelable();
AidlEnumDeclaration* enum_decl = defined_type->AsEnumDeclaration();
- AIDL_FATAL_IF(!!interface + !!parcelable + !!unstructured_parcelable + !!enum_decl != 1,
- defined_type);
+ AidlUnionDecl* union_decl = defined_type->AsUnionDeclaration();
+ AIDL_FATAL_IF(
+ !!interface + !!parcelable + !!unstructured_parcelable + !!enum_decl + !!union_decl != 1,
+ defined_type);
// Ensure that foo.bar.IFoo is defined in <some_path>/foo/bar/IFoo.aidl
if (num_defined_types == 1 && !check_filename(input_file_name, *defined_type)) {
diff --git a/aidl_checkapi.cpp b/aidl_checkapi.cpp
index 0f2f4b0..f20b5ca 100644
--- a/aidl_checkapi.cpp
+++ b/aidl_checkapi.cpp
@@ -182,8 +182,8 @@
return specifier.IsNullable();
}
-static bool are_compatible_parcelables(const AidlStructuredParcelable& older,
- const AidlStructuredParcelable& newer) {
+template <typename ParcelableType>
+static bool are_compatible_parcelables(const ParcelableType& older, const ParcelableType& newer) {
const auto& old_fields = older.GetFields();
const auto& new_fields = newer.GetFields();
if (old_fields.size() > new_fields.size()) {
@@ -370,6 +370,16 @@
}
compatible &= are_compatible_parcelables(*(old_type->AsStructuredParcelable()),
*(new_type->AsStructuredParcelable()));
+ } else if (old_type->AsUnionDeclaration() != nullptr) {
+ if (new_type->AsUnionDeclaration() == nullptr) {
+ AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
+ << " is changed from " << old_type->GetPreprocessDeclarationName()
+ << " to " << new_type->GetPreprocessDeclarationName();
+ compatible = false;
+ continue;
+ }
+ compatible &= are_compatible_parcelables(*(old_type->AsUnionDeclaration()),
+ *(new_type->AsUnionDeclaration()));
} else if (old_type->AsEnumDeclaration() != nullptr) {
if (new_type->AsEnumDeclaration() == nullptr) {
AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
diff --git a/aidl_language.cpp b/aidl_language.cpp
index b9985e5..04a4493 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -1110,6 +1110,32 @@
writer->Write("}\n");
}
+AidlUnionDecl::AidlUnionDecl(const AidlLocation& location, const std::string& name,
+ const std::string& package, const std::string& comments,
+ std::vector<std::unique_ptr<AidlVariableDeclaration>>* variables,
+ std::vector<std::string>* type_params)
+ : AidlParcelable(location, name, package, comments, "" /*cpp_header*/, type_params),
+ variables_(std::move(*variables)) {}
+
+std::set<AidlAnnotation::Type> AidlUnionDecl::GetSupportedAnnotations() const {
+ return {AidlAnnotation::Type::VINTF_STABILITY, AidlAnnotation::Type::HIDE,
+ AidlAnnotation::Type::JAVA_PASSTHROUGH};
+}
+
+void AidlUnionDecl::Dump(CodeWriter* writer) const {
+ DumpHeader(writer);
+ writer->Write("union %s {\n", GetName().c_str());
+ writer->Indent();
+ for (const auto& field : GetFields()) {
+ if (field->GetType().IsHidden()) {
+ AddHideComment(writer);
+ }
+ writer->Write("%s;\n", field->ToString().c_str());
+ }
+ writer->Dedent();
+ writer->Write("}\n");
+}
+
// TODO: we should treat every backend all the same in future.
bool AidlInterface::LanguageSpecificCheckValid(const AidlTypenames& typenames,
Options::Language lang) const {
diff --git a/aidl_language.h b/aidl_language.h
index a648232..6a5932d 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -646,6 +646,8 @@
class AidlInterface;
class AidlParcelable;
class AidlStructuredParcelable;
+
+class AidlUnionDecl;
// AidlDefinedType represents either an interface, parcelable, or enum that is
// defined in the source file.
class AidlDefinedType : public AidlAnnotatable {
@@ -676,6 +678,7 @@
virtual const AidlStructuredParcelable* AsStructuredParcelable() const { return nullptr; }
virtual const AidlParcelable* AsParcelable() const { return nullptr; }
virtual const AidlEnumDeclaration* AsEnumDeclaration() const { return nullptr; }
+ virtual const AidlUnionDecl* AsUnionDeclaration() const { return nullptr; }
virtual const AidlInterface* AsInterface() const { return nullptr; }
virtual const AidlParameterizable<std::string>* AsParameterizable() const { return nullptr; }
bool CheckValid(const AidlTypenames& typenames) const override;
@@ -692,6 +695,10 @@
return const_cast<AidlEnumDeclaration*>(
const_cast<const AidlDefinedType*>(this)->AsEnumDeclaration());
}
+ AidlUnionDecl* AsUnionDeclaration() {
+ return const_cast<AidlUnionDecl*>(
+ const_cast<const AidlDefinedType*>(this)->AsUnionDeclaration());
+ }
AidlInterface* AsInterface() {
return const_cast<AidlInterface*>(const_cast<const AidlDefinedType*>(this)->AsInterface());
}
@@ -703,6 +710,7 @@
const AidlParcelable* AsUnstructuredParcelable() const {
if (this->AsStructuredParcelable() != nullptr) return nullptr;
+ if (this->AsUnionDeclaration() != nullptr) return nullptr;
return this->AsParcelable();
}
AidlParcelable* AsUnstructuredParcelable() {
@@ -846,6 +854,40 @@
std::unique_ptr<const AidlTypeSpecifier> backing_type_;
};
+class AidlUnionDecl : public AidlParcelable {
+ public:
+ AidlUnionDecl(const AidlLocation& location, const std::string& name, const std::string& package,
+ const std::string& comments,
+ std::vector<std::unique_ptr<AidlVariableDeclaration>>* variables,
+ std::vector<std::string>* type_params);
+ virtual ~AidlUnionDecl() = default;
+
+ // non-copyable, non-movable
+ AidlUnionDecl(const AidlUnionDecl&) = delete;
+ AidlUnionDecl(AidlUnionDecl&&) = delete;
+ AidlUnionDecl& operator=(const AidlUnionDecl&) = delete;
+ AidlUnionDecl& operator=(AidlUnionDecl&&) = delete;
+
+ std::set<AidlAnnotation::Type> GetSupportedAnnotations() const override;
+
+ const AidlNode& AsAidlNode() const override { return *this; }
+
+ const std::vector<std::unique_ptr<AidlVariableDeclaration>>& GetFields() const {
+ return variables_;
+ }
+ bool LanguageSpecificCheckValid(const AidlTypenames& /*typenames*/,
+ Options::Language) const override {
+ return true;
+ }
+ std::string GetPreprocessDeclarationName() const override { return "union"; }
+
+ void Dump(CodeWriter* writer) const override;
+ const AidlUnionDecl* AsUnionDeclaration() const override { return this; }
+
+ private:
+ const std::vector<std::unique_ptr<AidlVariableDeclaration>> variables_;
+};
+
class AidlInterface final : public AidlDefinedType {
public:
AidlInterface(const AidlLocation& location, const std::string& name, const std::string& comments,
diff --git a/aidl_language_l.ll b/aidl_language_l.ll
index 7646f3f..a41b96a 100644
--- a/aidl_language_l.ll
+++ b/aidl_language_l.ll
@@ -124,6 +124,9 @@
enum { yylval->token = new AidlToken("enum", extra_text);
return yy::parser::token::ENUM;
}
+union { yylval->token = new AidlToken("union", extra_text);
+ return yy::parser::token::UNION;
+ }
/* scalars */
{identifier} { yylval->token = new AidlToken(yytext, extra_text);
diff --git a/aidl_language_y.yy b/aidl_language_y.yy
index 8179596..7ce8859 100644
--- a/aidl_language_y.yy
+++ b/aidl_language_y.yy
@@ -81,7 +81,6 @@
AidlConstantValue* const_expr;
AidlEnumerator* enumerator;
std::vector<std::unique_ptr<AidlEnumerator>>* enumerators;
- AidlEnumDeclaration* enum_decl;
std::vector<std::unique_ptr<AidlConstantValue>>* constant_value_list;
std::vector<std::unique_ptr<AidlArgument>>* arg_list;
AidlVariableDeclaration* variable;
@@ -89,8 +88,6 @@
AidlMethod* method;
AidlMember* constant;
std::vector<std::unique_ptr<AidlMember>>* interface_members;
- AidlInterface* interface;
- AidlParcelable* parcelable;
AidlDefinedType* declaration;
std::vector<std::unique_ptr<AidlTypeSpecifier>>* type_args;
std::vector<std::string>* type_params;
@@ -110,6 +107,7 @@
%token<token> PARCELABLE "parcelable"
%token<token> ONEWAY "oneway"
%token<token> ENUM "enum"
+%token<token> UNION "union"
%token<token> CONST "const"
%token<character> CHARVALUE "char literal"
@@ -151,18 +149,19 @@
%right UNARY_PLUS UNARY_MINUS '!' '~'
%type<declaration> decl
+%type<declaration> unannotated_decl
+%type<declaration> interface_decl
+%type<declaration> parcelable_decl
+%type<declaration> enum_decl
+%type<declaration> union_decl
%type<variable_list> variable_decls
%type<variable> variable_decl
%type<type_params> optional_type_params
%type<interface_members> interface_members
-%type<declaration> unannotated_decl
-%type<interface> interface_decl
-%type<parcelable> parcelable_decl
%type<method> method_decl
%type<constant> constant_decl
%type<enumerator> enumerator
%type<enumerators> enumerators enum_decl_body
-%type<enum_decl> enum_decl
%type<param> parameter
%type<param_list> parameter_list
%type<param_list> parameter_non_empty_list
@@ -275,11 +274,9 @@
unannotated_decl
: parcelable_decl
- { $$ = $1; }
| interface_decl
- { $$ = $1; }
| enum_decl
- { $$ = $1; }
+ | union_decl
;
type_params
@@ -563,6 +560,15 @@
}
;
+union_decl
+ : UNION qualified_name optional_type_params '{' variable_decls '}' {
+ $$ = new AidlUnionDecl(loc(@2), $2->GetText(), ps->Package(), $1->GetComments(), $5, $3);
+ delete $1;
+ delete $2;
+ delete $5;
+ }
+ ;
+
method_decl
: type identifier '(' arg_list ')' ';' {
$$ = new AidlMethod(loc(@2), false, $1, $2->GetText(), $4, $1->GetComments());
diff --git a/aidl_to_java.cpp b/aidl_to_java.cpp
index 658348b..c5d3f97 100644
--- a/aidl_to_java.cpp
+++ b/aidl_to_java.cpp
@@ -762,7 +762,7 @@
} else {
const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName());
AIDL_FATAL_IF(t == nullptr, c.type) << "Unknown type: " << c.type.GetName();
- if (t->AsParcelable() != nullptr) {
+ if (t->AsParcelable() != nullptr || t->AsUnionDeclaration() != nullptr) {
if (c.type.IsArray()) {
c.writer << c.parcel << ".readTypedArray(" << c.var << ", " << c.type.GetName()
<< ".CREATOR);\n";
diff --git a/generate_java.cpp b/generate_java.cpp
index eff0bd9..17f6283 100644
--- a/generate_java.cpp
+++ b/generate_java.cpp
@@ -31,16 +31,25 @@
#include "code_writer.h"
#include "logging.h"
-using std::unique_ptr;
-using ::android::aidl::java::Variable;
+using ::android::base::EndsWith;
+using ::android::base::Join;
+using ::android::base::StartsWith;
using std::string;
+using std::unique_ptr;
namespace {
-inline string get_setter_name(const AidlNode& context, const string& variablename) {
- AIDL_FATAL_IF(variablename.size() <= 0, context) << "A field name cannot be empty.";
- std::ostringstream out;
- out << "set" << static_cast<char>(toupper(variablename[0])) << variablename.substr(1);
- return out.str();
+// join two non-empty strings according to `camelCase` naming.
+inline string camelcase_join(const string& a, const string& b, const AidlNode& context) {
+ AIDL_FATAL_IF(b.size() <= 0 || a.size() <= 0, context) << "Name cannot be empty.";
+ std::string name = a + b;
+ name[a.size()] = static_cast<char>(toupper(name[a.size()]));
+ return name;
+}
+inline string getter_name(const AidlVariableDeclaration& variable) {
+ return camelcase_join("get", variable.GetName(), variable);
+}
+inline string setter_name(const AidlVariableDeclaration& variable) {
+ return camelcase_join("set", variable.GetName(), variable);
}
} // namespace
@@ -83,6 +92,14 @@
return true;
}
+bool generate_java_union_declaration(const std::string& filename, const AidlUnionDecl* decl,
+ const AidlTypenames& typenames,
+ const IoDelegate& io_delegate) {
+ CodeWriterPtr code_writer = io_delegate.GetCodeWriter(filename);
+ generate_union(*code_writer, decl, typenames);
+ return true;
+}
+
bool generate_java(const std::string& filename, const AidlDefinedType* defined_type,
const AidlTypenames& typenames, const IoDelegate& io_delegate,
const Options& options) {
@@ -100,6 +117,10 @@
return generate_java_interface(filename, interface, typenames, io_delegate, options);
}
+ if (const AidlUnionDecl* union_decl = defined_type->AsUnionDeclaration(); union_decl != nullptr) {
+ return generate_java_union_declaration(filename, union_decl, typenames, io_delegate);
+ }
+
AIDL_FATAL(defined_type) << "Unrecognized type sent for Java generation.";
return false;
}
@@ -162,7 +183,7 @@
out << " = " << variable->ValueString(ConstantValueDecorator);
}
out << ";\n";
- out << "public Builder " << get_setter_name(*variable, variable->GetName()) << "("
+ out << "public Builder " << setter_name(*variable) << "("
<< JavaSignatureOf(variable->GetType(), typenames) << " " << variable->GetName()
<< ") {\n"
<< " "
@@ -338,8 +359,7 @@
}
CreateFromParcelFor(context);
if (parcel->IsJavaOnlyImmutable()) {
- context.writer.Write("%s.%s(%s);\n", builder_variable.c_str(),
- get_setter_name(*field, field->GetName()).c_str(),
+ context.writer.Write("%s.%s(%s);\n", builder_variable.c_str(), setter_name(*field).c_str(),
field_variable_name.c_str());
}
writer->Close();
@@ -427,6 +447,209 @@
code_writer->Write("}\n");
}
+void generate_union(CodeWriter& out, const AidlUnionDecl* decl, const AidlTypenames& typenames) {
+ const string tag_type = "int";
+ const AidlTypeSpecifier tag_type_specifier(AIDL_LOCATION_HERE, tag_type, false /* isArray */,
+ nullptr /* type_params */, "");
+ const string tag_name = "_tag";
+ const string value_name = "_value";
+ const string clazz = decl->GetName();
+
+ out << "/*\n";
+ out << " * This file is auto-generated. DO NOT MODIFY.\n";
+ out << " */\n";
+
+ out << "package " + decl->GetPackage() + ";\n";
+ out << "\n";
+ out << decl->GetComments() << "\n";
+ for (const auto& annotation : generate_java_annotations(*decl)) {
+ out << annotation << "\n";
+ }
+
+ out << "public final class " + clazz + " implements android.os.Parcelable {\n";
+ out.Indent();
+
+ size_t tag_index = 0;
+ out << "// tags union fields\n";
+ for (const auto& variable : decl->GetFields()) {
+ auto raw_type = variable->GetType().ToString();
+ out << "public final static " + tag_type + " " + variable->GetName() + " = " +
+ std::to_string(tag_index++) + "; // " + raw_type + "\n";
+ }
+ out << "\n";
+
+ out << "private " + tag_type + " " + tag_name + ";\n";
+ out << "private Object " + value_name + ";\n";
+ out << "\n";
+
+ const auto& first_field = decl->GetFields()[0];
+ // ctor()
+ out << "public " + clazz + "() {\n";
+ out << " this(" + first_field->GetName() + ", " +
+ first_field->ValueString(ConstantValueDecorator) + ");\n";
+ out << "}\n";
+ // ctor(Parcel)
+ out << "private " + clazz + "(android.os.Parcel _aidl_parcel) {\n";
+ out << " readFromParcel(_aidl_parcel);\n";
+ out << "}\n";
+ // ctor(tag, value)
+ out << "private " + clazz + "(" + tag_type + " tag, Object value) {\n";
+ out << " _set(tag, value);\n";
+ out << "}\n";
+ out << "\n";
+
+ // getTag()
+ out << "public " + tag_type + " " + "getTag() {\n";
+ out << " return " + tag_name + ";\n";
+ out << "}\n";
+ out << "\n";
+
+ // value ctor, getter, setter for each field
+ for (const auto& variable : decl->GetFields()) {
+ auto var_name = variable->GetName();
+ auto var_type = JavaSignatureOf(variable->GetType(), typenames);
+ auto raw_type = variable->GetType().ToString();
+
+ out << "// " + raw_type + " " + var_name + "\n";
+ // value ctor
+ out << variable->GetType().GetComments() + "\n";
+ out << "public static " + clazz + " " + var_name + "(" + var_type + " " + value_name + ") {\n";
+ out << " return new " + clazz + "(" + var_name + ", " + value_name + ");\n";
+ out << "}\n";
+ // getter
+ if (variable->GetType().IsGeneric()) {
+ out << "@SuppressWarnings(\"unchecked\")\n";
+ }
+ out << "public " + var_type + " " + getter_name(*variable) + "() {\n";
+ out << " _assertTag(" + var_name + ");\n";
+ out << " return (" + var_type + ") " + value_name + ";\n";
+ out << "}\n";
+ // setter
+ out << "public void " + setter_name(*variable) + "(" + var_type + " " + value_name + ") {\n";
+ out << " _set(" + var_name + ", " + value_name + ");\n";
+ out << "}\n";
+ out << "\n";
+ }
+
+ if (decl->IsVintfStability()) {
+ out << "@Override\n";
+ out << "public final int getStability() {\n";
+ out << " return android.os.Parcelable.PARCELABLE_STABILITY_VINTF;\n";
+ out << "}\n";
+ out << "\n";
+ }
+
+ out << "public static final android.os.Parcelable.Creator<" << clazz << "> CREATOR = "
+ << "new android.os.Parcelable.Creator<" << clazz << ">() {\n";
+ out << " @Override\n";
+ out << " public " << clazz << " createFromParcel(android.os.Parcel _aidl_source) {\n";
+ out << " return new Union(_aidl_source);\n"; // to avoid unnecessary allocation of "default"
+ out << " }\n";
+ out << " @Override\n";
+ out << " public " << clazz << "[] newArray(int _aidl_size) {\n";
+ out << " return new " << clazz << "[_aidl_size];\n";
+ out << " }\n";
+ out << "};\n";
+
+ auto write_to_parcel = [&](const AidlTypeSpecifier& type, std::string name, std::string parcel) {
+ string code;
+ CodeWriterPtr writer = CodeWriter::ForString(&code);
+ CodeGeneratorContext context{
+ .writer = *(writer.get()),
+ .typenames = typenames,
+ .type = type,
+ .parcel = parcel,
+ .var = name,
+ .is_return_value = false,
+ };
+ WriteToParcelFor(context);
+ writer->Close();
+ return code;
+ };
+
+ out << "@Override\n";
+ out << "public final void writeToParcel(android.os.Parcel _aidl_parcel, int _aidl_flag) {\n";
+ out << " " + write_to_parcel(tag_type_specifier, tag_name, "_aidl_parcel");
+ out << " switch (" + tag_name + ") {\n";
+ for (const auto& variable : decl->GetFields()) {
+ out << " case " + variable->GetName() + ":\n";
+ out << " " +
+ write_to_parcel(variable->GetType(), getter_name(*variable) + "()", "_aidl_parcel");
+ out << " break;\n";
+ }
+ out << " }\n";
+ out << "}\n";
+
+ // keep this across different fields in order to create the classloader
+ // at most once.
+ bool is_classloader_created = false;
+ auto read_from_parcel = [&](const AidlTypeSpecifier& type, std::string name, std::string parcel) {
+ string code;
+ CodeWriterPtr writer = CodeWriter::ForString(&code);
+ CodeGeneratorContext context{
+ .writer = *(writer.get()),
+ .typenames = typenames,
+ .type = type,
+ .parcel = parcel,
+ .var = name,
+ .is_classloader_created = &is_classloader_created,
+ };
+ CreateFromParcelFor(context);
+ writer->Close();
+ return code;
+ };
+
+ // Not override, but as a user-defined parcelable, this method should be public
+ out << "public void readFromParcel(android.os.Parcel _aidl_parcel) {\n";
+ out << " " + tag_type + " _aidl_tag;\n";
+ out << " " + read_from_parcel(tag_type_specifier, "_aidl_tag", "_aidl_parcel");
+ out << " switch (_aidl_tag) {\n";
+ for (const auto& variable : decl->GetFields()) {
+ auto var_name = variable->GetName();
+ auto var_type = JavaSignatureOf(variable->GetType(), typenames);
+ out << " case " + var_name + ": {\n";
+ out << " " + var_type + " _aidl_value;\n";
+ out << " " + read_from_parcel(variable->GetType(), "_aidl_value", "_aidl_parcel");
+ out << " _set(_aidl_tag, _aidl_value);\n";
+ out << " return; }\n";
+ }
+ out << " }\n";
+ out << " throw new RuntimeException(\"union: out of range: \" + _aidl_tag);\n";
+ out << "}\n";
+
+ out << "@Override\n";
+ out << "public int describeContents() {\n";
+ out << " return 0;\n";
+ out << "}\n";
+ out << "\n";
+
+ // helper: _assertTag
+ out << "private void _assertTag(" + tag_type + " tag) {\n";
+ out << " if (getTag() != tag) {\n";
+ out << " throw new IllegalStateException(\"bad access: \" + _tagString(tag) + \", \" + "
+ "_tagString(tag) + \" is available.\");\n";
+ out << " }\n";
+ out << "}\n";
+ // helper: _tagString
+ out << "private String _tagString(" + tag_type + " " + tag_name + ") {\n";
+ out << " switch (" + tag_name + ") {\n";
+ for (const auto& variable : decl->GetFields()) {
+ auto var_name = variable->GetName();
+ out << " case " + var_name + ": return \"" + var_name + "\";\n";
+ }
+ out << " }\n";
+ out << " throw new IllegalStateException(\"unknown field: \" + " + tag_name + ");\n";
+ out << "}\n";
+ // helper: _set
+ out << "private void _set(" + tag_type + " tag, Object value) {\n";
+ out << " this." + tag_name + " = tag;\n";
+ out << " this." + value_name + " = value;\n";
+ out << "}\n";
+
+ out.Dedent();
+ out << "}\n";
+}
+
std::string dump_location(const AidlNode& method) {
return method.PrintLocation();
}
@@ -440,7 +663,7 @@
parameters_decl.push_back(param_name + " = " + param_value);
}
parameters_decl.push_back("overrideSourcePosition=\"" + dump_location(a) + "\"");
- return "(" + base::Join(parameters_decl, ", ") + ")";
+ return "(" + Join(parameters_decl, ", ") + ")";
}
std::vector<std::string> generate_java_annotations(const AidlAnnotatable& a) {
@@ -455,9 +678,9 @@
generate_java_unsupportedappusage_parameters(*unsupported_app_usage));
}
- auto strip_double_quote = [](const AidlTypeSpecifier& type, const std::string& raw_value) -> std::string {
- if (!android::base::StartsWith(raw_value, "\"") ||
- !android::base::EndsWith(raw_value, "\"")) {
+ auto strip_double_quote = [](const AidlTypeSpecifier& type,
+ const std::string& raw_value) -> std::string {
+ if (!StartsWith(raw_value, "\"") || !EndsWith(raw_value, "\"")) {
AIDL_FATAL(type) << "Java passthrough annotation " << raw_value << " is not properly quoted";
return "";
}
diff --git a/generate_java.h b/generate_java.h
index d2c8a6d..3ed2546 100644
--- a/generate_java.h
+++ b/generate_java.h
@@ -40,6 +40,8 @@
void generate_enum(const CodeWriterPtr& code_writer, const AidlEnumDeclaration* enum_decl,
const AidlTypenames& typenames);
+void generate_union(CodeWriter& out, const AidlUnionDecl* decl, const AidlTypenames& typenames);
+
std::vector<std::string> generate_java_annotations(const AidlAnnotatable& a);
} // namespace java
diff --git a/tests/android/aidl/tests/Union.aidl b/tests/android/aidl/tests/Union.aidl
new file mode 100644
index 0000000..c1b6e52
--- /dev/null
+++ b/tests/android/aidl/tests/Union.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.aidl.tests;
+
+union Union {
+ int n;
+ int m;
+ int[] ns;
+ String s;
+ @utf8InCpp String s2;
+ @nullable IBinder ibinder;
+ List<String> ss;
+}
+
diff --git a/tests/java/src/android/aidl/tests/AidlJavaTests.java b/tests/java/src/android/aidl/tests/AidlJavaTests.java
index 0762cb2..b53c96f 100644
--- a/tests/java/src/android/aidl/tests/AidlJavaTests.java
+++ b/tests/java/src/android/aidl/tests/AidlJavaTests.java
@@ -9,15 +9,9 @@
public static void main(String[] args) {
JUnitCore junit = new JUnitCore();
junit.addListener(new TextListener(System.out));
- Result result = junit.run(
- ExtensionTests.class,
- GenericTests.class,
- JavaOnlyImmutableAnnotationTests.class,
- MapTests.class,
- NullableTests.class,
- TestServiceClient.class,
- TestVersionedInterface.class
- );
+ Result result = junit.run(ExtensionTests.class, GenericTests.class,
+ JavaOnlyImmutableAnnotationTests.class, MapTests.class, NullableTests.class,
+ TestServiceClient.class, TestVersionedInterface.class, UnionTests.class);
System.out.println(result.wasSuccessful() ? "TEST SUCCESS" : "TEST FAILURE");
}
diff --git a/tests/java/src/android/aidl/tests/UnionTests.java b/tests/java/src/android/aidl/tests/UnionTests.java
new file mode 100644
index 0000000..6b903b1
--- /dev/null
+++ b/tests/java/src/android/aidl/tests/UnionTests.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020, 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.
+ */
+
+package android.aidl.tests;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+import android.aidl.tests.Union;
+import android.os.Parcel;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UnionTests {
+ @Test
+ public void defaultConstructorInitsWithFirstField() {
+ Union u = new Union();
+ assertThat(u.getTag(), is(Union.n));
+ assertThat(u.getN(), is(0));
+ }
+
+ @Test
+ public void updatesUnionWithSetter() {
+ Union u = new Union();
+ u.setNs(new int[] {1, 2, 3});
+ assertThat(u.getTag(), is(Union.ns));
+ assertThat(u.getNs(), is(new int[] {1, 2, 3}));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void gettingWrongFieldThrowsException() {
+ Union u = new Union();
+ u.getSs();
+ }
+
+ @Test
+ public void readWriteViaParcel() {
+ List<String> ss = Arrays.asList("hello", "world");
+
+ Union u = Union.ss(ss);
+ Parcel parcel = Parcel.obtain();
+ u.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Union v = Union.CREATOR.createFromParcel(parcel);
+
+ assertThat(v.getTag(), is(Union.ss));
+ assertThat(v.getSs(), is(ss));
+
+ parcel.recycle();
+ }
+}