Coverage Report

Created: 2026-07-16 06:29

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