Coverage Report

Created: 2026-09-03 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/eval/eval/logic_step.cc
Line
Count
Source
1
#include "eval/eval/logic_step.h"
2
3
#include <cstddef>
4
#include <cstdint>
5
#include <memory>
6
#include <optional>
7
#include <utility>
8
9
#include "absl/status/status.h"
10
#include "absl/status/statusor.h"
11
#include "absl/types/optional.h"
12
#include "absl/types/span.h"
13
#include "base/builtins.h"
14
#include "common/casting.h"
15
#include "common/value.h"
16
#include "common/value_kind.h"
17
#include "eval/eval/attribute_trail.h"
18
#include "eval/eval/direct_expression_step.h"
19
#include "eval/eval/evaluator_core.h"
20
#include "eval/eval/expression_step_base.h"
21
#include "eval/internal/errors.h"
22
#include "internal/status_macros.h"
23
#include "runtime/internal/errors.h"
24
25
namespace google::api::expr::runtime {
26
27
namespace {
28
29
using ::cel::BoolValue;
30
using ::cel::Cast;
31
using ::cel::ErrorValue;
32
using ::cel::InstanceOf;
33
using ::cel::UnknownValue;
34
using ::cel::Value;
35
using ::cel::ValueKind;
36
using ::cel::runtime_internal::CreateNoMatchingOverloadError;
37
38
enum class OpType { kAnd, kOr };
39
40
// Shared logic for the fall through case (we didn't see the shortcircuit
41
// value).
42
absl::Status ReturnLogicResult(ExecutionFrameBase& frame, OpType op_type,
43
                               Value& lhs_result, Value& rhs_result,
44
                               AttributeTrail& attribute_trail,
45
0
                               AttributeTrail& rhs_attr) {
46
0
  ValueKind lhs_kind = lhs_result.kind();
47
0
  ValueKind rhs_kind = rhs_result.kind();
48
49
0
  if (frame.unknown_processing_enabled()) {
50
0
    if (lhs_kind == ValueKind::kUnknown && rhs_kind == ValueKind::kUnknown) {
51
0
      lhs_result = frame.attribute_utility().MergeUnknownValues(
52
0
          Cast<UnknownValue>(lhs_result), Cast<UnknownValue>(rhs_result));
53
      // Clear attribute trail so this doesn't get re-identified as a new
54
      // unknown and reset the accumulated attributes.
55
0
      attribute_trail = AttributeTrail();
56
0
      return absl::OkStatus();
57
0
    } else if (lhs_kind == ValueKind::kUnknown) {
58
0
      return absl::OkStatus();
59
0
    } else if (rhs_kind == ValueKind::kUnknown) {
60
0
      lhs_result = std::move(rhs_result);
61
0
      attribute_trail = std::move(rhs_attr);
62
0
      return absl::OkStatus();
63
0
    }
64
0
  }
65
66
0
  if (lhs_kind == ValueKind::kError) {
67
0
    return absl::OkStatus();
68
0
  } else if (rhs_kind == ValueKind::kError) {
69
0
    lhs_result = std::move(rhs_result);
70
0
    attribute_trail = std::move(rhs_attr);
71
0
    return absl::OkStatus();
72
0
  }
73
74
0
  if (lhs_kind == ValueKind::kBool && rhs_kind == ValueKind::kBool) {
75
0
    return absl::OkStatus();
76
0
  }
77
78
  // Otherwise, add a no overload error.
79
0
  attribute_trail = AttributeTrail();
80
0
  lhs_result = cel::ErrorValue(CreateNoMatchingOverloadError(
81
0
      op_type == OpType::kOr ? cel::builtin::kOr : cel::builtin::kAnd));
82
0
  return absl::OkStatus();
83
0
}
84
85
class ExhaustiveDirectLogicStep : public DirectExpressionStep {
86
 public:
87
  explicit ExhaustiveDirectLogicStep(std::unique_ptr<DirectExpressionStep> lhs,
88
                                     std::unique_ptr<DirectExpressionStep> rhs,
89
                                     OpType op_type, int64_t expr_id)
90
0
      : DirectExpressionStep(expr_id),
91
0
        lhs_(std::move(lhs)),
92
0
        rhs_(std::move(rhs)),
93
0
        op_type_(op_type) {}
94
95
  absl::Status Evaluate(ExecutionFrameBase& frame, cel::Value& result,
96
                        AttributeTrail& attribute_trail) const override;
97
98
 private:
99
  std::unique_ptr<DirectExpressionStep> lhs_;
100
  std::unique_ptr<DirectExpressionStep> rhs_;
101
  OpType op_type_;
102
};
103
104
absl::Status ExhaustiveDirectLogicStep::Evaluate(
105
    ExecutionFrameBase& frame, cel::Value& result,
106
0
    AttributeTrail& attribute_trail) const {
107
0
  CEL_RETURN_IF_ERROR(lhs_->Evaluate(frame, result, attribute_trail));
108
0
  ValueKind lhs_kind = result.kind();
109
110
0
  Value rhs_result;
111
0
  AttributeTrail rhs_attr;
112
0
  CEL_RETURN_IF_ERROR(rhs_->Evaluate(frame, rhs_result, attribute_trail));
113
114
0
  ValueKind rhs_kind = rhs_result.kind();
115
0
  if (lhs_kind == ValueKind::kBool) {
116
0
    bool lhs_bool = Cast<BoolValue>(result).NativeValue();
117
0
    if ((op_type_ == OpType::kOr && lhs_bool) ||
118
0
        (op_type_ == OpType::kAnd && !lhs_bool)) {
119
0
      return absl::OkStatus();
120
0
    }
121
0
  }
122
123
0
  if (rhs_kind == ValueKind::kBool) {
124
0
    bool rhs_bool = Cast<BoolValue>(rhs_result).NativeValue();
125
0
    if ((op_type_ == OpType::kOr && rhs_bool) ||
126
0
        (op_type_ == OpType::kAnd && !rhs_bool)) {
127
0
      result = std::move(rhs_result);
128
0
      attribute_trail = std::move(rhs_attr);
129
0
      return absl::OkStatus();
130
0
    }
131
0
  }
132
133
0
  return ReturnLogicResult(frame, op_type_, result, rhs_result, attribute_trail,
134
0
                           rhs_attr);
135
0
}
136
137
class DirectLogicStep : public DirectExpressionStep {
138
 public:
139
  explicit DirectLogicStep(std::unique_ptr<DirectExpressionStep> lhs,
140
                           std::unique_ptr<DirectExpressionStep> rhs,
141
                           OpType op_type, int64_t expr_id)
142
0
      : DirectExpressionStep(expr_id),
143
0
        lhs_(std::move(lhs)),
144
0
        rhs_(std::move(rhs)),
145
0
        op_type_(op_type) {}
146
147
  absl::Status Evaluate(ExecutionFrameBase& frame, cel::Value& result,
148
                        AttributeTrail& attribute_trail) const override;
149
150
 private:
151
  std::unique_ptr<DirectExpressionStep> lhs_;
152
  std::unique_ptr<DirectExpressionStep> rhs_;
153
  OpType op_type_;
154
};
155
156
absl::Status DirectLogicStep::Evaluate(ExecutionFrameBase& frame, Value& result,
157
0
                                       AttributeTrail& attribute_trail) const {
158
0
  CEL_RETURN_IF_ERROR(lhs_->Evaluate(frame, result, attribute_trail));
159
0
  ValueKind lhs_kind = result.kind();
160
0
  if (lhs_kind == ValueKind::kBool) {
161
0
    bool lhs_bool = Cast<BoolValue>(result).NativeValue();
162
0
    if ((op_type_ == OpType::kOr && lhs_bool) ||
163
0
        (op_type_ == OpType::kAnd && !lhs_bool)) {
164
0
      return absl::OkStatus();
165
0
    }
166
0
  }
167
168
0
  Value rhs_result;
169
0
  AttributeTrail rhs_attr;
170
171
0
  CEL_RETURN_IF_ERROR(rhs_->Evaluate(frame, rhs_result, attribute_trail));
172
173
0
  ValueKind rhs_kind = rhs_result.kind();
174
175
0
  if (rhs_kind == ValueKind::kBool) {
176
0
    bool rhs_bool = Cast<BoolValue>(rhs_result).NativeValue();
177
0
    if ((op_type_ == OpType::kOr && rhs_bool) ||
178
0
        (op_type_ == OpType::kAnd && !rhs_bool)) {
179
0
      result = std::move(rhs_result);
180
0
      attribute_trail = std::move(rhs_attr);
181
0
      return absl::OkStatus();
182
0
    }
183
0
  }
184
185
0
  return ReturnLogicResult(frame, op_type_, result, rhs_result, attribute_trail,
186
0
                           rhs_attr);
187
0
}
188
189
class LogicalOpStep : public ExpressionStepBase {
190
 public:
191
  // Constructs FunctionStep that uses overloads specified.
192
  LogicalOpStep(OpType op_type, size_t count, int64_t expr_id)
193
5.78k
      : ExpressionStepBase(expr_id), op_type_(op_type), count_(count) {
194
5.78k
    shortcircuit_ = (op_type_ == OpType::kOr);
195
5.78k
  }
196
197
  absl::Status Evaluate(ExecutionFrame* frame) const override;
198
199
 private:
200
  void Calculate(ExecutionFrame* frame, absl::Span<const Value> args,
201
291k
                 Value& result) const {
202
291k
    std::optional<size_t> error_pos;
203
204
870k
    for (size_t i = 0; i < args.size(); i++) {
205
583k
      const Value& arg = args[i];
206
583k
      switch (arg.kind()) {
207
20.4k
        case ValueKind::kBool:
208
20.4k
          if (arg.GetBool() == shortcircuit_) {
209
4.69k
            result = arg;
210
4.69k
            return;
211
4.69k
          }
212
15.7k
          break;
213
15.7k
        case ValueKind::kUnknown:
214
0
          break;
215
513k
        case ValueKind::kError:
216
562k
        default:
217
562k
          if (!error_pos.has_value()) {
218
288k
            error_pos = i;
219
288k
          }
220
562k
          break;
221
583k
      }
222
583k
    }
223
224
    // As opposed to regular function, logical operation treat Unknowns with
225
    // higher precedence than error. This is due to the fact that after Unknown
226
    // is resolved to actual value, it may short-circuit and thus hide the
227
    // error.
228
286k
    if (frame->enable_unknowns()) {
229
      // Check if unknown?
230
0
      absl::optional<cel::UnknownValue> unknown_set =
231
0
          frame->attribute_utility().MergeUnknowns(args);
232
0
      if (unknown_set.has_value()) {
233
0
        result = std::move(*unknown_set);
234
0
        return;
235
0
      }
236
0
    }
237
238
286k
    if (!error_pos.has_value()) {
239
2.08k
      result = cel::BoolValue(!shortcircuit_);
240
2.08k
      return;
241
2.08k
    }
242
243
284k
    result = args[error_pos.value()];
244
284k
    if (!result.IsError()) {
245
22.3k
      result = cel::ErrorValue(CreateNoMatchingOverloadError(
246
22.3k
          (op_type_ == OpType::kOr) ? cel::builtin::kOr : cel::builtin::kAnd));
247
22.3k
    }
248
284k
  }
249
250
  const OpType op_type_;
251
  size_t count_;
252
  bool shortcircuit_;
253
};
254
255
291k
absl::Status LogicalOpStep::Evaluate(ExecutionFrame* frame) const {
256
  // Must have 2 or more values on the stack.
257
291k
  if (!frame->value_stack().HasEnough(count_)) {
258
0
    return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
259
0
  }
260
261
  // Create Span object that contains input arguments to the function.
262
291k
  auto args = frame->value_stack().GetSpan(count_);
263
291k
  Value result;
264
291k
  Calculate(frame, args, result);
265
291k
  frame->value_stack().PopAndPush(args.size(), std::move(result));
266
267
291k
  return absl::OkStatus();
268
291k
}
269
270
std::unique_ptr<DirectExpressionStep> CreateDirectLogicStep(
271
    std::unique_ptr<DirectExpressionStep> lhs,
272
    std::unique_ptr<DirectExpressionStep> rhs, int64_t expr_id, OpType op_type,
273
0
    bool shortcircuiting) {
274
0
  if (shortcircuiting) {
275
0
    return std::make_unique<DirectLogicStep>(std::move(lhs), std::move(rhs),
276
0
                                             op_type, expr_id);
277
0
  } else {
278
0
    return std::make_unique<ExhaustiveDirectLogicStep>(
279
0
        std::move(lhs), std::move(rhs), op_type, expr_id);
280
0
  }
281
0
}
282
283
class DirectNotStep : public DirectExpressionStep {
284
 public:
285
  explicit DirectNotStep(std::unique_ptr<DirectExpressionStep> operand,
286
                         int64_t expr_id)
287
0
      : DirectExpressionStep(expr_id), operand_(std::move(operand)) {}
288
  absl::Status Evaluate(ExecutionFrameBase& frame, Value& result,
289
                        AttributeTrail& attribute_trail) const override;
290
291
 private:
292
  std::unique_ptr<DirectExpressionStep> operand_;
293
};
294
295
absl::Status DirectNotStep::Evaluate(ExecutionFrameBase& frame, Value& result,
296
0
                                     AttributeTrail& attribute_trail) const {
297
0
  CEL_RETURN_IF_ERROR(operand_->Evaluate(frame, result, attribute_trail));
298
299
0
  if (frame.unknown_processing_enabled()) {
300
0
    if (frame.attribute_utility().CheckForUnknownPartial(attribute_trail)) {
301
0
      result = frame.attribute_utility().CreateUnknownSet(
302
0
          attribute_trail.attribute());
303
0
      return absl::OkStatus();
304
0
    }
305
0
  }
306
307
0
  switch (result.kind()) {
308
0
    case ValueKind::kBool:
309
0
      result = BoolValue{!result.GetBool().NativeValue()};
310
0
      break;
311
0
    case ValueKind::kUnknown:
312
0
    case ValueKind::kError:
313
      // just forward.
314
0
      break;
315
0
    default:
316
0
      result =
317
0
          cel::ErrorValue(CreateNoMatchingOverloadError(cel::builtin::kNot));
318
0
      break;
319
0
  }
320
321
0
  return absl::OkStatus();
322
0
}
323
324
class IterativeNotStep : public ExpressionStepBase {
325
 public:
326
777
  explicit IterativeNotStep(int64_t expr_id) : ExpressionStepBase(expr_id) {}
327
328
  absl::Status Evaluate(ExecutionFrame* frame) const override;
329
};
330
331
46.8k
absl::Status IterativeNotStep::Evaluate(ExecutionFrame* frame) const {
332
46.8k
  if (!frame->value_stack().HasEnough(1)) {
333
0
    return absl::InternalError("Value stack underflow");
334
0
  }
335
46.8k
  const Value& operand = frame->value_stack().Peek();
336
337
46.8k
  if (frame->unknown_processing_enabled()) {
338
0
    const AttributeTrail& attribute_trail =
339
0
        frame->value_stack().PeekAttribute();
340
0
    if (frame->attribute_utility().CheckForUnknownPartial(attribute_trail)) {
341
0
      frame->value_stack().PopAndPush(
342
0
          frame->attribute_utility().CreateUnknownSet(
343
0
              attribute_trail.attribute()));
344
0
      return absl::OkStatus();
345
0
    }
346
0
  }
347
348
46.8k
  switch (operand.kind()) {
349
241
    case ValueKind::kBool:
350
241
      frame->value_stack().PopAndPush(
351
241
          BoolValue{!operand.GetBool().NativeValue()});
352
241
      break;
353
0
    case ValueKind::kUnknown:
354
6.51k
    case ValueKind::kError:
355
      // just forward.
356
6.51k
      break;
357
40.0k
    default:
358
40.0k
      frame->value_stack().PopAndPush(
359
40.0k
          cel::ErrorValue(CreateNoMatchingOverloadError(cel::builtin::kNot)));
360
40.0k
      break;
361
46.8k
  }
362
363
46.8k
  return absl::OkStatus();
364
46.8k
}
365
366
class DirectNotStrictlyFalseStep : public DirectExpressionStep {
367
 public:
368
  explicit DirectNotStrictlyFalseStep(
369
      std::unique_ptr<DirectExpressionStep> operand, int64_t expr_id)
370
0
      : DirectExpressionStep(expr_id), operand_(std::move(operand)) {}
371
  absl::Status Evaluate(ExecutionFrameBase& frame, Value& result,
372
                        AttributeTrail& attribute_trail) const override;
373
374
 private:
375
  std::unique_ptr<DirectExpressionStep> operand_;
376
};
377
378
absl::Status DirectNotStrictlyFalseStep::Evaluate(
379
    ExecutionFrameBase& frame, Value& result,
380
0
    AttributeTrail& attribute_trail) const {
381
0
  CEL_RETURN_IF_ERROR(operand_->Evaluate(frame, result, attribute_trail));
382
383
0
  switch (result.kind()) {
384
0
    case ValueKind::kBool:
385
      // just forward.
386
0
      break;
387
0
    case ValueKind::kUnknown:
388
0
    case ValueKind::kError:
389
0
      result = BoolValue(true);
390
0
      break;
391
0
    default:
392
0
      result =
393
0
          cel::ErrorValue(CreateNoMatchingOverloadError(cel::builtin::kNot));
394
0
      break;
395
0
  }
396
397
0
  return absl::OkStatus();
398
0
}
399
400
class IterativeNotStrictlyFalseStep : public ExpressionStepBase {
401
 public:
402
  explicit IterativeNotStrictlyFalseStep(int64_t expr_id)
403
0
      : ExpressionStepBase(expr_id) {}
404
405
  absl::Status Evaluate(ExecutionFrame* frame) const override;
406
};
407
408
absl::Status IterativeNotStrictlyFalseStep::Evaluate(
409
0
    ExecutionFrame* frame) const {
410
0
  if (!frame->value_stack().HasEnough(1)) {
411
0
    return absl::InternalError("Value stack underflow");
412
0
  }
413
0
  const Value& operand = frame->value_stack().Peek();
414
415
0
  switch (operand.kind()) {
416
0
    case ValueKind::kBool:
417
      // just forward.
418
0
      break;
419
0
    case ValueKind::kUnknown:
420
0
    case ValueKind::kError:
421
0
      frame->value_stack().PopAndPush(BoolValue(true));
422
0
      break;
423
0
    default:
424
0
      frame->value_stack().PopAndPush(
425
0
          cel::ErrorValue(CreateNoMatchingOverloadError(cel::builtin::kNot)));
426
0
      break;
427
0
  }
428
429
0
  return absl::OkStatus();
430
0
}
431
432
}  // namespace
433
434
// Factory method for "And" Execution step
435
std::unique_ptr<DirectExpressionStep> CreateDirectAndStep(
436
    std::unique_ptr<DirectExpressionStep> lhs,
437
    std::unique_ptr<DirectExpressionStep> rhs, int64_t expr_id,
438
0
    bool shortcircuiting) {
439
0
  return CreateDirectLogicStep(std::move(lhs), std::move(rhs), expr_id,
440
0
                               OpType::kAnd, shortcircuiting);
441
0
}
442
443
// Factory method for "Or" Execution step
444
std::unique_ptr<DirectExpressionStep> CreateDirectOrStep(
445
    std::unique_ptr<DirectExpressionStep> lhs,
446
    std::unique_ptr<DirectExpressionStep> rhs, int64_t expr_id,
447
0
    bool shortcircuiting) {
448
0
  return CreateDirectLogicStep(std::move(lhs), std::move(rhs), expr_id,
449
0
                               OpType::kOr, shortcircuiting);
450
0
}
451
452
// Factory method for "And" Execution step
453
absl::StatusOr<std::unique_ptr<ExpressionStep>> CreateAndStep(size_t num_args,
454
1.67k
                                                              int64_t expr_id) {
455
1.67k
  return std::make_unique<LogicalOpStep>(OpType::kAnd, num_args, expr_id);
456
1.67k
}
457
458
// Factory method for "Or" Execution step
459
absl::StatusOr<std::unique_ptr<ExpressionStep>> CreateOrStep(size_t num_args,
460
4.11k
                                                             int64_t expr_id) {
461
4.11k
  return std::make_unique<LogicalOpStep>(OpType::kOr, num_args, expr_id);
462
4.11k
}
463
464
// Factory method for recursive logical not "!" Execution step
465
std::unique_ptr<DirectExpressionStep> CreateDirectNotStep(
466
0
    std::unique_ptr<DirectExpressionStep> operand, int64_t expr_id) {
467
0
  return std::make_unique<DirectNotStep>(std::move(operand), expr_id);
468
0
}
469
470
// Factory method for iterative logical not "!" Execution step
471
777
std::unique_ptr<ExpressionStep> CreateNotStep(int64_t expr_id) {
472
777
  return std::make_unique<IterativeNotStep>(expr_id);
473
777
}
474
475
// Factory method for recursive logical "@not_strictly_false" Execution step.
476
std::unique_ptr<DirectExpressionStep> CreateDirectNotStrictlyFalseStep(
477
0
    std::unique_ptr<DirectExpressionStep> operand, int64_t expr_id) {
478
0
  return std::make_unique<DirectNotStrictlyFalseStep>(std::move(operand),
479
0
                                                      expr_id);
480
0
}
481
482
// Factory method for iterative logical "@not_strictly_false" Execution step.
483
0
std::unique_ptr<ExpressionStep> CreateNotStrictlyFalseStep(int64_t expr_id) {
484
0
  return std::make_unique<IterativeNotStrictlyFalseStep>(expr_id);
485
0
}
486
487
}  // namespace google::api::expr::runtime