/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 |