slint/sixtyfps_runtime/interpreter/eval.rs
Olivier Goffart caca0d0ba4 Put the component in a Pin<>
Removed the drop and create from the ComponentVTable:
since we are not using VBox<ComponentVTable>, this simplifies a bit
the code of the interpreter and everything else.

But there is still a lot of changes everywhere to support that the Component
is pinned.
This is just for the component. Which would be required if later we want
to access the properties as Pin<Property<_>>. But we have not yet ability
to do projections
2020-06-24 14:13:27 +02:00

304 lines
13 KiB
Rust

use sixtyfps_compilerlib::expression_tree::{Expression, NamedReference};
use sixtyfps_compilerlib::{object_tree::ElementRc, typeregister::Type};
use sixtyfps_corelib as corelib;
use sixtyfps_corelib::{abi::datastructures::ItemRef, EvaluationContext, Resource, SharedString};
use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
rc::Rc,
};
pub trait ErasedPropertyInfo {
fn get(&self, item: ItemRef, context: &EvaluationContext) -> Value;
fn set(&self, item: ItemRef, value: Value);
fn set_binding(&self, item: ItemRef, binding: Box<dyn Fn(&EvaluationContext) -> Value>);
fn offset(&self) -> usize;
}
impl<Item: vtable::HasStaticVTable<corelib::abi::datastructures::ItemVTable>> ErasedPropertyInfo
for &'static dyn corelib::rtti::PropertyInfo<Item, Value>
{
fn get(&self, item: ItemRef, context: &EvaluationContext) -> Value {
(*self).get(item.downcast().unwrap(), context).unwrap()
}
fn set(&self, item: ItemRef, value: Value) {
(*self).set(item.downcast().unwrap(), value).unwrap()
}
fn set_binding(&self, item: ItemRef, binding: Box<dyn Fn(&EvaluationContext) -> Value>) {
(*self).set_binding(item.downcast().unwrap(), binding);
}
fn offset(&self) -> usize {
(*self).offset()
}
}
#[derive(Debug, Clone)]
/// This is a dynamically typed Value used in the interpreter, it need to be able
/// to be converted from and to anything that can be stored in a Property
pub enum Value {
/// There is nothing in this value. That's the default.
/// For example, a function that do not return a result would return a Value::Void
Void,
/// An i32 or a float
Number(f64),
/// String
String(SharedString),
/// Bool
Bool(bool),
/// A resource (typically an image)
Resource(Resource),
/// An Array
Array(Vec<Value>),
/// An object
Object(HashMap<String, Value>),
}
impl Default for Value {
fn default() -> Self {
Value::Void
}
}
impl corelib::rtti::ValueType for Value {}
/// Helper macro to implement the TryFrom / TryInto for Value
///
/// For example
/// `declare_value_conversion!(Number => [u32, u64, i32, i64, f32, f64] );`
/// means that Value::Number can be converted to / from each of the said rust types
macro_rules! declare_value_conversion {
( $value:ident => [$($ty:ty),*] ) => {
$(
impl TryFrom<$ty> for Value {
type Error = ();
fn try_from(v: $ty) -> Result<Self, ()> {
//Ok(Value::$value(v.try_into().map_err(|_|())?))
Ok(Value::$value(v as _))
}
}
impl TryInto<$ty> for Value {
type Error = ();
fn try_into(self) -> Result<$ty, ()> {
match self {
//Self::$value(x) => x.try_into().map_err(|_|()),
Self::$value(x) => Ok(x as _),
_ => Err(())
}
}
}
)*
};
}
declare_value_conversion!(Number => [u32, u64, i32, i64, f32, f64, usize, isize] );
declare_value_conversion!(String => [SharedString] );
declare_value_conversion!(Bool => [bool] );
declare_value_conversion!(Resource => [Resource] );
declare_value_conversion!(Object => [HashMap<String, Value>] );
/// Evaluate an expression and return a Value as the result of this expression
pub fn eval_expression(
e: &Expression,
component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext,
) -> Value {
match e {
Expression::Invalid => panic!("invalid expression while evaluating"),
Expression::Uncompiled(_) => panic!("uncompiled expression while evaluating"),
Expression::StringLiteral(s) => Value::String(s.as_str().into()),
Expression::NumberLiteral(n) => Value::Number(*n),
Expression::SignalReference { .. } => panic!("signal in expression"),
Expression::PropertyReference(NamedReference { element, name }) => {
let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) =
enclosing_component_for_element(&element, eval_context);
let element = element.borrow();
if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
{
if let Some(x) = component_type.custom_properties.get(name) {
return unsafe {
x.prop
.get(&*component_mem.offset(x.offset as isize), &eval_context)
.unwrap()
};
}
};
let item_info = &component_type.items[element.id.as_str()];
core::mem::drop(element);
let item = unsafe { item_info.item_from_component(component_mem) };
item_info.rtti.properties[name.as_str()].get(item, &eval_context)
}
Expression::RepeaterIndexReference { element } => {
if element.upgrade().unwrap().borrow().base_type
== Type::Component(component_type.original.clone())
{
let x = &component_type.custom_properties["index"];
unsafe {
x.prop
.get(
&*eval_context.component.as_ptr().offset(x.offset as isize),
&eval_context,
)
.unwrap()
}
} else {
todo!();
}
}
Expression::RepeaterModelReference { element } => {
if element.upgrade().unwrap().borrow().base_type
== Type::Component(component_type.original.clone())
{
let x = &component_type.custom_properties["model_data"];
unsafe {
x.prop
.get(
&*eval_context.component.as_ptr().offset(x.offset as isize),
&eval_context,
)
.unwrap()
}
} else {
todo!();
}
}
Expression::ObjectAccess { base, name } => {
if let Value::Object(mut o) = eval_expression(base, component_type, eval_context) {
o.remove(name).unwrap_or(Value::Void)
} else {
Value::Void
}
}
Expression::Cast { from, to } => {
let v = eval_expression(&*from, component_type, eval_context);
match (v, to) {
(Value::Number(n), Type::Int32) => Value::Number(n.round()),
(Value::Number(n), Type::String) => {
Value::String(SharedString::from(format!("{}", n).as_str()))
}
(v, _) => v,
}
}
Expression::CodeBlock(sub) => {
let mut v = Value::Void;
for e in sub {
v = eval_expression(e, component_type, eval_context);
}
v
}
Expression::FunctionCall { function, .. } => {
if let Expression::SignalReference(NamedReference { element, name }) = &**function {
let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) =
enclosing_component_for_element(&element, eval_context);
let item_info = &component_type.items[element.borrow().id.as_str()];
let item = unsafe { item_info.item_from_component(component_mem) };
let signal = unsafe {
&mut *(item_info
.rtti
.signals
.get(name.as_str())
.map(|o| item.as_ptr().add(*o) as *const u8)
.or_else(|| {
component_type
.custom_signals
.get(name.as_str())
.map(|o| component_mem.add(*o))
})
.unwrap_or_else(|| panic!("unkown signal {}", name))
as *mut corelib::Signal<()>)
};
signal.emit(eval_context, ());
Value::Void
} else {
panic!("call of something not a signal")
}
}
Expression::SelfAssignment { lhs, rhs, op } => match &**lhs {
Expression::PropertyReference(NamedReference { element, name }) => {
let eval = |lhs| {
let rhs = eval_expression(&**rhs, component_type, eval_context);
match (lhs, rhs, op) {
(Value::Number(a), Value::Number(b), '+') => Value::Number(a + b),
(Value::Number(a), Value::Number(b), '-') => Value::Number(a - b),
(Value::Number(a), Value::Number(b), '/') => Value::Number(a / b),
(Value::Number(a), Value::Number(b), '*') => Value::Number(a * b),
(lhs, rhs, op) => panic!("unsupported {:?} {} {:?}", lhs, op, rhs),
}
};
let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) =
enclosing_component_for_element(&element, eval_context);
let component = element.borrow().enclosing_component.upgrade().unwrap();
if element.borrow().id == component.root_element.borrow().id {
if let Some(x) = component_type.custom_properties.get(name) {
unsafe {
let p = &*component_mem.offset(x.offset as isize);
x.prop.set(p, eval(x.prop.get(p, &eval_context).unwrap())).unwrap();
}
return Value::Void;
}
};
let item_info = &component_type.items[element.borrow().id.as_str()];
let item = unsafe { item_info.item_from_component(component_mem) };
let p = &item_info.rtti.properties[name.as_str()];
p.set(item, eval(p.get(item, &eval_context)));
Value::Void
}
_ => panic!("typechecking should make sure this was a PropertyReference"),
},
Expression::BinaryExpression { lhs, rhs, op } => {
let lhs = eval_expression(&**lhs, component_type, eval_context);
let rhs = eval_expression(&**rhs, component_type, eval_context);
match (lhs, rhs, op) {
(Value::Number(a), Value::Number(b), '+') => Value::Number(a + b),
(Value::Number(a), Value::Number(b), '-') => Value::Number(a - b),
(Value::Number(a), Value::Number(b), '/') => Value::Number(a / b),
(Value::Number(a), Value::Number(b), '*') => Value::Number(a * b),
(lhs, rhs, op) => panic!("unsupported {:?} {} {:?}", lhs, op, rhs),
}
}
Expression::ResourceReference { absolute_source_path } => {
Value::Resource(Resource::AbsoluteFilePath(absolute_source_path.as_str().into()))
}
Expression::Condition { condition, true_expr, false_expr } => {
match eval_expression(&**condition, component_type, eval_context).try_into()
as Result<bool, _>
{
Ok(true) => eval_expression(&**true_expr, component_type, eval_context),
Ok(false) => eval_expression(&**false_expr, component_type, eval_context),
_ => panic!("conditional expression did not evaluate to boolean"),
}
}
Expression::Array { values, .. } => Value::Array(
values.iter().map(|e| eval_expression(e, component_type, eval_context)).collect(),
),
Expression::Object { values, .. } => Value::Object(
values
.iter()
.map(|(k, v)| (k.clone(), eval_expression(v, component_type, eval_context)))
.collect(),
),
}
}
fn enclosing_component_for_element<'a>(
element: &ElementRc,
eval_context: &'a corelib::EvaluationContext<'a>,
) -> (*const u8, &'a crate::ComponentDescription, &'a corelib::EvaluationContext<'a>) {
let component_type =
unsafe { crate::dynamic_component::get_component_type(eval_context.component) };
if Rc::ptr_eq(
&element.borrow().enclosing_component.upgrade().unwrap(),
&component_type.original,
) {
let mem = eval_context.component.as_ptr();
(mem, component_type, eval_context)
} else {
debug_assert!(component_type.original.parent_element.upgrade().is_some());
enclosing_component_for_element(element, eval_context.parent_context.unwrap())
}
}