Coverage Report

Created: 2026-10-09 06:57

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