Coverage Report

Created: 2026-05-27 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/runtime/standard/container_membership_functions.cc
Line
Count
Source
1
// Copyright 2023 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 "runtime/standard/container_membership_functions.h"
16
17
#include <array>
18
#include <cstdint>
19
#include <utility>
20
21
#include "absl/base/nullability.h"
22
#include "absl/status/status.h"
23
#include "absl/status/statusor.h"
24
#include "absl/strings/string_view.h"
25
#include "base/builtins.h"
26
#include "base/function_adapter.h"
27
#include "common/value.h"
28
#include "internal/number.h"
29
#include "internal/status_macros.h"
30
#include "runtime/function_registry.h"
31
#include "runtime/register_function_helper.h"
32
#include "runtime/runtime_options.h"
33
#include "google/protobuf/arena.h"
34
#include "google/protobuf/descriptor.h"
35
#include "google/protobuf/message.h"
36
37
namespace cel {
38
namespace {
39
40
using ::cel::internal::Number;
41
42
static constexpr std::array<absl::string_view, 3> in_operators = {
43
    cel::builtin::kIn,            // @in for map and list types.
44
    cel::builtin::kInFunction,    // deprecated in() -- for backwards compat
45
    cel::builtin::kInDeprecated,  // deprecated _in_ -- for backwards compat
46
};
47
48
template <class T>
49
bool ValueEquals(const Value& value, T other);
50
51
template <>
52
0
bool ValueEquals(const Value& value, bool other) {
53
0
  if (auto bool_value = As<BoolValue>(value); bool_value) {
54
0
    return bool_value->NativeValue() == other;
55
0
  }
56
0
  return false;
57
0
}
58
59
template <>
60
0
bool ValueEquals(const Value& value, int64_t other) {
61
0
  if (auto int_value = As<IntValue>(value); int_value) {
62
0
    return int_value->NativeValue() == other;
63
0
  }
64
0
  return false;
65
0
}
66
67
template <>
68
0
bool ValueEquals(const Value& value, uint64_t other) {
69
0
  if (auto uint_value = As<UintValue>(value); uint_value) {
70
0
    return uint_value->NativeValue() == other;
71
0
  }
72
0
  return false;
73
0
}
74
75
template <>
76
0
bool ValueEquals(const Value& value, double other) {
77
0
  if (auto double_value = As<DoubleValue>(value); double_value) {
78
0
    return double_value->NativeValue() == other;
79
0
  }
80
0
  return false;
81
0
}
82
83
template <>
84
0
bool ValueEquals(const Value& value, const StringValue& other) {
85
0
  if (auto string_value = As<StringValue>(value); string_value) {
86
0
    return string_value->Equals(other);
87
0
  }
88
0
  return false;
89
0
}
90
91
template <>
92
0
bool ValueEquals(const Value& value, const BytesValue& other) {
93
0
  if (auto bytes_value = As<BytesValue>(value); bytes_value) {
94
0
    return bytes_value->Equals(other);
95
0
  }
96
0
  return false;
97
0
}
98
99
// Template function implementing CEL in() function
100
template <typename T>
101
absl::StatusOr<bool> In(
102
    T value, const ListValue& list,
103
    const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
104
    google::protobuf::MessageFactory* absl_nonnull message_factory,
105
0
    google::protobuf::Arena* absl_nonnull arena) {
106
0
  CEL_ASSIGN_OR_RETURN(auto size, list.Size());
107
0
  Value element;
108
0
  for (int i = 0; i < size; i++) {
109
0
    CEL_RETURN_IF_ERROR(
110
0
        list.Get(i, descriptor_pool, message_factory, arena, &element));
111
0
    if (ValueEquals<T>(element, value)) {
112
0
      return true;
113
0
    }
114
0
  }
115
116
0
  return false;
117
0
}
Unexecuted instantiation: container_membership_functions.cc:absl::lts_20260107::StatusOr<bool> cel::(anonymous namespace)::In<bool>(bool, cel::ListValue const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*)
Unexecuted instantiation: container_membership_functions.cc:absl::lts_20260107::StatusOr<bool> cel::(anonymous namespace)::In<long>(long, cel::ListValue const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*)
Unexecuted instantiation: container_membership_functions.cc:absl::lts_20260107::StatusOr<bool> cel::(anonymous namespace)::In<unsigned long>(unsigned long, cel::ListValue const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*)
Unexecuted instantiation: container_membership_functions.cc:absl::lts_20260107::StatusOr<bool> cel::(anonymous namespace)::In<double>(double, cel::ListValue const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*)
Unexecuted instantiation: container_membership_functions.cc:absl::lts_20260107::StatusOr<bool> cel::(anonymous namespace)::In<cel::StringValue const&>(cel::StringValue const&, cel::ListValue const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*)
Unexecuted instantiation: container_membership_functions.cc:absl::lts_20260107::StatusOr<bool> cel::(anonymous namespace)::In<cel::BytesValue const&>(cel::BytesValue const&, cel::ListValue const&, google::protobuf::DescriptorPool const*, google::protobuf::MessageFactory*, google::protobuf::Arena*)
118
119
// Implementation for @in operator using heterogeneous equality.
120
absl::StatusOr<Value> HeterogeneousEqualityIn(
121
    const Value& value, const ListValue& list,
122
    const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
123
    google::protobuf::MessageFactory* absl_nonnull message_factory,
124
0
    google::protobuf::Arena* absl_nonnull arena) {
125
0
  return list.Contains(value, descriptor_pool, message_factory, arena);
126
0
}
127
128
absl::Status RegisterListMembershipFunctions(FunctionRegistry& registry,
129
14.5k
                                             const RuntimeOptions& options) {
130
43.5k
  for (absl::string_view op : in_operators) {
131
43.5k
    if (options.enable_heterogeneous_equality) {
132
43.5k
      CEL_RETURN_IF_ERROR(
133
43.5k
          (RegisterHelper<BinaryFunctionAdapter<
134
43.5k
               absl::StatusOr<Value>, const Value&, const ListValue&>>::
135
43.5k
               RegisterGlobalOverload(op, &HeterogeneousEqualityIn, registry)));
136
43.5k
    } else {
137
0
      CEL_RETURN_IF_ERROR(
138
0
          (RegisterHelper<BinaryFunctionAdapter<absl::StatusOr<bool>, bool,
139
0
                                                const ListValue&>>::
140
0
               RegisterGlobalOverload(op, In<bool>, registry)));
141
0
      CEL_RETURN_IF_ERROR(
142
0
          (RegisterHelper<BinaryFunctionAdapter<absl::StatusOr<bool>, int64_t,
143
0
                                                const ListValue&>>::
144
0
               RegisterGlobalOverload(op, In<int64_t>, registry)));
145
0
      CEL_RETURN_IF_ERROR(
146
0
          (RegisterHelper<BinaryFunctionAdapter<absl::StatusOr<bool>, uint64_t,
147
0
                                                const ListValue&>>::
148
0
               RegisterGlobalOverload(op, In<uint64_t>, registry)));
149
0
      CEL_RETURN_IF_ERROR(
150
0
          (RegisterHelper<BinaryFunctionAdapter<absl::StatusOr<bool>, double,
151
0
                                                const ListValue&>>::
152
0
               RegisterGlobalOverload(op, In<double>, registry)));
153
0
      CEL_RETURN_IF_ERROR(
154
0
          (RegisterHelper<BinaryFunctionAdapter<
155
0
               absl::StatusOr<bool>, const StringValue&, const ListValue&>>::
156
0
               RegisterGlobalOverload(op, In<const StringValue&>, registry)));
157
0
      CEL_RETURN_IF_ERROR(
158
0
          (RegisterHelper<BinaryFunctionAdapter<
159
0
               absl::StatusOr<bool>, const BytesValue&, const ListValue&>>::
160
0
               RegisterGlobalOverload(op, In<const BytesValue&>, registry)));
161
0
    }
162
43.5k
  }
163
14.5k
  return absl::OkStatus();
164
14.5k
}
165
166
absl::Status RegisterMapMembershipFunctions(FunctionRegistry& registry,
167
14.5k
                                            const RuntimeOptions& options) {
168
14.5k
  const bool enable_heterogeneous_equality =
169
14.5k
      options.enable_heterogeneous_equality;
170
171
14.5k
  auto boolKeyInSet =
172
14.5k
      [enable_heterogeneous_equality](
173
14.5k
          bool key, const MapValue& map_value,
174
14.5k
          const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
175
14.5k
          google::protobuf::MessageFactory* absl_nonnull message_factory,
176
14.5k
          google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr<Value> {
177
0
    Value has;
178
0
    CEL_RETURN_IF_ERROR(map_value.Has(BoolValue(key), descriptor_pool,
179
0
                                      message_factory, arena, &has));
180
0
    if (has.IsTrue()) {
181
0
      return has;
182
0
    }
183
0
    if (enable_heterogeneous_equality) {
184
0
      return BoolValue(false);
185
0
    }
186
0
    return has;
187
0
  };
188
189
14.5k
  auto intKeyInSet =
190
14.5k
      [enable_heterogeneous_equality](
191
14.5k
          int64_t key, const MapValue& map_value,
192
14.5k
          const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
193
14.5k
          google::protobuf::MessageFactory* absl_nonnull message_factory,
194
14.5k
          google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr<Value> {
195
0
    Value result;
196
0
    CEL_RETURN_IF_ERROR(map_value.Has(IntValue(key), descriptor_pool,
197
0
                                      message_factory, arena, &result));
198
0
    if (enable_heterogeneous_equality) {
199
0
      if (result.IsTrue()) {
200
0
        return result;
201
0
      }
202
0
      Number number = Number::FromInt64(key);
203
0
      if (number.LosslessConvertibleToUint()) {
204
0
        Value result_alt;
205
0
        CEL_RETURN_IF_ERROR(map_value.Has(UintValue(number.AsUint()),
206
0
                                          descriptor_pool, message_factory,
207
0
                                          arena, &result_alt));
208
0
        if (result_alt.IsTrue()) {
209
0
          return result_alt;
210
0
        }
211
0
      }
212
0
      return BoolValue(false);
213
0
    }
214
0
    return result;
215
0
  };
216
217
14.5k
  auto stringKeyInSet =
218
14.5k
      [enable_heterogeneous_equality](
219
14.5k
          const StringValue& key, const MapValue& map_value,
220
14.5k
          const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
221
14.5k
          google::protobuf::MessageFactory* absl_nonnull message_factory,
222
14.5k
          google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr<Value> {
223
0
    Value result;
224
0
    CEL_RETURN_IF_ERROR(
225
0
        map_value.Has(key, descriptor_pool, message_factory, arena, &result));
226
0
    if (result.IsBool()) {
227
0
      return result;
228
0
    }
229
0
    if (enable_heterogeneous_equality) {
230
0
      return BoolValue(false);
231
0
    }
232
0
    return result;
233
0
  };
234
235
14.5k
  auto uintKeyInSet =
236
14.5k
      [enable_heterogeneous_equality](
237
14.5k
          uint64_t key, const MapValue& map_value,
238
14.5k
          const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
239
14.5k
          google::protobuf::MessageFactory* absl_nonnull message_factory,
240
14.5k
          google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr<Value> {
241
0
    Value has;
242
0
    CEL_RETURN_IF_ERROR(map_value.Has(UintValue(key), descriptor_pool,
243
0
                                      message_factory, arena, &has));
244
0
    if (enable_heterogeneous_equality) {
245
0
      if (has.IsTrue()) {
246
0
        return has;
247
0
      }
248
0
      Value has_alt;
249
0
      Number number = Number::FromUint64(key);
250
0
      if (number.LosslessConvertibleToInt()) {
251
0
        CEL_RETURN_IF_ERROR(map_value.Has(IntValue(number.AsInt()),
252
0
                                          descriptor_pool, message_factory,
253
0
                                          arena, &has_alt));
254
0
        if (has.IsTrue()) {
255
0
          return has;
256
0
        }
257
0
      }
258
0
      return BoolValue(false);
259
0
    }
260
0
    return has;
261
0
  };
262
263
14.5k
  auto doubleKeyInSet =
264
14.5k
      [](double key, const MapValue& map_value,
265
14.5k
         const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
266
14.5k
         google::protobuf::MessageFactory* absl_nonnull message_factory,
267
14.5k
         google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr<Value> {
268
0
    Number number = Number::FromDouble(key);
269
0
    if (number.LosslessConvertibleToInt()) {
270
0
      Value has;
271
0
      CEL_RETURN_IF_ERROR(map_value.Has(IntValue(number.AsInt()),
272
0
                                        descriptor_pool, message_factory, arena,
273
0
                                        &has));
274
0
      if (has.IsTrue()) {
275
0
        return has;
276
0
      }
277
0
    }
278
0
    if (number.LosslessConvertibleToUint()) {
279
0
      Value has;
280
0
      CEL_RETURN_IF_ERROR(map_value.Has(UintValue(number.AsUint()),
281
0
                                        descriptor_pool, message_factory, arena,
282
0
                                        &has));
283
0
      if (has.IsTrue()) {
284
0
        return has;
285
0
      }
286
0
    }
287
0
    return BoolValue(false);
288
0
  };
289
290
43.5k
  for (auto op : in_operators) {
291
43.5k
    auto status = RegisterHelper<BinaryFunctionAdapter<
292
43.5k
        absl::StatusOr<Value>, const StringValue&,
293
43.5k
        const MapValue&>>::RegisterGlobalOverload(op, stringKeyInSet, registry);
294
43.5k
    if (!status.ok()) return status;
295
296
43.5k
    status = RegisterHelper<
297
43.5k
        BinaryFunctionAdapter<absl::StatusOr<Value>, bool, const MapValue&>>::
298
43.5k
        RegisterGlobalOverload(op, boolKeyInSet, registry);
299
43.5k
    if (!status.ok()) return status;
300
301
43.5k
    status = RegisterHelper<BinaryFunctionAdapter<absl::StatusOr<Value>,
302
43.5k
                                                  int64_t, const MapValue&>>::
303
43.5k
        RegisterGlobalOverload(op, intKeyInSet, registry);
304
43.5k
    if (!status.ok()) return status;
305
306
43.5k
    status = RegisterHelper<BinaryFunctionAdapter<absl::StatusOr<Value>,
307
43.5k
                                                  uint64_t, const MapValue&>>::
308
43.5k
        RegisterGlobalOverload(op, uintKeyInSet, registry);
309
43.5k
    if (!status.ok()) return status;
310
311
43.5k
    if (enable_heterogeneous_equality) {
312
43.5k
      status = RegisterHelper<BinaryFunctionAdapter<absl::StatusOr<Value>,
313
43.5k
                                                    double, const MapValue&>>::
314
43.5k
          RegisterGlobalOverload(op, doubleKeyInSet, registry);
315
43.5k
      if (!status.ok()) return status;
316
43.5k
    }
317
43.5k
  }
318
14.5k
  return absl::OkStatus();
319
14.5k
}
320
321
}  // namespace
322
323
absl::Status RegisterContainerMembershipFunctions(
324
14.5k
    FunctionRegistry& registry, const RuntimeOptions& options) {
325
14.5k
  if (options.enable_list_contains) {
326
14.5k
    CEL_RETURN_IF_ERROR(RegisterListMembershipFunctions(registry, options));
327
14.5k
  }
328
14.5k
  return RegisterMapMembershipFunctions(registry, options);
329
14.5k
}
330
331
}  // namespace cel