Don't allow instances to be created from dom roots

This commit is contained in:
Filip Tibell 2023-03-22 10:28:49 +01:00
parent b63d016818
commit 9f13b8e667
No known key found for this signature in database

View file

@ -24,28 +24,31 @@ lazy_static::lazy_static! {
pub struct Instance { pub struct Instance {
dom_ref: DomRef, dom_ref: DomRef,
class_name: String, class_name: String,
is_root: bool,
} }
impl Instance { impl Instance {
/** /**
Creates a new `Instance` from an existing dom object ref. Creates a new `Instance` from an existing dom object ref.
Panics if the instance does not exist in the internal dom. Panics if the instance does not exist in the internal dom,
or if the given dom object ref points to the dom root.
*/ */
fn new(dom_ref: DomRef) -> Self { fn new(dom_ref: DomRef) -> Self {
let reader = INTERNAL_DOM let dom = INTERNAL_DOM
.try_read() .try_read()
.expect("Failed to get read access to document"); .expect("Failed to get read access to document");
let instance = reader let instance = dom
.get_by_ref(dom_ref) .get_by_ref(dom_ref)
.expect("Failed to find instance in document"); .expect("Failed to find instance in document");
if instance.referent() == dom.root_ref() {
panic!("Instances can not be created from dom roots")
}
Self { Self {
dom_ref, dom_ref,
class_name: instance.class.clone(), class_name: instance.class.clone(),
is_root: dom_ref == reader.root_ref(),
} }
} }
@ -69,7 +72,6 @@ impl Instance {
Self { Self {
dom_ref, dom_ref,
class_name: class_name.to_string(), class_name: class_name.to_string(),
is_root: false,
} }
} }
@ -78,6 +80,8 @@ impl Instance {
it from an external weak dom to the internal one. it from an external weak dom to the internal one.
An orphaned instance is an instance at the root of a weak dom. An orphaned instance is an instance at the root of a weak dom.
Panics if the given dom ref is the root dom ref of the external weak dom.
*/ */
pub fn from_external_dom(external_dom: &mut WeakDom, external_dom_ref: DomRef) -> Self { pub fn from_external_dom(external_dom: &mut WeakDom, external_dom_ref: DomRef) -> Self {
{ {
@ -95,7 +99,8 @@ impl Instance {
/** /**
Transfers an instance to an external weak dom. Transfers an instance to an external weak dom.
This will place the instance at the root of the weak dom and return its referent. This will place the instance as a child of the
root of the weak dom, and return its referent.
*/ */
pub fn into_external_dom(self, external_dom: &mut WeakDom) -> DomRef { pub fn into_external_dom(self, external_dom: &mut WeakDom) -> DomRef {
let mut dom = INTERNAL_DOM let mut dom = INTERNAL_DOM
@ -177,8 +182,8 @@ impl Instance {
} }
/** /**
Destroys the instance, unless it is the root instance, removing Destroys the instance, removing it completely
it completely from the weak dom with no way of recovering it. from the weak dom with no way of recovering it.
All member methods will throw errors when called from lua and panic All member methods will throw errors when called from lua and panic
when called from rust after the instance has been destroyed. when called from rust after the instance has been destroyed.
@ -190,7 +195,7 @@ impl Instance {
on the Roblox Developer Hub on the Roblox Developer Hub
*/ */
pub fn destroy(&mut self) -> bool { pub fn destroy(&mut self) -> bool {
if self.is_root || self.is_destroyed() { if self.is_destroyed() {
false false
} else { } else {
let mut dom = INTERNAL_DOM let mut dom = INTERNAL_DOM
@ -313,24 +318,16 @@ impl Instance {
on the Roblox Developer Hub on the Roblox Developer Hub
*/ */
pub fn get_parent(&self) -> Option<Instance> { pub fn get_parent(&self) -> Option<Instance> {
if self.is_root { let dom = INTERNAL_DOM
return None; .try_read()
} .expect("Failed to get read access to document");
let (nil_parent_ref, parent_ref) = { let parent_ref = dom
let dom = INTERNAL_DOM .get_by_ref(self.dom_ref)
.try_read() .expect("Failed to find instance in document")
.expect("Failed to get read access to document"); .parent();
let parent_ref = dom if parent_ref == dom.root_ref() {
.get_by_ref(self.dom_ref)
.expect("Failed to find instance in document")
.parent();
(dom.root_ref(), parent_ref)
};
if parent_ref == nil_parent_ref {
None None
} else { } else {
Some(Self::new(parent_ref)) Some(Self::new(parent_ref))
@ -349,10 +346,6 @@ impl Instance {
on the Roblox Developer Hub on the Roblox Developer Hub
*/ */
pub fn set_parent(&self, parent: Option<Instance>) { pub fn set_parent(&self, parent: Option<Instance>) {
if self.is_root {
panic!("Root instance can not be reparented")
}
let mut dom = INTERNAL_DOM let mut dom = INTERNAL_DOM
.try_write() .try_write()
.expect("Failed to get write access to target document"); .expect("Failed to get write access to target document");
@ -405,16 +398,15 @@ impl Instance {
on the Roblox Developer Hub on the Roblox Developer Hub
*/ */
pub fn get_children(&self) -> Vec<Instance> { pub fn get_children(&self) -> Vec<Instance> {
let children = { let dom = INTERNAL_DOM
let dom = INTERNAL_DOM .try_read()
.try_read() .expect("Failed to get read access to document");
.expect("Failed to get read access to document");
dom.get_by_ref(self.dom_ref) let children = dom
.expect("Failed to find instance in document") .get_by_ref(self.dom_ref)
.children() .expect("Failed to find instance in document")
.to_vec() .children()
}; .to_vec();
children.into_iter().map(Self::new).collect() children.into_iter().map(Self::new).collect()
} }
@ -430,28 +422,24 @@ impl Instance {
on the Roblox Developer Hub on the Roblox Developer Hub
*/ */
pub fn get_descendants(&self) -> Vec<Instance> { pub fn get_descendants(&self) -> Vec<Instance> {
let descendants = { let dom = INTERNAL_DOM
let dom = INTERNAL_DOM .try_read()
.try_read() .expect("Failed to get read access to document");
.expect("Failed to get read access to document");
let mut descendants = Vec::new(); let mut descendants = Vec::new();
let mut queue = VecDeque::from_iter( let mut queue = VecDeque::from_iter(
dom.get_by_ref(self.dom_ref) dom.get_by_ref(self.dom_ref)
.expect("Failed to find instance in document") .expect("Failed to find instance in document")
.children(), .children(),
); );
while let Some(queue_ref) = queue.pop_front() { while let Some(queue_ref) = queue.pop_front() {
descendants.push(*queue_ref); descendants.push(*queue_ref);
let queue_inst = dom.get_by_ref(*queue_ref).unwrap(); let queue_inst = dom.get_by_ref(*queue_ref).unwrap();
for queue_ref_inner in queue_inst.children().iter().rev() { for queue_ref_inner in queue_inst.children().iter().rev() {
queue.push_front(queue_ref_inner); queue.push_front(queue_ref_inner);
}
} }
}
descendants
};
descendants.into_iter().map(Self::new).collect() descendants.into_iter().map(Self::new).collect()
} }
@ -712,12 +700,7 @@ impl LuaUserData for Instance {
return Ok(()); return Ok(());
} }
"Parent" => { "Parent" => {
if this.is_root { if this.get_class_name() == "DataModel" {
return Err(LuaError::RuntimeError(format!(
"Failed to set property '{}' - root instance can not be reparented",
prop_name
)));
} else if this.get_class_name() == "DataModel" {
return Err(LuaError::RuntimeError(format!( return Err(LuaError::RuntimeError(format!(
"Failed to set property '{}' - DataModel can not be reparented", "Failed to set property '{}' - DataModel can not be reparented",
prop_name prop_name