/proc/self/cwd/eval/eval/container_access_step.cc
Line | Count | Source |
1 | | #include "eval/eval/container_access_step.h" |
2 | | |
3 | | #include <cstdint> |
4 | | #include <memory> |
5 | | #include <utility> |
6 | | |
7 | | #include "absl/log/absl_check.h" |
8 | | #include "absl/status/status.h" |
9 | | #include "absl/status/statusor.h" |
10 | | #include "absl/strings/str_cat.h" |
11 | | #include "absl/types/optional.h" |
12 | | #include "absl/types/span.h" |
13 | | #include "base/attribute.h" |
14 | | #include "common/casting.h" |
15 | | #include "common/expr.h" |
16 | | #include "common/kind.h" |
17 | | #include "common/value.h" |
18 | | #include "common/value_kind.h" |
19 | | #include "eval/eval/attribute_trail.h" |
20 | | #include "eval/eval/attribute_utility.h" |
21 | | #include "eval/eval/direct_expression_step.h" |
22 | | #include "eval/eval/evaluator_core.h" |
23 | | #include "eval/eval/expression_step_base.h" |
24 | | #include "eval/internal/errors.h" |
25 | | #include "internal/number.h" |
26 | | #include "internal/status_macros.h" |
27 | | #include "runtime/internal/errors.h" |
28 | | |
29 | | namespace google::api::expr::runtime { |
30 | | |
31 | | namespace { |
32 | | |
33 | | using ::cel::AttributeQualifier; |
34 | | using ::cel::Cast; |
35 | | using ::cel::ErrorValue; |
36 | | using ::cel::InstanceOf; |
37 | | using ::cel::IntValue; |
38 | | using ::cel::ListValue; |
39 | | using ::cel::MapValue; |
40 | | using ::cel::UintValue; |
41 | | using ::cel::Value; |
42 | | using ::cel::ValueKind; |
43 | | using ::cel::ValueKindToString; |
44 | | using ::cel::internal::Number; |
45 | | using ::cel::runtime_internal::CreateNoSuchKeyError; |
46 | | |
47 | | inline constexpr int kNumContainerAccessArguments = 2; |
48 | | |
49 | 218k | absl::optional<Number> CelNumberFromValue(const Value& value) { |
50 | 218k | switch (value->kind()) { |
51 | 56.0k | case ValueKind::kInt64: |
52 | 56.0k | return Number::FromInt64(value.GetInt().NativeValue()); |
53 | 119k | case ValueKind::kUint64: |
54 | 119k | return Number::FromUint64(value.GetUint().NativeValue()); |
55 | 18.0k | case ValueKind::kDouble: |
56 | 18.0k | return Number::FromDouble(value.GetDouble().NativeValue()); |
57 | 25.4k | default: |
58 | 25.4k | return std::nullopt; |
59 | 218k | } |
60 | 218k | } |
61 | | |
62 | 2.71k | absl::Status CheckMapKeyType(const Value& key) { |
63 | 2.71k | ValueKind kind = key->kind(); |
64 | 2.71k | switch (kind) { |
65 | 1.37k | case ValueKind::kString: |
66 | 1.37k | case ValueKind::kInt64: |
67 | 1.37k | case ValueKind::kUint64: |
68 | 2.45k | case ValueKind::kBool: |
69 | 2.45k | return absl::OkStatus(); |
70 | 260 | default: |
71 | 260 | return absl::InvalidArgumentError(absl::StrCat( |
72 | 260 | "Invalid map key type: '", ValueKindToString(kind), "'")); |
73 | 2.71k | } |
74 | 2.71k | } |
75 | | |
76 | 0 | AttributeQualifier AttributeQualifierFromValue(const Value& v) { |
77 | 0 | switch (v->kind()) { |
78 | 0 | case ValueKind::kString: |
79 | 0 | return AttributeQualifier::OfString(v.GetString().ToString()); |
80 | 0 | case ValueKind::kInt64: |
81 | 0 | return AttributeQualifier::OfInt(v.GetInt().NativeValue()); |
82 | 0 | case ValueKind::kUint64: |
83 | 0 | return AttributeQualifier::OfUint(v.GetUint().NativeValue()); |
84 | 0 | case ValueKind::kBool: |
85 | 0 | return AttributeQualifier::OfBool(v.GetBool().NativeValue()); |
86 | 0 | default: |
87 | | // Non-matching qualifier. |
88 | 0 | return AttributeQualifier(); |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | | void LookupInMap(const MapValue& cel_map, const Value& key, |
93 | 192k | ExecutionFrameBase& frame, Value& result) { |
94 | 192k | if (frame.options().enable_heterogeneous_equality) { |
95 | | // Double isn't a supported key type but may be convertible to an integer. |
96 | 192k | absl::optional<Number> number = CelNumberFromValue(key); |
97 | 192k | if (number.has_value()) { |
98 | | // Consider uint as uint first then try coercion (prefer matching the |
99 | | // original type of the key value). |
100 | 189k | if (key->Is<UintValue>()) { |
101 | 118k | auto lookup = |
102 | 118k | cel_map.Find(key, frame.descriptor_pool(), frame.message_factory(), |
103 | 118k | frame.arena(), &result); |
104 | 118k | if (!lookup.ok()) { |
105 | 0 | result = cel::ErrorValue(std::move(lookup).status()); |
106 | 0 | return; |
107 | 0 | } |
108 | 118k | if (*lookup) { |
109 | 118k | ABSL_DCHECK(!result.IsUnknown()); |
110 | 118k | return; |
111 | 118k | } |
112 | 118k | } |
113 | | // double / int / uint -> int |
114 | 71.1k | if (number->LosslessConvertibleToInt()) { |
115 | 60.3k | auto lookup = |
116 | 60.3k | cel_map.Find(IntValue(number->AsInt()), frame.descriptor_pool(), |
117 | 60.3k | frame.message_factory(), frame.arena(), &result); |
118 | 60.3k | if (!lookup.ok()) { |
119 | 0 | result = cel::ErrorValue(std::move(lookup).status()); |
120 | 0 | return; |
121 | 0 | } |
122 | 60.3k | if (*lookup) { |
123 | 48.0k | ABSL_DCHECK(!result.IsUnknown()); |
124 | 48.0k | return; |
125 | 48.0k | } |
126 | 60.3k | } |
127 | | // double / int -> uint |
128 | 23.1k | if (number->LosslessConvertibleToUint()) { |
129 | 10.5k | auto lookup = |
130 | 10.5k | cel_map.Find(UintValue(number->AsUint()), frame.descriptor_pool(), |
131 | 10.5k | frame.message_factory(), frame.arena(), &result); |
132 | 10.5k | if (!lookup.ok()) { |
133 | 0 | result = cel::ErrorValue(std::move(lookup).status()); |
134 | 0 | return; |
135 | 0 | } |
136 | 10.5k | if (*lookup) { |
137 | 229 | ABSL_DCHECK(!result.IsUnknown()); |
138 | 229 | return; |
139 | 229 | } |
140 | 10.5k | } |
141 | 22.9k | result = cel::ErrorValue(CreateNoSuchKeyError(key->DebugString())); |
142 | 22.9k | return; |
143 | 23.1k | } |
144 | 192k | } |
145 | | |
146 | 2.71k | absl::Status status = CheckMapKeyType(key); |
147 | 2.71k | if (!status.ok()) { |
148 | 260 | result = cel::ErrorValue(std::move(status)); |
149 | 260 | return; |
150 | 260 | } |
151 | | |
152 | 2.45k | absl::Status lookup = |
153 | 2.45k | cel_map.Get(key, frame.descriptor_pool(), frame.message_factory(), |
154 | 2.45k | frame.arena(), &result); |
155 | 2.45k | if (!lookup.ok()) { |
156 | 0 | result = cel::ErrorValue(std::move(lookup)); |
157 | 0 | } |
158 | 2.45k | ABSL_DCHECK(!result.IsUnknown()); |
159 | 2.45k | } |
160 | | |
161 | | void LookupInList(const ListValue& cel_list, const Value& key, |
162 | 26.2k | ExecutionFrameBase& frame, Value& result) { |
163 | 26.2k | absl::optional<int64_t> maybe_idx; |
164 | 26.2k | if (frame.options().enable_heterogeneous_equality) { |
165 | 26.2k | auto number = CelNumberFromValue(key); |
166 | 26.2k | if (number.has_value() && number->LosslessConvertibleToInt()) { |
167 | 3.26k | maybe_idx = number->AsInt(); |
168 | 3.26k | } |
169 | 26.2k | } else if (InstanceOf<IntValue>(key)) { |
170 | 0 | maybe_idx = key.GetInt().NativeValue(); |
171 | 0 | } |
172 | | |
173 | 26.2k | if (!maybe_idx.has_value()) { |
174 | 22.9k | result = cel::ErrorValue(absl::UnknownError( |
175 | 22.9k | absl::StrCat("Index error: expected integer type, got ", |
176 | 22.9k | cel::KindToString(ValueKindToKind(key->kind()))))); |
177 | 22.9k | return; |
178 | 22.9k | } |
179 | | |
180 | 3.26k | int64_t idx = *maybe_idx; |
181 | 3.26k | auto size = cel_list.Size(); |
182 | 3.26k | if (!size.ok()) { |
183 | 0 | result = cel::ErrorValue(size.status()); |
184 | 0 | return; |
185 | 0 | } |
186 | 3.26k | if (idx < 0 || idx >= *size) { |
187 | 2.48k | result = cel::ErrorValue(absl::UnknownError( |
188 | 2.48k | absl::StrCat("Index error: index=", idx, " size=", *size))); |
189 | 2.48k | return; |
190 | 2.48k | } |
191 | | |
192 | 774 | absl::Status lookup = |
193 | 774 | cel_list.Get(idx, frame.descriptor_pool(), frame.message_factory(), |
194 | 774 | frame.arena(), &result); |
195 | | |
196 | 774 | if (!lookup.ok()) { |
197 | 0 | result = cel::ErrorValue(std::move(lookup)); |
198 | 0 | } |
199 | 774 | ABSL_DCHECK(!result.IsUnknown()); |
200 | 774 | } |
201 | | |
202 | | void LookupInContainer(const Value& container, const Value& key, |
203 | 231k | ExecutionFrameBase& frame, Value& result) { |
204 | | // Select steps can be applied to either maps or messages |
205 | 231k | switch (container.kind()) { |
206 | 192k | case ValueKind::kMap: { |
207 | 192k | LookupInMap(Cast<MapValue>(container), key, frame, result); |
208 | 192k | return; |
209 | 0 | } |
210 | 26.2k | case ValueKind::kList: { |
211 | 26.2k | LookupInList(Cast<ListValue>(container), key, frame, result); |
212 | 26.2k | return; |
213 | 0 | } |
214 | 12.5k | default: |
215 | 12.5k | result = cel::ErrorValue(absl::InvalidArgumentError( |
216 | 12.5k | absl::StrCat("Invalid container type: '", |
217 | 12.5k | ValueKindToString(container->kind()), "'"))); |
218 | 12.5k | return; |
219 | 231k | } |
220 | 231k | } |
221 | | |
222 | | void PerformLookup(ExecutionFrameBase& frame, const Value& container, |
223 | | const Value& key, const AttributeTrail& container_trail, |
224 | | bool enable_optional_types, Value& result, |
225 | 301k | AttributeTrail& trail) { |
226 | 301k | if (frame.unknown_processing_enabled()) { |
227 | 0 | AttributeUtility::Accumulator unknowns = |
228 | 0 | frame.attribute_utility().CreateAccumulator(); |
229 | 0 | unknowns.MaybeAdd(container); |
230 | 0 | unknowns.MaybeAdd(key); |
231 | |
|
232 | 0 | if (!unknowns.IsEmpty()) { |
233 | 0 | result = std::move(unknowns).Build(); |
234 | 0 | return; |
235 | 0 | } |
236 | | |
237 | 0 | trail = container_trail.Step(AttributeQualifierFromValue(key)); |
238 | |
|
239 | 0 | if (frame.attribute_utility().CheckForUnknownExact(trail)) { |
240 | 0 | result = frame.attribute_utility().CreateUnknownSet(trail.attribute()); |
241 | 0 | return; |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | 301k | if (InstanceOf<ErrorValue>(container)) { |
246 | 37.6k | result = container; |
247 | 37.6k | return; |
248 | 37.6k | } |
249 | 263k | if (InstanceOf<ErrorValue>(key)) { |
250 | 32.3k | result = key; |
251 | 32.3k | return; |
252 | 32.3k | } |
253 | | |
254 | 231k | if (enable_optional_types && container.IsOptional()) { |
255 | 0 | const auto& optional_value = container.GetOptional(); |
256 | 0 | if (!optional_value.HasValue()) { |
257 | 0 | result = cel::OptionalValue::None(); |
258 | 0 | return; |
259 | 0 | } |
260 | 0 | Value value; |
261 | 0 | optional_value.Value(&value); |
262 | 0 | LookupInContainer(value, key, frame, result); |
263 | 0 | if (auto error_value = cel::As<cel::ErrorValue>(result); |
264 | 0 | error_value && cel::IsNoSuchKey(*error_value)) { |
265 | 0 | result = cel::OptionalValue::None(); |
266 | 0 | return; |
267 | 0 | } |
268 | 0 | result = cel::OptionalValue::Of(std::move(result), frame.arena()); |
269 | 0 | return; |
270 | 0 | } |
271 | | |
272 | 231k | LookupInContainer(container, key, frame, result); |
273 | 231k | } |
274 | | |
275 | | // ContainerAccessStep performs message field access specified by Expr::Select |
276 | | // message. |
277 | | class ContainerAccessStep : public ExpressionStepBase { |
278 | | public: |
279 | | ContainerAccessStep(int64_t expr_id, bool enable_optional_types) |
280 | 3.61k | : ExpressionStepBase(expr_id), |
281 | 3.61k | enable_optional_types_(enable_optional_types) {} |
282 | | |
283 | | absl::Status Evaluate(ExecutionFrame* frame) const override; |
284 | | |
285 | | private: |
286 | | bool enable_optional_types_; |
287 | | }; |
288 | | |
289 | 301k | absl::Status ContainerAccessStep::Evaluate(ExecutionFrame* frame) const { |
290 | 301k | if (!frame->value_stack().HasEnough(kNumContainerAccessArguments)) { |
291 | 0 | return absl::Status( |
292 | 0 | absl::StatusCode::kInternal, |
293 | 0 | "Insufficient arguments supplied for ContainerAccess-type expression"); |
294 | 0 | } |
295 | | |
296 | 301k | Value result; |
297 | 301k | AttributeTrail result_trail; |
298 | 301k | auto args = frame->value_stack().GetSpan(kNumContainerAccessArguments); |
299 | 301k | const AttributeTrail& container_trail = |
300 | 301k | frame->value_stack().GetAttributeSpan(kNumContainerAccessArguments)[0]; |
301 | | |
302 | 301k | PerformLookup(*frame, args[0], args[1], container_trail, |
303 | 301k | enable_optional_types_, result, result_trail); |
304 | 301k | frame->value_stack().PopAndPush(kNumContainerAccessArguments, |
305 | 301k | std::move(result), std::move(result_trail)); |
306 | | |
307 | 301k | return absl::OkStatus(); |
308 | 301k | } |
309 | | |
310 | | class DirectContainerAccessStep : public DirectExpressionStep { |
311 | | public: |
312 | | DirectContainerAccessStep( |
313 | | std::unique_ptr<DirectExpressionStep> container_step, |
314 | | std::unique_ptr<DirectExpressionStep> key_step, |
315 | | bool enable_optional_types, int64_t expr_id) |
316 | 0 | : DirectExpressionStep(expr_id), |
317 | 0 | container_step_(std::move(container_step)), |
318 | 0 | key_step_(std::move(key_step)), |
319 | 0 | enable_optional_types_(enable_optional_types) {} |
320 | | |
321 | | absl::Status Evaluate(ExecutionFrameBase& frame, Value& result, |
322 | | AttributeTrail& trail) const override; |
323 | | |
324 | | private: |
325 | | std::unique_ptr<DirectExpressionStep> container_step_; |
326 | | std::unique_ptr<DirectExpressionStep> key_step_; |
327 | | bool enable_optional_types_; |
328 | | }; |
329 | | |
330 | | absl::Status DirectContainerAccessStep::Evaluate(ExecutionFrameBase& frame, |
331 | | Value& result, |
332 | 0 | AttributeTrail& trail) const { |
333 | 0 | Value container; |
334 | 0 | Value key; |
335 | 0 | AttributeTrail container_trail; |
336 | 0 | AttributeTrail key_trail; |
337 | |
|
338 | 0 | CEL_RETURN_IF_ERROR( |
339 | 0 | container_step_->Evaluate(frame, container, container_trail)); |
340 | 0 | CEL_RETURN_IF_ERROR(key_step_->Evaluate(frame, key, key_trail)); |
341 | | |
342 | 0 | PerformLookup(frame, container, key, container_trail, enable_optional_types_, |
343 | 0 | result, trail); |
344 | |
|
345 | 0 | return absl::OkStatus(); |
346 | 0 | } |
347 | | |
348 | | } // namespace |
349 | | |
350 | | std::unique_ptr<DirectExpressionStep> CreateDirectContainerAccessStep( |
351 | | std::unique_ptr<DirectExpressionStep> container_step, |
352 | | std::unique_ptr<DirectExpressionStep> key_step, bool enable_optional_types, |
353 | 0 | int64_t expr_id) { |
354 | 0 | return std::make_unique<DirectContainerAccessStep>( |
355 | 0 | std::move(container_step), std::move(key_step), enable_optional_types, |
356 | 0 | expr_id); |
357 | 0 | } |
358 | | |
359 | | // Factory method for Select - based Execution step |
360 | | absl::StatusOr<std::unique_ptr<ExpressionStep>> CreateContainerAccessStep( |
361 | 3.61k | const cel::CallExpr& call, int64_t expr_id, bool enable_optional_types) { |
362 | 3.61k | int arg_count = call.args().size() + (call.has_target() ? 1 : 0); |
363 | 3.61k | if (arg_count != kNumContainerAccessArguments) { |
364 | 0 | return absl::InvalidArgumentError(absl::StrCat( |
365 | 0 | "Invalid argument count for index operation: ", arg_count)); |
366 | 0 | } |
367 | 3.61k | return std::make_unique<ContainerAccessStep>(expr_id, enable_optional_types); |
368 | 3.61k | } |
369 | | |
370 | | } // namespace google::api::expr::runtime |