/proc/self/cwd/eval/compiler/resolver.cc
Line | Count | Source |
1 | | // Copyright 2021 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include "eval/compiler/resolver.h" |
16 | | |
17 | | #include <cstddef> |
18 | | #include <cstdint> |
19 | | #include <cstdio> |
20 | | #include <string> |
21 | | #include <utility> |
22 | | #include <vector> |
23 | | |
24 | | #include "absl/base/no_destructor.h" |
25 | | #include "absl/container/flat_hash_map.h" |
26 | | #include "absl/status/statusor.h" |
27 | | #include "absl/strings/match.h" |
28 | | #include "absl/strings/str_cat.h" |
29 | | #include "absl/strings/str_split.h" |
30 | | #include "absl/strings/string_view.h" |
31 | | #include "absl/types/optional.h" |
32 | | #include "absl/types/span.h" |
33 | | #include "common/kind.h" |
34 | | #include "common/type.h" |
35 | | #include "common/type_reflector.h" |
36 | | #include "common/value.h" |
37 | | #include "internal/status_macros.h" |
38 | | #include "runtime/function_overload_reference.h" |
39 | | #include "runtime/function_registry.h" |
40 | | #include "runtime/type_registry.h" |
41 | | |
42 | | namespace google::api::expr::runtime { |
43 | | namespace { |
44 | | |
45 | | using ::cel::TypeValue; |
46 | | using ::cel::Value; |
47 | | using ::cel::runtime_internal::GetEnumValueTable; |
48 | | |
49 | 10.3k | std::vector<std::string> MakeNamespaceCandidates(absl::string_view container) { |
50 | 10.3k | std::vector<std::string> namespace_prefixes; |
51 | 10.3k | std::string prefix = ""; |
52 | 10.3k | namespace_prefixes.push_back(prefix); |
53 | 10.3k | auto container_elements = absl::StrSplit(container, '.'); |
54 | 10.3k | for (const auto& elem : container_elements) { |
55 | | // Tolerate trailing / leading '.'. |
56 | 10.3k | if (elem.empty()) { |
57 | 10.3k | continue; |
58 | 10.3k | } |
59 | 0 | absl::StrAppend(&prefix, elem, "."); |
60 | | // longest prefix first. |
61 | 0 | namespace_prefixes.insert(namespace_prefixes.begin(), prefix); |
62 | 0 | } |
63 | 10.3k | return namespace_prefixes; |
64 | 10.3k | } |
65 | | |
66 | | } // namespace |
67 | | |
68 | | Resolver::Resolver(absl::string_view container, |
69 | | const cel::FunctionRegistry& function_registry, |
70 | | const cel::TypeRegistry& type_registry, |
71 | | const cel::TypeReflector& type_reflector, |
72 | | bool resolve_qualified_type_identifiers) |
73 | 10.3k | : namespace_prefixes_(MakeNamespaceCandidates(container)), |
74 | 10.3k | enum_value_map_(GetEnumValueTable(type_registry)), |
75 | 10.3k | function_registry_(function_registry), |
76 | 10.3k | type_reflector_(type_reflector), |
77 | 10.3k | resolve_qualified_type_identifiers_(resolve_qualified_type_identifiers) {} |
78 | | |
79 | | std::vector<std::string> Resolver::FullyQualifiedNames(absl::string_view name, |
80 | 10.3k | int64_t expr_id) const { |
81 | | // TODO(issues/105): refactor the reference resolution into this method. |
82 | | // and handle the case where this id is in the reference map as either a |
83 | | // function name or identifier name. |
84 | 10.3k | std::vector<std::string> names; |
85 | | |
86 | 10.3k | auto prefixes = GetPrefixesFor(name); |
87 | 10.3k | names.reserve(prefixes.size()); |
88 | 10.3k | for (const auto& prefix : prefixes) { |
89 | 10.3k | std::string fully_qualified_name = absl::StrCat(prefix, name); |
90 | 10.3k | names.push_back(fully_qualified_name); |
91 | 10.3k | } |
92 | 10.3k | return names; |
93 | 10.3k | } |
94 | | |
95 | | absl::Span<const std::string> Resolver::GetPrefixesFor( |
96 | 357k | absl::string_view& name) const { |
97 | 357k | static const absl::NoDestructor<std::string> kEmptyPrefix(""); |
98 | 357k | if (absl::StartsWith(name, ".")) { |
99 | 1.60k | name = name.substr(1); |
100 | 1.60k | return absl::MakeConstSpan(kEmptyPrefix.get(), 1); |
101 | 1.60k | } |
102 | 355k | return namespace_prefixes_; |
103 | 357k | } |
104 | | |
105 | | std::optional<cel::Value> Resolver::FindConstant(absl::string_view name, |
106 | 68.6k | int64_t expr_id) const { |
107 | 68.6k | auto prefixes = GetPrefixesFor(name); |
108 | 68.6k | for (const auto& prefix : prefixes) { |
109 | 68.6k | std::string qualified_name = absl::StrCat(prefix, name); |
110 | | // Attempt to resolve the fully qualified name to a known enum. |
111 | 68.6k | auto enum_entry = enum_value_map_->find(qualified_name); |
112 | 68.6k | if (enum_entry != enum_value_map_->end()) { |
113 | 48 | return enum_entry->second; |
114 | 48 | } |
115 | | // Attempt to resolve the fully qualified name to a known type. |
116 | 68.5k | if (resolve_qualified_type_identifiers_) { |
117 | 0 | auto type_value = type_reflector_.FindType(qualified_name); |
118 | 0 | if (type_value.ok() && type_value->has_value()) { |
119 | 0 | return TypeValue(**type_value); |
120 | 0 | } |
121 | 0 | } |
122 | 68.5k | } |
123 | | |
124 | 68.5k | if (!resolve_qualified_type_identifiers_ && !absl::StrContains(name, '.')) { |
125 | 59.5k | auto type_value = type_reflector_.FindType(name); |
126 | | |
127 | 59.5k | if (type_value.ok() && type_value->has_value()) { |
128 | 717 | return TypeValue(**type_value); |
129 | 717 | } |
130 | 59.5k | } |
131 | 67.8k | return absl::nullopt; |
132 | 68.5k | } |
133 | | |
134 | | std::vector<cel::FunctionOverloadReference> Resolver::FindOverloads( |
135 | | absl::string_view name, bool receiver_style, |
136 | 10.3k | const std::vector<cel::Kind>& types, int64_t expr_id) const { |
137 | | // Resolve the fully qualified names and then search the function registry |
138 | | // for possible matches. |
139 | 10.3k | std::vector<cel::FunctionOverloadReference> funcs; |
140 | 10.3k | auto names = FullyQualifiedNames(name, expr_id); |
141 | 20.6k | for (auto it = names.begin(); it != names.end(); it++) { |
142 | | // Only one set of overloads is returned along the namespace hierarchy as |
143 | | // the function name resolution follows the same behavior as variable name |
144 | | // resolution, meaning the most specific definition wins. This is different |
145 | | // from how C++ namespaces work, as they will accumulate the overload set |
146 | | // over the namespace hierarchy. |
147 | 10.3k | funcs = function_registry_.FindStaticOverloads(*it, receiver_style, types); |
148 | 10.3k | if (!funcs.empty()) { |
149 | 0 | return funcs; |
150 | 0 | } |
151 | 10.3k | } |
152 | 10.3k | return funcs; |
153 | 10.3k | } |
154 | | |
155 | | std::vector<cel::FunctionOverloadReference> Resolver::FindOverloads( |
156 | | absl::string_view name, bool receiver_style, size_t arity, |
157 | 138k | int64_t expr_id) const { |
158 | 138k | std::vector<cel::FunctionOverloadReference> funcs; |
159 | 138k | auto prefixes = GetPrefixesFor(name); |
160 | 138k | for (const auto& prefix : prefixes) { |
161 | 138k | std::string qualified_name = absl::StrCat(prefix, name); |
162 | | // Only one set of overloads is returned along the namespace hierarchy as |
163 | | // the function name resolution follows the same behavior as variable name |
164 | | // resolution, meaning the most specific definition wins. This is different |
165 | | // from how C++ namespaces work, as they will accumulate the overload set |
166 | | // over the namespace hierarchy. |
167 | 138k | funcs = function_registry_.FindStaticOverloadsByArity( |
168 | 138k | qualified_name, receiver_style, arity); |
169 | 138k | if (!funcs.empty()) { |
170 | 137k | return funcs; |
171 | 137k | } |
172 | 138k | } |
173 | 122 | return funcs; |
174 | 138k | } |
175 | | |
176 | | std::vector<cel::FunctionRegistry::LazyOverload> Resolver::FindLazyOverloads( |
177 | | absl::string_view name, bool receiver_style, |
178 | 0 | const std::vector<cel::Kind>& types, int64_t expr_id) const { |
179 | | // Resolve the fully qualified names and then search the function registry |
180 | | // for possible matches. |
181 | 0 | std::vector<cel::FunctionRegistry::LazyOverload> funcs; |
182 | 0 | auto names = FullyQualifiedNames(name, expr_id); |
183 | 0 | for (const auto& name : names) { |
184 | 0 | funcs = function_registry_.FindLazyOverloads(name, receiver_style, types); |
185 | 0 | if (!funcs.empty()) { |
186 | 0 | return funcs; |
187 | 0 | } |
188 | 0 | } |
189 | 0 | return funcs; |
190 | 0 | } |
191 | | |
192 | | std::vector<cel::FunctionRegistry::LazyOverload> Resolver::FindLazyOverloads( |
193 | | absl::string_view name, bool receiver_style, size_t arity, |
194 | 138k | int64_t expr_id) const { |
195 | 138k | std::vector<cel::FunctionRegistry::LazyOverload> funcs; |
196 | 138k | auto prefixes = GetPrefixesFor(name); |
197 | 138k | for (const auto& prefix : prefixes) { |
198 | 138k | std::string qualified_name = absl::StrCat(prefix, name); |
199 | 138k | funcs = function_registry_.FindLazyOverloadsByArity(name, receiver_style, |
200 | 138k | arity); |
201 | 138k | if (!funcs.empty()) { |
202 | 0 | return funcs; |
203 | 0 | } |
204 | 138k | } |
205 | 138k | return funcs; |
206 | 138k | } |
207 | | |
208 | | absl::StatusOr<std::optional<std::pair<std::string, cel::Type>>> |
209 | 2.16k | Resolver::FindType(absl::string_view name, int64_t expr_id) const { |
210 | 2.16k | auto prefixes = GetPrefixesFor(name); |
211 | 2.16k | for (auto& prefix : prefixes) { |
212 | 2.16k | std::string qualified_name = absl::StrCat(prefix, name); |
213 | 2.16k | CEL_ASSIGN_OR_RETURN(auto maybe_type, |
214 | 2.16k | type_reflector_.FindType(qualified_name)); |
215 | 2.16k | if (maybe_type.has_value()) { |
216 | 1.93k | return std::make_pair(std::move(qualified_name), std::move(*maybe_type)); |
217 | 1.93k | } |
218 | 2.16k | } |
219 | 234 | return absl::nullopt; |
220 | 2.16k | } |
221 | | |
222 | | } // namespace google::api::expr::runtime |