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_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_functions.h"
16
17
#include <cstddef>
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 "base/builtins.h"
25
#include "base/function_adapter.h"
26
#include "common/value.h"
27
#include "common/values/list_value_builder.h"
28
#include "internal/status_macros.h"
29
#include "runtime/function_registry.h"
30
#include "runtime/runtime_options.h"
31
#include "google/protobuf/arena.h"
32
#include "google/protobuf/descriptor.h"
33
#include "google/protobuf/message.h"
34
35
namespace cel {
36
namespace {
37
38
0
absl::StatusOr<int64_t> MapSizeImpl(const MapValue& value) {
39
0
  return value.Size();
40
0
}
41
42
1
absl::StatusOr<int64_t> ListSizeImpl(const ListValue& value) {
43
1
  return value.Size();
44
1
}
45
46
// Concatenation for CelList type.
47
absl::StatusOr<ListValue> ConcatList(
48
    const ListValue& value1, const ListValue& value2,
49
    const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
50
    google::protobuf::MessageFactory* absl_nonnull message_factory,
51
3.48k
    google::protobuf::Arena* absl_nonnull arena) {
52
3.48k
  CEL_ASSIGN_OR_RETURN(auto size1, value1.Size());
53
3.48k
  if (size1 == 0) {
54
110
    return value2;
55
110
  }
56
6.75k
  CEL_ASSIGN_OR_RETURN(auto size2, value2.Size());
57
6.75k
  if (size2 == 0) {
58
405
    return value1;
59
405
  }
60
61
  // TODO(uncreated-issue/50): add option for checking lists have homogenous element
62
  // types and use a more specialized list type when possible.
63
2.97k
  auto list_builder = NewListValueBuilder(arena);
64
65
2.97k
  list_builder->Reserve(size1 + size2);
66
67
109k
  for (size_t i = 0; i < size1; i++) {
68
106k
    CEL_ASSIGN_OR_RETURN(
69
106k
        Value elem, value1.Get(i, descriptor_pool, message_factory, arena));
70
106k
    CEL_RETURN_IF_ERROR(list_builder->Add(std::move(elem)));
71
106k
  }
72
8.66k
  for (size_t i = 0; i < size2; i++) {
73
5.69k
    CEL_ASSIGN_OR_RETURN(
74
5.69k
        Value elem, value2.Get(i, descriptor_pool, message_factory, arena));
75
5.69k
    CEL_RETURN_IF_ERROR(list_builder->Add(std::move(elem)));
76
5.69k
  }
77
78
2.97k
  return std::move(*list_builder).Build();
79
2.97k
}
80
81
// AppendList will append the elements in value2 to value1.
82
//
83
// This call will only be invoked within comprehensions where `value1` is an
84
// intermediate result which cannot be directly assigned or co-mingled with a
85
// user-provided list.
86
0
absl::StatusOr<ListValue> AppendList(ListValue value1, const Value& value2) {
87
  // The `value1` object cannot be directly addressed and is an intermediate
88
  // variable. Once the comprehension completes this value will in effect be
89
  // treated as immutable.
90
0
  if (auto mutable_list_value =
91
0
          cel::common_internal::AsMutableListValue(value1);
92
0
      mutable_list_value) {
93
0
    CEL_RETURN_IF_ERROR(mutable_list_value->Append(value2));
94
0
    return value1;
95
0
  }
96
0
  return absl::InvalidArgumentError("Unexpected call to runtime list append.");
97
0
}
98
}  // namespace
99
100
absl::Status RegisterContainerFunctions(FunctionRegistry& registry,
101
14.5k
                                        const RuntimeOptions& options) {
102
  // receiver style = true/false
103
  // Support both the global and receiver style size() for lists and maps.
104
29.0k
  for (bool receiver_style : {true, false}) {
105
29.0k
    CEL_RETURN_IF_ERROR(registry.Register(
106
29.0k
        cel::UnaryFunctionAdapter<absl::StatusOr<int64_t>, const ListValue&>::
107
29.0k
            CreateDescriptor(cel::builtin::kSize, receiver_style),
108
29.0k
        UnaryFunctionAdapter<absl::StatusOr<int64_t>,
109
29.0k
                             const ListValue&>::WrapFunction(ListSizeImpl)));
110
111
29.0k
    CEL_RETURN_IF_ERROR(registry.Register(
112
29.0k
        UnaryFunctionAdapter<absl::StatusOr<int64_t>, const MapValue&>::
113
29.0k
            CreateDescriptor(cel::builtin::kSize, receiver_style),
114
29.0k
        UnaryFunctionAdapter<absl::StatusOr<int64_t>,
115
29.0k
                             const MapValue&>::WrapFunction(MapSizeImpl)));
116
29.0k
  }
117
118
14.5k
  if (options.enable_list_concat) {
119
14.5k
    CEL_RETURN_IF_ERROR(registry.Register(
120
14.5k
        BinaryFunctionAdapter<
121
14.5k
            absl::StatusOr<Value>, const ListValue&,
122
14.5k
            const ListValue&>::CreateDescriptor(cel::builtin::kAdd, false),
123
14.5k
        BinaryFunctionAdapter<absl::StatusOr<Value>, const ListValue&,
124
14.5k
                              const ListValue&>::WrapFunction(ConcatList)));
125
14.5k
  }
126
127
14.5k
  return registry.Register(
128
14.5k
      BinaryFunctionAdapter<
129
14.5k
          absl::StatusOr<ListValue>, ListValue,
130
14.5k
          const Value&>::CreateDescriptor(cel::builtin::kRuntimeListAppend,
131
14.5k
                                          false),
132
14.5k
      BinaryFunctionAdapter<absl::StatusOr<ListValue>, ListValue,
133
14.5k
                            const Value&>::WrapFunction(AppendList));
134
14.5k
}
135
136
}  // namespace cel