Implement GN variables that are custom starlark values. Note: You can't yet do anything with them. Bug: 528225104 Change-Id: I520a86f5d017ea3faaab7e912715193a6a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/24802 Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Matt Stark <msta@google.com>
diff --git a/src/gn/desc_builder.cc b/src/gn/desc_builder.cc index 67d28af..2fe6e38 100644 --- a/src/gn/desc_builder.cc +++ b/src/gn/desc_builder.cc
@@ -194,6 +194,8 @@ return ToBaseValue(val.list_value()); case Value::NONE: return base::Value(); + case Value::STARLARK_VALUE: + return base::Value(val.ToString(false)); } NOTREACHED(); return base::Value();
diff --git a/src/gn/ffi/bridge.cc b/src/gn/ffi/bridge.cc index f119648..a35ec04 100644 --- a/src/gn/ffi/bridge.cc +++ b/src/gn/ffi/bridge.cc
@@ -737,6 +737,18 @@ } #endif // CXXBRIDGE1_LAYOUT +namespace detail { +template <typename T, typename = void *> +struct operator_new { + void *operator()(::std::size_t sz) { return ::operator new(sz); } +}; + +template <typename T> +struct operator_new<T, decltype(T::operator new(sizeof(T)))> { + void *operator()(::std::size_t sz) { return T::operator new(sz); } +}; +} // namespace detail + template <typename T> union ManuallyDrop { T value; @@ -744,6 +756,14 @@ ~ManuallyDrop() {} }; +template <typename T> +union MaybeUninit { + T value; + void *operator new(::std::size_t sz) { return detail::operator_new<T>{}(sz); } + MaybeUninit() {} + ~MaybeUninit() {} +}; + namespace { template <bool> struct deleter_if { template <typename T> void operator()(T *) {} @@ -777,6 +797,7 @@ using Value = ::Value; using ParseNode = ::ParseNode; struct Session; +struct OwnedFrozenValue; #ifndef CXXBRIDGE1_STRUCT_Any #define CXXBRIDGE1_STRUCT_Any @@ -825,6 +846,7 @@ String = 3, List = 4, Scope = 5, + StarlarkValue = 6, }; #endif // CXXBRIDGE1_ENUM_ValueType @@ -845,6 +867,23 @@ }; #endif // CXXBRIDGE1_STRUCT_Session +#ifndef CXXBRIDGE1_STRUCT_OwnedFrozenValue +#define CXXBRIDGE1_STRUCT_OwnedFrozenValue +struct OwnedFrozenValue final : public ::rust::Opaque { + ::rust::Box<::OwnedFrozenValue> clone() const noexcept; + ::rust::String to_string() const noexcept; + bool eq(::OwnedFrozenValue const &other) const noexcept; + ~OwnedFrozenValue() = delete; + +private: + friend ::rust::layout; + struct layout { + static ::std::size_t size() noexcept; + static ::std::size_t align() noexcept; + }; +}; +#endif // CXXBRIDGE1_STRUCT_OwnedFrozenValue + extern "C" { void cxxbridge1$196$free_vector_buffer(::Any *ptr) noexcept { void (*free_vector_buffer$)(::Any *) = ::free_vector_buffer; @@ -1020,6 +1059,16 @@ void (*SetValueScope$)(::Value &, ::ParseNodePtr, ::std::unique_ptr<::Scope>) = ::SetValueScope; SetValueScope$(val, ::std::move(*origin), ::std::unique_ptr<::Scope>(scope)); } + +void cxxbridge1$196$SetValueStarlark(::Value &val, ::ParseNodePtr *origin, ::OwnedFrozenValue *starlark_val) noexcept { + void (*SetValueStarlark$)(::Value &, ::ParseNodePtr, ::rust::Box<::OwnedFrozenValue>) = ::SetValueStarlark; + SetValueStarlark$(val, ::std::move(*origin), ::rust::Box<::OwnedFrozenValue>::from_raw(starlark_val)); +} + +void cxxbridge1$196$Value$starlark_value(::Value const &self, ::OwnedFrozenValue const **return$) noexcept { + ::OwnedFrozenValue const &(::Value::*starlark_value$)() const = &::Value::starlark_value; + new (return$) ::OwnedFrozenValue const *(&(self.*starlark_value$)()); +} ::std::size_t cxxbridge1$196$Session$operator$sizeof() noexcept; ::std::size_t cxxbridge1$196$Session$operator$alignof() noexcept; @@ -1028,6 +1077,14 @@ ::Session *cxxbridge1$196$Session$new_for_testing() noexcept; void cxxbridge1$196$Session$load_values(::Session const &self, ::rust::Str label, ::rust::Str relative_to, ::rust::Slice<::rust::Str const> keys, ::Scope &scope, ::Settings const &settings, ::ParseNodePtr *origin, ::Err &err) noexcept; +::std::size_t cxxbridge1$196$OwnedFrozenValue$operator$sizeof() noexcept; +::std::size_t cxxbridge1$196$OwnedFrozenValue$operator$alignof() noexcept; + +::OwnedFrozenValue *cxxbridge1$196$OwnedFrozenValue$clone_cxx(::OwnedFrozenValue const &self) noexcept; + +void cxxbridge1$196$OwnedFrozenValue$to_string_cxx(::OwnedFrozenValue const &self, ::rust::String *return$) noexcept; + +bool cxxbridge1$196$OwnedFrozenValue$eq_cxx(::OwnedFrozenValue const &self, ::OwnedFrozenValue const &other) noexcept; } // extern "C" ::std::size_t Session::layout::size() noexcept { @@ -1051,6 +1108,28 @@ cxxbridge1$196$Session$load_values(*this, label, relative_to, keys, scope, settings, &origin$.value, err); } +::std::size_t OwnedFrozenValue::layout::size() noexcept { + return cxxbridge1$196$OwnedFrozenValue$operator$sizeof(); +} + +::std::size_t OwnedFrozenValue::layout::align() noexcept { + return cxxbridge1$196$OwnedFrozenValue$operator$alignof(); +} + +::rust::Box<::OwnedFrozenValue> OwnedFrozenValue::clone() const noexcept { + return ::rust::Box<::OwnedFrozenValue>::from_raw(cxxbridge1$196$OwnedFrozenValue$clone_cxx(*this)); +} + +::rust::String OwnedFrozenValue::to_string() const noexcept { + ::rust::MaybeUninit<::rust::String> return$; + cxxbridge1$196$OwnedFrozenValue$to_string_cxx(*this, &return$.value); + return ::std::move(return$.value); +} + +bool OwnedFrozenValue::eq(::OwnedFrozenValue const &other) const noexcept { + return cxxbridge1$196$OwnedFrozenValue$eq_cxx(*this, other); +} + extern "C" { static_assert(::rust::detail::is_complete<::std::remove_extent<::Err>::type>::value, "definition of `::Err` is required"); static_assert(sizeof(::std::unique_ptr<::Err>) == sizeof(void *), ""); @@ -1128,6 +1207,10 @@ ::rust::deleter_if<::rust::detail::is_complete<::Value>::value>{}(ptr); } +::OwnedFrozenValue *cxxbridge1$box$OwnedFrozenValue$alloc() noexcept; +void cxxbridge1$box$OwnedFrozenValue$dealloc(::OwnedFrozenValue *) noexcept; +void cxxbridge1$box$OwnedFrozenValue$drop(::rust::Box<::OwnedFrozenValue> *ptr) noexcept; + ::Session *cxxbridge1$box$Session$alloc() noexcept; void cxxbridge1$box$Session$dealloc(::Session *) noexcept; void cxxbridge1$box$Session$drop(::rust::Box<::Session> *ptr) noexcept; @@ -1136,6 +1219,18 @@ namespace rust { inline namespace cxxbridge1 { template <> +::OwnedFrozenValue *Box<::OwnedFrozenValue>::allocation::alloc() noexcept { + return cxxbridge1$box$OwnedFrozenValue$alloc(); +} +template <> +void Box<::OwnedFrozenValue>::allocation::dealloc(::OwnedFrozenValue *ptr) noexcept { + cxxbridge1$box$OwnedFrozenValue$dealloc(ptr); +} +template <> +void Box<::OwnedFrozenValue>::drop() noexcept { + cxxbridge1$box$OwnedFrozenValue$drop(this); +} +template <> ::Session *Box<::Session>::allocation::alloc() noexcept { return cxxbridge1$box$Session$alloc(); }
diff --git a/src/gn/ffi/bridge.h b/src/gn/ffi/bridge.h index 6435cb1..d49ba36 100644 --- a/src/gn/ffi/bridge.h +++ b/src/gn/ffi/bridge.h
@@ -754,6 +754,7 @@ using Value = ::Value; using ParseNode = ::ParseNode; struct Session; +struct OwnedFrozenValue; #ifndef CXXBRIDGE1_STRUCT_Any #define CXXBRIDGE1_STRUCT_Any @@ -802,6 +803,7 @@ String = 3, List = 4, Scope = 5, + StarlarkValue = 6, }; #endif // CXXBRIDGE1_ENUM_ValueType @@ -821,3 +823,20 @@ }; }; #endif // CXXBRIDGE1_STRUCT_Session + +#ifndef CXXBRIDGE1_STRUCT_OwnedFrozenValue +#define CXXBRIDGE1_STRUCT_OwnedFrozenValue +struct OwnedFrozenValue final : public ::rust::Opaque { + ::rust::Box<::OwnedFrozenValue> clone() const noexcept; + ::rust::String to_string() const noexcept; + bool eq(::OwnedFrozenValue const &other) const noexcept; + ~OwnedFrozenValue() = delete; + +private: + friend ::rust::layout; + struct layout { + static ::std::size_t size() noexcept; + static ::std::size_t align() noexcept; + }; +}; +#endif // CXXBRIDGE1_STRUCT_OwnedFrozenValue
diff --git a/src/gn/ffi/value.cc b/src/gn/ffi/value.cc index 4904326..2f3813c 100644 --- a/src/gn/ffi/value.cc +++ b/src/gn/ffi/value.cc
@@ -48,6 +48,12 @@ new (&self) Value(origin.ptr, std::move(scope)); } +void SetValueStarlark(Value& self, + ParseNodePtr origin, + rust::Box<OwnedFrozenValue> starlark_val) { + new (&self) Value(origin.ptr, std::move(starlark_val)); +} + SliceAny GetValueList(const Value& self) { return AsSlice(self.list_value()); }
diff --git a/src/gn/ffi/value.h b/src/gn/ffi/value.h index 16e2853..213eea9 100644 --- a/src/gn/ffi/value.h +++ b/src/gn/ffi/value.h
@@ -13,6 +13,7 @@ class Scope; struct ParseNodePtr; struct SliceAny; +struct OwnedFrozenValue; enum class ValueType : uint8_t; @@ -38,6 +39,9 @@ void SetValueScope(Value& self, ParseNodePtr origin, std::unique_ptr<Scope> scope); +void SetValueStarlark(Value& self, + ParseNodePtr origin, + rust::Box<OwnedFrozenValue> starlark_val); // Returns a "std::vector<Value>". // // Safety: Rust is required to convert this to a Slice<Value>.
diff --git a/src/gn/functions_unittest.cc b/src/gn/functions_unittest.cc index 1b7da65..31418ee 100644 --- a/src/gn/functions_unittest.cc +++ b/src/gn/functions_unittest.cc
@@ -747,22 +747,40 @@ setup.scope()->set_source_dir(SourceDir("//")); std::string scl_content = R"scl( -a = "hello" +def my_rule_impl(ctx): + pass + +my_rule = rule( + implementation = my_rule_impl +) +hello = "hello" )scl"; base::FilePath scl_path = temp_dir.GetPath().AppendASCII("rules.scl"); ASSERT_EQ( static_cast<int>(scl_content.size()), base::WriteFile(scl_path, scl_content.c_str(), scl_content.size())); - TestParseInput input(R"gn(load("//:rules.scl", "a"))gn"); + TestParseInput input(R"gn( +load("//:rules.scl", "hello", "my_rule", "my_rule_impl") + +assert(my_rule == my_rule) +assert(my_rule != my_rule_impl) +assert("${my_rule}" == "<rule: my_rule>") +copy_of_rule = my_rule +assert(copy_of_rule == my_rule) +)gn"); ASSERT_SUCCESS(input); Err err; input.parsed()->Execute(setup.scope(), &err); ASSERT_FALSE(err.has_error()) << err.message(); - const Value* val_a = setup.scope()->GetValue("a"); - ASSERT_TRUE(val_a); - EXPECT_EQ(Value::STRING, val_a->type()); - EXPECT_EQ("hello", val_a->string_value()); + const Value* val_hello = setup.scope()->GetValue("hello"); + ASSERT_TRUE(val_hello); + EXPECT_EQ(Value::STRING, val_hello->type()); + EXPECT_EQ("hello", val_hello->string_value()); + + const Value* val_my_rule = setup.scope()->GetValue("my_rule"); + ASSERT_TRUE(val_my_rule); + EXPECT_EQ(Value::STARLARK_VALUE, val_my_rule->type()); } }
diff --git a/src/gn/operators.cc b/src/gn/operators.cc index 93cbe72..2adb7b2 100644 --- a/src/gn/operators.cc +++ b/src/gn/operators.cc
@@ -266,7 +266,8 @@ case Value::BOOLEAN: case Value::INTEGER: // Filter out the individual int/string. case Value::STRING: - case Value::SCOPE: { + case Value::SCOPE: + case Value::STARLARK_VALUE: { bool found_match = false; for (size_t i = 0; i < v.size(); /* nothing */) { if (v[i] == to_remove) {
diff --git a/src/gn/starlark/crates/ffi/src/bridge.rs b/src/gn/starlark/crates/ffi/src/bridge.rs index 642dc76..3fb3d5b 100644 --- a/src/gn/starlark/crates/ffi/src/bridge.rs +++ b/src/gn/starlark/crates/ffi/src/bridge.rs
@@ -14,6 +14,22 @@ /// rust. use crate::session::Session; +pub struct OwnedFrozenValue(pub starlark::values::OwnedFrozenValue); + +impl OwnedFrozenValue { + pub fn clone_cxx(&self) -> Box<Self> { + Box::new(Self(self.0.clone())) + } + + pub fn to_string_cxx(&self) -> String { + self.0.value().to_string() + } + + pub fn eq_cxx(&self, other: &Self) -> bool { + self.0.value() == other.0.value() + } +} + #[cxx::bridge] // Allow let_underscore_drop because the cxx::bridge generated code has non-binding // lets on C++ types with destructors. @@ -52,6 +68,7 @@ String = 3, List = 4, Scope = 5, + StarlarkValue = 6, } unsafe extern "C++" { // include! simply tells cxxbridge to put the #include in the generated C++ @@ -186,6 +203,12 @@ origin: ParseNodePtr, scope: UniquePtr<Scope>, ); + pub(in crate::value) fn SetValueStarlark( + val: Pin<&mut Value>, + origin: ParseNodePtr, + starlark_val: Box<OwnedFrozenValue>, + ); + pub(in crate::value) fn starlark_value(self: &Value) -> &OwnedFrozenValue; } extern "Rust" { @@ -208,6 +231,14 @@ origin: ParseNodePtr, err: Pin<&mut Err>, ); + + type OwnedFrozenValue; + #[rust_name = "clone_cxx"] + fn clone(self: &OwnedFrozenValue) -> Box<OwnedFrozenValue>; + #[rust_name = "to_string_cxx"] + fn to_string(self: &OwnedFrozenValue) -> String; + #[rust_name = "eq_cxx"] + fn eq(self: &OwnedFrozenValue, other: &OwnedFrozenValue) -> bool; } }
diff --git a/src/gn/starlark/crates/ffi/src/errors.rs b/src/gn/starlark/crates/ffi/src/errors.rs index 649a918..d3b1255 100644 --- a/src/gn/starlark/crates/ffi/src/errors.rs +++ b/src/gn/starlark/crates/ffi/src/errors.rs
@@ -9,6 +9,8 @@ pub(crate) enum Error { #[error("Key '{0}' not found in module '{1}'")] KeyNotFound(String, Label), + #[error("Passing a custom starlark type to builtin GN target types is not supported: {0}")] + PassingNonFrozenStarlarkValueToGn(String), #[error("This is not allowed while executing a function loaded from a starlark file.")] RequiresBzlFile, }
diff --git a/src/gn/starlark/crates/ffi/src/lib.rs b/src/gn/starlark/crates/ffi/src/lib.rs index 2edea21..e2a9823 100644 --- a/src/gn/starlark/crates/ffi/src/lib.rs +++ b/src/gn/starlark/crates/ffi/src/lib.rs
@@ -32,7 +32,10 @@ mod test_with_scope; mod value; -pub use bridge::{Err, KeyValue, Label, OutputFile, Scope, Settings, SourceDir, Value, ValueType}; +pub use bridge::{ + Err, KeyValue, Label, OutputFile, OwnedFrozenValue, Scope, Settings, SourceDir, Value, + ValueType, +}; pub use mutability::Immutable; pub use opaque::{NonOpaque, OpaqueSized}; pub use scope::OwnedScope;
diff --git a/src/gn/starlark/crates/ffi/src/scope.rs b/src/gn/starlark/crates/ffi/src/scope.rs index eb9dab0..d74f3a3 100644 --- a/src/gn/starlark/crates/ffi/src/scope.rs +++ b/src/gn/starlark/crates/ffi/src/scope.rs
@@ -54,7 +54,10 @@ pub struct OwnedScope(pub cxx::UniquePtr<Scope>); impl types::Scope for OwnedScope { - fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, StarlarkValue<'v>)>) -> Self { + fn copy_with<'a, 'v>( + &self, + kv: impl Iterator<Item = (&'a str, StarlarkValue<'v>)>, + ) -> starlark::Result<Self> { let parent = self.0.as_ref().unwrap(); // 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 @@ -67,10 +70,10 @@ for (placeholder, val) in placeholders.as_slice_mut().iter_mut().zip(vals) { placeholder .as_mut() - .assign(val, child_pin.settings(), Default::default()); + .assign(val, None, child_pin.settings(), Default::default())?; } - Self(child_scope) + Ok(Self(child_scope)) } fn get<'v>(&self, key: &str, heap: &Heap<'v>) -> Option<StarlarkValue<'v>> { @@ -103,8 +106,9 @@ let val_int = heap.alloc(42); let val_str = heap.alloc("hello"); - let grandchild = - owned_scope.copy_with(vec![("foo", val_int), ("bar", val_str)].into_iter()); + let grandchild = owned_scope + .copy_with(vec![("foo", val_int), ("bar", val_str)].into_iter()) + .unwrap(); assert_eq!(grandchild.get("foo", &heap).unwrap().unpack_i32(), Some(42)); assert_eq!(
diff --git a/src/gn/starlark/crates/ffi/src/session.rs b/src/gn/starlark/crates/ffi/src/session.rs index a69d2b1..ae97f24 100644 --- a/src/gn/starlark/crates/ffi/src/session.rs +++ b/src/gn/starlark/crates/ffi/src/session.rs
@@ -98,7 +98,9 @@ .get(key) .map_err(|_| Error::KeyNotFound(key.to_string(), label.clone()))?; let mut cxx_value = crate::bridge::SetValue(scope.as_mut(), key, origin); - cxx_value.as_mut().assign(value.value(), settings, origin); + cxx_value + .as_mut() + .assign(value.value(), Some(value.owner()), settings, origin)?; } Ok(()) })());
diff --git a/src/gn/starlark/crates/ffi/src/value.rs b/src/gn/starlark/crates/ffi/src/value.rs index 6ef19f0..7c308de 100644 --- a/src/gn/starlark/crates/ffi/src/value.rs +++ b/src/gn/starlark/crates/ffi/src/value.rs
@@ -4,10 +4,12 @@ use std::pin::Pin; -use starlark::values::{list::ListRef, structs::StructRef}; +use starlark::values::{list::ListRef, structs::StructRef, OwnedFrozenValue}; use crate::{ + bridge, bridge::{SliceAny, Value, ValueType}, + errors::Error, Immutable, Scope, Settings, Slice, }; @@ -34,6 +36,13 @@ let scope = unsafe { &*scope_ptr }; heap.alloc(starlark::values::structs::AllocStruct(scope.get_kv(heap))) }, + ValueType::StarlarkValue => { + let rust_val = self.starlark_value(); + heap.add_reference(rust_val.0.owner()); + // Safety: This is safe when combined with the above line, which ensures it will + // not get GC'd. + starlark::values::Value::new_frozen(unsafe { rust_val.0.unchecked_frozen_value() }) + }, _ => unreachable!(), } } @@ -41,9 +50,10 @@ pub fn assign<'v>( mut self: Pin<&mut Self>, val: starlark::values::Value<'v>, + owner: Option<&starlark::values::FrozenHeapRef>, settings: &Settings, origin: crate::bridge::ParseNodePtr, - ) { + ) -> starlark::Result<()> { if val.is_none() { crate::bridge::SetValueNone(self.as_mut(), origin); } else if let Some(s) = val.unpack_str() { @@ -59,20 +69,27 @@ } .into(); for (el_pin, src) in slice.iter_mut().zip(l.iter()) { - el_pin.assign(src, settings, origin); + el_pin.assign(src, owner, settings, origin)?; } } else if let Some(s) = StructRef::from_value(val) { let keys: Vec<&str> = s.iter().map(|(k, _)| k.as_str()).collect(); let (r#struct, mut values) = Scope::new_struct(settings, &keys); for (v_starlark, v_cxx) in s.iter().map(|(_, v)| v).zip(values.as_slice_mut()) { - v_cxx.as_mut().assign(v_starlark, settings, origin); + v_cxx.as_mut().assign(v_starlark, owner, settings, origin)?; } crate::bridge::SetValueScope(self.as_mut(), origin, r#struct); } else { - todo!("Arbitrary starlark values not (yet) supported"); + let owned_frozen = if let (Some(owner), Some(frozen)) = (owner, val.unpack_frozen()) { + // Safety: The caller guarantees that owner owns val. + bridge::OwnedFrozenValue(unsafe { OwnedFrozenValue::new(owner.clone(), frozen) }) + } else { + return Err(Error::PassingNonFrozenStarlarkValueToGn(val.to_string()).into()); + }; + crate::bridge::SetValueStarlark(self.as_mut(), origin, Box::new(owned_frozen)); } + Ok(()) } } @@ -91,13 +108,17 @@ let scope = setup.scope(); let mut value = crate::bridge::NewValueForTesting(); - value.pin_mut().assign( - val, - scope.settings(), - crate::bridge::ParseNodePtr { - ptr: std::ptr::null(), - }, - ); + value + .pin_mut() + .assign( + val, + None, + scope.settings(), + crate::bridge::ParseNodePtr { + ptr: std::ptr::null(), + }, + ) + .unwrap(); value.to_rust(heap) }
diff --git a/src/gn/starlark/crates/rule/src/frozen_rule.rs b/src/gn/starlark/crates/rule/src/frozen_rule.rs index 68222df..c7aff2e 100644 --- a/src/gn/starlark/crates/rule/src/frozen_rule.rs +++ b/src/gn/starlark/crates/rule/src/frozen_rule.rs
@@ -117,7 +117,7 @@ // Collect all the arguments we don't recognise and pass them to the native // implementation. let kwargs: SmallMap<String, Value<'v>> = param_parser.next()?; - let child_scope = scope.copy_with(kwargs.iter().map(|(k, v)| (k.as_str(), *v))); + let child_scope = scope.copy_with(kwargs.iter().map(|(k, v)| (k.as_str(), *v)))?; context.create_target(Some(builtin), target_name, &child_scope, me, attrs)? } else { context.create_target(None, target_name, scope, me, attrs)?
diff --git a/src/gn/starlark/crates/testutils/src/eval_context.rs b/src/gn/starlark/crates/testutils/src/eval_context.rs index 3efc0d9..eac8062 100644 --- a/src/gn/starlark/crates/testutils/src/eval_context.rs +++ b/src/gn/starlark/crates/testutils/src/eval_context.rs
@@ -19,7 +19,7 @@ pub struct FakeScope(HashMap<String, Value<'static>>); impl Scope for FakeScope { - fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, Value<'v>)>) -> Self { + fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, Value<'v>)>) -> Result<Self> { let mut values = self.0.clone(); for (k, v) in kv { // Safety: Transmuting 'v to 'static is safe because this mock scope @@ -28,7 +28,7 @@ let static_val = unsafe { std::mem::transmute::<Value<'v>, Value<'static>>(v) }; values.insert(k.to_owned(), static_val); } - Self(values) + Ok(Self(values)) } fn get<'v>(&self, key: &str, _heap: &Heap<'v>) -> Option<Value<'v>> {
diff --git a/src/gn/starlark/crates/types/src/scope.rs b/src/gn/starlark/crates/types/src/scope.rs index 870d93a..e3e9490 100644 --- a/src/gn/starlark/crates/types/src/scope.rs +++ b/src/gn/starlark/crates/types/src/scope.rs
@@ -11,7 +11,12 @@ /// over that. pub trait Scope { /// Creates a copy of the scope with some additional values set. - fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, Value<'v>)>) -> Self; + fn copy_with<'a, 'v>( + &self, + kv: impl Iterator<Item = (&'a str, Value<'v>)>, + ) -> starlark::Result<Self> + where + Self: Sized; /// Retrieves a value from the key-value store. /// May allocate the value it retrieves on the heap.
diff --git a/src/gn/value.cc b/src/gn/value.cc index e2aa11a..85214d7 100644 --- a/src/gn/value.cc +++ b/src/gn/value.cc
@@ -9,6 +9,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "gn/ffi/bridge.h" #include "gn/scope.h" ValueList::ValueList() = default; @@ -37,6 +38,10 @@ case SCOPE: new (&scope_value_) std::unique_ptr<Scope>(); break; + case STARLARK_VALUE: + // There's no "default" value for a starlark value. + NOTREACHED(); + break; } } @@ -55,6 +60,11 @@ Value::Value(const ParseNode* origin, std::unique_ptr<Scope> scope) : type_(SCOPE), origin_(origin), scope_value_(std::move(scope)) {} +Value::Value(const ParseNode* origin, rust::Box<OwnedFrozenValue> starlark_val) + : type_(STARLARK_VALUE), + origin_(origin), + starlark_value_(std::move(starlark_val)) {} + Value::Value(const Value& other) : type_(other.type_), origin_(other.origin_) { switch (type_) { case NONE: @@ -76,6 +86,10 @@ other.scope_value_.get() ? other.scope_value_->MakeClosure() : nullptr); break; + case STARLARK_VALUE: + new (&starlark_value_) + rust::Box<OwnedFrozenValue>(other.starlark_value().clone()); + break; } } @@ -99,6 +113,10 @@ case SCOPE: new (&scope_value_) std::unique_ptr<Scope>(std::move(other.scope_value_)); break; + case STARLARK_VALUE: + new (&starlark_value_) + rust::Box<OwnedFrozenValue>(std::move(other.starlark_value_)); + break; } } @@ -130,6 +148,9 @@ case SCOPE: scope_value_.~unique_ptr(); break; + case STARLARK_VALUE: + starlark_value_.~Box(); + break; default:; } } @@ -149,6 +170,8 @@ return "list"; case SCOPE: return "scope"; + case STARLARK_VALUE: + return "starlark value"; default: NOTREACHED(); return "UNKNOWN"; @@ -245,6 +268,8 @@ return result; } + case STARLARK_VALUE: + return std::string(starlark_value().to_string()); } return std::string(); } @@ -276,6 +301,8 @@ return list_value() == other.list_value(); case Value::SCOPE: return scope_value()->CheckCurrentScopeValuesEqual(other.scope_value()); + case Value::STARLARK_VALUE: + return starlark_value().eq(other.starlark_value()); case Value::NONE: return false; default:
diff --git a/src/gn/value.h b/src/gn/value.h index ec4cb5a..2362c7a 100644 --- a/src/gn/value.h +++ b/src/gn/value.h
@@ -14,8 +14,10 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_refptr.h" +#include "cxx.h" #include "gn/err.h" +struct OwnedFrozenValue; class ParseNode; class Scope; class Value; @@ -43,6 +45,7 @@ STRING, LIST, SCOPE, + STARLARK_VALUE, }; Value(); @@ -57,6 +60,7 @@ // you can pass a null scope here if you promise to set it before any other // code gets it (code will generally assume the scope is not null). Value(const ParseNode* origin, std::unique_ptr<Scope> scope); + Value(const ParseNode* origin, rust::Box<OwnedFrozenValue> starlark_val); Value(const Value& other); Value(Value&& other) noexcept; @@ -114,6 +118,11 @@ } void SetScopeValue(std::unique_ptr<Scope> scope); + const OwnedFrozenValue& starlark_value() const { + DCHECK(type_ == STARLARK_VALUE); + return *starlark_value_; + } + // Converts the given value to a string. Returns true if strings should be // quoted or the ToString of a string should be the string itself. If the // string is quoted, it will also enable escaping. @@ -145,6 +154,7 @@ // copied, only performing a real copy when a modification is attempted. scoped_refptr<ValueList> list_ptr_; std::unique_ptr<Scope> scope_value_; + rust::Box<OwnedFrozenValue> starlark_value_; }; };