/* * 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 "aidl_language.h" #include "aidl_typenames.h" #include "parser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aidl.h" #include "aidl_language_y.h" #include "comments.h" #include "logging.h" #include "permission.h" #ifdef _WIN32 int isatty(int fd) { return (fd == 0); } #endif using android::aidl::IoDelegate; using android::base::Error; using android::base::Join; using android::base::Result; using android::base::Split; using std::cerr; using std::pair; using std::set; using std::string; using std::unique_ptr; using std::vector; namespace { bool IsJavaKeyword(const char* str) { static const std::vector kJavaKeywords{ "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while", "true", "false", "null", }; return std::find(kJavaKeywords.begin(), kJavaKeywords.end(), str) != kJavaKeywords.end(); } } // namespace AidlNode::~AidlNode() { if (!visited_) { unvisited_locations_.push_back(location_); } } void AidlNode::ClearUnvisitedNodes() { unvisited_locations_.clear(); } const std::vector& AidlNode::GetLocationsOfUnvisitedNodes() { return unvisited_locations_; } void AidlNode::MarkVisited() const { visited_ = true; } AidlNode::AidlNode(const AidlLocation& location, const Comments& comments) : location_(location), comments_(comments) {} std::string AidlNode::PrintLine() const { std::stringstream ss; ss << location_.file_ << ":" << location_.begin_.line; return ss.str(); } std::string AidlNode::PrintLocation() const { std::stringstream ss; ss << location_.file_ << ":" << location_.begin_.line << ":" << location_.begin_.column << ":" << location_.end_.line << ":" << location_.end_.column; return ss.str(); } std::vector AidlNode::unvisited_locations_; static const AidlTypeSpecifier kStringType{AIDL_LOCATION_HERE, "String", /*array=*/std::nullopt, nullptr, Comments{}}; static const AidlTypeSpecifier kStringArrayType{AIDL_LOCATION_HERE, "String", DynamicArray{}, nullptr, Comments{}}; static const AidlTypeSpecifier kIntType{AIDL_LOCATION_HERE, "int", /*array=*/std::nullopt, nullptr, Comments{}}; static const AidlTypeSpecifier kLongType{AIDL_LOCATION_HERE, "long", /*array=*/std::nullopt, nullptr, Comments{}}; static const AidlTypeSpecifier kBooleanType{AIDL_LOCATION_HERE, "boolean", /*array=*/std::nullopt, nullptr, Comments{}}; const std::vector& AidlAnnotation::AllSchemas() { static const std::vector kSchemas{ {AidlAnnotation::Type::NULLABLE, "nullable", CONTEXT_TYPE_SPECIFIER, {{"heap", kBooleanType}}}, {AidlAnnotation::Type::UTF8_IN_CPP, "utf8InCpp", CONTEXT_TYPE_SPECIFIER, {}}, {AidlAnnotation::Type::SENSITIVE_DATA, "SensitiveData", CONTEXT_TYPE_INTERFACE, {}}, {AidlAnnotation::Type::VINTF_STABILITY, "VintfStability", CONTEXT_TYPE, {}}, {AidlAnnotation::Type::UNSUPPORTED_APP_USAGE, "UnsupportedAppUsage", CONTEXT_TYPE | CONTEXT_MEMBER, {{"expectedSignature", kStringType}, {"implicitMember", kStringType}, {"maxTargetSdk", kIntType}, {"publicAlternatives", kStringType}, {"trackingBug", kLongType}}}, {AidlAnnotation::Type::JAVA_STABLE_PARCELABLE, "JavaOnlyStableParcelable", CONTEXT_TYPE_UNSTRUCTURED_PARCELABLE, {}}, {AidlAnnotation::Type::NDK_STABLE_PARCELABLE, "NdkOnlyStableParcelable", CONTEXT_TYPE_UNSTRUCTURED_PARCELABLE, {}}, {AidlAnnotation::Type::RUST_STABLE_PARCELABLE, "RustOnlyStableParcelable", CONTEXT_TYPE_UNSTRUCTURED_PARCELABLE, {}}, {AidlAnnotation::Type::BACKING, "Backing", CONTEXT_TYPE_ENUM, {{"type", kStringType, /* required= */ true}}}, {AidlAnnotation::Type::JAVA_PASSTHROUGH, "JavaPassthrough", CONTEXT_ALL, {{"annotation", kStringType, /* required= */ true}}, /* repeatable= */ true}, {AidlAnnotation::Type::JAVA_DERIVE, "JavaDerive", CONTEXT_TYPE_STRUCTURED_PARCELABLE | CONTEXT_TYPE_UNION | CONTEXT_TYPE_ENUM, {{"toString", kBooleanType}, {"equals", kBooleanType}}}, {AidlAnnotation::Type::JAVA_DEFAULT, "JavaDefault", CONTEXT_TYPE_INTERFACE, {}}, {AidlAnnotation::Type::JAVA_DELEGATOR, "JavaDelegator", CONTEXT_TYPE_INTERFACE, {}}, {AidlAnnotation::Type::JAVA_ONLY_IMMUTABLE, "JavaOnlyImmutable", CONTEXT_TYPE_STRUCTURED_PARCELABLE | CONTEXT_TYPE_UNION | CONTEXT_TYPE_UNSTRUCTURED_PARCELABLE, {}}, {AidlAnnotation::Type::JAVA_SUPPRESS_LINT, "JavaSuppressLint", CONTEXT_ALL, {{"value", kStringArrayType, /* required= */ true}}}, {AidlAnnotation::Type::FIXED_SIZE, "FixedSize", CONTEXT_TYPE_STRUCTURED_PARCELABLE | CONTEXT_TYPE_UNION, {}}, {AidlAnnotation::Type::DESCRIPTOR, "Descriptor", CONTEXT_TYPE_INTERFACE, {{"value", kStringType, /* required= */ true}}}, {AidlAnnotation::Type::RUST_DERIVE, "RustDerive", CONTEXT_TYPE_STRUCTURED_PARCELABLE | CONTEXT_TYPE_UNION, {{"Copy", kBooleanType}, {"Clone", kBooleanType}, {"PartialOrd", kBooleanType}, {"Ord", kBooleanType}, {"PartialEq", kBooleanType}, {"Eq", kBooleanType}, {"Hash", kBooleanType}}}, {AidlAnnotation::Type::SUPPRESS_WARNINGS, "SuppressWarnings", CONTEXT_TYPE | CONTEXT_MEMBER, {{"value", kStringArrayType, /* required= */ true}}}, {AidlAnnotation::Type::PERMISSION_ENFORCE, "EnforcePermission", CONTEXT_TYPE_INTERFACE | CONTEXT_METHOD, {{"value", kStringType}, {"anyOf", kStringArrayType}, {"allOf", kStringArrayType}}}, {AidlAnnotation::Type::PERMISSION_MANUAL, "PermissionManuallyEnforced", CONTEXT_TYPE_INTERFACE | CONTEXT_METHOD, {}}, {AidlAnnotation::Type::PERMISSION_NONE, "RequiresNoPermission", CONTEXT_TYPE_INTERFACE | CONTEXT_METHOD, {}}, {AidlAnnotation::Type::PROPAGATE_ALLOW_BLOCKING, "PropagateAllowBlocking", CONTEXT_METHOD, {}}, }; return kSchemas; } std::string AidlAnnotation::TypeToString(Type type) { for (const Schema& schema : AllSchemas()) { if (type == schema.type) return schema.name; } AIDL_FATAL(AIDL_LOCATION_HERE) << "Unrecognized type: " << static_cast(type); __builtin_unreachable(); } std::unique_ptr AidlAnnotation::Parse( const AidlLocation& location, const string& name, std::map> parameter_list, const Comments& comments) { const Schema* schema = nullptr; for (const Schema& a_schema : AllSchemas()) { if (a_schema.name == name) { schema = &a_schema; } } if (schema == nullptr) { std::ostringstream stream; stream << "'" << name << "' is not a recognized annotation. "; stream << "It must be one of:"; for (const Schema& s : AllSchemas()) { stream << " " << s.name; } stream << "."; AIDL_ERROR(location) << stream.str(); return {}; } return std::unique_ptr( new AidlAnnotation(location, *schema, std::move(parameter_list), comments)); } AidlAnnotation::AidlAnnotation(const AidlLocation& location, const Schema& schema, std::map> parameters, const Comments& comments) : AidlNode(location, comments), schema_(schema), parameters_(std::move(parameters)) {} struct ConstReferenceFinder : AidlVisitor { const AidlConstantReference* found = nullptr; void Visit(const AidlConstantReference& ref) override { if (!found) found = &ref; } static const AidlConstantReference* Find(const AidlConstantValue& c) { ConstReferenceFinder finder; VisitTopDown(finder, c); return finder.found; } }; // Checks if annotation complies with the schema // - every parameter is known and has well-typed value. // - every required parameter is present. bool AidlAnnotation::CheckValid() const { for (const auto& name_and_param : parameters_) { const std::string& param_name = name_and_param.first; const std::shared_ptr& param = name_and_param.second; const auto param_type = schema_.ParamType(param_name); if (!param_type) { std::ostringstream stream; stream << "Parameter " << param_name << " not supported "; stream << "for annotation " << GetName() << ". "; stream << "It must be one of:"; for (const auto& param : schema_.parameters) { stream << " " << param.name; } AIDL_ERROR(this) << stream.str(); return false; } const auto& found = ConstReferenceFinder::Find(*param); if (found) { AIDL_ERROR(found) << "Value must be a constant expression but contains reference to " << found->GetFieldName() << "."; return false; } if (!param->CheckValid()) { AIDL_ERROR(this) << "Invalid value for parameter " << param_name << " on annotation " << GetName() << "."; return false; } const std::string param_value = param->ValueString(param_type->type, AidlConstantValueDecorator); // Assume error on empty string. if (param_value == "") { AIDL_ERROR(this) << "Invalid value for parameter " << param_name << " on annotation " << GetName() << "."; return false; } } bool success = true; for (const auto& param : schema_.parameters) { if (param.required && parameters_.count(param.name) == 0) { AIDL_ERROR(this) << "Missing '" << param.name << "' on @" << GetName() << "."; success = false; } } if (!success) { return false; } // For @Enforce annotations, validates the expression. if (schema_.type == AidlAnnotation::Type::PERMISSION_ENFORCE) { auto expr = EnforceExpression(); if (!expr.ok()) { AIDL_ERROR(this) << "Unable to parse @EnforcePermission annotation: " << expr.error(); return false; } } return true; } Result> AidlAnnotation::EnforceExpression() const { auto single = ParamValue("value"); auto anyOf = ParamValue>("anyOf"); auto allOf = ParamValue>("allOf"); if (single.has_value()) { return std::make_unique(single.value()); } else if (anyOf.has_value()) { auto v = android::aidl::perm::AnyOf{anyOf.value()}; return std::make_unique(v); } else if (allOf.has_value()) { auto v = android::aidl::perm::AllOf{allOf.value()}; return std::make_unique(v); } return Error() << "No parameter for @EnforcePermission"; } // Checks if the annotation is applicable to the current context. // For example, annotations like @VintfStability, @FixedSize is not applicable to AidlTypeSpecifier // nodes. bool AidlAnnotation::CheckContext(TargetContext context) const { if (schema_.target_context & static_cast(context)) { return true; } const static map context_name_map{ {CONTEXT_TYPE_INTERFACE, "interface"}, {CONTEXT_TYPE_ENUM, "enum"}, {CONTEXT_TYPE_STRUCTURED_PARCELABLE, "parcelable definition"}, {CONTEXT_TYPE_UNION, "union"}, {CONTEXT_TYPE_UNSTRUCTURED_PARCELABLE, "parcelable declaration"}, {CONTEXT_CONST, "constant"}, {CONTEXT_FIELD, "field"}, {CONTEXT_METHOD, "method"}, {CONTEXT_TYPE_SPECIFIER, "type"}, }; vector available; for (const auto& [context, name] : context_name_map) { if (schema_.target_context & context) { available.push_back(name); } } AIDL_ERROR(this) << "@" << GetName() << " is not available. It can only annotate: " << Join(available, ", ") << "."; return false; } std::map AidlAnnotation::AnnotationParams( const ConstantValueDecorator& decorator) const { std::map raw_params; for (const auto& name_and_param : parameters_) { const std::string& param_name = name_and_param.first; const std::shared_ptr& param = name_and_param.second; const auto param_type = schema_.ParamType(param_name); AIDL_FATAL_IF(!param_type, this); raw_params.emplace(param_name, param->ValueString(param_type->type, decorator)); } return raw_params; } std::string AidlAnnotation::ToString() const { if (parameters_.empty()) { return "@" + GetName(); } else { vector param_strings; for (const auto& [name, value] : AnnotationParams(AidlConstantValueDecorator)) { param_strings.emplace_back(name + "=" + value); } return "@" + GetName() + "(" + Join(param_strings, ", ") + ")"; } } void AidlAnnotation::TraverseChildren(std::function traverse) const { for (const auto& [name, value] : parameters_) { (void)name; traverse(*value); } } static const AidlAnnotation* GetAnnotation( const vector>& annotations, AidlAnnotation::Type type) { for (const auto& a : annotations) { if (a->GetType() == type) { AIDL_FATAL_IF(a->Repeatable(), a) << "Trying to get a single annotation when it is repeatable."; return a.get(); } } return nullptr; } static const AidlAnnotation* GetScopedAnnotation(const AidlDefinedType& defined_type, AidlAnnotation::Type type) { const AidlAnnotation* annotation = GetAnnotation(defined_type.GetAnnotations(), type); if (annotation) { return annotation; } const AidlDefinedType* enclosing_type = defined_type.GetParentType(); if (enclosing_type) { return GetScopedAnnotation(*enclosing_type, type); } return nullptr; } AidlAnnotatable::AidlAnnotatable(const AidlLocation& location, const Comments& comments) : AidlCommentable(location, comments) {} bool AidlAnnotatable::IsNullable() const { return GetAnnotation(annotations_, AidlAnnotation::Type::NULLABLE); } bool AidlAnnotatable::IsHeapNullable() const { auto annot = GetAnnotation(annotations_, AidlAnnotation::Type::NULLABLE); if (annot) { return annot->ParamValue("heap").value_or(false); } return false; } bool AidlAnnotatable::IsUtf8InCpp() const { return GetAnnotation(annotations_, AidlAnnotation::Type::UTF8_IN_CPP); } bool AidlAnnotatable::IsSensitiveData() const { return GetAnnotation(annotations_, AidlAnnotation::Type::SENSITIVE_DATA); } bool AidlAnnotatable::IsVintfStability() const { auto defined_type = AidlCast(*this); AIDL_FATAL_IF(!defined_type, *this) << "@VintfStability is not attached to a type"; return GetScopedAnnotation(*defined_type, AidlAnnotation::Type::VINTF_STABILITY); } bool AidlAnnotatable::IsJavaOnlyImmutable() const { return GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_ONLY_IMMUTABLE); } bool AidlAnnotatable::IsFixedSize() const { return GetAnnotation(annotations_, AidlAnnotation::Type::FIXED_SIZE); } const AidlAnnotation* AidlAnnotatable::UnsupportedAppUsage() const { return GetAnnotation(annotations_, AidlAnnotation::Type::UNSUPPORTED_APP_USAGE); } std::vector AidlAnnotatable::RustDerive() const { std::vector ret; if (const auto* ann = GetAnnotation(annotations_, AidlAnnotation::Type::RUST_DERIVE)) { for (const auto& name_and_param : ann->AnnotationParams(AidlConstantValueDecorator)) { if (name_and_param.second == "true") { ret.push_back(name_and_param.first); } } } return ret; } const AidlAnnotation* AidlAnnotatable::BackingType() const { return GetAnnotation(annotations_, AidlAnnotation::Type::BACKING); } std::vector AidlAnnotatable::SuppressWarnings() const { auto annot = GetAnnotation(annotations_, AidlAnnotation::Type::SUPPRESS_WARNINGS); if (annot) { auto names = annot->ParamValue>("value"); AIDL_FATAL_IF(!names.has_value(), this); return std::move(names.value()); } return {}; } // Parses the @Enforce annotation expression. std::unique_ptr AidlAnnotatable::EnforceExpression() const { auto annot = GetAnnotation(annotations_, AidlAnnotation::Type::PERMISSION_ENFORCE); if (annot) { auto perm_expr = annot->EnforceExpression(); if (!perm_expr.ok()) { // This should have been caught during validation. AIDL_FATAL(this) << "Unable to parse @EnforcePermission annotation: " << perm_expr.error(); } return std::move(perm_expr.value()); } return {}; } bool AidlAnnotatable::IsPermissionManual() const { return GetAnnotation(annotations_, AidlAnnotation::Type::PERMISSION_MANUAL); } bool AidlAnnotatable::IsPermissionNone() const { return GetAnnotation(annotations_, AidlAnnotation::Type::PERMISSION_NONE); } bool AidlAnnotatable::IsPermissionAnnotated() const { return IsPermissionNone() || IsPermissionManual() || EnforceExpression(); } bool AidlAnnotatable::IsPropagateAllowBlocking() const { return GetAnnotation(annotations_, AidlAnnotation::Type::PROPAGATE_ALLOW_BLOCKING); } bool AidlAnnotatable::IsStableApiParcelable(Options::Language lang) const { if (lang == Options::Language::JAVA) return GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_STABLE_PARCELABLE); if (lang == Options::Language::NDK) return GetAnnotation(annotations_, AidlAnnotation::Type::NDK_STABLE_PARCELABLE); if (lang == Options::Language::RUST) return GetAnnotation(annotations_, AidlAnnotation::Type::RUST_STABLE_PARCELABLE); return false; } bool AidlAnnotatable::JavaDerive(const std::string& method) const { auto annotation = GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_DERIVE); if (annotation != nullptr) { return annotation->ParamValue(method).value_or(false); } return false; } bool AidlAnnotatable::IsJavaDefault() const { return GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_DEFAULT); } bool AidlAnnotatable::IsJavaDelegator() const { return GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_DELEGATOR); } std::string AidlAnnotatable::GetDescriptor() const { auto annotation = GetAnnotation(annotations_, AidlAnnotation::Type::DESCRIPTOR); if (annotation != nullptr) { return annotation->ParamValue("value").value(); } return ""; } bool AidlAnnotatable::CheckValid(const AidlTypenames&) const { for (const auto& annotation : GetAnnotations()) { if (!annotation->CheckValid()) { return false; } } std::map declared; for (const auto& annotation : GetAnnotations()) { const auto& [iter, inserted] = declared.emplace(annotation->GetType(), annotation->GetLocation()); if (!inserted && !annotation->Repeatable()) { AIDL_ERROR(this) << "'" << annotation->GetName() << "' is repeated, but not allowed. Previous location: " << iter->second; return false; } } return true; } string AidlAnnotatable::ToString() const { vector ret; for (const auto& a : annotations_) { ret.emplace_back(a->ToString()); } std::sort(ret.begin(), ret.end()); return Join(ret, " "); } AidlTypeSpecifier::AidlTypeSpecifier(const AidlLocation& location, const string& unresolved_name, std::optional array, vector>* type_params, const Comments& comments) : AidlAnnotatable(location, comments), AidlParameterizable>(type_params), unresolved_name_(unresolved_name), array_(std::move(array)), split_name_(Split(unresolved_name, ".")) {} void AidlTypeSpecifier::ViewAsArrayBase(std::function func) const { AIDL_FATAL_IF(!array_.has_value(), this); // Declaring array of generic type cannot happen, it is grammar error. AIDL_FATAL_IF(IsGeneric(), this); bool is_mutated = mutated_; mutated_ = true; // mutate the array type to its base by removing a single dimension // e.g.) T[] => T, T[N][M] => T[M] (note that, M is removed) if (IsFixedSizeArray() && std::get(*array_).dimensions.size() > 1) { auto& dimensions = std::get(*array_).dimensions; auto dim = std::move(dimensions.front()); dimensions.erase(dimensions.begin()); func(*this); dimensions.insert(dimensions.begin(), std::move(dim)); } else { ArrayType array_type = std::move(array_.value()); array_ = std::nullopt; func(*this); array_ = std::move(array_type); } mutated_ = is_mutated; } bool AidlTypeSpecifier::MakeArray(ArrayType array_type) { // T becomes T[] or T[N] if (!IsArray()) { array_ = std::move(array_type); return true; } // T[N] becomes T[N][M] if (auto fixed_size_array = std::get_if(&array_type); fixed_size_array != nullptr && IsFixedSizeArray()) { // concat dimensions for (auto& dim : fixed_size_array->dimensions) { std::get(*array_).dimensions.push_back(std::move(dim)); } return true; } return false; } std::vector FixedSizeArray::GetDimensionInts() const { std::vector ints; for (const auto& dim : dimensions) { ints.push_back(dim->EvaluatedValue()); } return ints; } std::vector AidlTypeSpecifier::GetFixedSizeArrayDimensions() const { AIDL_FATAL_IF(!IsFixedSizeArray(), "not a fixed-size array"); return std::get(GetArray()).GetDimensionInts(); } string AidlTypeSpecifier::Signature() const { string ret = GetName(); if (IsGeneric()) { vector arg_names; for (const auto& ta : GetTypeParameters()) { arg_names.emplace_back(ta->Signature()); } ret += "<" + Join(arg_names, ",") + ">"; } if (IsArray()) { if (IsFixedSizeArray()) { for (const auto& dim : GetFixedSizeArrayDimensions()) { ret += "[" + std::to_string(dim) + "]"; } } else { ret += "[]"; } } return ret; } string AidlTypeSpecifier::ToString() const { string ret = Signature(); string annotations = AidlAnnotatable::ToString(); if (annotations != "") { ret = annotations + " " + ret; } return ret; } // When `scope` is specified, name is resolved first based on it. // `scope` can be null for built-in types and fully-qualified types. bool AidlTypeSpecifier::Resolve(const AidlTypenames& typenames, const AidlScope* scope) { AIDL_FATAL_IF(IsResolved(), this); std::string name = unresolved_name_; if (scope) { name = scope->ResolveName(name); } AidlTypenames::ResolvedTypename result = typenames.ResolveTypename(name); if (result.is_resolved) { fully_qualified_name_ = result.canonical_name; split_name_ = Split(fully_qualified_name_, "."); defined_type_ = result.defined_type; } return result.is_resolved; } const AidlDefinedType* AidlTypeSpecifier::GetDefinedType() const { return defined_type_; } bool AidlTypeSpecifier::CheckValid(const AidlTypenames& typenames) const { if (!AidlAnnotatable::CheckValid(typenames)) { return false; } if (IsGeneric()) { const auto& types = GetTypeParameters(); for (const auto& arg : types) { if (!arg->CheckValid(typenames)) { return false; } } const string& type_name = GetName(); // TODO(b/136048684) Disallow to use primitive types only if it is List or Map. if (type_name == "List" || type_name == "Map") { if (std::any_of(types.begin(), types.end(), [&](auto& type_ptr) { return !type_ptr->IsArray() && (typenames.GetEnumDeclaration(*type_ptr) || AidlTypenames::IsPrimitiveTypename(type_ptr->GetName())); })) { AIDL_ERROR(this) << "A generic type cannot have any primitive type parameters."; return false; } } const auto defined_type = typenames.TryGetDefinedType(type_name); const auto parameterizable = defined_type != nullptr ? defined_type->AsParameterizable() : nullptr; const bool is_user_defined_generic_type = parameterizable != nullptr && parameterizable->IsGeneric(); const size_t num_params = GetTypeParameters().size(); if (type_name == "List") { if (num_params > 1) { AIDL_ERROR(this) << "List can only have one type parameter, but got: '" << Signature() << "'"; return false; } static const char* kListUsage = "List supports interface/parcelable/union, String, IBinder, and ParcelFileDescriptor."; const AidlTypeSpecifier& contained_type = *GetTypeParameters()[0]; if (contained_type.IsArray()) { AIDL_ERROR(this) << "List of arrays is not supported. " << kListUsage; return false; } const string& contained_type_name = contained_type.GetName(); if (AidlTypenames::IsBuiltinTypename(contained_type_name)) { if (contained_type_name != "String" && contained_type_name != "IBinder" && contained_type_name != "ParcelFileDescriptor") { AIDL_ERROR(this) << "List<" << contained_type_name << "> is not supported. " << kListUsage; return false; } } } else if (type_name == "Map") { if (num_params != 0 && num_params != 2) { AIDL_ERROR(this) << "Map must have 0 or 2 type parameters, but got " << "'" << Signature() << "'"; return false; } if (num_params == 2) { const string& key_type = GetTypeParameters()[0]->Signature(); if (key_type != "String") { AIDL_ERROR(this) << "The type of key in map must be String, but it is " << "'" << key_type << "'"; return false; } } } else if (is_user_defined_generic_type) { const size_t allowed = parameterizable->GetTypeParameters().size(); if (num_params != allowed) { AIDL_ERROR(this) << type_name << " must have " << allowed << " type parameters, but got " << num_params; return false; } } else { AIDL_ERROR(this) << type_name << " is not a generic type."; return false; } } const bool is_generic_string_list = GetName() == "List" && IsGeneric() && GetTypeParameters().size() == 1 && GetTypeParameters()[0]->GetName() == "String"; if (IsUtf8InCpp() && (GetName() != "String" && !is_generic_string_list)) { AIDL_ERROR(this) << "@utf8InCpp can only be used on String, String[], and List."; return false; } if (GetName() == "void") { if (IsArray() || IsNullable() || IsUtf8InCpp()) { AIDL_ERROR(this) << "void type cannot be an array or nullable or utf8 string"; return false; } } if (IsArray()) { if (GetName() == "ParcelableHolder" || GetName() == "List" || GetName() == "Map" || GetName() == "CharSequence") { AIDL_ERROR(this) << "Arrays of " << GetName() << " are not supported."; return false; } } if (IsNullable()) { if (AidlTypenames::IsPrimitiveTypename(GetName()) && !IsArray()) { AIDL_ERROR(this) << "Primitive type cannot get nullable annotation"; return false; } const auto defined_type = typenames.TryGetDefinedType(GetName()); if (defined_type != nullptr && defined_type->AsEnumDeclaration() != nullptr && !IsArray()) { AIDL_ERROR(this) << "Enum type cannot get nullable annotation"; return false; } if (GetName() == "ParcelableHolder") { AIDL_ERROR(this) << "ParcelableHolder cannot be nullable."; return false; } if (IsHeapNullable()) { if (!defined_type || IsArray() || !defined_type->AsParcelable()) { AIDL_ERROR(this) << "@nullable(heap=true) is available to parcelables."; return false; } } } if (IsFixedSizeArray()) { for (const auto& dim : std::get(GetArray()).dimensions) { if (!dim->Evaluate()) { return false; } if (dim->GetType() > AidlConstantValue::Type::INT32) { AIDL_ERROR(this) << "Array size must be a positive number: " << dim->Literal(); return false; } auto value = dim->EvaluatedValue(); if (value < 0) { AIDL_ERROR(this) << "Array size must be a positive number: " << value; return false; } } } return true; } void AidlTypeSpecifier::TraverseChildren(std::function traverse) const { AidlAnnotatable::TraverseChildren(traverse); if (IsGeneric()) { for (const auto& tp : GetTypeParameters()) { traverse(*tp); } } if (IsFixedSizeArray()) { for (const auto& dim : std::get(GetArray()).dimensions) { traverse(*dim); } } } std::string AidlConstantValueDecorator( const AidlTypeSpecifier& type, const std::variant>& raw_value) { if (type.IsArray()) { const auto& values = std::get>(raw_value); return "{" + Join(values, ", ") + "}"; } const std::string& value = std::get(raw_value); if (auto defined_type = type.GetDefinedType(); defined_type) { auto enum_type = defined_type->AsEnumDeclaration(); AIDL_FATAL_IF(!enum_type, type) << "Invalid type for \"" << value << "\""; return type.GetName() + "." + value.substr(value.find_last_of('.') + 1); } return value; } AidlVariableDeclaration::AidlVariableDeclaration(const AidlLocation& location, AidlTypeSpecifier* type, const std::string& name) : AidlVariableDeclaration(location, type, name, AidlConstantValue::Default(*type)) { default_user_specified_ = false; } AidlVariableDeclaration::AidlVariableDeclaration(const AidlLocation& location, AidlTypeSpecifier* type, const std::string& name, AidlConstantValue* default_value) : AidlMember(location, type->GetComments()), type_(type), name_(name), default_user_specified_(true), default_value_(default_value) {} bool AidlVariableDeclaration::HasUsefulDefaultValue() const { if (GetDefaultValue()) { return true; } // null is accepted as a valid default value in all backends if (GetType().IsNullable()) { return true; } return false; } bool AidlVariableDeclaration::CheckValid(const AidlTypenames& typenames) const { bool valid = true; valid &= type_->CheckValid(typenames); if (type_->GetName() == "void") { AIDL_ERROR(this) << "Declaration " << name_ << " is void, but declarations cannot be of void type."; valid = false; } if (default_value_ == nullptr) return valid; valid &= default_value_->CheckValid(); if (!valid) return false; return !ValueString(AidlConstantValueDecorator).empty(); } string AidlVariableDeclaration::GetCapitalizedName() const { AIDL_FATAL_IF(name_.size() <= 0, *this) << "Name can't be empty."; string str = name_; str[0] = static_cast(toupper(str[0])); return str; } string AidlVariableDeclaration::ToString() const { string ret = type_->ToString() + " " + name_; if (default_value_ != nullptr && default_user_specified_) { ret += " = " + ValueString(AidlConstantValueDecorator); } return ret; } string AidlVariableDeclaration::Signature() const { return type_->Signature() + " " + name_; } std::string AidlVariableDeclaration::ValueString(const ConstantValueDecorator& decorator) const { if (default_value_ != nullptr) { return default_value_->ValueString(GetType(), decorator); } else { return ""; } } void AidlVariableDeclaration::TraverseChildren( std::function traverse) const { traverse(GetType()); if (auto default_value = GetDefaultValue(); default_value) { traverse(*default_value); } } AidlArgument::AidlArgument(const AidlLocation& location, AidlArgument::Direction direction, AidlTypeSpecifier* type, const std::string& name) : AidlVariableDeclaration(location, type, name), direction_(direction), direction_specified_(true) {} AidlArgument::AidlArgument(const AidlLocation& location, AidlTypeSpecifier* type, const std::string& name) : AidlVariableDeclaration(location, type, name), direction_(AidlArgument::IN_DIR), direction_specified_(false) {} static std::string to_string(AidlArgument::Direction direction) { switch (direction) { case AidlArgument::IN_DIR: return "in"; case AidlArgument::OUT_DIR: return "out"; case AidlArgument::INOUT_DIR: return "inout"; } } string AidlArgument::GetDirectionSpecifier() const { string ret; if (direction_specified_) { ret = to_string(direction_); } return ret; } string AidlArgument::ToString() const { if (direction_specified_) { return GetDirectionSpecifier() + " " + AidlVariableDeclaration::ToString(); } else { return AidlVariableDeclaration::ToString(); } } static std::string FormatDirections(const std::set& directions) { std::vector out; for (const auto& d : directions) { out.push_back(to_string(d)); } if (out.size() <= 1) { // [] => "" or [A] => "A" return Join(out, ""); } else if (out.size() == 2) { // [A,B] => "A or B" return Join(out, " or "); } else { // [A,B,C] => "A, B, or C" out.back() = "or " + out.back(); return Join(out, ", "); } } bool AidlArgument::CheckValid(const AidlTypenames& typenames) const { if (!GetType().CheckValid(typenames)) { return false; } const auto& aspect = typenames.GetArgumentAspect(GetType()); if (aspect.possible_directions.size() == 0) { AIDL_ERROR(this) << aspect.name << " cannot be an argument type"; return false; } // when direction is not specified, "in" is assumed and should be the only possible direction if (!DirectionWasSpecified() && aspect.possible_directions != std::set{AidlArgument::IN_DIR}) { AIDL_ERROR(this) << "The direction of '" << GetName() << "' is not specified. " << aspect.name << " can be an " << FormatDirections(aspect.possible_directions) << " parameter."; return false; } if (aspect.possible_directions.count(GetDirection()) == 0) { AIDL_ERROR(this) << "'" << GetName() << "' can't be an " << GetDirectionSpecifier() << " parameter because " << aspect.name << " can only be an " << FormatDirections(aspect.possible_directions) << " parameter."; return false; } return true; } bool AidlCommentable::IsHidden() const { return android::aidl::HasHideInComments(GetComments()); } bool AidlCommentable::IsDeprecated() const { return android::aidl::FindDeprecated(GetComments()).has_value(); } AidlMember::AidlMember(const AidlLocation& location, const Comments& comments) : AidlAnnotatable(location, comments) {} AidlConstantDeclaration::AidlConstantDeclaration(const AidlLocation& location, AidlTypeSpecifier* type, const std::string& name, AidlConstantValue* value) : AidlMember(location, type->GetComments()), type_(type), name_(name), value_(value) {} bool AidlConstantDeclaration::CheckValid(const AidlTypenames& typenames) const { bool valid = true; valid &= type_->CheckValid(typenames); valid &= value_->CheckValid(); valid = valid && !ValueString(AidlConstantValueDecorator).empty(); if (!valid) return false; const static set kSupportedConstTypes = {"String", "byte", "int", "long", "float", "double"}; if (kSupportedConstTypes.find(type_->Signature()) == kSupportedConstTypes.end()) { AIDL_ERROR(this) << "Constant of type " << type_->Signature() << " is not supported."; return false; } return true; } string AidlConstantDeclaration::ToString() const { return "const " + type_->ToString() + " " + name_ + " = " + ValueString(AidlConstantValueDecorator); } string AidlConstantDeclaration::Signature() const { return type_->Signature() + " " + name_; } AidlMethod::AidlMethod(const AidlLocation& location, bool oneway, AidlTypeSpecifier* type, const std::string& name, std::vector>* args, const Comments& comments) : AidlMethod(location, oneway, type, name, args, comments, 0) { has_id_ = false; } AidlMethod::AidlMethod(const AidlLocation& location, bool oneway, AidlTypeSpecifier* type, const std::string& name, std::vector>* args, const Comments& comments, int id) : AidlMember(location, comments), oneway_(oneway), oneway_annotation_(oneway), type_(type), name_(name), arguments_(std::move(*args)), id_(id) { has_id_ = true; delete args; for (const unique_ptr& a : arguments_) { if (a->IsIn()) { in_arguments_.push_back(a.get()); } if (a->IsOut()) { out_arguments_.push_back(a.get()); } } } string AidlMethod::Signature() const { vector arg_signatures; for (const auto& arg : GetArguments()) { arg_signatures.emplace_back(arg->GetType().Signature()); } return GetName() + "(" + Join(arg_signatures, ", ") + ")"; } string AidlMethod::ToString() const { vector arg_strings; for (const auto& arg : GetArguments()) { arg_strings.emplace_back(arg->ToString()); } string ret = (IsOneway() ? "oneway " : "") + GetType().ToString() + " " + GetName() + "(" + Join(arg_strings, ", ") + ")"; if (HasId()) { ret += " = " + std::to_string(GetId()); } return ret; } bool AidlMethod::CheckValid(const AidlTypenames& typenames) const { if (!GetType().CheckValid(typenames)) { return false; } // TODO(b/156872582): Support it when ParcelableHolder supports every backend. if (GetType().GetName() == "ParcelableHolder") { AIDL_ERROR(this) << "ParcelableHolder cannot be a return type"; return false; } if (IsOneway() && GetType().GetName() != "void") { AIDL_ERROR(this) << "oneway method '" << GetName() << "' cannot return a value"; return false; } set argument_names; for (const auto& arg : GetArguments()) { auto it = argument_names.find(arg->GetName()); if (it != argument_names.end()) { AIDL_ERROR(this) << "method '" << GetName() << "' has duplicate argument name '" << arg->GetName() << "'"; return false; } argument_names.insert(arg->GetName()); if (!arg->CheckValid(typenames)) { return false; } if (IsOneway() && arg->IsOut()) { AIDL_ERROR(this) << "oneway method '" << this->GetName() << "' cannot have out parameters"; return false; } // check that the name doesn't match a keyword if (IsJavaKeyword(arg->GetName().c_str())) { AIDL_ERROR(arg) << "Argument name is a Java or aidl keyword"; return false; } // Reserve a namespace for internal use if (android::base::StartsWith(arg->GetName(), "_aidl")) { AIDL_ERROR(arg) << "Argument name cannot begin with '_aidl'"; return false; } if (arg->GetType().GetName() == "void") { AIDL_ERROR(arg->GetType()) << "'void' is an invalid type for the parameter '" << arg->GetName() << "'"; return false; } } return true; } AidlDefinedType::AidlDefinedType(const AidlLocation& location, const std::string& name, const Comments& comments, const std::string& package, std::vector>* members) : AidlMember(location, comments), AidlScope(this), 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 = AidlCast(*m); constant) { constants_.emplace_back(constant); } else if (auto variable = AidlCast(*m); variable) { variables_.emplace_back(variable); } else if (auto method = AidlCast(*m); method) { methods_.emplace_back(method); } else if (auto type = AidlCast(*m); type) { type->SetEnclosingScope(this); types_.emplace_back(type); } else { AIDL_FATAL(*m) << "Unknown member type."; } members_.push_back(m.release()); } delete members; } } bool AidlDefinedType::CheckValid(const AidlTypenames& typenames) const { if (!AidlAnnotatable::CheckValid(typenames)) { return false; } if (!CheckValidWithMembers(typenames)) { return false; } return true; } std::string AidlDefinedType::GetCanonicalName() const { if (auto parent = GetParentType(); parent) { return parent->GetCanonicalName() + "." + GetName(); } if (package_.empty()) { return GetName(); } return GetPackage() + "." + GetName(); } bool AidlDefinedType::CheckValidWithMembers(const AidlTypenames& typenames) const { bool success = true; for (const auto& t : GetNestedTypes()) { success = success && t->CheckValid(typenames); } if (auto parameterizable = AsParameterizable(); parameterizable && parameterizable->IsGeneric() && !GetNestedTypes().empty()) { AIDL_ERROR(this) << "Generic types can't have nested types."; return false; } std::set nested_type_names; for (const auto& t : GetNestedTypes()) { bool duplicated = !nested_type_names.emplace(t->GetName()).second; if (duplicated) { AIDL_ERROR(t) << "Redefinition of '" << t->GetName() << "'."; success = false; } // nested type can't have a parent name if (t->GetName() == GetName()) { AIDL_ERROR(t) << "Nested type '" << GetName() << "' has the same name as its parent."; success = false; } // Having unstructured parcelables as nested types doesn't make sense because they are defined // somewhere else in native languages (e.g. C++, Java...). if (AidlCast(*t)) { AIDL_ERROR(t) << "'" << t->GetName() << "' is nested. Unstructured parcelables should be at the root scope."; return false; } } if (!TopologicalVisit(GetNestedTypes(), [](auto&) {})) { AIDL_ERROR(this) << GetName() << " has nested types with cyclic references. C++ and NDK backends don't " "support cyclic references."; return false; } for (const auto& v : GetFields()) { const bool field_valid = v->CheckValid(typenames); success = success && field_valid; } // field names should be unique std::set fieldnames; for (const auto& v : GetFields()) { bool duplicated = !fieldnames.emplace(v->GetName()).second; if (duplicated) { AIDL_ERROR(v) << "'" << GetName() << "' has duplicate field name '" << v->GetName() << "'"; success = false; } } // immutable parcelables should have immutable fields. if (IsJavaOnlyImmutable()) { for (const auto& v : GetFields()) { if (!typenames.CanBeJavaOnlyImmutable(v->GetType())) { AIDL_ERROR(v) << "The @JavaOnlyImmutable '" << GetName() << "' has a " << "non-immutable field named '" << v->GetName() << "'."; success = false; } } } // Rust derive fields must be transitive const std::vector rust_derives = RustDerive(); for (const auto& v : GetFields()) { const AidlDefinedType* field = typenames.TryGetDefinedType(v->GetType().GetName()); if (!field) continue; // could get this from CONTEXT_*, but we don't currently save this info when we validated // contexts if (!field->AsStructuredParcelable() && !field->AsUnionDeclaration()) continue; auto subs = field->RustDerive(); for (const std::string& derive : rust_derives) { if (std::find(subs.begin(), subs.end(), derive) == subs.end()) { AIDL_ERROR(v) << "Field " << v->GetName() << " of type with @RustDerive " << derive << " also needs to derive this"; success = false; } } } set constant_names; for (const auto& constant : GetConstantDeclarations()) { if (constant_names.count(constant->GetName()) > 0) { AIDL_ERROR(constant) << "Found duplicate constant name '" << constant->GetName() << "'"; success = false; } constant_names.insert(constant->GetName()); success = success && constant->CheckValid(typenames); } return success; } bool AidlDefinedType::CheckValidForGetterNames() const { bool success = true; std::set getters; for (const auto& v : GetFields()) { bool duplicated = !getters.emplace(v->GetCapitalizedName()).second; if (duplicated) { AIDL_ERROR(v) << "'" << GetName() << "' has duplicate field name '" << v->GetName() << "' after capitalizing the first letter"; success = false; } } return success; } const AidlDefinedType* AidlDefinedType::GetParentType() const { AIDL_FATAL_IF(GetEnclosingScope() == nullptr, this) << "Scope is not set."; return AidlCast(GetEnclosingScope()->GetNode()); } const AidlDefinedType* AidlDefinedType::GetRootType() const { const AidlDefinedType* root = this; for (auto parent = root->GetParentType(); parent; parent = parent->GetParentType()) { root = parent; } return root; } // Resolve `name` in the current scope. If not found, delegate to the parent std::string AidlDefinedType::ResolveName(const std::string& name) const { // For example, in the following, t1's type Baz means x.Foo.Bar.Baz // while t2's type is y.Baz. // package x; // import y.Baz; // parcelable Foo { // parcelable Bar { // enum Baz { ... } // Baz t1; // -> should be x.Foo.Bar.Baz // } // Baz t2; // -> should be y.Baz // Bar.Baz t3; // -> should be x.Foo.Bar.Baz // } AIDL_FATAL_IF(!GetEnclosingScope(), this) << "Type should have an enclosing scope.(e.g. AidlDocument)"; if (AidlTypenames::IsBuiltinTypename(name)) { return name; } const auto first_dot = name.find_first_of('.'); // For "Outer.Inner", we look up "Outer" in the import list. const std::string class_name = (first_dot == std::string::npos) ? name : name.substr(0, first_dot); // Keep ".Inner", to make a fully-qualified name const std::string nested_type = (first_dot == std::string::npos) ? "" : name.substr(first_dot); // check if it is a nested type for (const auto& type : GetNestedTypes()) { if (type->GetName() == class_name) { return type->GetCanonicalName() + nested_type; } } return GetEnclosingScope()->ResolveName(name); } template <> const AidlDefinedType* AidlCast(const AidlNode& node) { struct Visitor : AidlVisitor { const AidlDefinedType* defined_type = nullptr; void Visit(const AidlInterface& t) override { defined_type = &t; } void Visit(const AidlEnumDeclaration& t) override { defined_type = &t; } void Visit(const AidlStructuredParcelable& t) override { defined_type = &t; } void Visit(const AidlUnionDecl& t) override { defined_type = &t; } void Visit(const AidlParcelable& t) override { defined_type = &t; } } v; node.DispatchVisit(v); return v.defined_type; } const AidlDocument& AidlDefinedType::GetDocument() const { const AidlDefinedType* root = GetRootType(); auto scope = root->GetEnclosingScope(); AIDL_FATAL_IF(!scope, this) << "no scope defined."; auto doc = AidlCast(scope->GetNode()); AIDL_FATAL_IF(!doc, this) << "root scope is not a document."; return *doc; } AidlParcelable::AidlParcelable(const AidlLocation& location, const std::string& name, const std::string& package, const Comments& comments, const AidlUnstructuredHeaders& headers, std::vector* type_params, std::vector>* members) : AidlDefinedType(location, name, comments, package, members), AidlParameterizable(type_params), headers_(headers) { // Strip off quotation marks if we actually have headers. if (headers_.cpp.length() >= 2) { headers_.cpp = headers_.cpp.substr(1, headers_.cpp.length() - 2); } if (headers_.ndk.length() >= 2) { headers_.ndk = headers_.ndk.substr(1, headers_.ndk.length() - 2); } if (headers_.rust_type.length() >= 2) { headers_.rust_type = headers_.rust_type.substr(1, headers_.rust_type.length() - 2); } } template bool AidlParameterizable::CheckValid() const { return true; }; template <> bool AidlParameterizable::CheckValid() const { if (!IsGeneric()) { return true; } std::unordered_set set(GetTypeParameters().begin(), GetTypeParameters().end()); if (set.size() != GetTypeParameters().size()) { AIDL_ERROR(this->AsAidlNode()) << "Every type parameter should be unique."; return false; } return true; } bool AidlParcelable::CheckValid(const AidlTypenames& typenames) const { if (!AidlDefinedType::CheckValid(typenames)) { return false; } if (!AidlParameterizable::CheckValid()) { return false; } bool success = true; if (IsFixedSize()) { for (const auto& v : GetFields()) { if (!typenames.CanBeFixedSize(v->GetType())) { AIDL_ERROR(v) << "The @FixedSize parcelable '" << this->GetName() << "' has a " << "non-fixed size field named " << v->GetName() << "."; success = false; } } } return success; } AidlStructuredParcelable::AidlStructuredParcelable( const AidlLocation& location, const std::string& name, const std::string& package, const Comments& comments, std::vector* type_params, std::vector>* members) : AidlParcelable(location, name, package, comments, {} /*headers*/, type_params, members) {} bool AidlStructuredParcelable::CheckValid(const AidlTypenames& typenames) const { if (!AidlParcelable::CheckValid(typenames)) { return false; } bool success = true; if (IsJavaOnlyImmutable()) { // Immutable parcelables provide getters if (!CheckValidForGetterNames()) { success = false; } } return success; } // TODO: we should treat every backend all the same in future. bool AidlTypeSpecifier::LanguageSpecificCheckValid(Options::Language lang) const { if (this->GetName() == "FileDescriptor" && (lang == Options::Language::NDK || lang == Options::Language::RUST)) { AIDL_ERROR(this) << "FileDescriptor isn't supported by the " << to_string(lang) << " backend."; return false; } if (lang != Options::Language::JAVA) { if (this->GetName() == "List" && !this->IsGeneric()) { AIDL_ERROR(this) << "Currently, only the Java backend supports non-generic List."; return false; } if (this->GetName() == "Map" || this->GetName() == "CharSequence") { AIDL_ERROR(this) << "Currently, only Java backend supports " << this->GetName() << "."; return false; } } return true; } // TODO: we should treat every backend all the same in future. bool AidlDefinedType::LanguageSpecificCheckValid(Options::Language lang) const { struct Visitor : AidlVisitor { Visitor(Options::Language lang) : lang(lang) {} void Visit(const AidlTypeSpecifier& type) override { success = success && type.LanguageSpecificCheckValid(lang); } Options::Language lang; bool success = true; } v(lang); VisitTopDown(v, *this); return v.success; } AidlEnumerator::AidlEnumerator(const AidlLocation& location, const std::string& name, AidlConstantValue* value, const Comments& comments) : AidlCommentable(location, comments), name_(name), value_(value), value_user_specified_(value != nullptr) {} bool AidlEnumerator::CheckValid(const AidlTypeSpecifier& enum_backing_type) const { if (GetValue() == nullptr) { return false; } if (!GetValue()->CheckValid()) { return false; } if (GetValue()->ValueString(enum_backing_type, AidlConstantValueDecorator).empty()) { AIDL_ERROR(this) << "Enumerator type differs from enum backing type."; return false; } return true; } string AidlEnumerator::ValueString(const AidlTypeSpecifier& backing_type, const ConstantValueDecorator& decorator) const { return GetValue()->ValueString(backing_type, decorator); } AidlEnumDeclaration::AidlEnumDeclaration(const AidlLocation& location, const std::string& name, std::vector>* enumerators, const std::string& package, const Comments& comments) : AidlDefinedType(location, name, comments, package, nullptr), enumerators_(std::move(*enumerators)) { // Fill missing enumerator values with // This can't be done in Autofill() because type/ref resolution depends on this. // For example, with enum E { A, B = A }, B's value 'A' is a reference which can't be // resolved if A has no value set. const AidlEnumerator* previous = nullptr; for (const auto& enumerator : enumerators_) { if (enumerator->GetValue() == nullptr) { auto loc = enumerator->GetLocation(); if (previous == nullptr) { enumerator->SetValue( std::unique_ptr(AidlConstantValue::Integral(loc, "0"))); } else { auto prev_value = std::make_unique(loc, previous->GetName()); enumerator->SetValue(std::make_unique( loc, std::move(prev_value), "+", std::unique_ptr(AidlConstantValue::Integral(loc, "1")))); } } previous = enumerator.get(); } } bool AidlEnumDeclaration::Autofill(const AidlTypenames& typenames) { if (auto annot = BackingType(); annot != nullptr) { // Autofill() is called before the grand CheckValid(). But AidlAnnotation::ParamValue() // calls AidlConstantValue::evaluate() which requires CheckValid() to be called before. So we // need to call CheckValid(). if (!annot->CheckValid()) { return false; } auto type = annot->ParamValue("type").value(); backing_type_ = typenames.MakeResolvedType(annot->GetLocation(), type, false); } else { // Default to byte type for enums. backing_type_ = typenames.MakeResolvedType(GetLocation(), "byte", false); } // we only support/test a few backing types, so make sure this is a supported // one (otherwise boolean might work, which isn't supported/tested in all // backends) static std::set kBackingTypes = {"byte", "int", "long"}; if (kBackingTypes.find(backing_type_->GetName()) == kBackingTypes.end()) { AIDL_ERROR(this) << "Invalid backing type: " << backing_type_->GetName() << ". Backing type must be one of: " << Join(kBackingTypes, ", "); return false; } return true; } bool AidlEnumDeclaration::CheckValid(const AidlTypenames& typenames) const { if (!AidlDefinedType::CheckValid(typenames)) { return false; } if (!GetMembers().empty()) { AIDL_ERROR(this) << "Enum doesn't support fields/constants/methods."; return false; } if (backing_type_ == nullptr) { AIDL_ERROR(this) << "Enum declaration missing backing type."; return false; } bool success = true; for (const auto& enumerator : enumerators_) { success = success && enumerator->CheckValid(GetBackingType()); } return success; } AidlUnionDecl::AidlUnionDecl(const AidlLocation& location, const std::string& name, const std::string& package, const Comments& comments, std::vector* type_params, std::vector>* members) : AidlParcelable(location, name, package, comments, {} /*headers*/, type_params, members) {} bool AidlUnionDecl::CheckValid(const AidlTypenames& typenames) const { // visit parents if (!AidlParcelable::CheckValid(typenames)) { return false; } // unions provide getters always if (!CheckValidForGetterNames()) { return false; } // now, visit self! bool success = true; // TODO(b/170807936) do we need to allow ParcelableHolder in union? for (const auto& v : GetFields()) { if (v->GetType().GetName() == "ParcelableHolder") { AIDL_ERROR(*v) << "A union can't have a member of ParcelableHolder '" << v->GetName() << "'"; success = false; } } if (GetFields().empty()) { AIDL_ERROR(*this) << "The union '" << this->GetName() << "' has no fields."; return false; } // first member should have useful default value (implicit or explicit) const auto& first = GetFields()[0]; if (!first->HasUsefulDefaultValue()) { // Most types can be initialized without a default value. For example, // interface types are inherently nullable. But, enum types should have // an explicit default value. if (!first->GetType().IsArray() && typenames.GetEnumDeclaration(first->GetType())) { AIDL_ERROR(first) << "The union's first member should have a useful default value. Enum types can be " "initialized with a reference. (e.g. ... = MyEnum.FOO;)"; return false; } // In Java, array types are initialized as null without a default value. To be sure that default // initialized unions are accepted by other backends we require arrays also have a default // value. if (first->GetType().IsArray()) { AIDL_ERROR(first) << "The union's first member should have a useful default value. Arrays can be " "initialized with values(e.g. ... = { values... };) or marked as @nullable."; return false; } } return success; } AidlInterface::AidlInterface(const AidlLocation& location, const std::string& name, const Comments& comments, bool oneway, const std::string& package, std::vector>* members) : AidlDefinedType(location, name, comments, package, members), oneway_annotation_(oneway) { for (auto& m : GetMethods()) { m.get()->ApplyInterfaceOneway(oneway); } } bool AidlInterface::CheckValid(const AidlTypenames& typenames) const { if (!AidlDefinedType::CheckValid(typenames)) { return false; } // Has to be a pointer due to deleting copy constructor. No idea why. map method_names; for (const auto& m : GetMethods()) { if (!m->CheckValid(typenames)) { return false; } auto it = method_names.find(m->GetName()); // prevent duplicate methods if (it == method_names.end()) { method_names[m->GetName()] = m.get(); } else { AIDL_ERROR(m) << "attempt to redefine method " << m->GetName() << ":"; AIDL_ERROR(it->second) << "previously defined here."; return false; } static set reserved_methods{"asBinder()", "getInterfaceHash()", "getInterfaceVersion()", "getTransactionName(int)"}; if (reserved_methods.find(m->Signature()) != reserved_methods.end()) { AIDL_ERROR(m) << " method " << m->Signature() << " is reserved for internal use."; return false; } if (!CheckValidPermissionAnnotations(*m.get())) { return false; } } return true; } bool AidlInterface::CheckValidPermissionAnnotations(const AidlMethod& m) const { if (IsPermissionAnnotated() && m.GetType().IsPermissionAnnotated()) { AIDL_ERROR(m) << "The interface " << GetName() << " uses a permission annotation but the method " << m.GetName() << " is also annotated.\n" << "Consider distributing the annotation to each method."; return false; } return true; } bool AidlInterface::UsesPermissions() const { if (EnforceExpression()) { return true; } for (auto& m : GetMethods()) { if (m->GetType().EnforceExpression()) { return true; } } return false; } std::string AidlInterface::GetDescriptor() const { std::string annotatedDescriptor = AidlAnnotatable::GetDescriptor(); if (annotatedDescriptor != "") { return annotatedDescriptor; } return GetCanonicalName(); } AidlDocument::AidlDocument(const AidlLocation& location, const Comments& comments, std::vector imports, std::vector> defined_types, bool is_preprocessed) : AidlCommentable(location, comments), AidlScope(this), imports_(std::move(imports)), defined_types_(std::move(defined_types)), is_preprocessed_(is_preprocessed) { for (const auto& t : defined_types_) { t->SetEnclosingScope(this); } } // Resolves type name in the current document. // - built-in types // - imported types // - top-level type std::string AidlDocument::ResolveName(const std::string& name) const { if (AidlTypenames::IsBuiltinTypename(name)) { return name; } const auto first_dot = name.find_first_of('.'); // For "Outer.Inner", we look up "Outer" in the import list. const std::string class_name = (first_dot == std::string::npos) ? name : name.substr(0, first_dot); // Keep ".Inner", to make a fully-qualified name const std::string nested_type = (first_dot == std::string::npos) ? "" : name.substr(first_dot); for (const auto& import : Imports()) { if (SimpleName(import) == class_name) { return import + nested_type; } } // check if it is a top-level type. for (const auto& type : DefinedTypes()) { if (type->GetName() == class_name) { return type->GetCanonicalName() + nested_type; } } // name itself might be fully-qualified name. return name; }