/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TypeChecker.h" #include #include #include #include #include "Common.h" using android::base::Result; using android::properties::ParsePropertyInfoFile; using android::properties::PropertyInfoEntry; namespace { std::string SyspropTypeToContextType(const sysprop::Property& prop) { switch (prop.type()) { case sysprop::Integer: case sysprop::Long: return "int"; case sysprop::UInt: case sysprop::ULong: return "uint"; case sysprop::Double: return "double"; case sysprop::Boolean: return "bool"; case sysprop::Enum: { // Sort both values and then join auto prop_values = android::base::Split(prop.enum_values(), "|"); std::sort(prop_values.begin(), prop_values.end()); return "enum " + android::base::Join(prop_values, " "); } default: // All other types (string, all kinds of list) should fall here return "string"; } } bool IsCompatible(const sysprop::Property& prop, const std::string& ctx_type) { if (prop.type() == sysprop::Enum) { // special case: we need to first sort values and then compare auto prop_values = ParseEnumValues(prop.enum_values()); std::sort(prop_values.begin(), prop_values.end()); // ctx_type must be "enum [value1] [value2] ..." auto ctx_values = android::base::Split(ctx_type, " "); if (ctx_values.empty() || ctx_values[0] != "enum") { return false; } ctx_values.erase(ctx_values.begin()); std::sort(ctx_values.begin(), ctx_values.end()); return prop_values == ctx_values; } return SyspropTypeToContextType(prop) == ctx_type; } std::string GetTypeName(const sysprop::Property& prop) { if (prop.type() == sysprop::Enum) { return "Enum " + prop.enum_values(); } return sysprop::Type_Name(prop.type()); } } // namespace Result CheckPropertyTypes(const sysprop::SyspropLibraryApis& api, const std::vector& entries) { std::string err; // map from exact property names to types in property_contexts std::unordered_map types; for (auto& entry : entries) { // skip prefix entries. if (!entry.exact_match) continue; // Duplicated prop check is intentionally skipped. // Build will fail if any duplication happens. types.emplace(entry.name, entry.type); } for (auto& props : api.props()) { for (auto& prop : props.prop()) { std::vector prop_names{prop.prop_name()}; std::string legacy_name = prop.legacy_prop_name(); if (!legacy_name.empty()) prop_names.push_back(legacy_name); for (auto& prop_name : prop_names) { // Skip check if there is no exactly matched property. auto itr = types.find(prop_name); if (itr == types.end()) continue; if (!IsCompatible(prop, itr->second)) { if (!err.empty()) err += "\n"; err += "Type of prop '" + prop_name + "' is incompatible with property_contexts\n"; err += "In sysprop_library: " + GetTypeName(prop) + "\n"; err += "In property_contexts: " + itr->second + " (should be '" + SyspropTypeToContextType(prop) + "')\n"; } } } } if (err.empty()) return {}; else return Errorf("{}", err); }