From 08b54458382a4150ba6334f5a117fcbad6198f1e Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Tue, 22 Apr 2025 13:38:11 -0700
Subject: [PATCH] roblox_emulator: fix subtle ustr bugs

---
 .../src/runner/instance/instance.rs           | 23 ++++++++++---------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/lib/roblox_emulator/src/runner/instance/instance.rs b/lib/roblox_emulator/src/runner/instance/instance.rs
index 78c6bf4..8e24d87 100644
--- a/lib/roblox_emulator/src/runner/instance/instance.rs
+++ b/lib/roblox_emulator/src/runner/instance/instance.rs
@@ -219,22 +219,23 @@ impl mlua::UserData for Instance{
 		});
 		methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,index):(Instance,mlua::String)|{
 			let index_str=&*index.to_str()?;
-			let Some(index_ustr)=Ustr::from_existing(index_str)else{
-				return Ok(mlua::Value::Nil)
-			};
 			dom_mut(lua,|dom|{
 				let instance=this.get(dom)?;
 				//println!("__index t={} i={index:?}",instance.name);
 				let db=rbx_reflection_database::get();
 				let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
-				//Find existing property
-				match instance.properties.get(&index_ustr)
-					.cloned()
+				// Find existing property
+				// Interestingly, ustr can know ahead of time if
+				// a property does not exist in any runtime instance
+				match Ustr::from_existing(index_str)
+					.and_then(|index_ustr|
+						instance.properties.get(&index_ustr).cloned()
+					)
 					//Find default value
 					.or_else(||db.find_default_property(class,index_str).cloned())
 					//Find virtual property
 					.or_else(||db.superclasses_iter(class).find_map(|class|
-						find_virtual_property(&instance.properties,class,&index_ustr)
+						find_virtual_property(&instance.properties,class,index_str)
 					))
 				{
 					Some(rbx_types::Variant::Int32(val))=>return val.into_lua(lua),
@@ -276,9 +277,6 @@ impl mlua::UserData for Instance{
 		});
 		methods.add_meta_function(mlua::MetaMethod::NewIndex,|lua,(this,index,value):(Instance,mlua::String,mlua::Value)|{
 			let index_str=&*index.to_str()?;
-			let Some(index_ustr)=Ustr::from_existing(index_str)else{
-				return Ok(())
-			};
 			dom_mut(lua,|dom|{
 				let instance=this.get_mut(dom)?;
 				let db=rbx_reflection_database::get();
@@ -288,6 +286,9 @@ impl mlua::UserData for Instance{
 				).ok_or_else(||
 					mlua::Error::runtime(format!("Property '{index_str}' missing on class '{}'",class.name))
 				)?;
+				// the index is known to be a real property at this point
+				// allow creating a permanent ustr (memory leak)
+				let index_ustr=rbx_dom_weak::ustr(index_str);
 				match &property.data_type{
 					rbx_reflection::DataType::Value(rbx_types::VariantType::Vector3)=>{
 						let typed_value:Vector3=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected Userdata"))?.borrow()?;
@@ -469,7 +470,7 @@ static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
 fn find_virtual_property(
 	properties:&rbx_dom_weak::UstrMap<rbx_types::Variant>,
 	class:&rbx_reflection::ClassDescriptor,
-	index:&Ustr,
+	index:&str,
 )->Option<rbx_types::Variant>{
 	//Find virtual property
 	let class_virtual_properties=VIRTUAL_PROPERTY_DATABASE.get(&class.name)?;