Skip to content

Commit

Permalink
Introduce maybe_managed_ptr
Browse files Browse the repository at this point in the history
Summary:
This diff introduces `maybe_managed_ptr` (also `object::ptr`). This is useful to implement `native_function` (D67617014) where values can be returned by reference, or created via some computation from scratch.

See `folly::MaybeManagedPtr` for further motiviations.

No-op — this is just introducing aliases

Reviewed By: iahs

Differential Revision: D67655643

fbshipit-source-id: 6c62b7379ce958ff7f35930ab93946ab16dd8578
  • Loading branch information
praihan authored and facebook-github-bot committed Dec 27, 2024
1 parent 6275cb0 commit 3dcee52
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 58 deletions.
2 changes: 1 addition & 1 deletion thrift/compiler/whisker/eval_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class global_scope_object
explicit global_scope_object(map properties)
: properties_(std::move(properties)) {}

std::shared_ptr<const native_object::map_like> as_map_like() const override {
native_object::map_like::ptr as_map_like() const override {
return shared_from_this();
}

Expand Down
7 changes: 3 additions & 4 deletions thrift/compiler/whisker/mstch_compat.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ class mstch_array_proxy final
: proxied_(std::move(array)) {}

private:
std::shared_ptr<const native_object::array_like> as_array_like()
const override {
native_object::array_like::ptr as_array_like() const override {
return shared_from_this();
}

Expand Down Expand Up @@ -106,7 +105,7 @@ class mstch_map_proxy final
explicit mstch_map_proxy(mstch_map&& map) : proxied_(std::move(map)) {}

private:
std::shared_ptr<const native_object::map_like> as_map_like() const override {
native_object::map_like::ptr as_map_like() const override {
return shared_from_this();
}

Expand Down Expand Up @@ -176,7 +175,7 @@ class mstch_object_proxy
explicit mstch_object_proxy(std::shared_ptr<mstch_object>&& obj)
: proxied_(std::move(obj)) {}

std::shared_ptr<const native_object::map_like> as_map_like() const override {
native_object::map_like::ptr as_map_like() const override {
return shared_from_this();
}

Expand Down
51 changes: 42 additions & 9 deletions thrift/compiler/whisker/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,20 @@ struct object_print_options {
unsigned max_depth = std::numeric_limits<unsigned>::max();
};

/**
* A shared_ptr which represents a (possibly) managed object.
*
* When storing a raw pointer, maybe_managed_ptr does not manage the pointer's
* lifetime, i.e. never calls `free` on the pointer.
*
* When storing a shared_ptr, maybe_managed_ptr will release the shared_ptr
* upon its own destruction.
*
* See `folly::MaybeManagedPtr`.
*/
template <typename T>
using maybe_managed_ptr = std::shared_ptr<const T>;

/**
* A native_object is the most powerful type in Whisker. Its properties and
* behavior are defined by highly customizable C++ code.
Expand Down Expand Up @@ -133,6 +147,7 @@ class native_object {
*/
class map_like {
public:
using ptr = maybe_managed_ptr<const map_like>;
virtual ~map_like() = default;
/**
* Searches for a property on an object whose name matches the provided
Expand Down Expand Up @@ -168,24 +183,22 @@ class native_object {
* public native_object::map_like,
* public std::enable_shared_from_this<my_object> {
* public:
* std::shared_ptr<const native_object::map_like> as_map_like()
* const override {
* native_object::map_like:ptr as_map_like() const override {
* return shared_from_this();
* }
*
* // Implement map-like functions...
* };
*/
virtual std::shared_ptr<const native_object::map_like> as_map_like() const {
return nullptr;
}
virtual native_object::map_like::ptr as_map_like() const { return nullptr; }

/**
* A class that allows "array-like" random access over an underlying sequence
* of objects.
*/
class array_like {
public:
using ptr = maybe_managed_ptr<const array_like>;
virtual ~array_like() = default;
/**
* Returns the number of elements in the sequence.
Expand Down Expand Up @@ -214,16 +227,14 @@ class native_object {
* public native_object::array_like,
* public std::enable_shared_from_this<my_object> {
* public:
* std::shared_ptr<const native_object::array_like> as_array_like()
* const override {
* native_object::array_like::ptr as_array_like() const override {
* return shared_from_this();
* }
*
* // Implement array-like functions...
* };
*/
virtual std::shared_ptr<const native_object::array_like> as_array_like()
const {
virtual native_object::array_like::ptr as_array_like() const {
return nullptr;
}

Expand Down Expand Up @@ -311,6 +322,28 @@ class object final : private detail::object_base<object> {
base&& steal_variant() & { return std::move(*this); }

public:
using ptr = maybe_managed_ptr<object>;

/**
* Returns a shared_ptr which is an unmanaged reference to the provided
* object.
*
* The caller must guarantee that underlying object is kept alive for the
* duration of an expression evaluation in which the object is used.
*/
static ptr as_ref(const object& o) {
return ptr(std::shared_ptr<void>(), &o);
}
static ptr as_ref(object&&) = delete;

/**
* Returns a shared_ptr which directly owns a copy of the provided object.
* This allows native_function to return values that are dynamically computed.
*/
static ptr managed(object&& o) {
return std::make_shared<const object>(std::move(o));
}

/* implicit */ object(null = {}) : base(std::in_place_type<null>) {}
explicit object(boolean value) : base(bool(value)) {}
explicit object(i64 value) : base(value) {}
Expand Down
68 changes: 31 additions & 37 deletions thrift/compiler/whisker/render.cc
Original file line number Diff line number Diff line change
Expand Up @@ -341,67 +341,61 @@ class render_engine {
});
}

std::shared_ptr<const object> evaluate(const ast::expression& expr) {
using object_ptr = std::shared_ptr<const object>;
static const auto as_ref = [](const object& o) -> object_ptr {
// Empty deleter here implies that the object is externally managed.
// Thus, the returned shared_ptr acts like a reference.
return object_ptr(std::shared_ptr<void>(), &o);
};

using function_call = ast::expression::function_call;
object::ptr evaluate(const ast::expression& expr) {
using expression = ast::expression;
using function_call = expression::function_call;
return detail::variant_match(
expr.which,
[](const ast::expression::string_literal& s) -> object_ptr {
return std::make_shared<const object>(string(s.text));
[](const expression::string_literal& s) -> object::ptr {
return object::managed(whisker::make::string(s.text));
},
[](const ast::expression::i64_literal& i) -> object_ptr {
return std::make_shared<const object>(i64(i.value));
[](const expression::i64_literal& i) -> object::ptr {
return object::managed(whisker::make::i64(i.value));
},
[](const ast::expression::null_literal&) -> object_ptr {
return as_ref(whisker::make::null);
[](const expression::null_literal&) -> object::ptr {
return object::as_ref(whisker::make::null);
},
[](const ast::expression::true_literal&) -> object_ptr {
return as_ref(whisker::make::true_);
[](const expression::true_literal&) -> object::ptr {
return object::as_ref(whisker::make::true_);
},
[](const ast::expression::false_literal&) -> object_ptr {
return as_ref(whisker::make::false_);
[](const expression::false_literal&) -> object::ptr {
return object::as_ref(whisker::make::false_);
},
[&](const ast::variable_lookup& variable_lookup) -> object_ptr {
return as_ref(lookup_variable(variable_lookup));
[&](const ast::variable_lookup& variable_lookup) -> object::ptr {
return object::as_ref(lookup_variable(variable_lookup));
},
[&](const function_call& func) -> object_ptr {
[&](const function_call& func) -> object::ptr {
return detail::variant_match(
func.which,
[&](function_call::builtin_not) -> object_ptr {
[&](function_call::builtin_not) -> object::ptr {
// enforced by the parser
assert(func.positional_arguments.size() == 1);
assert(func.named_arguments.empty());
return evaluate_as_bool(func.positional_arguments[0])
? as_ref(whisker::make::false_)
: as_ref(whisker::make::true_);
? object::as_ref(whisker::make::false_)
: object::as_ref(whisker::make::true_);
},
[&](function_call::builtin_and) -> object_ptr {
[&](function_call::builtin_and) -> object::ptr {
// enforced by the parser
assert(func.named_arguments.empty());
for (const ast::expression& arg : func.positional_arguments) {
for (const expression& arg : func.positional_arguments) {
if (!evaluate_as_bool(arg)) {
return as_ref(whisker::make::false_);
return object::as_ref(whisker::make::false_);
}
}
return as_ref(whisker::make::true_);
return object::as_ref(whisker::make::true_);
},
[&](function_call::builtin_or) -> object_ptr {
[&](function_call::builtin_or) -> object::ptr {
// enforced by the parser
assert(func.named_arguments.empty());
for (const ast::expression& arg : func.positional_arguments) {
for (const expression& arg : func.positional_arguments) {
if (evaluate_as_bool(arg)) {
return as_ref(whisker::make::true_);
return object::as_ref(whisker::make::true_);
}
}
return as_ref(whisker::make::false_);
return object::as_ref(whisker::make::false_);
},
[&](const function_call::user_defined& f) -> object_ptr {
[&](const function_call::user_defined& f) -> object::ptr {
diags_.error(
f.name.loc.begin,
"User-defined function '{}' not supported yet.",
Expand Down Expand Up @@ -437,7 +431,7 @@ class render_engine {
}

void visit(const ast::interpolation& interpolation) {
std::shared_ptr<const object> result = evaluate(interpolation.content);
object::ptr result = evaluate(interpolation.content);

const auto report_unprintable_message_only = [&](diagnostic_level level) {
maybe_report(interpolation.loc, level, [&] {
Expand Down Expand Up @@ -500,7 +494,7 @@ class render_engine {
}
}
bool evaluate_as_bool(const ast::expression& expr) {
std::shared_ptr<const object> result = evaluate(expr);
object::ptr result = evaluate(expr);
return result->visit(
[&](boolean value) { return value; },
[&](const auto& value) {
Expand Down Expand Up @@ -654,7 +648,7 @@ class render_engine {

void visit(const ast::with_block& with_block) {
const ast::expression& expr = with_block.value;
std::shared_ptr<const object> result = evaluate(expr);
object::ptr result = evaluate(expr);
result->visit(
[&](const map&) {
// maps can be de-structured.
Expand Down
4 changes: 2 additions & 2 deletions thrift/compiler/whisker/test/eval_context_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class double_property_name
public native_object::map_like,
public std::enable_shared_from_this<double_property_name> {
public:
std::shared_ptr<const native_object::map_like> as_map_like() const override {
native_object::map_like::ptr as_map_like() const override {
return shared_from_this();
}

Expand Down Expand Up @@ -72,7 +72,7 @@ class delegate_to : public native_object,
: delegate_(std::move(delegate)) {}

private:
std::shared_ptr<const native_object::map_like> as_map_like() const override {
native_object::map_like::ptr as_map_like() const override {
return shared_from_this();
}

Expand Down
8 changes: 3 additions & 5 deletions thrift/compiler/whisker/test/render_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class double_property_name
public native_object::map_like,
public std::enable_shared_from_this<double_property_name> {
public:
std::shared_ptr<const native_object::map_like> as_map_like() const override {
native_object::map_like::ptr as_map_like() const override {
return shared_from_this();
}

Expand Down Expand Up @@ -204,8 +204,7 @@ TEST_F(RenderTest, section_block_array_iterable_native_object) {
explicit array_like_native_object(array values)
: values_(std::move(values)) {}

std::shared_ptr<const native_object::array_like> as_array_like()
const override {
native_object::array_like::ptr as_array_like() const override {
return shared_from_this();
}
std::size_t size() const override { return values_.size(); }
Expand Down Expand Up @@ -294,8 +293,7 @@ TEST_F(RenderTest, section_block_map_like_native_object) {
public:
explicit map_like_native_object(map values) : values_(std::move(values)) {}

std::shared_ptr<const native_object::map_like> as_map_like()
const override {
native_object::map_like::ptr as_map_like() const override {
return shared_from_this();
}

Expand Down

0 comments on commit 3dcee52

Please sign in to comment.