• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "TypeChecker.h"
18 
19 #include <algorithm>
20 #include <string>
21 #include <unordered_map>
22 
23 #include <android-base/strings.h>
24 
25 #include "Common.h"
26 
27 using android::base::Result;
28 using android::properties::ParsePropertyInfoFile;
29 using android::properties::PropertyInfoEntry;
30 
31 namespace {
SyspropTypeToContextType(const sysprop::Property & prop)32 std::string SyspropTypeToContextType(const sysprop::Property& prop) {
33   switch (prop.type()) {
34     case sysprop::Integer:
35     case sysprop::Long:
36       return "int";
37     case sysprop::UInt:
38     case sysprop::ULong:
39       return "uint";
40     case sysprop::Double:
41       return "double";
42     case sysprop::Boolean:
43       return "bool";
44     case sysprop::Enum: {
45       // Sort both values and then join
46       auto prop_values = android::base::Split(prop.enum_values(), "|");
47       std::sort(prop_values.begin(), prop_values.end());
48       return "enum " + android::base::Join(prop_values, " ");
49     }
50     default:
51       // All other types (string, all kinds of list) should fall here
52       return "string";
53   }
54 }
55 
IsCompatible(const sysprop::Property & prop,const std::string & ctx_type)56 bool IsCompatible(const sysprop::Property& prop, const std::string& ctx_type) {
57   if (prop.type() == sysprop::Enum) {
58     // special case: we need to first sort values and then compare
59     auto prop_values = ParseEnumValues(prop.enum_values());
60     std::sort(prop_values.begin(), prop_values.end());
61 
62     // ctx_type must be "enum [value1] [value2] ..."
63     auto ctx_values = android::base::Split(ctx_type, " ");
64     if (ctx_values.empty() || ctx_values[0] != "enum") {
65       return false;
66     }
67     ctx_values.erase(ctx_values.begin());
68     std::sort(ctx_values.begin(), ctx_values.end());
69 
70     return prop_values == ctx_values;
71   }
72 
73   return SyspropTypeToContextType(prop) == ctx_type;
74 }
75 
GetTypeName(const sysprop::Property & prop)76 std::string GetTypeName(const sysprop::Property& prop) {
77   if (prop.type() == sysprop::Enum) {
78     return "Enum " + prop.enum_values();
79   }
80 
81   return sysprop::Type_Name(prop.type());
82 }
83 }  // namespace
84 
CheckPropertyTypes(const sysprop::SyspropLibraryApis & api,const std::vector<PropertyInfoEntry> & entries)85 Result<void> CheckPropertyTypes(const sysprop::SyspropLibraryApis& api,
86                                 const std::vector<PropertyInfoEntry>& entries) {
87   std::string err;
88 
89   // map from exact property names to types in property_contexts
90   std::unordered_map<std::string, std::string> types;
91   for (auto& entry : entries) {
92     // skip prefix entries.
93     if (!entry.exact_match) continue;
94 
95     // Duplicated prop check is intentionally skipped.
96     // Build will fail if any duplication happens.
97     types.emplace(entry.name, entry.type);
98   }
99 
100   for (auto& props : api.props()) {
101     for (auto& prop : props.prop()) {
102       std::vector<std::string> prop_names{prop.prop_name()};
103       std::string legacy_name = prop.legacy_prop_name();
104       if (!legacy_name.empty()) prop_names.push_back(legacy_name);
105 
106       for (auto& prop_name : prop_names) {
107         // Skip check if there is no exactly matched property.
108         auto itr = types.find(prop_name);
109         if (itr == types.end()) continue;
110 
111         if (!IsCompatible(prop, itr->second)) {
112           if (!err.empty()) err += "\n";
113           err += "Type of prop '" + prop_name +
114                  "' is incompatible with property_contexts\n";
115           err += "In sysprop_library: " + GetTypeName(prop) + "\n";
116           err += "In property_contexts: " + itr->second + " (should be '" +
117                  SyspropTypeToContextType(prop) + "')\n";
118         }
119       }
120     }
121   }
122 
123   if (err.empty())
124     return {};
125   else
126     return Errorf("{}", err);
127 }
128