From e98d8e8a2be181e006e4bf8706cae23fdcc9c243 Mon Sep 17 00:00:00 2001 From: Devin Jean Date: Mon, 13 Nov 2023 10:33:05 -0600 Subject: [PATCH] System send Process instead of Entity --- src/cli.rs | 12 ++- src/process.rs | 222 ++++++++++++++++++++++++-------------------- src/runtime.rs | 45 ++++----- src/std_system.rs | 13 +-- src/test/mod.rs | 28 +++--- src/test/project.rs | 12 ++- 6 files changed, 182 insertions(+), 150 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 584a4b7..47b9516 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -189,8 +189,9 @@ fn run_proj_tty>>(project_name: &str, server: String let config = overrides.fallback(&Config { command: { let update_flag = update_flag.clone(); - Some(Rc::new(move |_, _, key, command, entity| match command { + Some(Rc::new(move |_, _, key, command, proc| match command { Command::Print { style: _, value } => { + let entity = &*proc.current_entity().borrow(); if let Some(value) = value { print!("{entity:?} > {value:?}\r\n"); update_flag.set(true); @@ -204,8 +205,9 @@ fn run_proj_tty>>(project_name: &str, server: String request: { let update_flag = update_flag.clone(); let input_queries = input_queries.clone(); - Some(Rc::new(move |_, _, key, request, entity| match request { + Some(Rc::new(move |_, _, key, request, proc| match request { Request::Input { prompt } => { + let entity = &*proc.current_entity().borrow(); input_queries.borrow_mut().push_back((format!("{entity:?} {prompt:?} > "), key)); update_flag.set(true); RequestStatus::Handled @@ -297,8 +299,9 @@ fn run_proj_tty>>(project_name: &str, server: String fn run_proj_non_tty>>(project_name: &str, server: String, role: &ast::Role, overrides: Config>, clock: Arc) { let config = overrides.fallback(&Config { request: None, - command: Some(Rc::new(move |_, _, key, command, entity| match command { + command: Some(Rc::new(move |_, _, key, command, proc| match command { Command::Print { style: _, value } => { + let entity = &*proc.current_entity().borrow(); if let Some(value) = value { println!("{entity:?} > {value:?}") } key.complete(Ok(())); CommandStatus::Handled @@ -382,8 +385,9 @@ fn run_server>>(nb_server: String, addr: String, por let weak_state = Arc::downgrade(&state); let config = overrides.fallback(&Config { request: None, - command: Some(Rc::new(move |_, _, key, command, entity| match command { + command: Some(Rc::new(move |_, _, key, command, proc| match command { Command::Print { style: _, value } => { + let entity = &*proc.current_entity().borrow(); if let Some(value) = value { tee_println!(weak_state.upgrade() => "{entity:?} > {value:?}") } key.complete(Ok(())); CommandStatus::Handled diff --git a/src/process.rs b/src/process.rs index f796ba9..ca6e441 100644 --- a/src/process.rs +++ b/src/process.rs @@ -170,6 +170,7 @@ enum Defer, S: System> { Barrier { condition: BarrierCondition, aft_pos: usize }, Sleep { until: u64, aft_pos: usize }, } +#[derive(Clone, Copy)] enum RequestAction { Rpc, Syscall, Input, Push, } @@ -245,6 +246,12 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { pub fn is_running(&self) -> bool { self.running } + /// Gets the current active entity of this process. + /// Note that this is not necessarily the same entity that was used to construct the process, + /// as blocks such as "tell _ to _" and "ask _ for _" can change the context entity. + pub fn current_entity(&self) -> Gc<'gc, RefLock>> { + self.call_stack.last().unwrap().entity + } /// Executes a single bytecode instruction. /// The return value can be used to determine what additional effects the script has requested, /// as well as to retrieve the return value or execution error in the event that the process terminates. @@ -278,9 +285,6 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { res.map_err(|cause| ExecError { cause, pos: self.pos }) } fn step_impl(&mut self, mc: &Mutation<'gc>) -> Result, ErrorCause> { - let mut global_context = self.global_context.borrow_mut(mc); - let global_context = &mut *global_context; - fn process_result<'gc, C: CustomTypes, S: System, T>(result: Result, error_scheme: ErrorScheme, stack: Option<&mut Vec>>, last_ok: Option<&mut Option>>, last_err: Option<&mut Option>>, to_value: fn(T) -> Option>) -> Result<(), ErrorCause> { match result { Ok(x) => match to_value(x) { @@ -318,76 +322,98 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { } Ok(()) } + fn prep_call_closure<'gc, C: CustomTypes, S: System>(mc: &Mutation<'gc>, value_stack: &mut Vec>, args: usize) -> Result<(usize, SymbolTable<'gc, C, S>), ErrorCause> { + let mut values = value_stack.drain(value_stack.len() - (args + 1)..); + let closure = values.next().unwrap().as_closure()?; + let mut closure = closure.borrow_mut(mc); + if closure.params.len() != args { + return Err(ErrorCause::ClosureArgCount { expected: closure.params.len(), got: args }); + } + + let mut locals = SymbolTable::default(); + for (k, v) in closure.captures.iter_mut() { + locals.define_or_redefine(k, v.alias(mc)); + } + for (var, value) in iter::zip(&closure.params, values) { + locals.define_or_redefine(var, value.into()); + } - let context_entity = self.call_stack.last().unwrap().entity; + Ok((closure.pos, locals)) + } - match &self.defer { + let system = self.global_context.borrow().system.clone(); + match self.defer.take() { None => (), - Some(Defer::Request { key, aft_pos, action }) => match global_context.system.poll_request(mc, key, &mut *context_entity.borrow_mut(mc))? { + Some(Defer::Request { key, aft_pos, action }) => match system.poll_request(mc, &key, self)? { AsyncResult::Completed(x) => { + let settings = &self.global_context.borrow().settings; match action { - RequestAction::Syscall => process_result(x, global_context.settings.syscall_error_scheme, Some(&mut self.value_stack), None, Some(&mut self.last_syscall_error), Some)?, - RequestAction::Rpc => process_result(x, global_context.settings.rpc_error_scheme, Some(&mut self.value_stack), None, Some(&mut self.last_rpc_error), Some)?, + RequestAction::Syscall => process_result(x, settings.syscall_error_scheme, Some(&mut self.value_stack), None, Some(&mut self.last_syscall_error), Some)?, + RequestAction::Rpc => process_result(x, settings.rpc_error_scheme, Some(&mut self.value_stack), None, Some(&mut self.last_rpc_error), Some)?, RequestAction::Input => process_result(x, ErrorScheme::Hard, None, Some(&mut self.last_answer), None, Some)?, RequestAction::Push => process_result(x, ErrorScheme::Hard, Some(&mut self.value_stack), None, None, Some)?, } - self.pos = *aft_pos; - self.defer = None; + self.pos = aft_pos; + } + AsyncResult::Pending => { + self.defer = Some(Defer::Request { key, aft_pos, action }); + return Ok(ProcessStep::Yield); } - AsyncResult::Pending => return Ok(ProcessStep::Yield), AsyncResult::Consumed => panic!(), } - Some(Defer::Command { key, aft_pos }) => match global_context.system.poll_command(mc, key, &mut *context_entity.borrow_mut(mc))? { + Some(Defer::Command { key, aft_pos }) => match system.poll_command(mc, &key, self)? { AsyncResult::Completed(x) => { process_result(x, ErrorScheme::Hard, None, None, None, |_| None)?; - self.pos = *aft_pos; - self.defer = None; + self.pos = aft_pos; + } + AsyncResult::Pending => { + self.defer = Some(Defer::Command { key, aft_pos }); + return Ok(ProcessStep::Yield); } - AsyncResult::Pending => return Ok(ProcessStep::Yield), AsyncResult::Consumed => panic!(), } - Some(Defer::MessageReply { key, aft_pos }) => match global_context.system.poll_reply(key) { + Some(Defer::MessageReply { key, aft_pos }) => match system.poll_reply(&key) { AsyncResult::Completed(x) => { let value = match x { Some(x) => Value::from_simple(mc, SimpleValue::from_netsblox_json(x)?), None => empty_string().into(), }; self.value_stack.push(value); - self.pos = *aft_pos; - self.defer = None; + self.pos = aft_pos; + } + AsyncResult::Pending => { + self.defer = Some(Defer::MessageReply { key, aft_pos }); + return Ok(ProcessStep::Yield); } - AsyncResult::Pending => return Ok(ProcessStep::Yield), AsyncResult::Consumed => panic!(), } Some(Defer::Barrier { condition, aft_pos }) => match condition.is_completed() { true => { - self.pos = *aft_pos; - self.defer = None; + self.pos = aft_pos; + } + false => { + self.defer = Some(Defer::Barrier { condition, aft_pos }); + return Ok(ProcessStep::Yield); } - false => return Ok(ProcessStep::Yield), } - Some(Defer::Sleep { until, aft_pos }) => match global_context.system.time(Precision::Low).to_arbitrary_ms()? >= *until { + Some(Defer::Sleep { until, aft_pos }) => match system.time(Precision::Low).to_arbitrary_ms()? >= until { true => { - self.pos = *aft_pos; - self.defer = None; + self.pos = aft_pos; + } + false => { + self.defer = Some(Defer::Sleep { until, aft_pos }); + return Ok(ProcessStep::Yield); } - false => return Ok(ProcessStep::Yield), } } - let mut entity = context_entity.borrow_mut(mc); - let (parent_scope, current_scope) = match self.call_stack.as_mut_slice() { - [] => unreachable!(), - [x] => (None, &mut x.locals), - [.., x, y] => (Some(&mut x.locals), &mut y.locals), - }; - let mut context = [&mut global_context.globals, &mut entity.fields, current_scope]; - let mut context = LookupGroup::new(&mut context); - + let mut global_context = self.global_context.borrow_mut(mc); + let global_context = &mut *global_context; macro_rules! lookup_var { ($var:expr => $m:ident) => {{ let var = $var; - match context.$m(var) { + let local_frame = self.call_stack.last_mut().unwrap(); + match LookupGroup::new(&mut [&mut global_context.globals, &mut local_frame.entity.borrow_mut(mc).fields, &mut local_frame.locals]).$m(var) { Some(x) => x, None => return Err(ErrorCause::UndefinedVariable { name: var.into() }), } @@ -396,25 +422,6 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { (mut $var:expr) => {lookup_var!($var => lookup_mut)}; } - fn prep_call_closure<'gc, C: CustomTypes, S: System>(mc: &Mutation<'gc>, value_stack: &mut Vec>, args: usize) -> Result<(usize, SymbolTable<'gc, C, S>), ErrorCause> { - let mut values = value_stack.drain(value_stack.len() - (args + 1)..); - let closure = values.next().unwrap().as_closure()?; - let mut closure = closure.borrow_mut(mc); - if closure.params.len() != args { - return Err(ErrorCause::ClosureArgCount { expected: closure.params.len(), got: args }); - } - - let mut locals = SymbolTable::default(); - for (k, v) in closure.captures.iter_mut() { - locals.define_or_redefine(k, v.alias(mc)); - } - for (var, value) in iter::zip(&closure.params, values) { - locals.define_or_redefine(var, value.into()); - } - - Ok((closure.pos, locals)) - } - let (ins, aft_pos) = Instruction::read(&global_context.bytecode.code, &global_context.bytecode.data, self.pos); match ins { Instruction::Yield => { @@ -463,7 +470,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { None => return Err(ErrorCause::UndefinedEntity { name: name.into() }), } Instruction::PushSelf => { - self.value_stack.push(context_entity.into()); + self.value_stack.push(self.current_entity().into()); self.pos = aft_pos; } Instruction::PopValue => { @@ -648,7 +655,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let val = self.value_stack.pop().unwrap(); let mut list = list.borrow_mut(mc); - let index = ops::prep_rand_index(&*global_context.system, list.len() + 1)?; + let index = ops::prep_rand_index(&*system, list.len() + 1)?; list.insert(index, val); self.pos = aft_pos; } @@ -656,7 +663,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::ListGet => { let list = self.value_stack.pop().unwrap(); let index = self.value_stack.pop().unwrap(); - self.value_stack.push(ops::index_list(mc, &*global_context.system, &list, &index)?); + self.value_stack.push(ops::index_list(mc, &*system, &list, &index)?); self.pos = aft_pos; } Instruction::ListGetLast => { @@ -670,7 +677,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::ListGetRandom => { let list = self.value_stack.pop().unwrap().as_list()?; let list = list.borrow(); - let index = ops::prep_rand_index(&*global_context.system, list.len())?; + let index = ops::prep_rand_index(&*system, list.len())?; self.value_stack.push(list[index].clone()); self.pos = aft_pos; } @@ -698,7 +705,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let list = self.value_stack.pop().unwrap().as_list()?; let mut list = list.borrow_mut(mc); - let index = ops::prep_rand_index(&*global_context.system, list.len())?; + let index = ops::prep_rand_index(&*system, list.len())?; list[index] = value; self.pos = aft_pos; } @@ -734,7 +741,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::BinaryOp { op } => { let b = self.value_stack.pop().unwrap(); let a = self.value_stack.pop().unwrap(); - self.value_stack.push(ops::binary_op(mc, &*global_context.system, &a, &b, op)?); + self.value_stack.push(ops::binary_op(mc, &*system, &a, &b, op)?); self.pos = aft_pos; } Instruction::VariadicOp { op, len } => { @@ -795,14 +802,14 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let res = match len { VariadicLen::Fixed(len) => { let stack_size = self.value_stack.len(); - let res = combine(mc, &*global_context.system, &mut self.value_stack[stack_size - len..].iter())?; + let res = combine(mc, &*system, &mut self.value_stack[stack_size - len..].iter())?; self.value_stack.drain(stack_size - len..); res } VariadicLen::Dynamic => { let src = self.value_stack.pop().unwrap().as_list()?; let src = src.borrow(); - combine(mc, &*global_context.system, &mut src.iter())? + combine(mc, &*system, &mut src.iter())? } }; self.value_stack.push(res); @@ -822,23 +829,27 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { } Instruction::UnaryOp { op } => { let x = self.value_stack.pop().unwrap(); - self.value_stack.push(ops::unary_op(mc, &*global_context.system, &x, op)?); + self.value_stack.push(ops::unary_op(mc, &*system, &x, op)?); self.pos = aft_pos; } Instruction::DeclareLocal { var } => { - context.locals_mut().define_if_undefined(var, || Shared::Unique(Number::new(0.0).unwrap().into())); + self.call_stack.last_mut().unwrap().locals.define_if_undefined(var, || Shared::Unique(Number::new(0.0).unwrap().into())); self.pos = aft_pos; } Instruction::InitUpvar { var } => { let target = lookup_var!(var).get().clone(); let target = target.as_string()?; - let parent_scope = parent_scope.ok_or(ErrorCause::UpvarAtRoot)?; - let parent_def = match parent_scope.lookup_mut(target.as_ref()) { + let (parent_scope, current_scope) = match self.call_stack.as_mut_slice() { + [] => unreachable!(), + [_] => return Err(ErrorCause::UpvarAtRoot), + [.., x, y] => (x, y), + }; + let parent_def = match parent_scope.locals.lookup_mut(target.as_ref()) { Some(x) => x, None => return Err(ErrorCause::UndefinedVariable { name: var.into() }), }; - context.locals_mut().define_or_redefine(var, parent_def.alias(mc)); + current_scope.locals.define_or_redefine(var, parent_def.alias(mc)); self.pos = aft_pos; } Instruction::Assign { var } => { @@ -849,13 +860,13 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::BinaryOpAssign { var, op } => { let b = self.value_stack.pop().unwrap(); let a = lookup_var!(var).get().clone(); - lookup_var!(mut var).set(mc, ops::binary_op(mc, &*global_context.system, &a, &b, op)?); + lookup_var!(mut var).set(mc, ops::binary_op(mc, &*system, &a, &b, op)?); self.pos = aft_pos; } Instruction::Watcher { create, var } => { let watcher = Watcher { - entity: Gc::downgrade(context_entity), + entity: Gc::downgrade(self.current_entity()), name: var.to_owned(), value: Gc::downgrade(lookup_var!(mut var).alias_inner(mc)), }; @@ -874,8 +885,9 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { } Instruction::Call { pos, tokens } => { - if self.call_stack.len() >= global_context.settings.max_call_depth { - return Err(ErrorCause::CallDepthLimit { limit: global_context.settings.max_call_depth }); + let limit = global_context.settings.max_call_depth; + if self.call_stack.len() >= limit { + return Err(ErrorCause::CallDepthLimit { limit }); } let params = lossless_split(tokens).collect::>(); @@ -892,7 +904,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { warp_counter: self.warp_counter, value_stack_size: self.value_stack.len(), handler_stack_size: self.handler_stack.len(), - entity: context_entity, + entity: self.current_entity(), locals, }); self.pos = pos; @@ -912,7 +924,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::CallClosure { new_entity, args } => { let (closure_pos, locals) = prep_call_closure(mc, &mut self.value_stack, args)?; let entity = match new_entity { - false => context_entity, + false => self.current_entity(), true => self.value_stack.pop().unwrap().as_entity()?, }; @@ -930,7 +942,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::ForkClosure { args } => { let (closure_pos, locals) = prep_call_closure(mc, &mut self.value_stack, args)?; self.pos = aft_pos; - return Ok(ProcessStep::Fork { pos: closure_pos, locals, entity: context_entity }); + return Ok(ProcessStep::Fork { pos: closure_pos, locals, entity: self.current_entity() }); } Instruction::Return => { let CallStackEntry { called_from, return_to, warp_counter, value_stack_size, handler_stack_size, .. } = self.call_stack.last().unwrap(); @@ -985,7 +997,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let args = iter::zip(arg_names, self.value_stack.drain(self.value_stack.len() - arg_count..)).collect(); self.defer = Some(Defer::Request { - key: global_context.system.perform_request(mc, Request::Rpc { service, rpc, args }, &mut *entity)?, + key: system.perform_request(mc, Request::Rpc { service, rpc, args }, self)?, action: RequestAction::Rpc, aft_pos }); @@ -1005,7 +1017,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let name = self.value_stack.pop().unwrap().as_string()?.into_owned(); self.defer = Some(Defer::Request { - key: global_context.system.perform_request(mc, Request::Syscall { name, args }, &mut *entity)?, + key: system.perform_request(mc, Request::Syscall { name, args }, self)?, action: RequestAction::Syscall, aft_pos }); @@ -1044,7 +1056,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let value = self.value_stack.pop().unwrap(); let is_empty = match &value { Value::String(x) => x.is_empty(), _ => false }; self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::Print { style, value: if is_empty { None } else { Some(value) } }, &mut *entity)?, + key: system.perform_command(mc, Command::Print { style, value: if is_empty { None } else { Some(value) } }, self)?, aft_pos, }); } @@ -1053,7 +1065,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let is_empty = match &prompt { Value::String(x) => x.is_empty(), _ => false }; self.defer = Some(Defer::Request { - key: global_context.system.perform_request(mc, Request::Input { prompt: if is_empty { None } else { Some(prompt) } }, &mut *entity)?, + key: system.perform_request(mc, Request::Input { prompt: if is_empty { None } else { Some(prompt) } }, self)?, action: RequestAction::Input, aft_pos }); @@ -1063,11 +1075,13 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { self.pos = aft_pos; } Instruction::ResetTimer => { - global_context.timer_start = global_context.system.time(Precision::Medium).to_arbitrary_ms()?; + let t = system.time(Precision::Medium).to_arbitrary_ms()?; + global_context.timer_start = t; self.pos = aft_pos; } Instruction::PushTimer => { - self.value_stack.push(Number::new(global_context.system.time(Precision::Low).to_arbitrary_ms()?.saturating_sub(global_context.timer_start) as f64 / 1000.0)?.into()); + let t = system.time(Precision::Low).to_arbitrary_ms()?; + self.value_stack.push(Number::new(t.saturating_sub(global_context.timer_start) as f64 / 1000.0)?.into()); self.pos = aft_pos; } Instruction::Sleep => { @@ -1076,10 +1090,10 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { self.pos = aft_pos; return Ok(ProcessStep::Yield); } - self.defer = Some(Defer::Sleep { until: global_context.system.time(Precision::Medium).to_arbitrary_ms()? + ms as u64, aft_pos }); + self.defer = Some(Defer::Sleep { until: system.time(Precision::Medium).to_arbitrary_ms()? + ms as u64, aft_pos }); } Instruction::PushRealTime { query } => { - let t = global_context.system.time(Precision::High).to_real_local()?; + let t = system.time(Precision::High).to_real_local()?; let v = match query { TimeQuery::UnixTimestampMs => (t.unix_timestamp_nanos() / 1000000) as f64, TimeQuery::Year => t.year() as f64, @@ -1119,7 +1133,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { iter::zip(field_names.into_iter(), self.value_stack.drain(self.value_stack.len() - field_count..)).map(|(k, v)| Ok((k, v.to_simple()?.into_netsblox_json()?))).collect::>>()? }; - match global_context.system.send_message(msg_type.into(), values, targets, expect_reply)? { + match system.send_message(msg_type.into(), values, targets, expect_reply)? { Some(key) => self.defer = Some(Defer::MessageReply { key, aft_pos }), None => self.pos = aft_pos, } @@ -1127,13 +1141,13 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::SendNetworkReply => { let value = self.value_stack.pop().unwrap().to_simple()?.into_netsblox_json()?; if let Some(key) = self.reply_key.take() { - global_context.system.send_reply(key, value)?; + system.send_reply(key, value)?; } self.pos = aft_pos; } Instruction::PushProperty { prop } => { self.defer = Some(Defer::Request { - key: global_context.system.perform_request(mc, Request::Property { prop }, &mut *entity)?, + key: system.perform_request(mc, Request::Property { prop }, self)?, action: RequestAction::Push, aft_pos }); @@ -1141,31 +1155,35 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::SetProperty { prop } => { let value = self.value_stack.pop().unwrap(); self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::SetProperty { prop, value }, &mut *entity)?, + key: system.perform_command(mc, Command::SetProperty { prop, value }, self)?, aft_pos, }); } Instruction::ChangeProperty { prop } => { let delta = self.value_stack.pop().unwrap(); self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::ChangeProperty { prop, delta }, &mut *entity)?, + key: system.perform_command(mc, Command::ChangeProperty { prop, delta }, self)?, aft_pos, }); } Instruction::PushCostume => { + let entity = self.current_entity().borrow(); self.value_stack.push(entity.costume.clone().map(|x| Value::Image(x)).unwrap_or_else(|| Value::String(empty_string()))); self.pos = aft_pos; } Instruction::PushCostumeNumber => { + let entity = self.current_entity().borrow(); let res = entity.costume.as_ref().and_then(|x| entity.costume_list.iter().enumerate().find(|c| Rc::ptr_eq(x, &c.1.1))).map(|x| x.0 + 1).unwrap_or(0); self.value_stack.push(Value::Number(Number::new(res as f64)?)); self.pos = aft_pos; } Instruction::PushCostumeList => { + let entity = self.current_entity().borrow(); self.value_stack.push(Value::List(Gc::new(mc, RefLock::new(entity.costume_list.iter().map(|x| Value::Image(x.1.clone())).collect())))); self.pos = aft_pos; } Instruction::SetCostume => { + let mut entity = self.current_entity().borrow_mut(mc); let new_costume = match self.value_stack.pop().unwrap() { Value::Image(x) => Some(x), Value::String(x) => match x.as_str() { @@ -1179,8 +1197,9 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { }; if new_costume.as_ref().map(Rc::as_ptr) != entity.costume.as_ref().map(Rc::as_ptr) { + entity.costume = new_costume; self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::SetCostume { costume: new_costume }, &mut *entity)?, + key: system.perform_command(mc, Command::SetCostume, self)?, aft_pos, }); } else { @@ -1188,13 +1207,15 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { } } Instruction::NextCostume => { + let mut entity = self.current_entity().borrow_mut(mc); match entity.costume.as_ref().and_then(|x| entity.costume_list.iter().enumerate().find(|c| Rc::ptr_eq(x, &c.1.1))).map(|x| x.0) { Some(idx) => { let new_costume = Some(entity.costume_list[(idx + 1) % entity.costume_list.len()].1.clone()); if new_costume.as_ref().map(Rc::as_ptr) != entity.costume.as_ref().map(Rc::as_ptr) { + entity.costume = new_costume; self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::SetCostume { costume: new_costume }, &mut *entity)?, + key: system.perform_command(mc, Command::SetCostume, self)?, aft_pos, }); } else { @@ -1205,7 +1226,6 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { } } Instruction::Clone => { - drop(entity); // drop our mutable borrow from earlier (in case target is self) let target_cell = self.value_stack.pop().unwrap().as_entity()?; let target = target_cell.borrow(); let new_entity = Gc::new(mc, RefLock::new(Entity { @@ -1224,13 +1244,13 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { } Instruction::ClearEffects => { self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::ClearEffects, &mut *entity)?, + key: system.perform_command(mc, Command::ClearEffects, self)?, aft_pos, }); } Instruction::ClearDrawings => { self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::ClearDrawings, &mut *entity)?, + key: system.perform_command(mc, Command::ClearDrawings, self)?, aft_pos, }); } @@ -1238,7 +1258,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let y = self.value_stack.pop().unwrap().as_number()?; let x = self.value_stack.pop().unwrap().as_number()?; self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::GotoXY { x, y }, &mut *entity)?, + key: system.perform_command(mc, Command::GotoXY { x, y }, self)?, aft_pos, }); } @@ -1248,14 +1268,14 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { if target.len() != 2 { return Err(ErrorCause::InvalidListLength { expected: 2, got: target.len() }); } let (x, y) = (target[0].as_number()?, target[1].as_number()?); self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::GotoXY { x, y }, &mut *entity)?, + key: system.perform_command(mc, Command::GotoXY { x, y }, self)?, aft_pos, }); } Value::Entity(target) => { let target = target.borrow(); self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::GotoEntity { target: &*target }, &mut *entity)?, + key: system.perform_command(mc, Command::GotoEntity { target: &*target }, self)?, aft_pos, }); } @@ -1265,7 +1285,7 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { let x = self.value_stack.pop().unwrap().as_number()?; let y = self.value_stack.pop().unwrap().as_number()?; self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::PointTowardsXY { x, y }, &mut *entity)?, + key: system.perform_command(mc, Command::PointTowardsXY { x, y }, self)?, aft_pos, }); } @@ -1275,14 +1295,14 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { if target.len() != 2 { return Err(ErrorCause::InvalidListLength { expected: 2, got: target.len() }); } let (x, y) = (target[0].as_number()?, target[1].as_number()?); self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::PointTowardsXY { x, y }, &mut *entity)?, + key: system.perform_command(mc, Command::PointTowardsXY { x, y }, self)?, aft_pos, }); } Value::Entity(target) => { let target = target.borrow(); self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::PointTowardsEntity { target: &*target }, &mut *entity)?, + key: system.perform_command(mc, Command::PointTowardsEntity { target: &*target }, self)?, aft_pos, }); } @@ -1291,14 +1311,14 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { Instruction::Forward => { let distance = self.value_stack.pop().unwrap().as_number()?; self.defer = Some(Defer::Command { - key: global_context.system.perform_command(mc, Command::Forward { distance }, &mut *entity)?, + key: system.perform_command(mc, Command::Forward { distance }, self)?, aft_pos, }); } Instruction::UnknownBlock { name, args } => { let args = self.value_stack.drain(self.value_stack.len() - args..).collect(); self.defer = Some(Defer::Request { - key: global_context.system.perform_request(mc, Request::UnknownBlock { name: name.into(), args }, &mut *entity)?, + key: system.perform_request(mc, Request::UnknownBlock { name: name.into(), args }, self)?, action: RequestAction::Push, aft_pos }); diff --git a/src/runtime.rs b/src/runtime.rs index c558267..9caa362 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -22,6 +22,7 @@ use crate::gc::*; use crate::json::*; use crate::real_time::*; use crate::bytecode::*; +use crate::process::*; /// Error type used by [`NumberChecker`]. #[derive(Debug)] @@ -1297,10 +1298,6 @@ impl<'gc, 'a, 'b, C: CustomTypes, S: System> LookupGroup<'gc, 'a, 'b, C, S } None } - /// Gets a mutable reference to the last (most-local) context. - pub fn locals_mut(&mut self) -> &mut SymbolTable<'gc, C, S> { - self.0.last_mut().unwrap() - } } /// The error promotion paradigm to use for certain types of runtime errors. @@ -1619,9 +1616,10 @@ pub enum Command<'gc, 'a, C: CustomTypes, S: System> { /// Clear all drawings made by all sprites. ClearDrawings, - /// Sets the costume on the entity. This should essentially assigns the costume to [`Entity::costume`], - /// but is treated as a system command so that custom code can be executed when an entity switches costumes. - SetCostume { costume: Option> }, + /// Sets the costume on the entity. + /// At this point the costume has already been assigned to [`Entity::costume`], + /// so this is just a hook for any custom update code that is needed for external purposes. + SetCostume, /// Moves the entity to a specific location. GotoXY { x: Number, y: Number }, @@ -1677,22 +1675,15 @@ pub enum CommandStatus<'gc, 'a, C: CustomTypes, S: System> { #[educe(Clone)] pub struct Config, S: System> { /// A function used to perform asynchronous requests that yield a value back to the runtime. - pub request: Option Fn(&S, &Mutation<'gc>, S::RequestKey, Request<'gc, C, S>, &mut Entity<'gc, C, S>) -> RequestStatus<'gc, C, S>>>, + pub request: Option Fn(&S, &Mutation<'gc>, S::RequestKey, Request<'gc, C, S>, &mut Process<'gc, C, S>) -> RequestStatus<'gc, C, S>>>, /// A function used to perform asynchronous tasks whose completion is awaited by the runtime. - pub command: Option Fn(&S, &Mutation<'gc>, S::CommandKey, Command<'gc, 'a, C, S>, &mut Entity<'gc, C, S>) -> CommandStatus<'gc, 'a, C, S>>>, + pub command: Option Fn(&S, &Mutation<'gc>, S::CommandKey, Command<'gc, 'a, C, S>, &mut Process<'gc, C, S>) -> CommandStatus<'gc, 'a, C, S>>>, } impl, S: System> Default for Config { fn default() -> Self { Self { request: None, - command: Some(Rc::new(|_, _, key, command, entity| match command { - Command::SetCostume { costume } => { - entity.costume = costume; - key.complete(Ok(())); - CommandStatus::Handled - } - _ => CommandStatus::UseDefault { key, command }, - })), + command: None, } } } @@ -1701,20 +1692,20 @@ impl, S: System> Config { pub fn fallback(&self, other: &Self) -> Self { Self { request: match (self.request.clone(), other.request.clone()) { - (Some(a), Some(b)) => Some(Rc::new(move |system, mc, key, request, entity| { - match a(system, mc, key, request, entity) { + (Some(a), Some(b)) => Some(Rc::new(move |system, mc, key, request, proc| { + match a(system, mc, key, request, proc) { RequestStatus::Handled => RequestStatus::Handled, - RequestStatus::UseDefault { key, request } => b(system, mc, key, request, entity), + RequestStatus::UseDefault { key, request } => b(system, mc, key, request, proc), } })), (Some(a), None) | (None, Some(a)) => Some(a), (None, None) => None, }, command: match (self.command.clone(), other.command.clone()) { - (Some(a), Some(b)) => Some(Rc::new(move |system, mc, key, command, entity| { - match a(system, mc, key, command, entity) { + (Some(a), Some(b)) => Some(Rc::new(move |system, mc, key, command, proc| { + match a(system, mc, key, command, proc) { CommandStatus::Handled => CommandStatus::Handled, - CommandStatus::UseDefault { key, command } => b(system, mc, key, command, entity), + CommandStatus::UseDefault { key, command } => b(system, mc, key, command, proc), } })), (Some(a), None) | (None, Some(a)) => Some(a), @@ -1819,18 +1810,18 @@ pub trait System>: 'static + Sized { /// Performs a general request which returns a value to the system. /// Ideally, this function should be non-blocking, and the requestor will await the result asynchronously. /// The [`Entity`] that made the request is provided for context. - fn perform_request<'gc>(&self, mc: &Mutation<'gc>, request: Request<'gc, C, Self>, entity: &mut Entity<'gc, C, Self>) -> Result>; + fn perform_request<'gc>(&self, mc: &Mutation<'gc>, request: Request<'gc, C, Self>, proc: &mut Process<'gc, C, Self>) -> Result>; /// Poll for the completion of an asynchronous request. /// The [`Entity`] that made the request is provided for context. - fn poll_request<'gc>(&self, mc: &Mutation<'gc>, key: &Self::RequestKey, entity: &mut Entity<'gc, C, Self>) -> Result, String>>, ErrorCause>; + fn poll_request<'gc>(&self, mc: &Mutation<'gc>, key: &Self::RequestKey, proc: &mut Process<'gc, C, Self>) -> Result, String>>, ErrorCause>; /// Performs a general command which does not return a value to the system. /// Ideally, this function should be non-blocking, and the commander will await the task's completion asynchronously. /// The [`Entity`] that issued the command is provided for context. - fn perform_command<'gc>(&self, mc: &Mutation<'gc>, command: Command<'gc, '_, C, Self>, entity: &mut Entity<'gc, C, Self>) -> Result>; + fn perform_command<'gc>(&self, mc: &Mutation<'gc>, command: Command<'gc, '_, C, Self>, proc: &mut Process<'gc, C, Self>) -> Result>; /// Poll for the completion of an asynchronous command. /// The [`Entity`] that issued the command is provided for context. - fn poll_command<'gc>(&self, mc: &Mutation<'gc>, key: &Self::CommandKey, entity: &mut Entity<'gc, C, Self>) -> Result>, ErrorCause>; + fn poll_command<'gc>(&self, mc: &Mutation<'gc>, key: &Self::CommandKey, proc: &mut Process<'gc, C, Self>) -> Result>, ErrorCause>; /// Sends a message containing a set of named `values` to each of the specified `targets`. /// The `expect_reply` value controls whether or not to use a reply mechanism to asynchronously receive a response from the target(s). diff --git a/src/std_system.rs b/src/std_system.rs index c28c524..4ff6401 100644 --- a/src/std_system.rs +++ b/src/std_system.rs @@ -27,6 +27,7 @@ use uuid::Uuid; use crate::real_time::*; use crate::runtime::*; +use crate::process::*; use crate::json::*; use crate::gc::*; use crate::*; @@ -417,11 +418,11 @@ impl>> System for StdSystem { SysTime::Real { local: self.clock.read(precision) } } - fn perform_request<'gc>(&self, mc: &Mutation<'gc>, request: Request<'gc, C, Self>, entity: &mut Entity<'gc, C, Self>) -> Result> { + fn perform_request<'gc>(&self, mc: &Mutation<'gc>, request: Request<'gc, C, Self>, proc: &mut Process<'gc, C, Self>) -> Result> { Ok(match self.config.request.as_ref() { Some(handler) => { let key = RequestKey(Arc::new(Mutex::new(AsyncResult::new()))); - match handler(self, mc, RequestKey(key.0.clone()), request, entity) { + match handler(self, mc, RequestKey(key.0.clone()), request, proc) { RequestStatus::Handled => key, RequestStatus::UseDefault { key: _, request } => return Err(ErrorCause::NotSupported { feature: request.feature() }), } @@ -429,7 +430,7 @@ impl>> System for StdSystem { None => return Err(ErrorCause::NotSupported { feature: request.feature() }), }) } - fn poll_request<'gc>(&self, mc: &Mutation<'gc>, key: &Self::RequestKey, _: &mut Entity<'gc, C, Self>) -> Result, String>>, ErrorCause> { + fn poll_request<'gc>(&self, mc: &Mutation<'gc>, key: &Self::RequestKey, _: &mut Process<'gc, C, Self>) -> Result, String>>, ErrorCause> { Ok(match key.poll() { AsyncResult::Completed(Ok(x)) => AsyncResult::Completed(Ok(C::from_intermediate(mc, x)?)), AsyncResult::Completed(Err(x)) => AsyncResult::Completed(Err(x)), @@ -438,11 +439,11 @@ impl>> System for StdSystem { }) } - fn perform_command<'gc>(&self, mc: &Mutation<'gc>, command: Command<'gc, '_, C, Self>, entity: &mut Entity<'gc, C, Self>) -> Result> { + fn perform_command<'gc>(&self, mc: &Mutation<'gc>, command: Command<'gc, '_, C, Self>, proc: &mut Process<'gc, C, Self>) -> Result> { Ok(match self.config.command.as_ref() { Some(handler) => { let key = CommandKey(Arc::new(Mutex::new(AsyncResult::new()))); - match handler(self, mc, CommandKey(key.0.clone()), command, entity) { + match handler(self, mc, CommandKey(key.0.clone()), command, proc) { CommandStatus::Handled => key, CommandStatus::UseDefault { key: _, command } => return Err(ErrorCause::NotSupported { feature: command.feature() }), } @@ -450,7 +451,7 @@ impl>> System for StdSystem { None => return Err(ErrorCause::NotSupported { feature: command.feature() }), }) } - fn poll_command<'gc>(&self, _: &Mutation<'gc>, key: &Self::CommandKey, _: &mut Entity<'gc, C, Self>) -> Result>, ErrorCause> { + fn poll_command<'gc>(&self, _: &Mutation<'gc>, key: &Self::CommandKey, _: &mut Process<'gc, C, Self>) -> Result>, ErrorCause> { Ok(key.poll()) } diff --git a/src/test/mod.rs b/src/test/mod.rs index de64b22..b8dd098 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -46,18 +46,24 @@ impl From<&Entity<'_, C, StdSystem>> for ProcessState { fn default_properties_config() -> Config> { Config { - request: Some(Rc::new(|_, _, key, request, entity| match request { - Request::Property { prop } => entity.state.props.perform_get_property(key, prop), - _ => RequestStatus::UseDefault { key, request }, + request: Some(Rc::new(|_, _, key, request, proc| { + let entity = proc.current_entity().borrow(); + match request { + Request::Property { prop } => entity.state.props.perform_get_property(key, prop), + _ => RequestStatus::UseDefault { key, request }, + } })), - command: Some(Rc::new(|_, _, key, command, entity| match command { - Command::SetProperty { prop, value } => entity.state.props.perform_set_property(key, prop, value), - Command::ChangeProperty { prop, delta } => entity.state.props.perform_change_property(key, prop, delta), - Command::ClearEffects => entity.state.props.perform_clear_effects(key), - Command::GotoXY { x, y } => entity.state.props.perform_goto_xy(key, x, y), - Command::PointTowardsXY { x, y } => entity.state.props.perform_point_towards_xy(key, x, y), - Command::Forward { distance } => entity.state.props.perform_forward(key, distance), - _ => CommandStatus::UseDefault { key, command }, + command: Some(Rc::new(|_, mc, key, command, proc| { + let mut entity = proc.current_entity().borrow_mut(mc); + match command { + Command::SetProperty { prop, value } => entity.state.props.perform_set_property(key, prop, value), + Command::ChangeProperty { prop, delta } => entity.state.props.perform_change_property(key, prop, delta), + Command::ClearEffects => entity.state.props.perform_clear_effects(key), + Command::GotoXY { x, y } => entity.state.props.perform_goto_xy(key, x, y), + Command::PointTowardsXY { x, y } => entity.state.props.perform_point_towards_xy(key, x, y), + Command::Forward { distance } => entity.state.props.perform_forward(key, distance), + _ => CommandStatus::UseDefault { key, command }, + } })), } } diff --git a/src/test/project.rs b/src/test/project.rs index b1c8c84..36cf5f9 100644 --- a/src/test/project.rs +++ b/src/test/project.rs @@ -249,7 +249,17 @@ fn test_proj_watchers() { #[test] fn test_proj_costumes() { - let system = Rc::new(StdSystem::new_sync(BASE_URL.to_owned(), None, Config::default(), Arc::new(Clock::new(UtcOffset::UTC, None)))); + let config = Config::> { + request: None, + command: Some(Rc::new(move |_, _, key, command, _| match command { + Command::SetCostume => { + key.complete(Ok(())); + CommandStatus::Handled + } + _ => CommandStatus::UseDefault { key, command }, + })), + }; + let system = Rc::new(StdSystem::new_sync(BASE_URL.to_owned(), None, config, Arc::new(Clock::new(UtcOffset::UTC, None)))); let proj = get_running_project(include_str!("projects/costumes.xml"), system); proj.mutate(|mc, proj| { run_till_term(mc, &mut *proj.proj.borrow_mut(mc)).unwrap();