Coverage Report

Created: 2026-09-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/eval/eval/equality_steps.cc
Line
Count
Source
1
// Copyright 2025 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
#include "eval/eval/equality_steps.h"
15
16
#include <cstdint>
17
#include <memory>
18
#include <utility>
19
20
#include "absl/status/status.h"
21
#include "absl/status/statusor.h"
22
#include "base/builtins.h"
23
#include "common/value.h"
24
#include "common/value_kind.h"
25
#include "eval/eval/attribute_trail.h"
26
#include "eval/eval/direct_expression_step.h"
27
#include "eval/eval/evaluator_core.h"
28
#include "eval/eval/expression_step_base.h"
29
#include "internal/number.h"
30
#include "internal/status_macros.h"
31
#include "runtime/internal/errors.h"
32
#include "runtime/standard/equality_functions.h"
33
34
namespace google::api::expr::runtime {
35
36
namespace {
37
38
using ::cel::BoolValue;
39
using ::cel::IntValue;
40
using ::cel::MapValue;
41
using ::cel::UintValue;
42
using ::cel::Value;
43
44
using ::cel::ValueKind;
45
using ::cel::internal::Number;
46
using ::cel::runtime_internal::ValueEqualImpl;
47
48
absl::StatusOr<Value> EvaluateEquality(
49
    ExecutionFrameBase& frame, const Value& lhs, const AttributeTrail& lhs_attr,
50
451k
    const Value& rhs, const AttributeTrail& rhs_attr, bool negation) {
51
451k
  if (lhs.IsError()) {
52
312k
    return lhs;
53
312k
  }
54
55
139k
  if (rhs.IsError()) {
56
40.6k
    return rhs;
57
40.6k
  }
58
59
98.4k
  if (frame.unknown_processing_enabled()) {
60
0
    auto accu = frame.attribute_utility().CreateAccumulator();
61
0
    accu.MaybeAdd(lhs, lhs_attr);
62
0
    accu.MaybeAdd(rhs, rhs_attr);
63
0
    if (!accu.IsEmpty()) {
64
0
      return std::move(accu).Build();
65
0
    }
66
0
  }
67
68
196k
  CEL_ASSIGN_OR_RETURN(auto is_equal,
69
196k
                       ValueEqualImpl(lhs, rhs, frame.descriptor_pool(),
70
196k
                                      frame.message_factory(), frame.arena()));
71
196k
  if (!is_equal.has_value()) {
72
0
    return cel::ErrorValue(cel::runtime_internal::CreateNoMatchingOverloadError(
73
0
        negation ? cel::builtin::kInequal : cel::builtin::kEqual));
74
0
  }
75
98.4k
  return negation ? BoolValue(!*is_equal) : BoolValue(*is_equal);
76
196k
}
77
78
class DirectEqualityStep : public DirectExpressionStep {
79
 public:
80
  explicit DirectEqualityStep(std::unique_ptr<DirectExpressionStep> lhs,
81
                              std::unique_ptr<DirectExpressionStep> rhs,
82
                              bool negation, int64_t expr_id)
83
0
      : DirectExpressionStep(expr_id),
84
0
        lhs_(std::move(lhs)),
85
0
        rhs_(std::move(rhs)),
86
0
        negation_(negation) {}
87
88
  absl::Status Evaluate(ExecutionFrameBase& frame, Value& result,
89
0
                        AttributeTrail& attribute_trail) const override {
90
0
    AttributeTrail lhs_attr;
91
0
    CEL_RETURN_IF_ERROR(lhs_->Evaluate(frame, result, lhs_attr));
92
93
0
    Value rhs_result;
94
0
    AttributeTrail rhs_attr;
95
0
    CEL_RETURN_IF_ERROR(rhs_->Evaluate(frame, rhs_result, rhs_attr));
96
0
    CEL_ASSIGN_OR_RETURN(
97
0
        result, EvaluateEquality(frame, result, lhs_attr, rhs_result, rhs_attr,
98
0
                                 negation_));
99
0
    return absl::OkStatus();
100
0
  }
101
102
 private:
103
  std::unique_ptr<DirectExpressionStep> lhs_;
104
  std::unique_ptr<DirectExpressionStep> rhs_;
105
  bool negation_;
106
};
107
108
class IterativeEqualityStep : public ExpressionStepBase {
109
 public:
110
  explicit IterativeEqualityStep(bool negation, int64_t expr_id)
111
11.1k
      : ExpressionStepBase(expr_id), negation_(negation) {}
112
113
451k
  absl::Status Evaluate(ExecutionFrame* frame) const override {
114
451k
    if (!frame->value_stack().HasEnough(2)) {
115
0
      return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
116
0
    }
117
451k
    auto args = frame->value_stack().GetSpan(2);
118
451k
    auto attrs = frame->value_stack().GetAttributeSpan(2);
119
120
451k
    CEL_ASSIGN_OR_RETURN(Value result,
121
451k
                         EvaluateEquality(*frame, args[0], attrs[0], args[1],
122
451k
                                          attrs[1], negation_));
123
124
451k
    frame->value_stack().PopAndPush(2, std::move(result));
125
451k
    return absl::OkStatus();
126
451k
  }
127
128
 private:
129
  bool negation_;
130
};
131
132
absl::StatusOr<Value> EvaluateInMap(ExecutionFrameBase& frame,
133
                                    const Value& item,
134
4.97k
                                    const MapValue& container) {
135
4.97k
  switch (item.kind()) {
136
559
    case ValueKind::kBool:
137
777
    case ValueKind::kString:
138
3.12k
    case ValueKind::kInt:
139
3.64k
    case ValueKind::kUint:
140
4.68k
    case ValueKind::kDouble:
141
4.68k
      break;
142
293
    default:
143
293
      return cel::ErrorValue(
144
293
          cel::runtime_internal::CreateNoMatchingOverloadError(
145
293
              cel::builtin::kIn));
146
4.97k
  }
147
4.68k
  Value result;
148
4.68k
  CEL_RETURN_IF_ERROR(container.Has(item, frame.descriptor_pool(),
149
4.68k
                                    frame.message_factory(), frame.arena(),
150
4.68k
                                    &result));
151
152
4.68k
  if (result.IsTrue()) {
153
242
    return result;
154
242
  }
155
156
4.44k
  if (item.IsDouble() || item.IsUint()) {
157
1.56k
    Number number = item.IsDouble()
158
1.56k
                        ? Number::FromDouble(item.GetDouble().NativeValue())
159
1.56k
                        : Number::FromUint64(item.GetUint().NativeValue());
160
1.56k
    if (number.LosslessConvertibleToInt()) {
161
918
      CEL_RETURN_IF_ERROR(
162
918
          container.Has(IntValue(number.AsInt()), frame.descriptor_pool(),
163
918
                        frame.message_factory(), frame.arena(), &result));
164
918
      if (result.IsTrue()) {
165
276
        return result;
166
276
      }
167
918
    }
168
1.56k
  }
169
170
4.16k
  if (item.IsDouble() || item.IsInt()) {
171
3.38k
    Number number = item.IsDouble()
172
3.38k
                        ? Number::FromDouble(item.GetDouble().NativeValue())
173
3.38k
                        : Number::FromInt64(item.GetInt().NativeValue());
174
3.38k
    if (number.LosslessConvertibleToUint()) {
175
2.59k
      CEL_RETURN_IF_ERROR(
176
2.59k
          container.Has(UintValue(number.AsUint()), frame.descriptor_pool(),
177
2.59k
                        frame.message_factory(), frame.arena(), &result));
178
2.59k
      if (result.IsTrue()) {
179
81
        return result;
180
81
      }
181
2.59k
    }
182
3.38k
  }
183
184
4.08k
  return BoolValue(false);
185
4.16k
}
186
187
absl::StatusOr<Value> EvaluateIn(ExecutionFrameBase& frame, const Value& item,
188
                                 const AttributeTrail& item_attr,
189
                                 const Value& container,
190
483k
                                 const AttributeTrail& container_attr) {
191
483k
  if (item.IsError()) {
192
92.9k
    return item;
193
92.9k
  }
194
390k
  if (container.IsError()) {
195
21.3k
    return container;
196
21.3k
  }
197
198
369k
  if (frame.unknown_processing_enabled()) {
199
0
    auto accu = frame.attribute_utility().CreateAccumulator();
200
0
    accu.MaybeAdd(item, item_attr);
201
0
    accu.MaybeAdd(container, container_attr);
202
0
    if (!accu.IsEmpty()) {
203
0
      return std::move(accu).Build();
204
0
    }
205
0
  }
206
369k
  if (container.IsList()) {
207
364k
    return container.GetList().Contains(item, frame.descriptor_pool(),
208
364k
                                        frame.message_factory(), frame.arena());
209
364k
  }
210
5.49k
  if (container.IsMap()) {
211
4.97k
    return EvaluateInMap(frame, item, container.GetMap());
212
4.97k
  }
213
519
  return cel::ErrorValue(
214
519
      cel::runtime_internal::CreateNoMatchingOverloadError(cel::builtin::kIn));
215
5.49k
}
216
217
class DirectInStep : public DirectExpressionStep {
218
 public:
219
  explicit DirectInStep(std::unique_ptr<DirectExpressionStep> item,
220
                        std::unique_ptr<DirectExpressionStep> container,
221
                        int64_t expr_id)
222
0
      : DirectExpressionStep(expr_id),
223
0
        item_(std::move(item)),
224
0
        container_(std::move(container)) {}
225
226
  absl::Status Evaluate(ExecutionFrameBase& frame, Value& result,
227
0
                        AttributeTrail& attribute_trail) const override {
228
0
    AttributeTrail item_attr;
229
0
    CEL_RETURN_IF_ERROR(item_->Evaluate(frame, result, item_attr));
230
231
0
    Value container_result;
232
0
    AttributeTrail container_attr;
233
0
    CEL_RETURN_IF_ERROR(
234
0
        container_->Evaluate(frame, container_result, container_attr));
235
0
    CEL_ASSIGN_OR_RETURN(result, EvaluateIn(frame, result, item_attr,
236
0
                                            container_result, container_attr));
237
0
    return absl::OkStatus();
238
0
  }
239
240
 private:
241
  std::unique_ptr<DirectExpressionStep> item_;
242
  std::unique_ptr<DirectExpressionStep> container_;
243
};
244
245
class IterativeInStep : public ExpressionStepBase {
246
 public:
247
2.09k
  explicit IterativeInStep(int64_t expr_id) : ExpressionStepBase(expr_id) {}
248
249
483k
  absl::Status Evaluate(ExecutionFrame* frame) const override {
250
483k
    if (!frame->value_stack().HasEnough(2)) {
251
0
      return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
252
0
    }
253
254
483k
    auto args = frame->value_stack().GetSpan(2);
255
483k
    auto attrs = frame->value_stack().GetAttributeSpan(2);
256
257
483k
    CEL_ASSIGN_OR_RETURN(
258
483k
        Value result, EvaluateIn(*frame, args[0], attrs[0], args[1], attrs[1]));
259
483k
    frame->value_stack().PopAndPush(2, std::move(result));
260
483k
    return absl::OkStatus();
261
483k
  }
262
};
263
264
}  // namespace
265
266
// Factory method for recursive _==_ and _!=_ Execution step
267
std::unique_ptr<DirectExpressionStep> CreateDirectEqualityStep(
268
    std::unique_ptr<DirectExpressionStep> lhs,
269
0
    std::unique_ptr<DirectExpressionStep> rhs, bool negation, int64_t expr_id) {
270
0
  return std::make_unique<DirectEqualityStep>(std::move(lhs), std::move(rhs),
271
0
                                              negation, expr_id);
272
0
}
273
274
// Factory method for iterative _==_ and _!=_ Execution step
275
std::unique_ptr<ExpressionStep> CreateEqualityStep(bool negation,
276
11.1k
                                                   int64_t expr_id) {
277
11.1k
  return std::make_unique<IterativeEqualityStep>(negation, expr_id);
278
11.1k
}
279
280
// Factory method for recursive @in Execution step
281
std::unique_ptr<DirectExpressionStep> CreateDirectInStep(
282
    std::unique_ptr<DirectExpressionStep> item,
283
0
    std::unique_ptr<DirectExpressionStep> container, int64_t expr_id) {
284
0
  return std::make_unique<DirectInStep>(std::move(item), std::move(container),
285
0
                                        expr_id);
286
0
}
287
288
// Factory method for iterative @in Execution step
289
2.09k
std::unique_ptr<ExpressionStep> CreateInStep(int64_t expr_id) {
290
2.09k
  return std::make_unique<IterativeInStep>(expr_id);
291
2.09k
}
292
293
}  // namespace google::api::expr::runtime