| /* |
| * 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 <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <algorithm> |
| #include <iostream> |
| #include <set> |
| #include <sstream> |
| #include <string> |
| #include <utility> |
| |
| #include <android-base/parsedouble.h> |
| #include <android-base/parseint.h> |
| #include <android-base/strings.h> |
| |
| #include "aidl_language_y.h" |
| #include "comments.h" |
| #include "logging.h" |
| |
| #include "aidl.h" |
| |
| #ifdef _WIN32 |
| int isatty(int fd) |
| { |
| return (fd == 0); |
| } |
| #endif |
| |
| using android::aidl::IoDelegate; |
| using android::base::Join; |
| 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<std::string> 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(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(); |
| } |
| |
| static const AidlTypeSpecifier kStringType{AIDL_LOCATION_HERE, "String", false, nullptr, |
| Comments{}}; |
| static const AidlTypeSpecifier kStringArrayType{AIDL_LOCATION_HERE, "String", true, nullptr, |
| Comments{}}; |
| static const AidlTypeSpecifier kIntType{AIDL_LOCATION_HERE, "int", false, nullptr, Comments{}}; |
| static const AidlTypeSpecifier kLongType{AIDL_LOCATION_HERE, "long", false, nullptr, Comments{}}; |
| static const AidlTypeSpecifier kBooleanType{AIDL_LOCATION_HERE, "boolean", false, nullptr, |
| Comments{}}; |
| |
| const std::vector<AidlAnnotation::Schema>& AidlAnnotation::AllSchemas() { |
| static const std::vector<Schema> kSchemas{ |
| {AidlAnnotation::Type::NULLABLE, "nullable", CONTEXT_TYPE_SPECIFIER, {}}, |
| {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::HIDE, "Hide", CONTEXT_TYPE | CONTEXT_MEMBER, {}}, |
| {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, |
| {{"toString", kBooleanType}, {"equals", kBooleanType}}}, |
| {AidlAnnotation::Type::JAVA_ONLY_IMMUTABLE, |
| "JavaOnlyImmutable", |
| CONTEXT_TYPE_STRUCTURED_PARCELABLE | CONTEXT_TYPE_UNION | |
| CONTEXT_TYPE_UNSTRUCTURED_PARCELABLE, |
| {}}, |
| {AidlAnnotation::Type::FIXED_SIZE, "FixedSize", CONTEXT_TYPE_STRUCTURED_PARCELABLE, {}}, |
| {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}}}, |
| }; |
| 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<size_t>(type); |
| __builtin_unreachable(); |
| } |
| |
| AidlAnnotation* AidlAnnotation::Parse( |
| const AidlLocation& location, const string& name, |
| std::map<std::string, std::shared_ptr<AidlConstantValue>>* 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 nullptr; |
| } |
| if (parameter_list == nullptr) { |
| return new AidlAnnotation(location, *schema, {}, comments); |
| } |
| |
| return new AidlAnnotation(location, *schema, std::move(*parameter_list), comments); |
| } |
| |
| AidlAnnotation::AidlAnnotation( |
| const AidlLocation& location, const Schema& schema, |
| std::map<std::string, std::shared_ptr<AidlConstantValue>>&& parameters, |
| const Comments& comments) |
| : AidlNode(location, comments), schema_(schema), parameters_(std::move(parameters)) {} |
| |
| struct ConstReferenceFinder : AidlVisitor { |
| const AidlConstantReference* found; |
| 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<AidlConstantValue>& param = name_and_param.second; |
| |
| const ParamType* 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; |
| } |
| } |
| return success; |
| } |
| |
| // 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<uint32_t>(context)) { |
| return true; |
| } |
| const static map<TargetContext, string> context_name_map{ |
| {CONTEXT_TYPE_INTERFACE, "interface"}, |
| {CONTEXT_TYPE_ENUM, "enum"}, |
| {CONTEXT_TYPE_STRUCTURED_PARCELABLE, "structured parcelable"}, |
| {CONTEXT_TYPE_UNION, "union"}, |
| {CONTEXT_TYPE_UNSTRUCTURED_PARCELABLE, "parcelable"}, |
| {CONTEXT_CONST, "constant"}, |
| {CONTEXT_FIELD, "field"}, |
| {CONTEXT_METHOD, "method"}, |
| {CONTEXT_TYPE_SPECIFIER, "type"}, |
| }; |
| vector<string> 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 annotate {" |
| << Join(available, ", ") << "}."; |
| return false; |
| } |
| |
| std::map<std::string, std::string> AidlAnnotation::AnnotationParams( |
| const ConstantValueDecorator& decorator) const { |
| std::map<std::string, std::string> raw_params; |
| for (const auto& name_and_param : parameters_) { |
| const std::string& param_name = name_and_param.first; |
| const std::shared_ptr<AidlConstantValue>& param = name_and_param.second; |
| const ParamType* 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<string> 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<void(const AidlNode&)> traverse) const { |
| for (const auto& [name, value] : parameters_) { |
| (void)name; |
| traverse(*value); |
| } |
| } |
| |
| static const AidlAnnotation* GetAnnotation(const vector<AidlAnnotation>& 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; |
| } |
| } |
| 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::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 { |
| return GetAnnotation(annotations_, 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); |
| } |
| |
| const AidlAnnotation* AidlAnnotatable::RustDerive() const { |
| return GetAnnotation(annotations_, AidlAnnotation::Type::RUST_DERIVE); |
| } |
| |
| const AidlAnnotation* AidlAnnotatable::BackingType() const { |
| return GetAnnotation(annotations_, AidlAnnotation::Type::BACKING); |
| } |
| |
| std::vector<std::string> AidlAnnotatable::SuppressWarnings() const { |
| auto annot = GetAnnotation(annotations_, AidlAnnotation::Type::SUPPRESS_WARNINGS); |
| if (annot) { |
| auto names = annot->ParamValue<std::vector<std::string>>("value"); |
| AIDL_FATAL_IF(!names.has_value(), this); |
| return std::move(names.value()); |
| } |
| return {}; |
| } |
| |
| bool AidlAnnotatable::IsStableApiParcelable(Options::Language lang) const { |
| return lang == Options::Language::JAVA && |
| GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_STABLE_PARCELABLE); |
| } |
| |
| bool AidlAnnotatable::IsHide() const { |
| return GetAnnotation(annotations_, AidlAnnotation::Type::HIDE); |
| } |
| |
| bool AidlAnnotatable::JavaDerive(const std::string& method) const { |
| auto annotation = GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_DERIVE); |
| if (annotation != nullptr) { |
| return annotation->ParamValue<bool>(method).value_or(false); |
| } |
| return false; |
| } |
| |
| std::string AidlAnnotatable::GetDescriptor() const { |
| auto annotation = GetAnnotation(annotations_, AidlAnnotation::Type::DESCRIPTOR); |
| if (annotation != nullptr) { |
| return annotation->ParamValue<std::string>("value").value(); |
| } |
| return ""; |
| } |
| |
| bool AidlAnnotatable::CheckValid(const AidlTypenames&) const { |
| for (const auto& annotation : GetAnnotations()) { |
| if (!annotation.CheckValid()) { |
| return false; |
| } |
| } |
| |
| std::map<AidlAnnotation::Type, AidlLocation> 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<string> 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, |
| bool is_array, |
| vector<unique_ptr<AidlTypeSpecifier>>* type_params, |
| const Comments& comments) |
| : AidlAnnotatable(location, comments), |
| AidlParameterizable<unique_ptr<AidlTypeSpecifier>>(type_params), |
| unresolved_name_(unresolved_name), |
| is_array_(is_array), |
| split_name_(Split(unresolved_name, ".")) {} |
| |
| const AidlTypeSpecifier& AidlTypeSpecifier::ArrayBase() const { |
| AIDL_FATAL_IF(!is_array_, this); |
| // Declaring array of generic type cannot happen, it is grammar error. |
| AIDL_FATAL_IF(IsGeneric(), this); |
| |
| if (!array_base_) { |
| array_base_.reset(new AidlTypeSpecifier(*this)); |
| array_base_->is_array_ = false; |
| } |
| return *array_base_; |
| } |
| |
| string AidlTypeSpecifier::Signature() const { |
| string ret = GetName(); |
| if (IsGeneric()) { |
| vector<string> arg_names; |
| for (const auto& ta : GetTypeParameters()) { |
| arg_names.emplace_back(ta->Signature()); |
| } |
| ret += "<" + Join(arg_names, ",") + ">"; |
| } |
| if (IsArray()) { |
| ret += "[]"; |
| } |
| return ret; |
| } |
| |
| string AidlTypeSpecifier::ToString() const { |
| string ret = Signature(); |
| string annotations = AidlAnnotatable::ToString(); |
| if (annotations != "") { |
| ret = annotations + " " + ret; |
| } |
| return ret; |
| } |
| |
| bool AidlTypeSpecifier::Resolve(const AidlTypenames& typenames) { |
| AIDL_FATAL_IF(IsResolved(), this); |
| AidlTypenames::ResolvedTypename result = typenames.ResolveTypename(unresolved_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; |
| } |
| const AidlTypeSpecifier& contained_type = *GetTypeParameters()[0]; |
| if (contained_type.IsArray()) { |
| AIDL_ERROR(this) |
| << "List of arrays is not supported. List<T> supports parcelable/union, String, " |
| "IBinder, and ParcelFileDescriptor."; |
| 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. List<T> supports parcelable/union, String, " |
| "IBinder, and ParcelFileDescriptor."; |
| return false; |
| } |
| } else { // Defined types |
| if (typenames.GetInterface(contained_type)) { |
| AIDL_ERROR(this) << "List<" << contained_type_name |
| << "> is not supported. List<T> supports parcelable/union, String, " |
| "IBinder, and ParcelFileDescriptor."; |
| 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<String>."; |
| 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()) { |
| const auto defined_type = typenames.TryGetDefinedType(GetName()); |
| if (defined_type != nullptr && defined_type->AsInterface() != nullptr) { |
| AIDL_ERROR(this) << "Binder type cannot be an array"; |
| return false; |
| } |
| if (GetName() == "ParcelableHolder") { |
| AIDL_ERROR(this) << "Arrays of ParcelableHolder 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; |
| } |
| } |
| return true; |
| } |
| |
| std::string AidlConstantValueDecorator(const AidlTypeSpecifier& type, |
| const std::string& raw_value) { |
| if (type.IsArray()) { |
| return 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 \"" << raw_value << "\""; |
| return type.GetName() + "." + raw_value.substr(raw_value.find_last_of('.') + 1); |
| } |
| return raw_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<char>(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<void(const AidlNode&)> traverse) const { |
| traverse(GetType()); |
| if (IsDefaultUserSpecified()) { |
| traverse(*GetDefaultValue()); |
| } |
| } |
| |
| 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<AidlArgument::Direction>& directions) { |
| std::vector<std::string> 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) |
| : AidlCommentable(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(); |
| if (!valid) return false; |
| |
| const static set<string> kSupportedConstTypes = {"String", "byte", "int", "long"}; |
| 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<std::unique_ptr<AidlArgument>>* args, |
| const Comments& comments) |
| : AidlMethod(location, oneway, type, name, args, comments, 0, true) { |
| has_id_ = false; |
| } |
| |
| AidlMethod::AidlMethod(const AidlLocation& location, bool oneway, AidlTypeSpecifier* type, |
| const std::string& name, std::vector<std::unique_ptr<AidlArgument>>* args, |
| const Comments& comments, int id, bool is_user_defined) |
| : AidlMember(location, comments), |
| oneway_(oneway), |
| type_(type), |
| name_(name), |
| arguments_(std::move(*args)), |
| id_(id), |
| is_user_defined_(is_user_defined) { |
| has_id_ = true; |
| delete args; |
| for (const unique_ptr<AidlArgument>& 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<string> arg_signatures; |
| for (const auto& arg : GetArguments()) { |
| arg_signatures.emplace_back(arg->GetType().Signature()); |
| } |
| return GetName() + "(" + Join(arg_signatures, ", ") + ")"; |
| } |
| |
| string AidlMethod::ToString() const { |
| vector<string> 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; |
| } |
| |
| AidlDefinedType::AidlDefinedType(const AidlLocation& location, const std::string& name, |
| const Comments& comments, const std::string& package, |
| std::vector<std::unique_ptr<AidlMember>>* members) |
| : AidlAnnotatable(location, comments), |
| name_(name), |
| package_(package), |
| split_package_(package.empty() ? std::vector<std::string>() |
| : android::base::Split(package, ".")) { |
| if (members) { |
| for (auto& m : *members) { |
| if (auto constant = m->AsConstantDeclaration(); constant) { |
| constants_.emplace_back(constant); |
| } else if (auto variable = m->AsVariableDeclaration(); variable) { |
| variables_.emplace_back(variable); |
| } else if (auto method = m->AsMethod(); method) { |
| methods_.emplace_back(method); |
| } else { |
| AIDL_FATAL(*m); |
| } |
| 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 (package_.empty()) { |
| return GetName(); |
| } |
| return GetPackage() + "." + GetName(); |
| } |
| |
| bool AidlDefinedType::CheckValidWithMembers(const AidlTypenames& typenames) const { |
| bool success = true; |
| |
| for (const auto& v : GetFields()) { |
| const bool field_valid = v->CheckValid(typenames); |
| success = success && field_valid; |
| } |
| |
| // field names should be unique |
| std::set<std::string> 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; |
| } |
| } |
| } |
| |
| set<string> 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<std::string> 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; |
| } |
| |
| AidlParcelable::AidlParcelable(const AidlLocation& location, const std::string& name, |
| const std::string& package, const Comments& comments, |
| const std::string& cpp_header, std::vector<std::string>* type_params, |
| std::vector<std::unique_ptr<AidlMember>>* members) |
| : AidlDefinedType(location, name, comments, package, members), |
| AidlParameterizable<std::string>(type_params), |
| cpp_header_(cpp_header) { |
| // Strip off quotation marks if we actually have a cpp header. |
| if (cpp_header_.length() >= 2) { |
| cpp_header_ = cpp_header_.substr(1, cpp_header_.length() - 2); |
| } |
| } |
| template <typename T> |
| AidlParameterizable<T>::AidlParameterizable(const AidlParameterizable& other) { |
| // Copying is not supported if it has type parameters. |
| // It doesn't make a problem because only ArrayBase() makes a copy, |
| // and it can be called only if a type is not generic. |
| AIDL_FATAL_IF(other.IsGeneric(), AIDL_LOCATION_HERE); |
| } |
| |
| template <typename T> |
| bool AidlParameterizable<T>::CheckValid() const { |
| return true; |
| }; |
| |
| template <> |
| bool AidlParameterizable<std::string>::CheckValid() const { |
| if (!IsGeneric()) { |
| return true; |
| } |
| std::unordered_set<std::string> 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<std::string>::CheckValid()) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| AidlStructuredParcelable::AidlStructuredParcelable( |
| const AidlLocation& location, const std::string& name, const std::string& package, |
| const Comments& comments, std::vector<std::string>* type_params, |
| std::vector<std::unique_ptr<AidlMember>>* members) |
| : AidlParcelable(location, name, package, comments, "" /*cpp_header*/, type_params, members) {} |
| |
| bool AidlStructuredParcelable::CheckValid(const AidlTypenames& typenames) const { |
| if (!AidlParcelable::CheckValid(typenames)) { |
| 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; |
| } |
| } |
| } |
| |
| 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(const AidlTypenames& typenames, |
| Options::Language lang) const { |
| if (IsGeneric()) { |
| const auto& types = GetTypeParameters(); |
| for (const auto& arg : types) { |
| if (!arg->LanguageSpecificCheckValid(typenames, lang)) { |
| return false; |
| } |
| } |
| } |
| |
| if ((lang == Options::Language::NDK || lang == Options::Language::RUST) && IsArray() && |
| GetName() == "IBinder") { |
| AIDL_ERROR(this) << "The " << to_string(lang) << " backend does not support array of IBinder"; |
| return false; |
| } |
| if (lang == Options::Language::RUST && GetName() == "ParcelableHolder") { |
| // TODO(b/146611855): Remove it when Rust backend supports ParcelableHolder |
| AIDL_ERROR(this) << "The Rust backend does not support ParcelableHolder yet."; |
| return false; |
| } |
| if ((lang == Options::Language::NDK || lang == Options::Language::RUST) && IsArray() && |
| IsNullable()) { |
| if (GetName() == "ParcelFileDescriptor") { |
| AIDL_ERROR(this) << "The " << to_string(lang) |
| << " backend does not support nullable array of ParcelFileDescriptor"; |
| return false; |
| } |
| |
| const auto defined_type = typenames.TryGetDefinedType(GetName()); |
| if (defined_type != nullptr && defined_type->AsParcelable() != nullptr) { |
| AIDL_ERROR(this) << "The " << to_string(lang) |
| << " backend does not support nullable array of parcelable"; |
| return false; |
| } |
| } |
| 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 (this->IsGeneric()) { |
| if (this->GetName() == "List") { |
| if (lang == Options::Language::NDK) { |
| const AidlTypeSpecifier& contained_type = *GetTypeParameters()[0]; |
| const string& contained_type_name = contained_type.GetName(); |
| if (typenames.GetInterface(contained_type)) { |
| AIDL_ERROR(this) << "List<" << contained_type_name |
| << "> is not supported. List in NDK doesn't support interface."; |
| return false; |
| } |
| if (contained_type_name == "IBinder") { |
| AIDL_ERROR(this) << "List<" << contained_type_name |
| << "> is not supported. List in NDK doesn't support IBinder."; |
| return false; |
| } |
| } |
| } |
| } |
| |
| if (this->IsArray()) { |
| if (this->GetName() == "List" || this->GetName() == "Map" || |
| this->GetName() == "CharSequence") { |
| AIDL_ERROR(this) << this->GetName() << "[] is not supported."; |
| 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 AidlParcelable::LanguageSpecificCheckValid(const AidlTypenames& /*typenames*/, |
| Options::Language lang) const { |
| if (lang == Options::Language::CPP || lang == Options::Language::NDK) { |
| const AidlParcelable* unstructured_parcelable = this->AsUnstructuredParcelable(); |
| if (unstructured_parcelable != nullptr) { |
| if (unstructured_parcelable->GetCppHeader().empty()) { |
| AIDL_ERROR(unstructured_parcelable) |
| << "Unstructured parcelable must have C++ header defined."; |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| // TODO: we should treat every backend all the same in future. |
| bool AidlStructuredParcelable::LanguageSpecificCheckValid(const AidlTypenames& typenames, |
| Options::Language lang) const { |
| if (!AidlParcelable::LanguageSpecificCheckValid(typenames, lang)) { |
| return false; |
| } |
| for (const auto& v : this->GetFields()) { |
| if (!v->GetType().LanguageSpecificCheckValid(typenames, lang)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| 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<std::unique_ptr<AidlEnumerator>>* enumerators, |
| const std::string& package, const Comments& comments) |
| : AidlDefinedType(location, name, comments, package, nullptr), |
| enumerators_(std::move(*enumerators)) { |
| // Fill missing enumerator values with <prev + 1> |
| // 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>(AidlConstantValue::Integral(loc, "0"))); |
| } else { |
| auto prev_value = std::make_unique<AidlConstantReference>(loc, previous->GetName()); |
| enumerator->SetValue(std::make_unique<AidlBinaryConstExpression>( |
| loc, std::move(prev_value), "+", |
| std::unique_ptr<AidlConstantValue>(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<std::string>("type").value(); |
| backing_type_ = |
| std::make_unique<AidlTypeSpecifier>(annot->GetLocation(), type, false, nullptr, Comments{}); |
| } else { |
| // Default to byte type for enums. |
| backing_type_ = |
| std::make_unique<AidlTypeSpecifier>(AIDL_LOCATION_HERE, "byte", false, nullptr, Comments{}); |
| } |
| // Autofill() is called after type resolution, we resolve the backing type manually. |
| if (!backing_type_->Resolve(typenames)) { |
| AIDL_ERROR(this) << "Invalid backing type: " << backing_type_->GetName(); |
| } |
| 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<std::string>* type_params, |
| std::vector<std::unique_ptr<AidlMember>>* members) |
| : AidlParcelable(location, name, package, comments, "" /*cpp_header*/, 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; |
| } |
| |
| // TODO: we should treat every backend all the same in future. |
| bool AidlUnionDecl::LanguageSpecificCheckValid(const AidlTypenames& typenames, |
| Options::Language lang) const { |
| if (!AidlParcelable::LanguageSpecificCheckValid(typenames, lang)) { |
| return false; |
| } |
| for (const auto& v : this->GetFields()) { |
| if (!v->GetType().LanguageSpecificCheckValid(typenames, lang)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // TODO: we should treat every backend all the same in future. |
| bool AidlInterface::LanguageSpecificCheckValid(const AidlTypenames& typenames, |
| Options::Language lang) const { |
| for (const auto& m : this->GetMethods()) { |
| if (!m->GetType().LanguageSpecificCheckValid(typenames, lang)) { |
| return false; |
| } |
| for (const auto& arg : m->GetArguments()) { |
| if (!arg->GetType().LanguageSpecificCheckValid(typenames, lang)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| AidlInterface::AidlInterface(const AidlLocation& location, const std::string& name, |
| const Comments& comments, bool oneway, const std::string& package, |
| std::vector<std::unique_ptr<AidlMember>>* members) |
| : AidlDefinedType(location, name, comments, package, members) { |
| 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<string, const AidlMethod*> method_names; |
| for (const auto& m : GetMethods()) { |
| if (!m->GetType().CheckValid(typenames)) { |
| return false; |
| } |
| |
| // TODO(b/156872582): Support it when ParcelableHolder supports every backend. |
| if (m->GetType().GetName() == "ParcelableHolder") { |
| AIDL_ERROR(m) << "ParcelableHolder cannot be a return type"; |
| return false; |
| } |
| if (m->IsOneway() && m->GetType().GetName() != "void") { |
| AIDL_ERROR(m) << "oneway method '" << m->GetName() << "' cannot return a value"; |
| return false; |
| } |
| |
| set<string> argument_names; |
| for (const auto& arg : m->GetArguments()) { |
| auto it = argument_names.find(arg->GetName()); |
| if (it != argument_names.end()) { |
| AIDL_ERROR(m) << "method '" << m->GetName() << "' has duplicate argument name '" |
| << arg->GetName() << "'"; |
| return false; |
| } |
| argument_names.insert(arg->GetName()); |
| |
| if (!arg->CheckValid(typenames)) { |
| return false; |
| } |
| |
| if (m->IsOneway() && arg->IsOut()) { |
| AIDL_ERROR(m) << "oneway method '" << m->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; |
| } |
| } |
| |
| 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<string> 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; |
| } |
| } |
| |
| bool success = true; |
| set<string> 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; |
| } |
| |
| std::string AidlInterface::GetDescriptor() const { |
| std::string annotatedDescriptor = AidlAnnotatable::GetDescriptor(); |
| if (annotatedDescriptor != "") { |
| return annotatedDescriptor; |
| } |
| return GetCanonicalName(); |
| } |
| |
| AidlImport::AidlImport(const AidlLocation& location, const std::string& needed_class, |
| const Comments& comments) |
| : AidlNode(location, comments), needed_class_(needed_class) {} |
| |
| // Resolves unresolved type name to fully qualified typename to import |
| // case #1: SimpleName --> import p.SimpleName |
| // case #2: Outer.Inner --> import p.Outer |
| // case #3: p.SimpleName --> (as is) |
| std::optional<std::string> AidlDocument::ResolveName(const std::string& unresolved_name) const { |
| std::string canonical_name; |
| const auto first_dot = unresolved_name.find_first_of('.'); |
| const std::string class_name = |
| (first_dot == std::string::npos) ? unresolved_name : unresolved_name.substr(0, first_dot); |
| for (const auto& import : Imports()) { |
| const auto& fq_name = import->GetNeededClass(); |
| const auto last_dot = fq_name.find_last_of('.'); |
| const std::string imported_type_name = |
| (last_dot == std::string::npos) ? fq_name : fq_name.substr(last_dot + 1); |
| if (imported_type_name == class_name) { |
| if (canonical_name != "" && canonical_name != fq_name) { |
| AIDL_ERROR(import) << "Ambiguous type: " << canonical_name << " vs. " << fq_name; |
| return {}; |
| } |
| canonical_name = fq_name; |
| } |
| } |
| // if not found, use unresolved_name as it is |
| if (canonical_name == "") { |
| return unresolved_name; |
| } |
| return canonical_name; |
| } |