Skip to content

Commit 1808547

Browse files
committed
fix unsafeties
1 parent 247aec6 commit 1808547

File tree

10 files changed

+120
-86
lines changed

10 files changed

+120
-86
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
function on_test()
2+
local NewComponent = world.register_new_component("ScriptComponentA")
3+
4+
local new_entity = world.spawn()
5+
world.insert_component(new_entity, NewComponent, construct(types.ScriptComponent, {
6+
data = "Hello World"
7+
}))
8+
9+
local component_instance = world.get_component(new_entity, NewComponent)
10+
assert(component_instance.data == "Hello World", "unexpected value: " .. component_instance.data)
11+
12+
component_instance.data = {
13+
foo = "bar"
14+
}
15+
16+
assert(component_instance.data.foo == "bar", "unexpected value: " .. component_instance.data.foo)
17+
end

crates/bevy_mod_scripting_core/src/bindings/function/from_ref.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
//! Contains the [`FromScriptRef`] trait and its implementations.
22
3-
use std::{any::TypeId, ffi::OsString, path::PathBuf};
4-
use bevy::reflect::{
5-
DynamicEnum, DynamicList, DynamicMap, DynamicTuple, DynamicVariant, Map, PartialReflect,
6-
};
73
use crate::{
8-
bindings::{match_by_type, WorldGuard, FromScript},
4+
bindings::{match_by_type, FromScript, WorldGuard},
95
error::InteropError,
106
reflection_extensions::TypeInfoExtensions,
117
ScriptValue,
128
};
9+
use bevy::reflect::{
10+
DynamicEnum, DynamicList, DynamicMap, DynamicTuple, DynamicVariant, Map, PartialReflect,
11+
};
12+
use std::{any::TypeId, ffi::OsString, path::PathBuf};
1313

1414
/// Converts from a [`ScriptValue`] to a value equivalent to the given [`TypeId`].
1515
///
@@ -56,6 +56,7 @@ impl FromScriptRef for Box<dyn PartialReflect> {
5656
tq : String => return <String>::from_script(value, world).map(|a| Box::new(a) as _),
5757
tr : PathBuf => return <PathBuf>::from_script(value, world).map(|a| Box::new(a) as _),
5858
ts : OsString=> return <OsString>::from_script(value, world).map(|a| Box::new(a) as _),
59+
tsv: ScriptValue => return <ScriptValue>::from_script(value, world).map(|a| Box::new(a) as _),
5960
tn : () => return <()>::from_script(value, world).map(|a| Box::new(a) as _)
6061
}
6162
);

crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ fn into_script_ref(
102102
},
103103
tr : PathBuf => return downcast_into_value!(r, PathBuf).clone().into_script(world),
104104
ts : OsString=> return downcast_into_value!(r, OsString).clone().into_script(world),
105+
tsv: ScriptValue=> return Ok(downcast_into_value!(r, ScriptValue).clone()),
105106
tn : () => return Ok(ScriptValue::Unit)
106107
}
107108
);

crates/bevy_mod_scripting_core/src/bindings/query.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,19 @@ impl ScriptComponentRegistration {
146146
let mut entity = world
147147
.get_entity_mut(entity)
148148
.map_err(|_| InteropError::missing_entity(entity))?;
149-
let mut cast = instance.downcast::<ScriptComponent>().map_err(|v| {
149+
let cast = instance.downcast::<ScriptComponent>().map_err(|v| {
150150
InteropError::type_mismatch(TypeId::of::<ScriptComponent>(), Some(v.type_id()))
151151
})?;
152-
let ptr = (cast.as_mut() as *mut ScriptComponent).cast();
152+
// the reason we leak the box, is because we don't want to double drop the owning ptr
153+
154+
let ptr = (Box::leak(cast) as *mut ScriptComponent).cast();
153155
// Safety: cannot be null as we just created it from a valid reference
154156
let non_null_ptr = unsafe { NonNull::new_unchecked(ptr) };
155157
// Safety:
156158
// - we know the type is ScriptComponent, as we just created the pointer
157159
// - the box will stay valid for the life of this function, and we do not return the ptr
158160
// - pointer is alligned correctly
161+
// - nothing else will call drop on this
159162
let owning_ptr = unsafe { OwningPtr::new(non_null_ptr) };
160163
// Safety:
161164
// - Owning Ptr is valid as we just created it

crates/bevy_mod_scripting_core/src/bindings/script_component.rs

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,23 @@ impl WorldAccessGuard<'_> {
8080
"script registered component name must start with 'Script'",
8181
));
8282
}
83+
let component_registry = self.component_registry();
84+
let component_registry_read = component_registry.read();
85+
if component_registry_read.get(&component_name).is_some() {
86+
return Err(InteropError::unsupported_operation(
87+
None,
88+
None,
89+
"script registered component already exists",
90+
));
91+
}
8392

8493
let component_id = self.with_global_access(|w| {
94+
bevy::log::info!(
95+
"components present: {}. script: {}. World id: {:?}",
96+
w.components().len(),
97+
component_registry_read.components.len(),
98+
w.id()
99+
);
85100
let descriptor = unsafe {
86101
// Safety: same safety guarantees as ComponentDescriptor::new
87102
// we know the type in advance
@@ -93,10 +108,11 @@ impl WorldAccessGuard<'_> {
93108
needs_drop::<ScriptComponent>().then_some(|x| x.drop_as::<ScriptComponent>()),
94109
)
95110
};
96-
w.register_component_with_descriptor(descriptor)
111+
let o = w.register_component_with_descriptor(descriptor);
112+
bevy::log::info!("components present after: {}", w.components().len());
113+
o
97114
})?;
98-
99-
let component_registry = self.component_registry();
115+
drop(component_registry_read);
100116
let mut component_registry = component_registry.write();
101117

102118
let registration = ScriptComponentRegistration::new(
@@ -135,51 +151,53 @@ impl Plugin for DynamicScriptComponentPlugin {
135151
}
136152
}
137153

138-
// #[cfg(test)]
139-
// mod test {
140-
// use std::ptr::NonNull;
141-
142-
// use super::*;
143-
// use bevy::{ecs::world::World, ptr::OwningPtr};
144-
145-
// #[test]
146-
// fn test_script_component() {
147-
// let mut world = World::new();
148-
// let component_name = "MyScriptComponent";
149-
150-
// #[derive(Reflect, Component)]
151-
// struct UnderlyingComponent;
152-
153-
// // initialize component descriptor dynamically
154-
// let descriptor = unsafe {
155-
// // Safety: same safety guarantees as ComponentDescriptor::new
156-
// // we know the type in advance
157-
// // we only use this method to name the component
158-
// ComponentDescriptor::new_with_layout(
159-
// component_name,
160-
// UnderlyingComponent::STORAGE_TYPE,
161-
// Layout::new::<UnderlyingComponent>(),
162-
// needs_drop::<UnderlyingComponent>()
163-
// .then_some(|x| x.drop_as::<UnderlyingComponent>()),
164-
// )
165-
// };
166-
167-
// // register with the world
168-
// let component_id = world.register_component_with_descriptor(descriptor);
169-
170-
// // insert into the entity
171-
// let entity = world.spawn_empty().id();
172-
// let mut entity = world.entity_mut(entity);
173-
174-
// let value = Box::new(UnderlyingComponent);
175-
// let value_ref = Box::into_raw(value).cast::<u8>();
176-
// let ptr = unsafe { OwningPtr::new(NonNull::new(value_ref).unwrap()) };
177-
// unsafe { entity.insert_by_id(component_id, ptr) };
178-
179-
// // check it gets inserted
180-
// assert!(
181-
// entity.contains_id(component_id),
182-
// "entity does not contain freshly inserted component"
183-
// )
184-
// }
185-
// }
154+
#[cfg(test)]
155+
mod test {
156+
use std::ptr::NonNull;
157+
158+
use super::*;
159+
use bevy::{ecs::world::World, ptr::OwningPtr};
160+
161+
#[test]
162+
fn test_script_component() {
163+
let mut world = World::new();
164+
let component_name = "MyScriptComponent";
165+
166+
#[derive(Reflect, Component)]
167+
struct UnderlyingComponent;
168+
169+
// initialize component descriptor dynamically
170+
let descriptor = unsafe {
171+
// Safety: same safety guarantees as ComponentDescriptor::new
172+
// we know the type in advance
173+
// we only use this method to name the component
174+
ComponentDescriptor::new_with_layout(
175+
component_name,
176+
UnderlyingComponent::STORAGE_TYPE,
177+
Layout::new::<UnderlyingComponent>(),
178+
needs_drop::<UnderlyingComponent>()
179+
.then_some(|x| x.drop_as::<UnderlyingComponent>()),
180+
)
181+
};
182+
183+
// register with the world
184+
let component_id = world.register_component_with_descriptor(descriptor.clone());
185+
let component_id_2 = world.register_component_with_descriptor(descriptor);
186+
assert_eq!(component_id, component_id_2); // iam getting a double free for this in scritps somehow
187+
188+
// insert into the entity
189+
let entity = world.spawn_empty().id();
190+
let mut entity = world.entity_mut(entity);
191+
192+
let value = Box::new(UnderlyingComponent);
193+
let value_ref = Box::into_raw(value).cast::<u8>();
194+
let ptr = unsafe { OwningPtr::new(NonNull::new(value_ref).unwrap()) };
195+
unsafe { entity.insert_by_id(component_id, ptr) };
196+
197+
// check it gets inserted
198+
assert!(
199+
entity.contains_id(component_id),
200+
"entity does not contain freshly inserted component"
201+
)
202+
}
203+
}

crates/bevy_mod_scripting_core/src/bindings/world.rs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -961,31 +961,20 @@ impl WorldAccessGuard<'_> {
961961
registration: ScriptComponentRegistration,
962962
value: ReflectReference,
963963
) -> Result<(), InteropError> {
964-
let component_data = registration
965-
.type_registration()
966-
.type_registration()
967-
.data::<ReflectComponent>()
968-
.ok_or_else(|| {
969-
InteropError::missing_type_data(
970-
registration.registration.type_id(),
971-
"ReflectComponent".to_owned(),
972-
)
973-
})?;
974-
975-
with_global_access!(&self.inner.accesses, "Could not insert element", {
976-
let cell = self.as_unsafe_world_cell()?;
977-
let type_registry = self.type_registry();
978-
let type_registry = type_registry.read();
979-
let world_mut = unsafe { cell.world_mut() };
980-
let mut entity = world_mut
981-
.get_entity_mut(entity)
982-
.map_err(|_| InteropError::missing_entity(entity))?;
964+
let instance = <Box<dyn PartialReflect>>::from_script_ref(
965+
registration.type_registration().type_id(),
966+
ScriptValue::Reference(value),
967+
self.clone(),
968+
)?;
983969

984-
let ref_ = unsafe { value.reflect_unsafe(self.clone())? };
985-
component_data.apply_or_insert(&mut entity, ref_, &type_registry);
970+
let reflect = instance.try_into_reflect().map_err(|v| {
971+
InteropError::failed_from_reflect(
972+
Some(registration.type_registration().type_id()),
973+
format!("instance produced by conversion to target type when inserting component is not a full reflect type: {v:?}"),
974+
)
975+
})?;
986976

987-
Ok(())
988-
})?
977+
registration.insert_into_entity(self.clone(), entity, reflect)
989978
}
990979

991980
/// get the component from the entity

crates/bevy_mod_scripting_core/src/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,10 +401,10 @@ impl InteropError {
401401

402402
/// Thrown if a type cannot be converted from reflect, this can happen if the type was unable to
403403
/// re-construct itself from a dynamic value.
404-
pub fn failed_from_reflect(type_id: Option<TypeId>, reason: String) -> Self {
404+
pub fn failed_from_reflect(type_id: Option<TypeId>, reason: impl Into<String>) -> Self {
405405
Self(Arc::new(InteropErrorInner::FailedFromReflect {
406406
type_id,
407-
reason,
407+
reason: reason.into(),
408408
}))
409409
}
410410

crates/bevy_mod_scripting_core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ fn once_per_app_finalize(app: &mut App) {
252252
if app.world().contains_resource::<BMSFinalized>() {
253253
return;
254254
}
255+
println!("Running init");
255256
app.insert_resource(BMSFinalized);
256257

257258
// read extensions from asset settings

crates/bevy_mod_scripting_functions/src/core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,7 @@ impl GlobalNamespace {
12871287
let reflect_val = val.try_into_reflect().map_err(|_| {
12881288
InteropError::failed_from_reflect(
12891289
Some(registration.type_id()),
1290-
"Could not construct the type".into(),
1290+
"Could not construct the type",
12911291
)
12921292
})?;
12931293

crates/languages/bevy_mod_scripting_lua/tests/lua_tests.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ use std::{
1414
path::{Path, PathBuf},
1515
};
1616

17+
#[derive(Debug)]
1718
struct Test {
1819
path: PathBuf,
1920
}
2021

2122
impl Test {
2223
fn execute(self) -> Result<(), Failed> {
24+
println!("Running test: {:?}", self.path);
25+
2326
execute_integration_test::<LuaScriptingPlugin, _, _>(
2427
|world, type_registry| {
2528
let _ = world;
@@ -126,10 +129,11 @@ fn main() {
126129
let args = Arguments::from_args();
127130

128131
// Create a list of tests and/or benchmarks (in this case: two dummy tests).
129-
let tests = discover_all_tests()
132+
let all_tests = discover_all_tests();
133+
println!("discovered {} tests. {:?}", all_tests.len(), all_tests);
134+
let tests = all_tests
130135
.into_iter()
131136
.map(|t| Trial::test(t.name(), move || t.execute()));
132-
133137
// Run all tests and exit the application appropriatly.
134138
libtest_mimic::run(&args, tests.collect()).exit();
135139
}

0 commit comments

Comments
 (0)