Fix lifetime of starlark struct keys passed to C++.

This works fine *most* of the time, since we mostly return structs
stored on frozen heaps, thus guarunteeing they will live as long as
the starlark session. But we make no guaruntee of this.

Bug: 528225104
Change-Id: Ie76b00e2c10fdb4b166e1496fdae04eb6a6a6964
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/24680
Commit-Queue: Matt Stark <msta@google.com>
Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/starlark/crates/ffi/src/scope.rs b/src/gn/starlark/crates/ffi/src/scope.rs
index 09d2682..eb9dab0 100644
--- a/src/gn/starlark/crates/ffi/src/scope.rs
+++ b/src/gn/starlark/crates/ffi/src/scope.rs
@@ -5,11 +5,12 @@
 use std::pin::Pin;
 
 use starlark::values::{Heap, Value as StarlarkValue};
+use types::intern_string;
 
 use crate::{bridge::Value, Immutable, OwnedSlice, Scope};
 
 impl Scope {
-    pub(crate) fn new<'b>(
+    fn new<'b>(
         parent: &Self,
         keys: &[&str],
     ) -> (cxx::UniquePtr<Self>, OwnedSlice<Pin<&'b mut Value>>) {
@@ -55,7 +56,11 @@
 impl types::Scope for OwnedScope {
     fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, StarlarkValue<'v>)>) -> Self {
         let parent = self.0.as_ref().unwrap();
-        let (keys, vals): (Vec<&str>, Vec<StarlarkValue<'v>>) = kv.unzip();
+        // Scope stores a map from string_view to value. Since we don't know the
+        // lifetime of the string we were given, we must intern it in order to
+        // guarantee it can be safely dereferenced.
+        let (keys, vals): (Vec<&str>, Vec<StarlarkValue<'v>>) =
+            kv.map(|(s, v)| (intern_string(s), v)).unzip();
         let (mut child_scope, mut placeholders) = Scope::new(parent, &keys);
 
         let child_pin = child_scope.as_mut().unwrap();
diff --git a/src/gn/starlark/crates/types/src/file.rs b/src/gn/starlark/crates/types/src/file.rs
index 81b1dd6..32233b0 100644
--- a/src/gn/starlark/crates/types/src/file.rs
+++ b/src/gn/starlark/crates/types/src/file.rs
@@ -36,6 +36,15 @@
 
 starlark_simple_value!(File);
 
+/// Interns a string for the duration of the program.
+pub fn intern_string(s: &str) -> &'static str {
+    extern "C" {
+        fn intern_string(s: &str) -> &'static str;
+    }
+    // Safety: Just an ffi function
+    unsafe { intern_string(s) }
+}
+
 impl File {
     /// Creates a `File` from a string representing a path.
     /// The path is relative to the root_build_dir.
@@ -46,11 +55,7 @@
     /// Creates a `File` by interning a string representing a path.
     /// The path is relative to the root_build_dir.
     pub fn intern(s: &str) -> Self {
-        extern "C" {
-            fn intern_string(s: &str) -> &'static str;
-        }
-        // Safety: Just an ffi function
-        Self(unsafe { intern_string(s) })
+        Self(intern_string(s))
     }
 
     /// Returns the file path relative to the root_build_dir.
diff --git a/src/gn/starlark/crates/types/src/lib.rs b/src/gn/starlark/crates/types/src/lib.rs
index 44613d7..ae22eaf 100644
--- a/src/gn/starlark/crates/types/src/lib.rs
+++ b/src/gn/starlark/crates/types/src/lib.rs
@@ -21,7 +21,7 @@
 pub use ctx_state::CtxState;
 pub(crate) use errors::Error;
 pub use eval_context::{EvalContext, EvaluatorContextExt};
-pub use file::File;
+pub use file::{intern_string, File};
 pub use label::Label;
 pub use label_ref::LabelRef;
 pub use output_type::OutputType;