Coverage Report

Created: 2026-05-27 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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