2019-09-17 04:04:38 +00:00
|
|
|
//! Activation records
|
|
|
|
|
2019-10-21 22:37:04 +00:00
|
|
|
use crate::avm1::return_value::ReturnValue;
|
2019-10-06 21:45:14 +00:00
|
|
|
use crate::avm1::scope::Scope;
|
2019-12-06 18:24:36 +00:00
|
|
|
use crate::avm1::{Avm1, Error, Object, Value};
|
2019-10-03 02:07:00 +00:00
|
|
|
use crate::context::UpdateContext;
|
2019-12-19 23:42:26 +00:00
|
|
|
use crate::display_object::DisplayObject;
|
2019-10-06 21:45:14 +00:00
|
|
|
use crate::tag_utils::SwfSlice;
|
|
|
|
use gc_arena::{GcCell, MutationContext};
|
2019-10-12 00:54:52 +00:00
|
|
|
use smallvec::SmallVec;
|
2019-10-06 21:45:14 +00:00
|
|
|
use std::cell::{Ref, RefMut};
|
|
|
|
use std::sync::Arc;
|
2019-10-12 00:23:03 +00:00
|
|
|
|
|
|
|
/// Represents a particular register set.
|
2019-10-12 00:54:52 +00:00
|
|
|
///
|
2019-10-12 00:23:03 +00:00
|
|
|
/// This type exists primarily because SmallVec isn't garbage-collectable.
|
|
|
|
#[derive(Clone)]
|
2019-10-12 00:54:52 +00:00
|
|
|
pub struct RegisterSet<'gc>(SmallVec<[Value<'gc>; 8]>);
|
2019-10-12 00:23:03 +00:00
|
|
|
|
|
|
|
unsafe impl<'gc> gc_arena::Collect for RegisterSet<'gc> {
|
|
|
|
#[inline]
|
|
|
|
fn trace(&self, cc: gc_arena::CollectionContext) {
|
|
|
|
for register in &self.0 {
|
|
|
|
register.trace(cc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> RegisterSet<'gc> {
|
|
|
|
/// Create a new register set with a given number of specified registers.
|
2019-10-12 00:54:52 +00:00
|
|
|
///
|
2019-10-12 00:23:03 +00:00
|
|
|
/// The given registers will be set to `undefined`.
|
|
|
|
pub fn new(num: u8) -> Self {
|
|
|
|
Self(smallvec![Value::Undefined; num as usize])
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a reference to a given register, if it exists.
|
|
|
|
pub fn get(&self, num: u8) -> Option<&Value<'gc>> {
|
|
|
|
self.0.get(num as usize)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a mutable reference to a given register, if it exists.
|
|
|
|
pub fn get_mut(&mut self, num: u8) -> Option<&mut Value<'gc>> {
|
|
|
|
self.0.get_mut(num as usize)
|
|
|
|
}
|
2019-10-13 22:41:07 +00:00
|
|
|
|
|
|
|
pub fn len(&self) -> u8 {
|
|
|
|
self.0.len() as u8
|
|
|
|
}
|
2019-10-12 00:23:03 +00:00
|
|
|
}
|
2019-09-17 04:04:38 +00:00
|
|
|
|
|
|
|
/// Represents a single activation of a given AVM1 function or keyframe.
|
|
|
|
pub struct Activation<'gc> {
|
|
|
|
/// Represents the SWF version of a given function.
|
2019-10-06 21:45:14 +00:00
|
|
|
///
|
2019-09-17 04:04:38 +00:00
|
|
|
/// Certain AVM1 operations change behavior based on the version of the SWF
|
|
|
|
/// file they were defined in. For example, case sensitivity changes based
|
|
|
|
/// on the SWF version.
|
|
|
|
swf_version: u8,
|
|
|
|
|
|
|
|
/// Action data being executed by the reader below.
|
|
|
|
data: SwfSlice,
|
|
|
|
|
|
|
|
/// The current location of the instruction stream being executed.
|
|
|
|
pc: usize,
|
|
|
|
|
|
|
|
/// All defined local variables in this stack frame.
|
|
|
|
scope: GcCell<'gc, Scope<'gc>>,
|
2019-09-19 01:55:28 +00:00
|
|
|
|
2019-11-03 22:43:28 +00:00
|
|
|
/// The currently in use constant pool.
|
|
|
|
constant_pool: GcCell<'gc, Vec<String>>,
|
|
|
|
|
2019-09-19 01:55:28 +00:00
|
|
|
/// The immutable value of `this`.
|
2019-12-06 18:24:36 +00:00
|
|
|
this: Object<'gc>,
|
2019-09-21 22:37:44 +00:00
|
|
|
|
|
|
|
/// The arguments this function was called by.
|
2019-12-06 18:24:36 +00:00
|
|
|
arguments: Option<Object<'gc>>,
|
2019-09-23 02:47:55 +00:00
|
|
|
|
2019-10-30 18:36:45 +00:00
|
|
|
/// The return value of the activation.
|
|
|
|
return_value: Option<Value<'gc>>,
|
|
|
|
|
2019-09-23 02:47:55 +00:00
|
|
|
/// Indicates if this activation object represents a function or embedded
|
|
|
|
/// block (e.g. ActionWith).
|
2019-09-27 00:02:40 +00:00
|
|
|
is_function: bool,
|
|
|
|
|
|
|
|
/// Local registers, if any.
|
2019-10-06 21:45:14 +00:00
|
|
|
///
|
2019-09-27 00:02:40 +00:00
|
|
|
/// None indicates a function executing out of the global register set.
|
|
|
|
/// Some indicates the existence of local registers, even if none exist.
|
|
|
|
/// i.e. None(Vec::new()) means no registers should exist at all.
|
2019-10-06 21:45:14 +00:00
|
|
|
///
|
2019-09-27 00:02:40 +00:00
|
|
|
/// Registers are numbered from 1; r0 does not exist. Therefore this vec,
|
|
|
|
/// while nominally starting from zero, actually starts from r1.
|
2019-10-06 21:45:14 +00:00
|
|
|
///
|
2019-09-27 00:02:40 +00:00
|
|
|
/// Registers are stored in a `GcCell` so that rescopes (e.g. with) use the
|
|
|
|
/// same register set.
|
2019-10-12 00:23:03 +00:00
|
|
|
local_registers: Option<GcCell<'gc, RegisterSet<'gc>>>,
|
2019-10-03 02:07:00 +00:00
|
|
|
|
2019-10-22 19:01:08 +00:00
|
|
|
/// Flags that the current activation frame is being executed and has a
|
|
|
|
/// reader object copied from it. Taking out two readers on the same
|
|
|
|
/// activation frame is a programming error.
|
|
|
|
is_executing: bool,
|
2019-12-19 23:42:26 +00:00
|
|
|
|
|
|
|
/// The base clip of this stack frame.
|
|
|
|
/// This will be the movieclip that contains the bytecode.
|
|
|
|
base_clip: DisplayObject<'gc>,
|
|
|
|
|
|
|
|
/// The current target display object of this stack frame.
|
|
|
|
/// This can be changed with `tellTarget` (via `ActionSetTarget` and `ActionSetTarget2`).
|
|
|
|
target_clip: Option<DisplayObject<'gc>>,
|
2019-09-17 04:04:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<'gc> gc_arena::Collect for Activation<'gc> {
|
|
|
|
#[inline]
|
|
|
|
fn trace(&self, cc: gc_arena::CollectionContext) {
|
|
|
|
self.scope.trace(cc);
|
2019-12-17 00:14:49 +00:00
|
|
|
self.constant_pool.trace(cc);
|
2019-09-21 22:37:44 +00:00
|
|
|
self.this.trace(cc);
|
|
|
|
self.arguments.trace(cc);
|
2019-10-30 18:36:45 +00:00
|
|
|
self.return_value.trace(cc);
|
2019-09-27 00:02:40 +00:00
|
|
|
self.local_registers.trace(cc);
|
2019-12-19 23:42:26 +00:00
|
|
|
self.base_clip.trace(cc);
|
|
|
|
self.target_clip.trace(cc);
|
2019-09-17 04:04:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> Activation<'gc> {
|
2019-10-06 21:45:14 +00:00
|
|
|
pub fn from_action(
|
|
|
|
swf_version: u8,
|
|
|
|
code: SwfSlice,
|
|
|
|
scope: GcCell<'gc, Scope<'gc>>,
|
2019-11-03 22:43:28 +00:00
|
|
|
constant_pool: GcCell<'gc, Vec<String>>,
|
2019-12-19 23:42:26 +00:00
|
|
|
base_clip: DisplayObject<'gc>,
|
2019-12-06 18:24:36 +00:00
|
|
|
this: Object<'gc>,
|
|
|
|
arguments: Option<Object<'gc>>,
|
2019-10-06 21:45:14 +00:00
|
|
|
) -> Activation<'gc> {
|
2019-09-17 04:04:38 +00:00
|
|
|
Activation {
|
2019-10-06 21:45:14 +00:00
|
|
|
swf_version,
|
2019-09-17 04:04:38 +00:00
|
|
|
data: code,
|
|
|
|
pc: 0,
|
2019-10-06 21:45:14 +00:00
|
|
|
scope,
|
2019-11-03 22:43:28 +00:00
|
|
|
constant_pool,
|
2019-12-19 23:42:26 +00:00
|
|
|
base_clip,
|
|
|
|
target_clip: Some(base_clip),
|
2019-10-06 21:45:14 +00:00
|
|
|
this,
|
|
|
|
arguments,
|
2019-10-30 18:36:45 +00:00
|
|
|
return_value: None,
|
2019-09-27 00:02:40 +00:00
|
|
|
is_function: false,
|
2019-10-06 21:45:14 +00:00
|
|
|
local_registers: None,
|
2019-10-22 19:01:08 +00:00
|
|
|
is_executing: false,
|
2019-09-23 02:47:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-06 21:45:14 +00:00
|
|
|
pub fn from_function(
|
|
|
|
swf_version: u8,
|
|
|
|
code: SwfSlice,
|
|
|
|
scope: GcCell<'gc, Scope<'gc>>,
|
2019-11-03 22:43:28 +00:00
|
|
|
constant_pool: GcCell<'gc, Vec<String>>,
|
2019-12-19 23:42:26 +00:00
|
|
|
base_clip: DisplayObject<'gc>,
|
2019-12-06 18:24:36 +00:00
|
|
|
this: Object<'gc>,
|
|
|
|
arguments: Option<Object<'gc>>,
|
2019-10-06 21:45:14 +00:00
|
|
|
) -> Activation<'gc> {
|
2019-09-23 02:47:55 +00:00
|
|
|
Activation {
|
2019-10-06 21:45:14 +00:00
|
|
|
swf_version,
|
2019-09-23 02:47:55 +00:00
|
|
|
data: code,
|
|
|
|
pc: 0,
|
2019-10-06 21:45:14 +00:00
|
|
|
scope,
|
2019-11-03 22:43:28 +00:00
|
|
|
constant_pool,
|
2019-12-19 23:42:26 +00:00
|
|
|
base_clip,
|
|
|
|
target_clip: Some(base_clip),
|
2019-10-06 21:45:14 +00:00
|
|
|
this,
|
|
|
|
arguments,
|
2019-10-30 18:36:45 +00:00
|
|
|
return_value: None,
|
2019-09-27 00:02:40 +00:00
|
|
|
is_function: true,
|
2019-10-06 21:45:14 +00:00
|
|
|
local_registers: None,
|
2019-10-22 19:01:08 +00:00
|
|
|
is_executing: false,
|
2019-09-17 04:04:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-04 02:42:32 +00:00
|
|
|
/// Construct an empty stack frame with no code.
|
2019-10-06 21:45:14 +00:00
|
|
|
///
|
2019-12-16 02:10:28 +00:00
|
|
|
/// This is used by tests and by callback methods (`onEnterFrame`) to create a base
|
|
|
|
/// activation frame with access to the global context.
|
2019-10-06 21:45:14 +00:00
|
|
|
pub fn from_nothing(
|
|
|
|
swf_version: u8,
|
2019-12-06 18:24:36 +00:00
|
|
|
globals: Object<'gc>,
|
2019-10-06 21:45:14 +00:00
|
|
|
mc: MutationContext<'gc, '_>,
|
2019-12-19 23:42:26 +00:00
|
|
|
base_clip: DisplayObject<'gc>,
|
2019-10-06 21:45:14 +00:00
|
|
|
) -> Activation<'gc> {
|
2019-10-04 02:42:32 +00:00
|
|
|
let global_scope = GcCell::allocate(mc, Scope::from_global_object(globals));
|
|
|
|
let child_scope = GcCell::allocate(mc, Scope::new_local_scope(global_scope, mc));
|
2019-11-03 22:43:28 +00:00
|
|
|
let empty_constant_pool = GcCell::allocate(mc, Vec::new());
|
2019-10-04 02:42:32 +00:00
|
|
|
|
|
|
|
Activation {
|
2019-10-06 21:45:14 +00:00
|
|
|
swf_version,
|
2019-10-04 02:42:32 +00:00
|
|
|
data: SwfSlice {
|
|
|
|
data: Arc::new(Vec::new()),
|
|
|
|
start: 0,
|
2019-10-06 21:45:14 +00:00
|
|
|
end: 0,
|
2019-10-04 02:42:32 +00:00
|
|
|
},
|
|
|
|
pc: 0,
|
|
|
|
scope: child_scope,
|
2019-11-03 22:43:28 +00:00
|
|
|
constant_pool: empty_constant_pool,
|
2019-12-19 23:42:26 +00:00
|
|
|
base_clip,
|
|
|
|
target_clip: Some(base_clip),
|
2019-10-04 02:42:32 +00:00
|
|
|
this: globals,
|
|
|
|
arguments: None,
|
2019-10-30 18:36:45 +00:00
|
|
|
return_value: None,
|
2019-10-04 02:42:32 +00:00
|
|
|
is_function: false,
|
2019-10-06 21:45:14 +00:00
|
|
|
local_registers: None,
|
2019-10-22 19:01:08 +00:00
|
|
|
is_executing: false,
|
2019-10-04 02:42:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 01:10:49 +00:00
|
|
|
/// Create a new activation to run a block of code with a given scope.
|
|
|
|
pub fn to_rescope(&self, code: SwfSlice, scope: GcCell<'gc, Scope<'gc>>) -> Self {
|
|
|
|
Activation {
|
|
|
|
swf_version: self.swf_version,
|
|
|
|
data: code,
|
|
|
|
pc: 0,
|
2019-10-06 21:45:14 +00:00
|
|
|
scope,
|
2019-11-03 22:43:28 +00:00
|
|
|
constant_pool: self.constant_pool,
|
2019-12-19 23:42:26 +00:00
|
|
|
base_clip: self.base_clip,
|
|
|
|
target_clip: self.target_clip,
|
2019-09-23 01:10:49 +00:00
|
|
|
this: self.this,
|
2019-09-23 02:47:55 +00:00
|
|
|
arguments: self.arguments,
|
2019-10-30 18:36:45 +00:00
|
|
|
return_value: None,
|
2019-09-27 00:02:40 +00:00
|
|
|
is_function: false,
|
2019-10-06 21:45:14 +00:00
|
|
|
local_registers: self.local_registers,
|
2019-10-22 19:01:08 +00:00
|
|
|
is_executing: false,
|
2019-09-23 01:10:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-17 04:04:38 +00:00
|
|
|
/// Returns the SWF version of the action or function being executed.
|
|
|
|
pub fn swf_version(&self) -> u8 {
|
|
|
|
self.swf_version
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the data this stack frame executes from.
|
|
|
|
pub fn data(&self) -> SwfSlice {
|
|
|
|
self.data.clone()
|
|
|
|
}
|
|
|
|
|
2019-09-23 01:10:49 +00:00
|
|
|
/// Change the data being executed.
|
2019-12-06 18:24:36 +00:00
|
|
|
#[allow(dead_code)]
|
2019-09-23 01:10:49 +00:00
|
|
|
pub fn set_data(&mut self, new_data: SwfSlice) {
|
|
|
|
self.data = new_data;
|
|
|
|
}
|
|
|
|
|
2019-09-17 04:04:38 +00:00
|
|
|
/// Determines if a stack frame references the same function as a given
|
|
|
|
/// SwfSlice.
|
2019-12-06 18:24:36 +00:00
|
|
|
#[allow(dead_code)]
|
2019-09-17 04:04:38 +00:00
|
|
|
pub fn is_identical_fn(&self, other: &SwfSlice) -> bool {
|
|
|
|
Arc::ptr_eq(&self.data.data, &other.data)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a mutable reference to the current data offset.
|
|
|
|
pub fn pc(&self) -> usize {
|
|
|
|
self.pc
|
|
|
|
}
|
|
|
|
/// Change the current PC.
|
|
|
|
pub fn set_pc(&mut self, new_pc: usize) {
|
|
|
|
self.pc = new_pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns AVM local variable scope.
|
|
|
|
pub fn scope(&self) -> Ref<Scope<'gc>> {
|
|
|
|
self.scope.read()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns AVM local variable scope for mutation.
|
2019-12-06 18:24:36 +00:00
|
|
|
#[allow(dead_code)]
|
2019-09-17 04:04:38 +00:00
|
|
|
pub fn scope_mut(&mut self, mc: MutationContext<'gc, '_>) -> RefMut<Scope<'gc>> {
|
|
|
|
self.scope.write(mc)
|
|
|
|
}
|
|
|
|
|
2019-09-22 01:41:30 +00:00
|
|
|
/// Returns AVM local variable scope for reference.
|
|
|
|
pub fn scope_cell(&self) -> GcCell<'gc, Scope<'gc>> {
|
2019-10-06 21:45:14 +00:00
|
|
|
self.scope
|
2019-09-22 01:41:30 +00:00
|
|
|
}
|
|
|
|
|
2019-10-04 23:27:08 +00:00
|
|
|
/// Completely replace the current scope with a new one.
|
|
|
|
pub fn set_scope(&mut self, scope: GcCell<'gc, Scope<'gc>>) {
|
|
|
|
self.scope = scope;
|
|
|
|
}
|
|
|
|
|
2019-12-19 23:42:26 +00:00
|
|
|
/// Gets the base clip of this stack frame.
|
|
|
|
/// This is the movie clip that contains the executing bytecode.
|
|
|
|
pub fn base_clip(&self) -> DisplayObject<'gc> {
|
|
|
|
self.base_clip
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the current target clip of this stack frame.
|
|
|
|
/// This is the movie clip to which `GotoFrame` and other actions apply.
|
|
|
|
/// Changed via `ActionSetTarget`/`ActionSetTarget2`.
|
|
|
|
pub fn target_clip(&self) -> Option<DisplayObject<'gc>> {
|
|
|
|
self.target_clip
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Changes the target clip.
|
|
|
|
pub fn set_target_clip(&mut self, value: Option<DisplayObject<'gc>>) {
|
|
|
|
self.target_clip = value;
|
|
|
|
}
|
|
|
|
|
2020-01-07 03:52:02 +00:00
|
|
|
/// Indicates whether or not the end of this scope should return a value.
|
|
|
|
pub fn can_return(&self) -> bool {
|
2019-09-23 02:47:55 +00:00
|
|
|
self.is_function
|
|
|
|
}
|
|
|
|
|
2019-09-17 04:04:38 +00:00
|
|
|
/// Resolve a particular named local variable within this activation.
|
2019-10-19 02:53:29 +00:00
|
|
|
///
|
|
|
|
/// Because scopes are object chains, the same rules for `Object::get`
|
2019-10-26 03:21:14 +00:00
|
|
|
/// still apply here.
|
2019-10-10 02:58:53 +00:00
|
|
|
pub fn resolve(
|
|
|
|
&self,
|
|
|
|
name: &str,
|
|
|
|
avm: &mut Avm1<'gc>,
|
2019-10-27 18:58:30 +00:00
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
2019-10-26 03:21:14 +00:00
|
|
|
) -> Result<ReturnValue<'gc>, Error> {
|
2019-09-19 01:55:28 +00:00
|
|
|
if name == "this" {
|
2019-10-31 00:25:52 +00:00
|
|
|
return Ok(Value::Object(self.this).into());
|
2019-09-19 01:55:28 +00:00
|
|
|
}
|
|
|
|
|
2019-09-21 22:37:44 +00:00
|
|
|
if name == "arguments" && self.arguments.is_some() {
|
2019-10-31 00:25:52 +00:00
|
|
|
return Ok(Value::Object(self.arguments.unwrap()).into());
|
2019-09-21 22:37:44 +00:00
|
|
|
}
|
|
|
|
|
2019-10-10 02:58:53 +00:00
|
|
|
self.scope().resolve(name, avm, context, self.this)
|
2019-09-17 04:04:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if a particular property in the scope chain is defined.
|
|
|
|
pub fn is_defined(&self, name: &str) -> bool {
|
2019-09-19 01:55:28 +00:00
|
|
|
if name == "this" {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-09-21 22:37:44 +00:00
|
|
|
if name == "arguments" && self.arguments.is_some() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-09-17 04:04:38 +00:00
|
|
|
self.scope().is_defined(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Define a named local variable within this activation.
|
2019-10-21 11:00:28 +00:00
|
|
|
pub fn define(&self, name: &str, value: impl Into<Value<'gc>>, mc: MutationContext<'gc, '_>) {
|
2019-09-17 04:04:38 +00:00
|
|
|
self.scope().define(name, value, mc)
|
|
|
|
}
|
2019-09-22 01:41:30 +00:00
|
|
|
|
|
|
|
/// Returns value of `this` as a reference.
|
2019-12-06 18:24:36 +00:00
|
|
|
pub fn this_cell(&self) -> Object<'gc> {
|
2019-09-22 01:41:30 +00:00
|
|
|
self.this
|
|
|
|
}
|
2019-10-13 22:41:07 +00:00
|
|
|
|
|
|
|
/// Returns true if this activation has a given local register ID.
|
|
|
|
pub fn has_local_register(&self, id: u8) -> bool {
|
|
|
|
self.local_registers
|
2019-10-16 00:10:34 +00:00
|
|
|
.map(|rs| id < rs.read().len())
|
2019-10-13 22:41:07 +00:00
|
|
|
.unwrap_or(false)
|
2019-09-27 00:02:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-29 03:11:03 +00:00
|
|
|
pub fn allocate_local_registers(&mut self, num: u8, mc: MutationContext<'gc, '_>) {
|
2019-10-13 22:41:07 +00:00
|
|
|
self.local_registers = match num {
|
|
|
|
0 => None,
|
|
|
|
num => Some(GcCell::allocate(mc, RegisterSet::new(num))),
|
|
|
|
};
|
2019-09-29 03:11:03 +00:00
|
|
|
}
|
|
|
|
|
2019-09-27 00:02:40 +00:00
|
|
|
/// Retrieve a local register.
|
2019-10-13 22:41:07 +00:00
|
|
|
pub fn local_register(&self, id: u8) -> Option<Value<'gc>> {
|
2019-09-27 00:02:40 +00:00
|
|
|
if let Some(local_registers) = self.local_registers {
|
2019-10-13 22:41:07 +00:00
|
|
|
local_registers.read().get(id).cloned()
|
2019-09-27 00:02:40 +00:00
|
|
|
} else {
|
2019-10-13 22:41:07 +00:00
|
|
|
None
|
2019-09-27 00:02:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a local register.
|
2019-10-21 11:00:28 +00:00
|
|
|
pub fn set_local_register(
|
|
|
|
&mut self,
|
|
|
|
id: u8,
|
|
|
|
value: impl Into<Value<'gc>>,
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
) {
|
2019-09-27 00:02:40 +00:00
|
|
|
if let Some(ref mut local_registers) = self.local_registers {
|
2019-10-12 00:23:03 +00:00
|
|
|
if let Some(r) = local_registers.write(mc).get_mut(id) {
|
2019-10-21 11:00:28 +00:00
|
|
|
*r = value.into();
|
2019-10-06 21:45:14 +00:00
|
|
|
}
|
2019-09-27 00:02:40 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-03 02:07:00 +00:00
|
|
|
|
2019-11-03 22:43:28 +00:00
|
|
|
pub fn constant_pool(&self) -> GcCell<'gc, Vec<String>> {
|
|
|
|
self.constant_pool
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_constant_pool(&mut self, constant_pool: GcCell<'gc, Vec<String>>) {
|
|
|
|
self.constant_pool = constant_pool;
|
|
|
|
}
|
|
|
|
|
2019-10-22 19:01:08 +00:00
|
|
|
/// Attempts to lock the activation frame for execution.
|
|
|
|
///
|
2019-11-14 20:06:54 +00:00
|
|
|
/// If this frame is already executing, that is an error condition.
|
|
|
|
pub fn lock(&mut self) -> Result<(), Error> {
|
2019-10-22 19:01:08 +00:00
|
|
|
if self.is_executing {
|
2019-11-14 20:06:54 +00:00
|
|
|
return Err("Attempted to execute the same frame twice".into());
|
2019-10-22 19:01:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.is_executing = true;
|
2019-11-14 20:06:54 +00:00
|
|
|
|
|
|
|
Ok(())
|
2019-10-22 19:01:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Unlock the activation object. This allows future execution to run on it
|
|
|
|
/// again.
|
|
|
|
pub fn unlock_execution(&mut self) {
|
|
|
|
self.is_executing = false;
|
|
|
|
}
|
2019-10-30 18:36:45 +00:00
|
|
|
|
|
|
|
/// Retrieve the return value from a completed activation, if the function
|
|
|
|
/// has already returned.
|
2019-12-06 18:24:36 +00:00
|
|
|
#[allow(dead_code)]
|
2019-10-30 18:36:45 +00:00
|
|
|
pub fn return_value(&self) -> Option<Value<'gc>> {
|
|
|
|
self.return_value.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the return value.
|
|
|
|
pub fn set_return_value(&mut self, value: Value<'gc>) {
|
|
|
|
self.return_value = Some(value);
|
|
|
|
}
|
2019-10-06 21:45:14 +00:00
|
|
|
}
|