Skip to content

Commit

Permalink
Add method introspection to method calls, use explicit type args in g…
Browse files Browse the repository at this point in the history
…etReg
  • Loading branch information
SethDusek committed Sep 24, 2024
1 parent d78ca44 commit edf9576
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 73 deletions.
1 change: 1 addition & 0 deletions ergotree-interpreter/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ pub(crate) trait Evaluable {
}

type EvalFn = for<'ctx> fn(
mc: &SMethod,
env: &mut Env<'ctx>,
ctx: &Context<'ctx>,
Value<'ctx>,
Expand Down
4 changes: 2 additions & 2 deletions ergotree-interpreter/src/eval/method_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ impl Evaluable for MethodCall {
let ov = self.obj.eval(env, ectx)?;
let argsv: Result<Vec<Value>, EvalError> =
self.args.iter().map(|arg| arg.eval(env, ectx)).collect();
smethod_eval_fn(&self.method)?(env, ectx, ov, argsv?)
smethod_eval_fn(&self.method)?(&self.method, env, ectx, ov, argsv?)
}
}

Expand All @@ -42,7 +42,7 @@ mod tests {
let mc: Expr = MethodCall::new(
GlobalVars::SelfBox.into(),
sbox::GET_REG_METHOD.clone(),
vec![Constant::from(0i8).into()],
vec![Constant::from(0i32).into()],
)
.unwrap()
.into();
Expand Down
2 changes: 1 addition & 1 deletion ergotree-interpreter/src/eval/property_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl Evaluable for PropertyCall {
ectx: &Context<'ctx>,
) -> Result<Value<'ctx>, EvalError> {
let ov = self.obj.eval(env, ectx)?;
smethod_eval_fn(&self.method)?(env, ectx, ov, vec![])
smethod_eval_fn(&self.method)?(&self.method, env, ectx, ov, vec![])
}
}

Expand Down
30 changes: 15 additions & 15 deletions ergotree-interpreter/src/eval/savltree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,24 @@ use super::EvalError;
use super::EvalFn;
use ergotree_ir::types::stype::SType;

pub(crate) static DIGEST_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static DIGEST_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Coll(CollKind::NativeColl(NativeColl::CollByte(
avl_tree_data.digest.0.iter().map(|&b| b as i8).collect(),
))))
};

pub(crate) static ENABLED_OPERATIONS_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static ENABLED_OPERATIONS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Byte(avl_tree_data.tree_flags.serialize() as i8))
};

pub(crate) static KEY_LENGTH_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static KEY_LENGTH_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Int(avl_tree_data.key_length as i32))
};

pub(crate) static VALUE_LENGTH_OPT_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static VALUE_LENGTH_OPT_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Opt(Box::new(
avl_tree_data
Expand All @@ -46,22 +46,22 @@ pub(crate) static VALUE_LENGTH_OPT_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
)))
};

pub(crate) static IS_INSERT_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static IS_INSERT_ALLOWED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Boolean(avl_tree_data.tree_flags.insert_allowed()))
};

pub(crate) static IS_UPDATE_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static IS_UPDATE_ALLOWED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Boolean(avl_tree_data.tree_flags.update_allowed()))
};

pub(crate) static IS_REMOVE_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static IS_REMOVE_ALLOWED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Boolean(avl_tree_data.tree_flags.remove_allowed()))
};

pub(crate) static UPDATE_OPERATIONS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static UPDATE_OPERATIONS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
let new_operations = {
let v = args.first().cloned().ok_or_else(|| {
Expand All @@ -73,7 +73,7 @@ pub(crate) static UPDATE_OPERATIONS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
Ok(Value::AvlTree(Box::new(avl_tree_data)))
};

pub(crate) static UPDATE_DIGEST_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static UPDATE_DIGEST_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
let new_digest = {
let v = args.first().cloned().ok_or_else(|| {
Expand All @@ -86,7 +86,7 @@ pub(crate) static UPDATE_DIGEST_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
Ok(Value::AvlTree(Box::new(avl_tree_data)))
};

pub(crate) static GET_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static GET_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
let key = {
let v = args
Expand Down Expand Up @@ -135,7 +135,7 @@ pub(crate) static GET_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
};

pub(crate) static GET_MANY_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;

let keys = {
Expand Down Expand Up @@ -197,7 +197,7 @@ pub(crate) static GET_MANY_EVAL_FN: EvalFn =
};

pub(crate) static INSERT_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;

if !avl_tree_data.tree_flags.insert_allowed() {
Expand Down Expand Up @@ -260,7 +260,7 @@ pub(crate) static INSERT_EVAL_FN: EvalFn =
};

pub(crate) static REMOVE_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;

if !avl_tree_data.tree_flags.remove_allowed() {
Expand Down Expand Up @@ -319,7 +319,7 @@ pub(crate) static REMOVE_EVAL_FN: EvalFn =
}
};

pub(crate) static CONTAINS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static CONTAINS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
let key = {
let v = args
Expand Down Expand Up @@ -367,7 +367,7 @@ pub(crate) static CONTAINS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
};

pub(crate) static UPDATE_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;

if !avl_tree_data.tree_flags.update_allowed() {
Expand Down
89 changes: 73 additions & 16 deletions ergotree-interpreter/src/eval/sbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,61 @@ use ergotree_ir::chain::ergo_box::ErgoBox;
use ergotree_ir::mir::constant::TryExtractInto;
use ergotree_ir::mir::value::Value;
use ergotree_ir::reference::Ref;
use ergotree_ir::types::stype::SType;

use super::EvalFn;

pub(crate) static VALUE_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static VALUE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
Ok(Value::Long(
obj.try_extract_into::<Ref<'_, ErgoBox>>()?.value.as_i64(),
))
};

pub(crate) static GET_REG_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
let reg_id = args
pub(crate) static GET_REG_EVAL_FN: EvalFn = |mc, _env, _ctx, obj, args| {
#[allow(clippy::unwrap_used)]
let reg_id: i8 = args
.first()
.cloned()
.ok_or_else(|| EvalError::NotFound("register index is missing".to_string()))?
.try_extract_into::<i8>()?;
.try_extract_into::<i32>()?
.try_into()
.map_err(|e| {
EvalError::RegisterIdOutOfBounds(format!("register index is out of bounds: {:?} ", e))
})?;
let reg_id = reg_id.try_into().map_err(|e| {
EvalError::RegisterIdOutOfBounds(format!(
"register index {reg_id} is out of bounds: {:?} ",
e
))
})?;

Ok(Value::Opt(Box::new(
obj.try_extract_into::<Ref<'_, ErgoBox>>()?
.get_register(reg_id)
.map_err(|e| {
EvalError::NotFound(format!(
"Error getting the register id {reg_id} with error {e:?}"
))
})?
.map(|c| Value::from(c.v)),
)))
let reg_val_opt = obj
.try_extract_into::<Ref<'_, ErgoBox>>()?
.get_register(reg_id)
.map_err(|e| {
EvalError::NotFound(format!(
"Error getting the register id {reg_id} with error {e:?}"
))
})?;
// Return type of getReg[T] is always Option[T]
#[allow(clippy::unreachable)]
let SType::SOption(expected_type) = &*mc.tpe().t_range
else {
unreachable!()
};
match reg_val_opt {
Some(constant) if constant.tpe == **expected_type => {
Ok(Value::Opt(Box::new(Some(constant.v.into()))))
}
Some(constant) => Err(EvalError::UnexpectedValue(format!(
"Expected register {reg_id} to be of type {}, got {}",
expected_type, constant.tpe
))),
None => Ok(Value::Opt(Box::new(None))),
}
};

pub(crate) static TOKENS_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static TOKENS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let res: Value = obj
.try_extract_into::<Ref<'_, ErgoBox>>()?
.tokens_raw()
Expand All @@ -52,14 +72,19 @@ pub(crate) static TOKENS_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
#[cfg(test)]
#[cfg(feature = "arbitrary")]
mod tests {
use ergotree_ir::mir::constant::Constant;
use ergotree_ir::mir::expr::Expr;
use ergotree_ir::mir::global_vars::GlobalVars;
use ergotree_ir::mir::method_call::MethodCall;
use ergotree_ir::mir::property_call::PropertyCall;
use ergotree_ir::mir::value::Value;
use ergotree_ir::types::sbox;
use ergotree_ir::types::stype::SType;
use ergotree_ir::types::stype_param::STypeVar;
use sigma_test_util::force_any_val;

use crate::eval::context::Context;
use crate::eval::tests::eval_out;
use crate::eval::tests::{eval_out, try_eval_out};

#[test]
fn eval_box_value() {
Expand All @@ -81,4 +106,36 @@ mod tests {
ctx.self_box.tokens_raw()
);
}

// Attempt to extract SigmaProp from register of type SByte
#[test]
fn eval_reg_out() {
let type_args = std::iter::once((STypeVar::t(), SType::SLong)).collect();
let expr: Expr = MethodCall::with_type_args(
GlobalVars::SelfBox.into(),
sbox::GET_REG_METHOD.clone().with_concrete_types(&type_args),
vec![Constant::from(0i32).into()],
type_args,
)
.unwrap()
.into();
let ctx = force_any_val::<Context>();
try_eval_out::<Value>(&expr, &ctx).unwrap();
}

// Attempt to extract SigmaProp from register of type SLong
#[test]
fn eval_reg_out_wrong_type() {
let type_args = std::iter::once((STypeVar::t(), SType::SSigmaProp)).collect();
let expr: Expr = MethodCall::with_type_args(
GlobalVars::SelfBox.into(),
sbox::GET_REG_METHOD.clone().with_concrete_types(&type_args),
vec![Constant::from(0i32).into()],
type_args,
)
.unwrap()
.into();
let ctx = force_any_val::<Context>();
assert!(try_eval_out::<Value>(&expr, &ctx).is_err());
}
}
14 changes: 8 additions & 6 deletions ergotree-interpreter/src/eval/scoll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use ergotree_ir::mir::constant::TryExtractInto;
use ergotree_ir::mir::expr::Expr;
use ergotree_ir::mir::value::CollKind;
use ergotree_ir::mir::value::Value;
use ergotree_ir::types::smethod::SMethod;
use ergotree_ir::types::stuple::STuple;
use ergotree_ir::types::stype::SType::SInt;

Expand All @@ -14,7 +15,7 @@ use super::EvalFn;
use std::convert::TryFrom;
use std::sync::Arc;

pub(crate) static INDEX_OF_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static INDEX_OF_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
Ok(Value::Int({
let normalized_input_vals: Vec<Value> = match obj {
Value::Coll(coll) => Ok(coll.as_vec()),
Expand Down Expand Up @@ -44,6 +45,7 @@ pub(crate) static INDEX_OF_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
};

pub(crate) fn flatmap_eval<'ctx>(
_mc: &SMethod,
env: &mut Env<'ctx>,
ctx: &Context<'ctx>,
obj: Value<'ctx>,
Expand Down Expand Up @@ -126,7 +128,7 @@ pub(crate) fn flatmap_eval<'ctx>(
.map(Value::Coll)
}

pub(crate) static ZIP_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static ZIP_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let (type_1, coll_1) = match obj {
Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
_ => Err(EvalError::UnexpectedValue(format!(
Expand Down Expand Up @@ -157,7 +159,7 @@ pub(crate) static ZIP_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
}
};

pub(crate) static INDICES_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static INDICES_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let input_len = match obj {
Value::Coll(coll) => Ok(coll.len()),
_ => Err(EvalError::UnexpectedValue(format!(
Expand All @@ -180,7 +182,7 @@ pub(crate) static INDICES_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
}
};

pub(crate) static PATCH_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static PATCH_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let (input_tpe, normalized_input_vals) = match obj {
Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
_ => Err(EvalError::UnexpectedValue(format!(
Expand Down Expand Up @@ -221,7 +223,7 @@ pub(crate) static PATCH_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
Ok(Value::Coll(CollKind::from_collection(input_tpe, res)?))
};

pub(crate) static UPDATED_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static UPDATED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let (input_tpe, normalized_input_vals) = match obj {
Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
_ => Err(EvalError::UnexpectedValue(format!(
Expand Down Expand Up @@ -255,7 +257,7 @@ pub(crate) static UPDATED_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
};

pub(crate) static UPDATE_MANY_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let (input_tpe, normalized_input_vals) = match obj {
Value::Coll(coll) => Ok((coll.elem_tpe().clone(), coll.as_vec())),
_ => Err(EvalError::UnexpectedValue(format!(
Expand Down
Loading

0 comments on commit edf9576

Please sign in to comment.