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/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
521k
    const Value& rhs, const AttributeTrail& rhs_attr, bool negation) {
51
521k
  if (lhs.IsError()) {
52
385k
    return lhs;
53
385k
  }
54
55
135k
  if (rhs.IsError()) {
56
6.59k
    return rhs;
57
6.59k
  }
58
59
128k
  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
257k
  CEL_ASSIGN_OR_RETURN(auto is_equal,
69
257k
                       ValueEqualImpl(lhs, rhs, frame.descriptor_pool(),
70
257k
                                      frame.message_factory(), frame.arena()));
71
257k
  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
128k
  return negation ? BoolValue(!*is_equal) : BoolValue(*is_equal);
76
257k
}
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
8.63k
      : ExpressionStepBase(expr_id), negation_(negation) {}
112
113
521k
  absl::Status Evaluate(ExecutionFrame* frame) const override {
114
521k
    if (!frame->value_stack().HasEnough(2)) {
115
0
      return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
116
0
    }
117
521k
    auto args = frame->value_stack().GetSpan(2);
118
521k
    auto attrs = frame->value_stack().GetAttributeSpan(2);
119
120
521k
    CEL_ASSIGN_OR_RETURN(Value result,
121
521k
                         EvaluateEquality(*frame, args[0], attrs[0], args[1],
122
521k
                                          attrs[1], negation_));
123
124
521k
    frame->value_stack().PopAndPush(2, std::move(result));
125
521k
    return absl::OkStatus();
126
521k
  }
127
128
 private:
129
  bool negation_;
130
};
131
132
absl::StatusOr<Value> EvaluateInMap(ExecutionFrameBase& frame,
133
                                    const Value& item,
134
11.6k
                                    const MapValue& container) {
135
11.6k
  switch (item.kind()) {
136
1.13k
    case ValueKind::kBool:
137
1.70k
    case ValueKind::kString:
138
6.74k
    case ValueKind::kInt:
139
6.81k
    case ValueKind::kUint:
140
11.4k
    case ValueKind::kDouble:
141
11.4k
      break;
142
206
    default:
143
206
      return cel::ErrorValue(
144
206
          cel::runtime_internal::CreateNoMatchingOverloadError(
145
206
              cel::builtin::kIn));
146
11.6k
  }
147
11.4k
  Value result;
148
11.4k
  CEL_RETURN_IF_ERROR(container.Has(item, frame.descriptor_pool(),
149
11.4k
                                    frame.message_factory(), frame.arena(),
150
11.4k
                                    &result));
151
152
11.4k
  if (result.IsTrue()) {
153
697
    return result;
154
697
  }
155
156
10.7k
  if (item.IsDouble() || item.IsUint()) {
157
4.74k
    Number number = item.IsDouble()
158
4.74k
                        ? Number::FromDouble(item.GetDouble().NativeValue())
159
4.74k
                        : Number::FromUint64(item.GetUint().NativeValue());
160
4.74k
    if (number.LosslessConvertibleToInt()) {
161
878
      CEL_RETURN_IF_ERROR(
162
878
          container.Has(IntValue(number.AsInt()), frame.descriptor_pool(),
163
878
                        frame.message_factory(), frame.arena(), &result));
164
878
      if (result.IsTrue()) {
165
1
        return result;
166
1
      }
167
878
    }
168
4.74k
  }
169
170
10.7k
  if (item.IsDouble() || item.IsInt()) {
171
9.70k
    Number number = item.IsDouble()
172
9.70k
                        ? Number::FromDouble(item.GetDouble().NativeValue())
173
9.70k
                        : Number::FromInt64(item.GetInt().NativeValue());
174
9.70k
    if (number.LosslessConvertibleToUint()) {
175
4.41k
      CEL_RETURN_IF_ERROR(
176
4.41k
          container.Has(UintValue(number.AsUint()), frame.descriptor_pool(),
177
4.41k
                        frame.message_factory(), frame.arena(), &result));
178
4.41k
      if (result.IsTrue()) {
179
1
        return result;
180
1
      }
181
4.41k
    }
182
9.70k
  }
183
184
10.7k
  return BoolValue(false);
185
10.7k
}
186
187
absl::StatusOr<Value> EvaluateIn(ExecutionFrameBase& frame, const Value& item,
188
                                 const AttributeTrail& item_attr,
189
                                 const Value& container,
190
216k
                                 const AttributeTrail& container_attr) {
191
216k
  if (item.IsError()) {
192
98.1k
    return item;
193
98.1k
  }
194
118k
  if (container.IsError()) {
195
1.02k
    return container;
196
1.02k
  }
197
198
117k
  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
117k
  if (container.IsList()) {
207
105k
    return container.GetList().Contains(item, frame.descriptor_pool(),
208
105k
                                        frame.message_factory(), frame.arena());
209
105k
  }
210
12.5k
  if (container.IsMap()) {
211
11.6k
    return EvaluateInMap(frame, item, container.GetMap());
212
11.6k
  }
213
869
  return cel::ErrorValue(
214
869
      cel::runtime_internal::CreateNoMatchingOverloadError(cel::builtin::kIn));
215
12.5k
}
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
3.24k
  explicit IterativeInStep(int64_t expr_id) : ExpressionStepBase(expr_id) {}
248
249
216k
  absl::Status Evaluate(ExecutionFrame* frame) const override {
250
216k
    if (!frame->value_stack().HasEnough(2)) {
251
0
      return absl::Status(absl::StatusCode::kInternal, "Value stack underflow");
252
0
    }
253
254
216k
    auto args = frame->value_stack().GetSpan(2);
255
216k
    auto attrs = frame->value_stack().GetAttributeSpan(2);
256
257
216k
    CEL_ASSIGN_OR_RETURN(
258
216k
        Value result, EvaluateIn(*frame, args[0], attrs[0], args[1], attrs[1]));
259
216k
    frame->value_stack().PopAndPush(2, std::move(result));
260
216k
    return absl::OkStatus();
261
216k
  }
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
8.63k
                                                   int64_t expr_id) {
277
8.63k
  return std::make_unique<IterativeEqualityStep>(negation, expr_id);
278
8.63k
}
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
3.24k
std::unique_ptr<ExpressionStep> CreateInStep(int64_t expr_id) {
290
3.24k
  return std::make_unique<IterativeInStep>(expr_id);
291
3.24k
}
292
293
}  // namespace google::api::expr::runtime