Coverage Report

Created: 2026-05-27 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/eval/internal/cel_value_equal.cc
Line
Count
Source
1
// Copyright 2022 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
15
#include "eval/internal/cel_value_equal.h"
16
17
#include <cstdint>
18
19
#include "absl/time/time.h"
20
#include "absl/types/optional.h"
21
#include "common/kind.h"
22
#include "eval/public/cel_number.h"
23
#include "eval/public/cel_value.h"
24
#include "eval/public/message_wrapper.h"
25
#include "eval/public/structs/legacy_type_adapter.h"
26
#include "eval/public/structs/legacy_type_info_apis.h"
27
#include "internal/number.h"
28
#include "google/protobuf/arena.h"
29
30
namespace cel::interop_internal {
31
32
namespace {
33
34
using ::cel::internal::Number;
35
using ::google::api::expr::runtime::CelList;
36
using ::google::api::expr::runtime::CelMap;
37
using ::google::api::expr::runtime::CelValue;
38
using ::google::api::expr::runtime::GetNumberFromCelValue;
39
using ::google::api::expr::runtime::LegacyTypeAccessApis;
40
using ::google::api::expr::runtime::LegacyTypeInfoApis;
41
42
// Forward declaration of the functors for generic equality operator.
43
// Equal defined between compatible types.
44
struct HeterogeneousEqualProvider {
45
  absl::optional<bool> operator()(const CelValue& lhs,
46
                                  const CelValue& rhs) const;
47
};
48
49
// Comparison template functions
50
template <class Type>
51
absl::optional<bool> Inequal(Type lhs, Type rhs) {
52
  return lhs != rhs;
53
}
54
55
template <class Type>
56
0
absl::optional<bool> Equal(Type lhs, Type rhs) {
57
0
  return lhs == rhs;
58
0
}
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<std::__1::monostate>(std::__1::monostate, std::__1::monostate)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<bool>(bool, bool)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<long>(long, long)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<unsigned long>(unsigned long, unsigned long)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<double>(double, double)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<google::api::expr::runtime::CelValue::StringHolderBase<0> >(google::api::expr::runtime::CelValue::StringHolderBase<0>, google::api::expr::runtime::CelValue::StringHolderBase<0>)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<google::api::expr::runtime::CelValue::StringHolderBase<1> >(google::api::expr::runtime::CelValue::StringHolderBase<1>, google::api::expr::runtime::CelValue::StringHolderBase<1>)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<absl::lts_20260107::Duration>(absl::lts_20260107::Duration, absl::lts_20260107::Duration)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<absl::lts_20260107::Time>(absl::lts_20260107::Time, absl::lts_20260107::Time)
Unexecuted instantiation: cel_value_equal.cc:std::__1::optional<bool> cel::interop_internal::(anonymous namespace)::Equal<google::api::expr::runtime::CelValue::StringHolderBase<2> >(google::api::expr::runtime::CelValue::StringHolderBase<2>, google::api::expr::runtime::CelValue::StringHolderBase<2>)
59
60
// Equality for lists. Template parameter provides either heterogeneous or
61
// homogenous equality for comparing members.
62
template <typename EqualsProvider>
63
0
absl::optional<bool> ListEqual(const CelList* t1, const CelList* t2) {
64
0
  if (t1 == t2) {
65
0
    return true;
66
0
  }
67
0
  int index_size = t1->size();
68
0
  if (t2->size() != index_size) {
69
0
    return false;
70
0
  }
71
72
0
  google::protobuf::Arena arena;
73
0
  for (int i = 0; i < index_size; i++) {
74
0
    CelValue e1 = (*t1).Get(&arena, i);
75
0
    CelValue e2 = (*t2).Get(&arena, i);
76
0
    absl::optional<bool> eq = EqualsProvider()(e1, e2);
77
0
    if (eq.has_value()) {
78
0
      if (!(*eq)) {
79
0
        return false;
80
0
      }
81
0
    } else {
82
      // Propagate that the equality is undefined.
83
0
      return eq;
84
0
    }
85
0
  }
86
87
0
  return true;
88
0
}
89
90
// Equality for maps. Template parameter provides either heterogeneous or
91
// homogenous equality for comparing values.
92
template <typename EqualsProvider>
93
0
absl::optional<bool> MapEqual(const CelMap* t1, const CelMap* t2) {
94
0
  if (t1 == t2) {
95
0
    return true;
96
0
  }
97
0
  if (t1->size() != t2->size()) {
98
0
    return false;
99
0
  }
100
101
0
  google::protobuf::Arena arena;
102
0
  auto list_keys = t1->ListKeys(&arena);
103
0
  if (!list_keys.ok()) {
104
0
    return absl::nullopt;
105
0
  }
106
0
  const CelList* keys = *list_keys;
107
0
  for (int i = 0; i < keys->size(); i++) {
108
0
    CelValue key = (*keys).Get(&arena, i);
109
0
    CelValue v1 = (*t1).Get(&arena, key).value();
110
0
    absl::optional<CelValue> v2 = (*t2).Get(&arena, key);
111
0
    if (!v2.has_value()) {
112
0
      auto number = GetNumberFromCelValue(key);
113
0
      if (!number.has_value()) {
114
0
        return false;
115
0
      }
116
0
      if (!key.IsInt64() && number->LosslessConvertibleToInt()) {
117
0
        CelValue int_key = CelValue::CreateInt64(number->AsInt());
118
0
        absl::optional<bool> eq = EqualsProvider()(key, int_key);
119
0
        if (eq.has_value() && *eq) {
120
0
          v2 = (*t2).Get(&arena, int_key);
121
0
        }
122
0
      }
123
0
      if (!key.IsUint64() && !v2.has_value() &&
124
0
          number->LosslessConvertibleToUint()) {
125
0
        CelValue uint_key = CelValue::CreateUint64(number->AsUint());
126
0
        absl::optional<bool> eq = EqualsProvider()(key, uint_key);
127
0
        if (eq.has_value() && *eq) {
128
0
          v2 = (*t2).Get(&arena, uint_key);
129
0
        }
130
0
      }
131
0
    }
132
0
    if (!v2.has_value()) {
133
0
      return false;
134
0
    }
135
0
    absl::optional<bool> eq = EqualsProvider()(v1, *v2);
136
0
    if (!eq.has_value() || !*eq) {
137
      // Shortcircuit on value comparison errors and 'false' results.
138
0
      return eq;
139
0
    }
140
0
  }
141
142
0
  return true;
143
0
}
144
145
bool MessageEqual(const CelValue::MessageWrapper& m1,
146
0
                  const CelValue::MessageWrapper& m2) {
147
0
  const LegacyTypeInfoApis* lhs_type_info = m1.legacy_type_info();
148
0
  const LegacyTypeInfoApis* rhs_type_info = m2.legacy_type_info();
149
150
0
  if (lhs_type_info->GetTypename(m1) != rhs_type_info->GetTypename(m2)) {
151
0
    return false;
152
0
  }
153
154
0
  const LegacyTypeAccessApis* accessor = lhs_type_info->GetAccessApis(m1);
155
156
0
  if (accessor == nullptr) {
157
0
    return false;
158
0
  }
159
160
0
  return accessor->IsEqualTo(m1, m2);
161
0
}
162
163
// Generic equality for CEL values of the same type.
164
// EqualityProvider is used for equality among members of container types.
165
template <class EqualityProvider>
166
absl::optional<bool> HomogenousCelValueEqual(const CelValue& t1,
167
0
                                             const CelValue& t2) {
168
0
  if (t1.type() != t2.type()) {
169
0
    return absl::nullopt;
170
0
  }
171
0
  switch (t1.type()) {
172
0
    case Kind::kNullType:
173
0
      return Equal<CelValue::NullType>(CelValue::NullType(),
174
0
                                       CelValue::NullType());
175
0
    case Kind::kBool:
176
0
      return Equal<bool>(t1.BoolOrDie(), t2.BoolOrDie());
177
0
    case Kind::kInt64:
178
0
      return Equal<int64_t>(t1.Int64OrDie(), t2.Int64OrDie());
179
0
    case Kind::kUint64:
180
0
      return Equal<uint64_t>(t1.Uint64OrDie(), t2.Uint64OrDie());
181
0
    case Kind::kDouble:
182
0
      return Equal<double>(t1.DoubleOrDie(), t2.DoubleOrDie());
183
0
    case Kind::kString:
184
0
      return Equal<CelValue::StringHolder>(t1.StringOrDie(), t2.StringOrDie());
185
0
    case Kind::kBytes:
186
0
      return Equal<CelValue::BytesHolder>(t1.BytesOrDie(), t2.BytesOrDie());
187
0
    case Kind::kDuration:
188
0
      return Equal<absl::Duration>(t1.DurationOrDie(), t2.DurationOrDie());
189
0
    case Kind::kTimestamp:
190
0
      return Equal<absl::Time>(t1.TimestampOrDie(), t2.TimestampOrDie());
191
0
    case Kind::kList:
192
0
      return ListEqual<EqualityProvider>(t1.ListOrDie(), t2.ListOrDie());
193
0
    case Kind::kMap:
194
0
      return MapEqual<EqualityProvider>(t1.MapOrDie(), t2.MapOrDie());
195
0
    case Kind::kCelType:
196
0
      return Equal<CelValue::CelTypeHolder>(t1.CelTypeOrDie(),
197
0
                                            t2.CelTypeOrDie());
198
0
    default:
199
0
      break;
200
0
  }
201
0
  return absl::nullopt;
202
0
}
203
204
absl::optional<bool> HeterogeneousEqualProvider::operator()(
205
0
    const CelValue& lhs, const CelValue& rhs) const {
206
0
  return CelValueEqualImpl(lhs, rhs);
207
0
}
208
209
}  // namespace
210
211
// Equal operator is defined for all types at plan time. Runtime delegates to
212
// the correct implementation for types or returns nullopt if the comparison
213
// isn't defined.
214
0
absl::optional<bool> CelValueEqualImpl(const CelValue& v1, const CelValue& v2) {
215
0
  if (v1.type() == v2.type()) {
216
    // Message equality is only defined if heterogeneous comparisons are enabled
217
    // to preserve the legacy behavior for equality.
218
0
    if (CelValue::MessageWrapper lhs, rhs;
219
0
        v1.GetValue(&lhs) && v2.GetValue(&rhs)) {
220
0
      return MessageEqual(lhs, rhs);
221
0
    }
222
0
    return HomogenousCelValueEqual<HeterogeneousEqualProvider>(v1, v2);
223
0
  }
224
225
0
  absl::optional<Number> lhs = GetNumberFromCelValue(v1);
226
0
  absl::optional<Number> rhs = GetNumberFromCelValue(v2);
227
228
0
  if (rhs.has_value() && lhs.has_value()) {
229
0
    return *lhs == *rhs;
230
0
  }
231
232
  // TODO(uncreated-issue/6): It's currently possible for the interpreter to create a
233
  // map containing an Error. Return no matching overload to propagate an error
234
  // instead of a false result.
235
0
  if (v1.IsError() || v1.IsUnknownSet() || v2.IsError() || v2.IsUnknownSet()) {
236
0
    return absl::nullopt;
237
0
  }
238
239
0
  return false;
240
0
}
241
242
}  // namespace cel::interop_internal