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