Implement register underflow behavior.
This has the side effect of letting us remove the `Option` on register_count since setting this to `0` is equivalent now. Furthermore, we can skip an allocation if a function requests no registers.
This commit is contained in:
parent
911cf64cb0
commit
7e2cf03789
|
@ -489,13 +489,14 @@ impl<'gc> Avm1<'gc> {
|
|||
pub fn current_register(&self, id: u8) -> Value<'gc> {
|
||||
if self
|
||||
.current_stack_frame()
|
||||
.map(|sf| sf.read().has_local_registers())
|
||||
.map(|sf| sf.read().has_local_register(id))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.current_stack_frame()
|
||||
.unwrap()
|
||||
.read()
|
||||
.local_register(id)
|
||||
.unwrap_or(Value::Undefined)
|
||||
} else {
|
||||
self.registers
|
||||
.get(id as usize)
|
||||
|
@ -515,7 +516,7 @@ impl<'gc> Avm1<'gc> {
|
|||
) {
|
||||
if self
|
||||
.current_stack_frame()
|
||||
.map(|sf| sf.read().has_local_registers())
|
||||
.map(|sf| sf.read().has_local_register(id))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.current_stack_frame()
|
||||
|
|
|
@ -41,6 +41,10 @@ impl<'gc> RegisterSet<'gc> {
|
|||
pub fn get_mut(&mut self, num: u8) -> Option<&mut Value<'gc>> {
|
||||
self.0.get_mut(num as usize)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> u8 {
|
||||
self.0.len() as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a single activation of a given AVM1 function or keyframe.
|
||||
|
@ -275,25 +279,27 @@ impl<'gc> Activation<'gc> {
|
|||
pub fn this_cell(&self) -> GcCell<'gc, Object<'gc>> {
|
||||
self.this
|
||||
}
|
||||
/// Returns true if this function was called with a local register set.
|
||||
pub fn has_local_registers(&self) -> bool {
|
||||
self.local_registers.is_some()
|
||||
|
||||
/// Returns true if this activation has a given local register ID.
|
||||
pub fn has_local_register(&self, id: u8) -> bool {
|
||||
self.local_registers
|
||||
.map(|rs| id < rs.read().len() - 1)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn allocate_local_registers(&mut self, num: u8, mc: MutationContext<'gc, '_>) {
|
||||
self.local_registers = Some(GcCell::allocate(mc, RegisterSet::new(num)));
|
||||
self.local_registers = match num {
|
||||
0 => None,
|
||||
num => Some(GcCell::allocate(mc, RegisterSet::new(num))),
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieve a local register.
|
||||
pub fn local_register(&self, id: u8) -> Value<'gc> {
|
||||
pub fn local_register(&self, id: u8) -> Option<Value<'gc>> {
|
||||
if let Some(local_registers) = self.local_registers {
|
||||
local_registers
|
||||
.read()
|
||||
.get(id)
|
||||
.cloned()
|
||||
.unwrap_or(Value::Undefined)
|
||||
local_registers.read().get(id).cloned()
|
||||
} else {
|
||||
Value::Undefined
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,11 +29,8 @@ pub struct Avm1Function<'gc> {
|
|||
name: Option<String>,
|
||||
|
||||
/// The number of registers to allocate for this function's private register
|
||||
/// set.
|
||||
///
|
||||
/// If None, then no register set will be allocated and the preload options
|
||||
/// have no effect.
|
||||
register_count: Option<u8>,
|
||||
/// set. Any register beyond this ID will be served from the global one.
|
||||
register_count: u8,
|
||||
|
||||
preload_parent: bool,
|
||||
preload_root: bool,
|
||||
|
@ -75,7 +72,7 @@ impl<'gc> Avm1Function<'gc> {
|
|||
swf_version,
|
||||
data: actions,
|
||||
name,
|
||||
register_count: None,
|
||||
register_count: 0,
|
||||
preload_parent: false,
|
||||
preload_root: false,
|
||||
suppress_super: false,
|
||||
|
@ -115,7 +112,7 @@ impl<'gc> Avm1Function<'gc> {
|
|||
swf_version,
|
||||
data: actions,
|
||||
name,
|
||||
register_count: Some(swf_function.params.capacity() as u8),
|
||||
register_count: swf_function.params.capacity() as u8,
|
||||
preload_parent: swf_function.preload_parent,
|
||||
preload_root: swf_function.preload_root,
|
||||
suppress_super: swf_function.suppress_super,
|
||||
|
@ -142,7 +139,7 @@ impl<'gc> Avm1Function<'gc> {
|
|||
self.scope
|
||||
}
|
||||
|
||||
pub fn register_count(&self) -> Option<u8> {
|
||||
pub fn register_count(&self) -> u8 {
|
||||
self.register_count
|
||||
}
|
||||
}
|
||||
|
@ -215,52 +212,48 @@ impl<'gc> Executable<'gc> {
|
|||
Some(argcell),
|
||||
);
|
||||
|
||||
if let Some(register_count) = af.register_count() {
|
||||
frame.allocate_local_registers(register_count, ac.gc_context);
|
||||
frame.allocate_local_registers(af.register_count(), ac.gc_context);
|
||||
|
||||
let mut preload_r = 1;
|
||||
let mut preload_r = 1;
|
||||
|
||||
if af.preload_this {
|
||||
//TODO: What happens if you specify both suppress and
|
||||
//preload for this?
|
||||
frame.set_local_register(preload_r, Value::Object(this), ac.gc_context);
|
||||
preload_r += 1;
|
||||
}
|
||||
if af.preload_this {
|
||||
//TODO: What happens if you specify both suppress and
|
||||
//preload for this?
|
||||
frame.set_local_register(preload_r, Value::Object(this), ac.gc_context);
|
||||
preload_r += 1;
|
||||
}
|
||||
|
||||
if af.preload_arguments {
|
||||
//TODO: What happens if you specify both suppress and
|
||||
//preload for arguments?
|
||||
frame.set_local_register(preload_r, Value::Object(argcell), ac.gc_context);
|
||||
preload_r += 1;
|
||||
}
|
||||
if af.preload_arguments {
|
||||
//TODO: What happens if you specify both suppress and
|
||||
//preload for arguments?
|
||||
frame.set_local_register(preload_r, Value::Object(argcell), ac.gc_context);
|
||||
preload_r += 1;
|
||||
}
|
||||
|
||||
if af.preload_super {
|
||||
//TODO: super not implemented
|
||||
log::warn!(
|
||||
"Cannot preload super into register because it's not implemented"
|
||||
);
|
||||
//TODO: What happens if you specify both suppress and
|
||||
//preload for super?
|
||||
preload_r += 1;
|
||||
}
|
||||
if af.preload_super {
|
||||
//TODO: super not implemented
|
||||
log::warn!("Cannot preload super into register because it's not implemented");
|
||||
//TODO: What happens if you specify both suppress and
|
||||
//preload for super?
|
||||
preload_r += 1;
|
||||
}
|
||||
|
||||
if af.preload_root {
|
||||
frame.set_local_register(preload_r, avm.root_object(ac), ac.gc_context);
|
||||
preload_r += 1;
|
||||
}
|
||||
if af.preload_root {
|
||||
frame.set_local_register(preload_r, avm.root_object(ac), ac.gc_context);
|
||||
preload_r += 1;
|
||||
}
|
||||
|
||||
if af.preload_parent {
|
||||
frame.set_local_register(
|
||||
preload_r,
|
||||
child_scope.read().resolve("_parent", avm, ac, this),
|
||||
ac.gc_context,
|
||||
);
|
||||
preload_r += 1;
|
||||
}
|
||||
if af.preload_parent {
|
||||
frame.set_local_register(
|
||||
preload_r,
|
||||
child_scope.read().resolve("_parent", avm, ac, this),
|
||||
ac.gc_context,
|
||||
);
|
||||
preload_r += 1;
|
||||
}
|
||||
|
||||
if af.preload_global {
|
||||
frame.set_local_register(preload_r, avm.global_object(ac), ac.gc_context);
|
||||
}
|
||||
if af.preload_global {
|
||||
frame.set_local_register(preload_r, avm.global_object(ac), ac.gc_context);
|
||||
}
|
||||
|
||||
//TODO: What happens if the argument registers clash with the
|
||||
|
|
|
@ -53,6 +53,7 @@ swf_tests! {
|
|||
(delete, "avm1/delete", 3),
|
||||
(timeline_function_def, "avm1/timeline_function_def", 3),
|
||||
(root_global_parent, "avm1/root_global_parent", 3),
|
||||
(register_underflow, "avm1/register_underflow", 1),
|
||||
}
|
||||
|
||||
/// Loads an SWF and runs it through the Ruffle core for a number of frames.
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
global reg 0: 0
|
||||
global reg 1: 1
|
||||
global reg 2: 2
|
||||
global reg 3: 3
|
||||
Function start f(a), RegisterCount = 3, a => register2
|
||||
Function reg 0: undefined
|
||||
Function reg 1: undefined
|
||||
Function reg 2: 66
|
||||
Function reg 3: 3
|
||||
Changing registers to 9.
|
||||
Function end
|
||||
global reg 0: 0
|
||||
global reg 1: 1
|
||||
global reg 2: 2
|
||||
global reg 3: 9
|
Binary file not shown.
Loading…
Reference in New Issue