Skip to content

Commit ff25a53

Browse files
hchokshifacebook-github-bot
authored andcommitted
Refactor lookup_property to operate on variable_component
Summary: - Refactor `lookup_property` to operate on `variable_component` as a unit, rather than just the `property` identifier component - Update map and local variable lookups to use the full identifier as it would appear in source (`prototype:property`) - Pre-compute and return references for the `prototype:property` string to avoid regeneration and copy on every lookup Reviewed By: praihan Differential Revision: D79453987 fbshipit-source-id: 50836f776e0be49323784227090ee7bb826ae5a1
1 parent 128eeef commit ff25a53

File tree

5 files changed

+64
-22
lines changed

5 files changed

+64
-22
lines changed

third-party/thrift/src/thrift/compiler/whisker/ast.cc

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include <cassert>
2525
#include <iterator>
26+
#include <sstream>
2627

2728
namespace whisker::ast {
2829

@@ -32,12 +33,12 @@ template <typename T, typename ToStringFunc>
3233
std::string to_joined_string(
3334
const std::vector<T>& parts, char separator, ToStringFunc&& to_string) {
3435
assert(!parts.empty());
35-
std::string result = to_string(parts.front());
36+
std::ostringstream result;
37+
result << to_string(parts.front());
3638
for (auto part = std::next(parts.begin()); part != parts.end(); ++part) {
37-
result += separator;
38-
result += to_string(*part);
39+
result << separator << to_string(*part);
3940
}
40-
return result;
41+
return result.str();
4142
}
4243

4344
} // namespace
@@ -51,19 +52,31 @@ std::string text::joined() const {
5152
return result;
5253
}
5354

54-
std::string variable_component::as_string() const {
55-
return prototype.has_value()
55+
variable_component::variable_component(
56+
source_range loc_,
57+
std::optional<identifier> prototype_,
58+
identifier property_)
59+
: loc(loc_),
60+
prototype(std::move(prototype_)),
61+
property(std::move(property_)) {
62+
as_string_ = prototype.has_value()
5663
? fmt::format("{}:{}", prototype->name, property.name)
5764
: property.name;
5865
}
5966

67+
const std::string_view variable_component::as_string() const {
68+
return as_string_;
69+
}
70+
6071
std::string variable_lookup::chain_string() const {
6172
return detail::variant_match(
6273
chain,
6374
[](this_ref) -> std::string { return "this"; },
6475
[](const std::vector<variable_component>& ids) -> std::string {
6576
return to_joined_string(
66-
ids, '.', [](const variable_component& id) -> std::string {
77+
ids,
78+
'.',
79+
[](const variable_component& id) -> const std::string_view {
6780
return id.as_string();
6881
});
6982
});

third-party/thrift/src/thrift/compiler/whisker/ast.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@ struct variable_component {
179179
std::optional<identifier> prototype;
180180
identifier property;
181181

182+
variable_component() = delete;
183+
184+
// Explicit constructor to ensure string representation is initialized
185+
variable_component(
186+
source_range loc,
187+
std::optional<identifier> prototype,
188+
identifier property);
189+
182190
/**
183191
* Determines if two identifiers are syntactically equivalent, excluding
184192
* their location in source code.
@@ -190,7 +198,17 @@ struct variable_component {
190198
// Remove in C++20 which introduces comparison operator synthesis
191199
WHISKER_DEFINE_OPERATOR_INEQUALITY(variable_component)
192200

193-
std::string as_string() const;
201+
/**
202+
* Returns a source-identical string representation of this variable
203+
* component.
204+
* For a prototype-qualified lookup, it takes the form "prototype:property".
205+
* For a raw identifier lookup, it is just "property".
206+
*/
207+
const std::string_view as_string() const;
208+
209+
private:
210+
// Store string representation to avoid regenerating repeatedly
211+
std::string as_string_;
194212
};
195213

196214
/**

third-party/thrift/src/thrift/compiler/whisker/eval_context.cc

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ namespace detail {
6666
std::optional<object> find_property(
6767
diagnostics_engine& diags,
6868
const object& self,
69-
const ast::identifier& identifier) {
69+
const ast::variable_component& component) {
7070
using result = std::optional<object>;
7171
return self.visit(
7272
[](null) -> result { return std::nullopt; },
@@ -76,17 +76,22 @@ std::optional<object> find_property(
7676
[](boolean) -> result { return std::nullopt; },
7777
[](const array::ptr&) -> result { return std::nullopt; },
7878
[&](const map::ptr& m) -> result {
79-
return m->lookup_property(identifier.name);
79+
// A map doesn't have a prototype, so we treat the lookup as just a
80+
// simple string key.
81+
// Specifically for the case of mstch_compat, mstch_object_proxy is in
82+
// fact a Whisker map, so this also provides backwards compatibility for
83+
// legacy mstch properties which have colons in the name.
84+
return m->lookup_property(component.as_string());
8085
},
8186
[](const native_function::ptr&) -> result { return std::nullopt; },
8287
[&](const native_handle<>& h) -> result {
8388
if (const auto* descriptor =
84-
h.proto()->find_descriptor(identifier.name)) {
89+
h.proto()->find_descriptor(component.property.name)) {
8590
return detail::variant_match(
8691
*descriptor,
8792
[&](const prototype<>::property& prop) -> object {
8893
return prop.function->invoke(native_function::context{
89-
identifier.loc,
94+
component.loc,
9095
diags,
9196
self,
9297
{} /* positional arguments */,
@@ -104,11 +109,12 @@ std::optional<object> find_property(
104109
} // namespace detail
105110

106111
std::optional<object> eval_context::lexical_scope::lookup_property(
107-
diagnostics_engine& diags, const ast::identifier& identifier) {
108-
if (auto local = locals_.find(identifier.name); local != locals_.end()) {
112+
diagnostics_engine& diags, const ast::variable_component& component) {
113+
if (auto local = locals_.find(component.as_string());
114+
local != locals_.end()) {
109115
return local->second;
110116
}
111-
return detail::find_property(diags, this_ref_, identifier);
117+
return detail::find_property(diags, this_ref_, component);
112118
}
113119

114120
eval_context::eval_context(diagnostics_engine& diags, object globals)
@@ -194,16 +200,17 @@ eval_context::look_up_object(const ast::variable_lookup& lookup) {
194200
begin,
195201
end,
196202
std::back_inserter(result),
197-
[](const auto& component) { return component.as_string(); });
203+
[](const auto& component) {
204+
return std::string{component.as_string()};
205+
});
198206
return result;
199207
};
200208

201209
std::optional<object> current;
202210
// Crawl up through the scope stack since names can be shadowed.
203211
for (auto scope = stack_.rbegin(); scope != stack_.rend(); ++scope) {
204212
try {
205-
if (auto result =
206-
scope->lookup_property(diags_, path.front().property)) {
213+
if (auto result = scope->lookup_property(diags_, path.front())) {
207214
current = result;
208215
break;
209216
}
@@ -225,7 +232,7 @@ eval_context::look_up_object(const ast::variable_lookup& lookup) {
225232
++component) {
226233
try {
227234
std::optional<object> next =
228-
detail::find_property(diags_, *current, component->property);
235+
detail::find_property(diags_, *current, *component);
229236
if (!next.has_value()) {
230237
return unexpected(eval_property_lookup_error(
231238
*current, /* missing_from */

third-party/thrift/src/thrift/compiler/whisker/eval_context.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ namespace detail {
149149
std::optional<object> find_property(
150150
diagnostics_engine& diags,
151151
const object& self,
152-
const ast::identifier& identifier);
152+
const ast::variable_component& component);
153153

154154
} // namespace detail
155155

@@ -337,7 +337,7 @@ class eval_context {
337337
* throws
338338
*/
339339
std::optional<object> lookup_property(
340-
diagnostics_engine& diags, const ast::identifier& identifier);
340+
diagnostics_engine& diags, const ast::variable_component& component);
341341

342342
// Before C++20, std::unordered_map does not support heterogenous lookups
343343
using locals_map = std::map<std::string, object, std::less<>>;

third-party/thrift/src/thrift/compiler/whisker/mstch_compat.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,11 @@ class mstch_object_proxy
207207
return detail::find_property(
208208
diags_,
209209
*self_whisker_obj,
210-
ast::identifier{source_range{}, std::string(self_property->second)});
210+
ast::variable_component{
211+
source_range{},
212+
std::nullopt /* prototype */,
213+
ast::identifier{
214+
source_range{}, std::string(self_property->second)}});
211215
}
212216

213217
return std::nullopt;

0 commit comments

Comments
 (0)