Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 35 additions & 54 deletions core/src/avm1/activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@ use gc_arena::{Gc, Mutation};
use indexmap::IndexMap;
use rand::Rng;
use ruffle_macros::istr;
use std::borrow::Cow;
use std::cell::Cell;
use std::cmp::min;
use std::fmt;
use std::rc::Rc;
use swf::avm1::read::Reader;
use swf::avm1::types::*;
use url::form_urlencoded;
Expand Down Expand Up @@ -64,7 +62,7 @@ enum FrameControl<'gc> {
#[derive(Clone)]
pub struct ActivationIdentifier<'a> {
parent: Option<&'a ActivationIdentifier<'a>>,
name: Cow<'static, str>,
name: &'a str,
depth: u16,
function_count: u16,
special_count: u8,
Expand All @@ -76,36 +74,36 @@ impl fmt::Display for ActivationIdentifier<'_> {
write!(f, "{parent} / ")?;
}

f.write_str(&self.name)?;
f.write_str(self.name)?;

Ok(())
}
}

impl<'a> ActivationIdentifier<'a> {
pub fn root<S: Into<Cow<'static, str>>>(name: S) -> Self {
pub fn root(name: &'a str) -> Self {
Self {
parent: None,
name: name.into(),
name,
depth: 0,
function_count: 0,
special_count: 0,
}
}

pub fn child<S: Into<Cow<'static, str>>>(&'a self, name: S) -> Self {
pub fn child(&'a self, name: &'a str) -> Self {
Self {
parent: Some(self),
name: name.into(),
name,
depth: self.depth + 1,
function_count: self.function_count,
special_count: self.special_count,
}
}

pub fn function<'gc, S: Into<Cow<'static, str>>>(
pub fn function<'gc>(
&'a self,
name: S,
name: &'a str,
reason: ExecutionReason,
max_recursion_depth: u16,
) -> Result<Self, Error<'gc>> {
Expand All @@ -125,7 +123,7 @@ impl<'a> ActivationIdentifier<'a> {
};
Ok(Self {
parent: Some(self),
name: name.into(),
name,
depth: self.depth + 1,
function_count,
special_count,
Expand Down Expand Up @@ -162,16 +160,8 @@ pub struct Activation<'a, 'gc: 'a> {

/// Local registers, if any.
///
/// 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.
///
/// Registers are numbered from 1; r0 does not exist. Therefore this vec,
/// while nominally starting from zero, actually starts from r1.
///
/// Registers are stored in a `Rc` so that rescopes (e.g. with) use the
/// same register set.
local_registers: Option<Rc<[Cell<Value<'gc>>]>>,
/// An empty slice indicates a function executing out of the global register set.
local_registers: &'a [Cell<Value<'gc>>],

/// The base clip of this stack frame.
/// This will be the MovieClip that contains the bytecode.
Expand Down Expand Up @@ -238,6 +228,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
base_clip: DisplayObject<'gc>,
this: Value<'gc>,
callee: Option<Object<'gc>>,
local_registers: &'a [Cell<Value<'gc>>],
) -> Self {
avm_debug!(context.avm1, "START {id}");
Self {
Expand All @@ -250,14 +241,14 @@ impl<'a, 'gc> Activation<'a, 'gc> {
target_clip: Some(base_clip),
this,
callee,
local_registers: None,
local_registers,
}
}

/// Create a new activation to run a block of code with a given scope.
pub fn with_new_scope<'b, S: Into<Cow<'static, str>>>(
pub fn with_new_scope<'b>(
&'b mut self,
name: S,
name: &'b str,
scope: Gc<'gc, Scope<'gc>>,
) -> Activation<'b, 'gc> {
let id = self.id.child(name);
Expand All @@ -272,7 +263,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
target_clip: self.target_clip,
this: self.this,
callee: self.callee,
local_registers: self.local_registers.clone(),
local_registers: self.local_registers,
}
}

Expand Down Expand Up @@ -301,7 +292,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
target_clip: Some(base_clip),
this: scope.locals_cell().into(),
callee: None,
local_registers: None,
local_registers: &[],
context,
}
}
Expand Down Expand Up @@ -337,9 +328,9 @@ impl<'a, 'gc> Activation<'a, 'gc> {
}

/// Add a stack frame that executes code in timeline scope
pub fn run_child_frame_for_action<S: Into<Cow<'static, str>>>(
pub fn run_child_frame_for_action(
&mut self,
name: S,
name: &str,
active_clip: DisplayObject<'gc>,
code: SwfSlice,
) -> Result<ReturnType<'gc>, Error<'gc>> {
Expand Down Expand Up @@ -367,14 +358,15 @@ impl<'a, 'gc> Activation<'a, 'gc> {
active_clip,
clip_obj.into(),
None,
&[],
);
child_activation.run_actions(code)
}

/// Add a stack frame that executes code in initializer scope.
pub fn run_with_child_frame_for_display_object<F, R, S: Into<Cow<'static, str>>>(
pub fn run_with_child_frame_for_display_object<F, R>(
&mut self,
name: S,
name: &str,
active_clip: DisplayObject<'gc>,
swf_version: u8,
function: F,
Expand Down Expand Up @@ -403,6 +395,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
active_clip,
clip_obj.into(),
None,
&[],
);
function(&mut activation)
}
Expand Down Expand Up @@ -2245,9 +2238,10 @@ impl<'a, 'gc> Activation<'a, 'gc> {
self.base_clip,
self.this,
self.callee,
&[],
);

activation.local_registers = self.local_registers.clone();
activation.local_registers = self.local_registers;

match catch_vars {
CatchVar::Var(name) => {
Expand Down Expand Up @@ -2399,13 +2393,11 @@ impl<'a, 'gc> Activation<'a, 'gc> {
/// Value::Undefined, which is also a valid register value.
pub fn current_register(&self, id: u8) -> Value<'gc> {
let id = id as usize;
if let Some(local_registers) = &self.local_registers {
if let Some(reg) = local_registers.get(id) {
return reg.get();
} else if self.context.player_version <= 10 {
// Old FP versions do not fall back to the global register set.
return Value::Undefined;
}
if let Some(reg) = self.local_registers.get(id) {
return reg.get();
} else if !self.local_registers.is_empty() && self.context.player_version <= 10 {
// Old FP versions do not fall back to the global register set.
return Value::Undefined;
}

self.context
Expand All @@ -2430,16 +2422,12 @@ impl<'a, 'gc> Activation<'a, 'gc> {
///
/// If a given local register does not exist, this function does nothing.
pub fn set_local_register(&mut self, id: u8, value: Value<'gc>) -> bool {
if let Some(local_registers) = &self.local_registers {
if let Some(reg) = local_registers.get(id as usize) {
reg.set(value);
true
} else {
// Old FP versions do not fall back to the global register set.
self.context.player_version <= 10
}
if let Some(reg) = self.local_registers.get(id as usize) {
reg.set(value);
true
} else {
false
// Old FP versions do not fall back to the global register set.
!self.local_registers.is_empty() && self.context.player_version <= 10
}
}

Expand Down Expand Up @@ -3053,13 +3041,6 @@ impl<'a, 'gc> Activation<'a, 'gc> {
self.this
}

pub fn allocate_local_registers(&mut self, num: u8) {
self.local_registers = match num {
0 => None,
num => Some((0..num).map(|_| Cell::new(Value::Undefined)).collect()),
};
}

pub fn constant_pool(&self) -> Gc<'gc, Vec<Value<'gc>>> {
self.constant_pool
}
Expand Down
12 changes: 8 additions & 4 deletions core/src/avm1/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::string::{AvmString, StringContext, SwfStrExt as _};
use crate::tag_utils::SwfSlice;
use gc_arena::{Collect, Gc, Mutation};
use ruffle_macros::istr;
use std::{borrow::Cow, num::NonZeroU8};
use std::{borrow::Cow, cell::Cell, num::NonZeroU8};
use swf::{avm1::types::FunctionFlags, SwfStr};

/// Represents a function defined in Ruffle's code.
Expand Down Expand Up @@ -341,20 +341,24 @@ impl<'gc> Avm1Function<'gc> {
this
};

let local_registers: Box<[_]> = (0..self.register_count())
.map(|_| Cell::new(Value::Undefined))
.collect();

let max_recursion_depth = activation.context.avm1.max_recursion_depth();
let mut frame = Activation::from_action(
activation.context,
activation.id.function(name, reason, max_recursion_depth)?,
activation.id.function(&name, reason, max_recursion_depth)?,
swf_version,
child_scope,
self.constant_pool,
base_clip,
local_this,
Some(callee),
&local_registers,
);

frame.allocate_local_registers(self.register_count());

// Local register #0 is always left free.
let mut preload_r = 1;
self.load_this(&mut frame, this, &mut preload_r);
self.load_arguments(&mut frame, args, arguments_caller, &mut preload_r);
Expand Down
16 changes: 8 additions & 8 deletions core/src/avm1/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use crate::string::{AvmString, StringContext};
use crate::tag_utils::SwfSlice;
use crate::{avm1, avm_debug};
use gc_arena::{Collect, Gc, Mutation};
use std::borrow::Cow;
use swf::avm1::read::Reader;
use tracing::instrument;

Expand Down Expand Up @@ -136,9 +135,9 @@ impl<'gc> Avm1<'gc> {
/// Add a stack frame that executes code in timeline scope
///
/// This creates a new frame stack.
pub fn run_stack_frame_for_action<S: Into<Cow<'static, str>>>(
pub fn run_stack_frame_for_action(
active_clip: DisplayObject<'gc>,
name: S,
name: &str,
code: SwfSlice,
context: &mut UpdateContext<'gc>,
) {
Expand Down Expand Up @@ -175,6 +174,7 @@ impl<'gc> Avm1<'gc> {
active_clip,
clip_obj.into(),
None,
&[],
);
if let Err(e) = child_activation.run_actions(code) {
root_error_handler(&mut child_activation, e);
Expand Down Expand Up @@ -213,6 +213,7 @@ impl<'gc> Avm1<'gc> {
active_clip,
clip_obj.into(),
None,
&[],
);
function(&mut activation)
}
Expand Down Expand Up @@ -259,6 +260,7 @@ impl<'gc> Avm1<'gc> {
active_clip,
clip_obj.into(),
None,
&[],
);
if let Err(e) = child_activation.run_actions(code) {
root_error_handler(&mut child_activation, e);
Expand All @@ -281,11 +283,9 @@ impl<'gc> Avm1<'gc> {
return;
}

let mut activation = Activation::from_nothing(
context,
ActivationIdentifier::root(name.to_string()),
active_clip,
);
let name_utf8 = &name.to_utf8_lossy();
let mut activation =
Activation::from_nothing(context, ActivationIdentifier::root(name_utf8), active_clip);

let _ = obj.call_method(name, args, &mut activation, ExecutionReason::Special);
}
Expand Down
3 changes: 2 additions & 1 deletion core/src/streams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,11 +1369,12 @@ impl<'gc> NetStream<'gc> {
match avm_object {
Some(NetStreamKind::Avm1(object)) => {
let avm_string_name = AvmString::new_utf8_bytes(context.gc(), variable_name);
let activation_name = format!("[FLV {avm_string_name}]");

let root = context.stage.root_clip().expect("root");
let mut activation = Avm1Activation::from_nothing(
context,
Avm1ActivationIdentifier::root(format!("[FLV {avm_string_name}]")),
Avm1ActivationIdentifier::root(&activation_name),
root,
);

Expand Down
Loading