Skip to content

Commit c1a5771

Browse files
jnthntatumcopybara-github
authored andcommitted
Add support for injecting a custom attribute matcher.
PiperOrigin-RevId: 769263582
1 parent 3e92331 commit c1a5771

21 files changed

Lines changed: 678 additions & 209 deletions

base/attribute_set.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
#ifndef THIRD_PARTY_CEL_CPP_BASE_ATTRIBUTE_SET_H_
1616
#define THIRD_PARTY_CEL_CPP_BASE_ATTRIBUTE_SET_H_
1717

18-
#include <vector>
19-
2018
#include "absl/container/btree_set.h"
2119
#include "absl/types/span.h"
2220
#include "base/attribute.h"

eval/eval/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ cc_library(
4848
"//runtime",
4949
"//runtime:activation_interface",
5050
"//runtime:runtime_options",
51+
"//runtime/internal:activation_attribute_matcher_access",
5152
"@com_google_absl//absl/base:core_headers",
5253
"@com_google_absl//absl/base:nullability",
5354
"@com_google_absl//absl/log:absl_check",
@@ -1026,6 +1027,8 @@ cc_library(
10261027
"//common:value",
10271028
"//eval/internal:errors",
10281029
"//internal:status_macros",
1030+
"//runtime/internal:attribute_matcher",
1031+
"@com_google_absl//absl/base:nullability",
10291032
"@com_google_absl//absl/status:statusor",
10301033
"@com_google_absl//absl/types:optional",
10311034
"@com_google_absl//absl/types:span",
@@ -1039,6 +1042,7 @@ cc_test(
10391042
"attribute_utility_test.cc",
10401043
],
10411044
deps = [
1045+
":attribute_trail",
10421046
":attribute_utility",
10431047
"//base:attributes",
10441048
"//common:unknown",
@@ -1048,6 +1052,8 @@ cc_test(
10481052
"//eval/public:unknown_attribute_set",
10491053
"//eval/public:unknown_set",
10501054
"//internal:testing",
1055+
"//runtime/internal:attribute_matcher",
1056+
"@com_google_absl//absl/types:span",
10511057
"@com_google_protobuf//:protobuf",
10521058
],
10531059
)

eval/eval/attribute_utility.cc

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
#include "eval/eval/attribute_trail.h"
2020
#include "eval/internal/errors.h"
2121
#include "internal/status_macros.h"
22+
#include "runtime/internal/attribute_matcher.h"
2223

2324
namespace google::api::expr::runtime {
2425

26+
using ::cel::Attribute;
27+
using ::cel::AttributePattern;
2528
using ::cel::AttributeSet;
2629
using ::cel::Cast;
2730
using ::cel::ErrorValue;
@@ -31,25 +34,55 @@ using ::cel::InstanceOf;
3134
using ::cel::UnknownValue;
3235
using ::cel::Value;
3336
using ::cel::base_internal::UnknownSet;
37+
using ::cel::runtime_internal::AttributeMatcher;
3438

3539
using Accumulator = AttributeUtility::Accumulator;
40+
using MatchResult = AttributeMatcher::MatchResult;
41+
42+
DefaultAttributeMatcher::DefaultAttributeMatcher(
43+
absl::Span<const AttributePattern> unknown_patterns,
44+
absl::Span<const AttributePattern> missing_patterns)
45+
: unknown_patterns_(unknown_patterns),
46+
missing_patterns_(missing_patterns) {}
47+
48+
DefaultAttributeMatcher::DefaultAttributeMatcher() = default;
49+
50+
AttributeMatcher::MatchResult MatchAgainstPatterns(
51+
absl::Span<const AttributePattern> patterns, const Attribute& attr) {
52+
MatchResult result = MatchResult::NONE;
53+
for (const auto& pattern : patterns) {
54+
auto current_match = pattern.IsMatch(attr);
55+
if (current_match == cel::AttributePattern::MatchType::FULL) {
56+
return MatchResult::FULL;
57+
}
58+
if (current_match == cel::AttributePattern::MatchType::PARTIAL) {
59+
result = MatchResult::PARTIAL;
60+
}
61+
}
62+
return result;
63+
}
64+
65+
DefaultAttributeMatcher::MatchResult DefaultAttributeMatcher::CheckForUnknown(
66+
const Attribute& attr) const {
67+
return MatchAgainstPatterns(unknown_patterns_, attr);
68+
}
69+
70+
DefaultAttributeMatcher::MatchResult DefaultAttributeMatcher::CheckForMissing(
71+
const Attribute& attr) const {
72+
return MatchAgainstPatterns(missing_patterns_, attr);
73+
}
3674

3775
bool AttributeUtility::CheckForMissingAttribute(
3876
const AttributeTrail& trail) const {
3977
if (trail.empty()) {
4078
return false;
4179
}
42-
43-
for (const auto& pattern : missing_attribute_patterns_) {
44-
// (b/161297249) Preserving existing behavior for now, will add a streamz
45-
// for partial match, follow up with tightening up which fields are exposed
46-
// to the condition (w/ ajay and jim)
47-
if (pattern.IsMatch(trail.attribute()) ==
48-
cel::AttributePattern::MatchType::FULL) {
49-
return true;
50-
}
51-
}
52-
return false;
80+
// Missing attributes are only treated as errors if the attribute exactly
81+
// matches (so no guard against passing partial state to a function as with
82+
// unknowns). This was initially a design oversight, but is difficult to
83+
// change now.
84+
return matcher_->CheckForMissing(trail.attribute()) ==
85+
AttributeMatcher::MatchResult::FULL;
5386
}
5487

5588
// Checks whether particular corresponds to any patterns that define unknowns.
@@ -58,13 +91,11 @@ bool AttributeUtility::CheckForUnknown(const AttributeTrail& trail,
5891
if (trail.empty()) {
5992
return false;
6093
}
61-
for (const auto& pattern : unknown_patterns_) {
62-
auto current_match = pattern.IsMatch(trail.attribute());
63-
if (current_match == cel::AttributePattern::MatchType::FULL ||
64-
(use_partial &&
65-
current_match == cel::AttributePattern::MatchType::PARTIAL)) {
66-
return true;
67-
}
94+
MatchResult result = matcher_->CheckForUnknown(trail.attribute());
95+
96+
if (result == MatchResult::FULL ||
97+
(use_partial && result == MatchResult::PARTIAL)) {
98+
return true;
6899
}
69100
return false;
70101
}

eval/eval/attribute_utility.h

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,43 @@
11
#ifndef THIRD_PARTY_CEL_CPP_EVAL_EVAL_UNKNOWNS_UTILITY_H_
22
#define THIRD_PARTY_CEL_CPP_EVAL_EVAL_UNKNOWNS_UTILITY_H_
33

4+
#include <cstdint>
5+
6+
#include "absl/base/nullability.h"
47
#include "absl/status/statusor.h"
8+
#include "absl/types/optional.h"
59
#include "absl/types/span.h"
610
#include "base/attribute.h"
711
#include "base/attribute_set.h"
812
#include "base/function_result_set.h"
913
#include "common/function_descriptor.h"
1014
#include "common/value.h"
1115
#include "eval/eval/attribute_trail.h"
16+
#include "runtime/internal/attribute_matcher.h"
1217

1318
namespace google::api::expr::runtime {
1419

20+
// Default implementation of the attribute matcher.
21+
// Scans the attribute trail against a list of unknown or missing patterns.
22+
class DefaultAttributeMatcher : public cel::runtime_internal::AttributeMatcher {
23+
private:
24+
using MatchResult = cel::runtime_internal::AttributeMatcher::MatchResult;
25+
26+
public:
27+
DefaultAttributeMatcher(
28+
absl::Span<const cel::AttributePattern> unknown_patterns,
29+
absl::Span<const cel::AttributePattern> missing_patterns);
30+
31+
DefaultAttributeMatcher();
32+
33+
MatchResult CheckForUnknown(const cel::Attribute& attr) const override;
34+
MatchResult CheckForMissing(const cel::Attribute& attr) const override;
35+
36+
private:
37+
absl::Span<const cel::AttributePattern> unknown_patterns_;
38+
absl::Span<const cel::AttributePattern> missing_patterns_;
39+
};
40+
1541
// Helper class for handling unknowns and missing attribute logic. Provides
1642
// helpers for merging unknown sets from arguments on the stack and for
1743
// identifying unknown/missing attributes based on the patterns for a given
@@ -61,11 +87,14 @@ class AttributeUtility {
6187
bool unknown_present_;
6288
};
6389

64-
AttributeUtility(
65-
absl::Span<const cel::AttributePattern> unknown_patterns,
66-
absl::Span<const cel::AttributePattern> missing_attribute_patterns)
67-
: unknown_patterns_(unknown_patterns),
68-
missing_attribute_patterns_(missing_attribute_patterns) {}
90+
AttributeUtility(absl::Span<const cel::AttributePattern> unknown_patterns,
91+
absl::Span<const cel::AttributePattern> missing_patterns)
92+
: default_matcher_(unknown_patterns, missing_patterns),
93+
matcher_(&default_matcher_) {}
94+
95+
explicit AttributeUtility(
96+
const cel::runtime_internal::AttributeMatcher* ABSL_NONNULL matcher)
97+
: matcher_(matcher) {}
6998

7099
AttributeUtility(const AttributeUtility&) = delete;
71100
AttributeUtility& operator=(const AttributeUtility&) = delete;
@@ -131,13 +160,18 @@ class AttributeUtility {
131160
return Accumulator(*this);
132161
}
133162

163+
void set_matcher(
164+
const cel::runtime_internal::AttributeMatcher* ABSL_NONNULL matcher) {
165+
matcher_ = matcher;
166+
}
167+
134168
private:
135169
// Workaround friend visibility.
136170
void Add(Accumulator& a, const cel::UnknownValue& v) const;
137171
void Add(Accumulator& a, const AttributeTrail& attr) const;
138172

139-
absl::Span<const cel::AttributePattern> unknown_patterns_;
140-
absl::Span<const cel::AttributePattern> missing_attribute_patterns_;
173+
DefaultAttributeMatcher default_matcher_;
174+
const cel::runtime_internal::AttributeMatcher* ABSL_NONNULL matcher_;
141175
};
142176

143177
} // namespace google::api::expr::runtime

eval/eval/attribute_utility_test.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
#include "eval/eval/attribute_utility.h"
22

3+
#include <string>
34
#include <vector>
45

6+
#include "absl/types/span.h"
7+
#include "base/attribute.h"
58
#include "base/attribute_set.h"
69
#include "common/unknown.h"
710
#include "common/value.h"
11+
#include "eval/eval/attribute_trail.h"
812
#include "eval/public/cel_attribute.h"
913
#include "eval/public/cel_value.h"
1014
#include "eval/public/unknown_attribute_set.h"
1115
#include "eval/public/unknown_set.h"
1216
#include "internal/testing.h"
17+
#include "runtime/internal/attribute_matcher.h"
1318
#include "google/protobuf/arena.h"
1419

1520
namespace google::api::expr::runtime {
@@ -30,6 +35,8 @@ class AttributeUtilityTest : public ::testing::Test {
3035
google::protobuf::Arena arena_;
3136
};
3237

38+
absl::Span<const CelAttributePattern> NoPatterns() { return {}; }
39+
3340
TEST_F(AttributeUtilityTest, UnknownsUtilityCheckUnknowns) {
3441
std::vector<CelAttributePattern> unknown_patterns = {
3542
CelAttributePattern("unknown0", {CreateCelAttributeQualifierPattern(
@@ -160,4 +167,50 @@ TEST_F(AttributeUtilityTest, CreateUnknownSet) {
160167
EXPECT_EQ(elem, "destination.ip");
161168
}
162169

170+
class FakeMatcher : public cel::runtime_internal::AttributeMatcher {
171+
private:
172+
using MatchResult = cel::runtime_internal::AttributeMatcher::MatchResult;
173+
174+
public:
175+
MatchResult CheckForUnknown(const cel::Attribute& attr) const override {
176+
std::string attr_str = attr.AsString().value_or("");
177+
if (attr_str == "device.foo") {
178+
return MatchResult::FULL;
179+
} else if (attr_str == "device") {
180+
return MatchResult::PARTIAL;
181+
}
182+
return MatchResult::NONE;
183+
}
184+
185+
MatchResult CheckForMissing(const cel::Attribute& attr) const override {
186+
std::string attr_str = attr.AsString().value_or("");
187+
188+
if (attr_str == "device2.foo") {
189+
return MatchResult::FULL;
190+
} else if (attr_str == "device2") {
191+
return MatchResult::PARTIAL;
192+
}
193+
return MatchResult::NONE;
194+
}
195+
};
196+
197+
TEST_F(AttributeUtilityTest, CustomMatcher) {
198+
AttributeTrail trail("device");
199+
200+
AttributeUtility utility(NoPatterns(), NoPatterns());
201+
FakeMatcher matcher;
202+
utility.set_matcher(&matcher);
203+
EXPECT_TRUE(utility.CheckForUnknownPartial(trail));
204+
EXPECT_FALSE(utility.CheckForUnknownExact(trail));
205+
206+
trail = trail.Step(cel::AttributeQualifier::OfString("foo"));
207+
EXPECT_TRUE(utility.CheckForUnknownExact(trail));
208+
EXPECT_TRUE(utility.CheckForUnknownPartial(trail));
209+
210+
trail = AttributeTrail("device2");
211+
EXPECT_FALSE(utility.CheckForMissingAttribute(trail));
212+
trail = trail.Step(cel::AttributeQualifier::OfString("foo"));
213+
EXPECT_TRUE(utility.CheckForMissingAttribute(trail));
214+
}
215+
163216
} // namespace google::api::expr::runtime

eval/eval/evaluator_core.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "eval/eval/evaluator_stack.h"
3737
#include "eval/eval/iterator_stack.h"
3838
#include "runtime/activation_interface.h"
39+
#include "runtime/internal/activation_attribute_matcher_access.h"
3940
#include "runtime/runtime.h"
4041
#include "runtime/runtime_options.h"
4142
#include "google/protobuf/arena.h"
@@ -172,7 +173,15 @@ class ExecutionFrameBase {
172173
activation.GetMissingAttributes()),
173174
slots_(&ComprehensionSlots::GetEmptyInstance()),
174175
max_iterations_(options.comprehension_max_iterations),
175-
iterations_(0) {}
176+
iterations_(0) {
177+
if (unknown_processing_enabled()) {
178+
if (auto matcher = cel::runtime_internal::
179+
ActivationAttributeMatcherAccess::GetAttributeMatcher(activation);
180+
matcher != nullptr) {
181+
attribute_utility_.set_matcher(matcher);
182+
}
183+
}
184+
}
176185

177186
ExecutionFrameBase(const cel::ActivationInterface& activation,
178187
EvaluationListener callback,
@@ -193,7 +202,15 @@ class ExecutionFrameBase {
193202
activation.GetMissingAttributes()),
194203
slots_(&slots),
195204
max_iterations_(options.comprehension_max_iterations),
196-
iterations_(0) {}
205+
iterations_(0) {
206+
if (unknown_processing_enabled()) {
207+
if (auto matcher = cel::runtime_internal::
208+
ActivationAttributeMatcherAccess::GetAttributeMatcher(activation);
209+
matcher != nullptr) {
210+
attribute_utility_.set_matcher(matcher);
211+
}
212+
}
213+
}
197214

198215
const cel::ActivationInterface& activation() const { return *activation_; }
199216

eval/internal/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ cc_library(
8989
"//internal:status_macros",
9090
"//runtime:activation_interface",
9191
"//runtime:function_overload_reference",
92+
"//runtime/internal:activation_attribute_matcher_access",
93+
"//runtime/internal:attribute_matcher",
9294
"@com_google_absl//absl/base:nullability",
9395
"@com_google_absl//absl/status:statusor",
9496
"@com_google_absl//absl/strings",

eval/internal/adapter_activation_impl.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "eval/public/cel_value.h"
2626
#include "internal/status_macros.h"
2727
#include "runtime/function_overload_reference.h"
28+
#include "runtime/internal/activation_attribute_matcher_access.h"
29+
#include "runtime/internal/attribute_matcher.h"
2830
#include "google/protobuf/arena.h"
2931
#include "google/protobuf/descriptor.h"
3032
#include "google/protobuf/message.h"
@@ -76,4 +78,10 @@ absl::Span<const AttributePattern> AdapterActivationImpl::GetMissingAttributes()
7678
return legacy_activation_.missing_attribute_patterns();
7779
}
7880

81+
const runtime_internal::AttributeMatcher* ABSL_NULLABLE
82+
AdapterActivationImpl::GetAttributeMatcher() const {
83+
return runtime_internal::ActivationAttributeMatcherAccess::
84+
GetAttributeMatcher(legacy_activation_);
85+
}
86+
7987
} // namespace cel::interop_internal

eval/internal/adapter_activation_impl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "eval/public/base_activation.h"
2727
#include "runtime/activation_interface.h"
2828
#include "runtime/function_overload_reference.h"
29+
#include "runtime/internal/attribute_matcher.h"
2930
#include "google/protobuf/arena.h"
3031
#include "google/protobuf/descriptor.h"
3132
#include "google/protobuf/message.h"
@@ -56,6 +57,9 @@ class AdapterActivationImpl : public ActivationInterface {
5657
absl::Span<const cel::AttributePattern> GetMissingAttributes() const override;
5758

5859
private:
60+
const runtime_internal::AttributeMatcher* ABSL_NULLABLE GetAttributeMatcher()
61+
const override;
62+
5963
const google::api::expr::runtime::BaseActivation& legacy_activation_;
6064
};
6165

0 commit comments

Comments
 (0)