Fix UB in ffi scope function. GetCurrentScopeValues returns a map from identifiers to *copies* of values. So when we take a pointer to them in ffi/scope.cc, when the function ends, those pointers dangle. Change-Id: I6169e03d89151f1530fda54a143a2aef6a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/24480 Commit-Queue: Matt Stark <msta@google.com> Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/ffi/scope.cc b/src/gn/ffi/scope.cc index 84abed9..b564226 100644 --- a/src/gn/ffi/scope.cc +++ b/src/gn/ffi/scope.cc
@@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "gn/ffi/scope.h" +#include <ranges> +#include <vector> + #include "gn/ffi/bridge.h" +#include "gn/ffi/scope.h" #include "gn/ffi/slice.h" #include "gn/scope.h" #include "gn/value.h" @@ -54,16 +57,12 @@ // * my_macro would complain that it got an unexpected parameter "foo" // * my_struct.srcs would also be accessible. SliceAny GetScopeItems(const Scope& scope) { - Scope::KeyValueMap scope_values; - scope.GetCurrentScopeValues(&scope_values); - - std::vector<KeyValue> vec; - vec.reserve(scope_values.size()); - for (const auto& pair : scope_values) { - vec.push_back( - KeyValue{rust::Str(pair.first.data(), pair.first.size()), pair.second}); - } - return IntoSlice(std::move(vec)); + auto range = + scope.GetCurrentScopeValues() | std::views::transform([](auto pair) { + return KeyValue{rust::Str(pair.first.data(), pair.first.size()), + *pair.second}; + }); + return IntoSlice(std::vector<KeyValue>(range.begin(), range.end())); } const Value* GetValue(const Scope& scope, rust::Str ident) {
diff --git a/src/gn/scope.h b/src/gn/scope.h index c9db294..9747ff3 100644 --- a/src/gn/scope.h +++ b/src/gn/scope.h
@@ -7,6 +7,7 @@ #include <map> #include <memory> +#include <ranges> #include <set> #include <string> #include <string_view> @@ -234,6 +235,14 @@ // scopes. void GetCurrentScopeValues(KeyValueMap* output) const; + // Returns all values set in the current scope as a lazy view of + // std::pair<std::string_view, const Value*>. + auto GetCurrentScopeValues() const { + return values_ | std::views::transform([](const auto& pair) { + return std::make_pair(pair.first, &pair.second.value); + }); + } + // Returns true if the values in the current scope are the same as all // values in the given scope, without going to the parent scopes. Returns // false if not.