Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal change #346

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,10 @@ cc_library(
":function",
":function_descriptor",
":handle",
":kind",
"//base/internal:function_adapter",
"//internal:status_macros",
"@com_google_absl//absl/functional:bind_front",
"@com_google_absl//absl/log:die_if_null",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
Expand All @@ -356,6 +358,7 @@ cc_test(
"//internal:testing",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
],
)
Expand Down
132 changes: 131 additions & 1 deletion base/function_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,20 @@

#include <functional>
#include <memory>
#include <vector>

#include "absl/functional/bind_front.h"
#include "absl/log/die_if_null.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "base/function.h"
#include "base/function_descriptor.h"
#include "base/handle.h"
#include "base/internal/function_adapter.h"
#include "base/kind.h"
#include "base/value.h"
#include "internal/status_macros.h"

Expand All @@ -51,7 +55,85 @@ template <typename T>
struct AdaptedTypeTraits<const T&> {
using AssignableType = const T*;

static const T& ToArg(AssignableType v) { return *ABSL_DIE_IF_NULL(v); }
static std::reference_wrapper<const T> ToArg(AssignableType v) {
return *ABSL_DIE_IF_NULL(v); // Crash OK
}
};

template <typename... Args>
struct KindAdderImpl;

template <typename Arg, typename... Args>
struct KindAdderImpl<Arg, Args...> {
static void AddTo(std::vector<cel::Kind>& args) {
args.push_back(AdaptedKind<Arg>());
KindAdderImpl<Args...>::AddTo(args);
}
};

template <>
struct KindAdderImpl<> {
static void AddTo(std::vector<cel::Kind>& args) {}
};

template <typename... Args>
struct KindAdder {
static std::vector<cel::Kind> Kinds() {
std::vector<cel::Kind> args;
KindAdderImpl<Args...>::AddTo(args);
return args;
}
};

template <typename T>
struct ApplyReturnType {
using type = absl::StatusOr<T>;
};

template <typename T>
struct ApplyReturnType<absl::StatusOr<T>> {
using type = absl::StatusOr<T>;
};

template <int N, typename Arg, typename... Args>
struct IndexerImpl {
using type = typename IndexerImpl<N - 1, Args...>::type;
};

template <typename Arg, typename... Args>
struct IndexerImpl<0, Arg, Args...> {
using type = Arg;
};

template <int N, typename... Args>
struct Indexer {
static_assert(N < sizeof...(Args) && N >= 0);
using type = typename IndexerImpl<N, Args...>::type;
};

template <int N, typename... Args>
struct ApplyHelper {
template <typename T, typename Op>
static typename ApplyReturnType<T>::type Apply(
Op&& op, absl::Span<const Handle<Value>> input) {
constexpr int idx = sizeof...(Args) - N;
using Arg = typename Indexer<idx, Args...>::type;
using ArgTraits = internal::AdaptedTypeTraits<Arg>;
typename ArgTraits::AssignableType arg_i;
CEL_RETURN_IF_ERROR(internal::HandleToAdaptedVisitor{input[idx]}(&arg_i));

return ApplyHelper<N - 1, Args...>::template Apply<T>(
absl::bind_front(std::forward<Op>(op), ArgTraits::ToArg(arg_i)), input);
}
};

template <typename... Args>
struct ApplyHelper<0, Args...> {
template <typename T, typename Op>
static typename ApplyReturnType<T>::type Apply(
Op&& op, absl::Span<const Handle<Value>> input) {
return op();
}
};

} // namespace internal
Expand Down Expand Up @@ -223,6 +305,54 @@ class UnaryFunctionAdapter {
};
};

// Generic adapter class for generating CEL extension functions from an
// n-argument function. Prefer using the Binary and Unary versions. They are
// simpler and cover most use cases.
//
// See documentation for Binary Function adapter for general recommendations.
template <typename T, typename... Args>
class VariadicFunctionAdapter {
public:
using FunctionType = std::function<T(ValueFactory&, Args...)>;

static std::unique_ptr<cel::Function> WrapFunction(FunctionType fn) {
return std::make_unique<VariadicFunctionImpl>(std::move(fn));
}

static FunctionDescriptor CreateDescriptor(absl::string_view name,
bool receiver_style,
bool is_strict = true) {
return FunctionDescriptor(name, receiver_style,
internal::KindAdder<Args...>::Kinds(), is_strict);
}

private:
class VariadicFunctionImpl : public cel::Function {
public:
explicit VariadicFunctionImpl(FunctionType fn) : fn_(std::move(fn)) {}

absl::StatusOr<Handle<Value>> Invoke(
const FunctionEvaluationContext& context,
absl::Span<const Handle<Value>> args) const override {
if (args.size() != sizeof...(Args)) {
return absl::InvalidArgumentError(
absl::StrCat("unexpected number of arguments for variadic(",
sizeof...(Args), ") function"));
}

CEL_ASSIGN_OR_RETURN(
T result,
(internal::ApplyHelper<sizeof...(Args), Args...>::template Apply<T>(
absl::bind_front(fn_, std::ref(context.value_factory())), args)));
return internal::AdaptedToHandleVisitor{context.value_factory()}(
std::move(result));
}

private:
FunctionType fn_;
};
};

} // namespace cel

#endif // THIRD_PARTY_CEL_CPP_BASE_FUNCTION_ADAPTER_H_
103 changes: 103 additions & 0 deletions base/function_adapter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,22 @@

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/time/time.h"
#include "base/function.h"
#include "base/function_descriptor.h"
#include "base/handle.h"
#include "base/kind.h"
#include "base/memory.h"
#include "base/type_factory.h"
#include "base/type_manager.h"
#include "base/type_provider.h"
#include "base/value.h"
#include "base/value_factory.h"
#include "base/values/bool_value.h"
#include "base/values/bytes_value.h"
#include "base/values/double_value.h"
#include "base/values/duration_value.h"
#include "base/values/int_value.h"
#include "base/values/timestamp_value.h"
#include "base/values/uint_value.h"
Expand All @@ -42,6 +46,7 @@ namespace {

using testing::ElementsAre;
using testing::HasSubstr;
using testing::IsEmpty;
using cel::internal::StatusIs;

class FunctionAdapterTest : public ::testing::Test {
Expand Down Expand Up @@ -719,5 +724,103 @@ TEST_F(FunctionAdapterTest, BinaryFunctionAdapterCreateDescriptorNonStrict) {
EXPECT_THAT(desc.types(), ElementsAre(Kind::kAny, Kind::kAny));
}

TEST_F(FunctionAdapterTest, VariadicFunctionAdapterCreateDescriptor0Args) {
FunctionDescriptor desc =
VariadicFunctionAdapter<absl::StatusOr<Handle<Value>>>::CreateDescriptor(
"ZeroArgs", false);

EXPECT_EQ(desc.name(), "ZeroArgs");
EXPECT_TRUE(desc.is_strict());
EXPECT_FALSE(desc.receiver_style());
EXPECT_THAT(desc.types(), IsEmpty());
}

TEST_F(FunctionAdapterTest, VariadicFunctionAdapterWrapFunction0Args) {
std::unique_ptr<Function> fn =
VariadicFunctionAdapter<absl::StatusOr<Handle<Value>>>::WrapFunction(
[](ValueFactory& value_factory) {
return value_factory.CreateStringValue("abc");
});

ASSERT_OK_AND_ASSIGN(auto result, fn->Invoke(test_context(), {}));
ASSERT_TRUE(result->Is<StringValue>());
EXPECT_EQ(result.As<StringValue>()->ToString(), "abc");
}

TEST_F(FunctionAdapterTest, VariadicFunctionAdapterCreateDescriptor3Args) {
FunctionDescriptor desc = VariadicFunctionAdapter<
absl::StatusOr<Handle<Value>>, int64_t, bool,
const StringValue&>::CreateDescriptor("MyFormatter", false);

EXPECT_EQ(desc.name(), "MyFormatter");
EXPECT_TRUE(desc.is_strict());
EXPECT_FALSE(desc.receiver_style());
EXPECT_THAT(desc.types(),
ElementsAre(Kind::kInt64, Kind::kBool, Kind::kString));
}

TEST_F(FunctionAdapterTest, VariadicFunctionAdapterWrapFunction3Args) {
std::unique_ptr<Function> fn = VariadicFunctionAdapter<
absl::StatusOr<Handle<Value>>, int64_t, bool,
const StringValue&>::WrapFunction([](ValueFactory& value_factory,
int64_t int_val, bool bool_val,
const StringValue& string_val)
-> absl::StatusOr<Handle<Value>> {
return value_factory.CreateStringValue(
absl::StrCat(int_val, "_", (bool_val ? "true" : "false"), "_",
string_val.ToString()));
});

std::vector<Handle<Value>> args{value_factory().CreateIntValue(42),
value_factory().CreateBoolValue(false)};
ASSERT_OK_AND_ASSIGN(args.emplace_back(),
value_factory().CreateStringValue("abcd"));
ASSERT_OK_AND_ASSIGN(auto result, fn->Invoke(test_context(), args));
ASSERT_TRUE(result->Is<StringValue>());
EXPECT_EQ(result.As<StringValue>()->ToString(), "42_false_abcd");
}

TEST_F(FunctionAdapterTest,
VariadicFunctionAdapterWrapFunction3ArgsBadArgType) {
std::unique_ptr<Function> fn = VariadicFunctionAdapter<
absl::StatusOr<Handle<Value>>, int64_t, bool,
const StringValue&>::WrapFunction([](ValueFactory& value_factory,
int64_t int_val, bool bool_val,
const StringValue& string_val)
-> absl::StatusOr<Handle<Value>> {
return value_factory.CreateStringValue(
absl::StrCat(int_val, "_", (bool_val ? "true" : "false"), "_",
string_val.ToString()));
});

std::vector<Handle<Value>> args{value_factory().CreateIntValue(42),
value_factory().CreateBoolValue(false)};
ASSERT_OK_AND_ASSIGN(args.emplace_back(),
value_factory().CreateTimestampValue(absl::UnixEpoch()));
EXPECT_THAT(fn->Invoke(test_context(), args),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("expected string value")));
}

TEST_F(FunctionAdapterTest,
VariadicFunctionAdapterWrapFunction3ArgsBadArgCount) {
std::unique_ptr<Function> fn = VariadicFunctionAdapter<
absl::StatusOr<Handle<Value>>, int64_t, bool,
const StringValue&>::WrapFunction([](ValueFactory& value_factory,
int64_t int_val, bool bool_val,
const StringValue& string_val)
-> absl::StatusOr<Handle<Value>> {
return value_factory.CreateStringValue(
absl::StrCat(int_val, "_", (bool_val ? "true" : "false"), "_",
string_val.ToString()));
});

std::vector<Handle<Value>> args{value_factory().CreateIntValue(42),
value_factory().CreateBoolValue(false)};
EXPECT_THAT(fn->Invoke(test_context(), args),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("unexpected number of arguments")));
}

} // namespace
} // namespace cel
37 changes: 37 additions & 0 deletions extensions/protobuf/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,40 @@ cc_test(
"@com_google_protobuf//:protobuf",
],
)

cc_library(
name = "bind_proto_to_activation",
srcs = ["bind_proto_to_activation.cc"],
hdrs = ["bind_proto_to_activation.h"],
deps = [
":data",
"//base:data",
"//base:handle",
"//internal:status_macros",
"//runtime:activation",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_protobuf//:protobuf",
],
)

cc_test(
name = "bind_proto_to_activation_test",
srcs = ["bind_proto_to_activation_test.cc"],
deps = [
":bind_proto_to_activation",
":data",
":memory_manager",
"//base:data",
"//base:handle",
"//base:memory",
"//internal:testing",
"//runtime:activation",
"//runtime:managed_value_factory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/types:optional",
"@com_google_cel_spec//proto/test/v1/proto2:test_all_types_cc_proto",
"@com_google_protobuf//:protobuf",
],
)
Loading