Fix a number of bugs preventing the with-scope test from working at all:

1. We no longer implicitly return Undefined unless we're specifically returning from a function (this also keeps actions from filling the stack with Undefined)
2. With scopes are now always inserted behind the current set of locals rather than overriding them
3. `ActionSubtract` now subtracts (instead of adds)
This commit is contained in:
David Wendt 2019-09-22 22:47:55 -04:00 committed by Mike Welsh
parent fc1ce7692b
commit 9b81a92516
3 changed files with 62 additions and 9 deletions

View File

@ -124,7 +124,7 @@ impl<'gc> Avm1<'gc> {
args: GcCell<'gc, Object<'gc>>,
action_context: &mut ActionContext<'_, 'gc, '_>) {
let child_scope = GcCell::allocate(action_context.gc_context, Scope::new_local_scope(avm1func.scope(), action_context.gc_context));
self.stack_frames.push(Activation::from_action(avm1func.swf_version(), avm1func.data(), child_scope, this, Some(args)));
self.stack_frames.push(Activation::from_function(avm1func.swf_version(), avm1func.data(), child_scope, this, Some(args)));
}
/// Retrieve the current AVM execution frame.
@ -193,8 +193,11 @@ impl<'gc> Avm1<'gc> {
if reader.pos() >= (data.end - data.start) {
//Executing beyond the end of a function constitutes an implicit return.
if self.current_stack_frame().unwrap().can_implicit_return() {
self.push(Value::Undefined);
}
self.retire_stack_frame();
self.push(Value::Undefined);
} else if let Some(action) = reader.read_action()? {
let result = match action {
Action::Add => self.action_add(context),
@ -312,8 +315,11 @@ impl<'gc> Avm1<'gc> {
}
} else {
//The explicit end opcode was encountered so return here
if self.current_stack_frame().unwrap().can_implicit_return() {
self.push(Value::Undefined);
}
self.retire_stack_frame();
self.push(Value::Undefined);
}
Ok(())
@ -402,6 +408,7 @@ impl<'gc> Avm1<'gc> {
// ECMA-262 s. 11.6.1
let a = self.pop()?;
let b = self.pop()?;
// TODO(Herschel):
if let Value::String(a) = a {
let mut s = b.into_string();
@ -1538,9 +1545,8 @@ impl<'gc> Avm1<'gc> {
fn action_with(&mut self, context: &mut ActionContext<'_, 'gc, '_>, actions: &[u8]) -> Result<(), Error> {
let object = self.pop()?.as_object()?;
let block = self.current_stack_frame().unwrap().data().to_subslice(actions).unwrap();
let with_scope = Scope::new(self.current_stack_frame().unwrap().scope_cell(), scope::ScopeClass::With, object);
let scope_cell = GcCell::allocate(context.gc_context, with_scope);
let new_activation = self.current_stack_frame().unwrap().to_rescope(block, scope_cell);
let with_scope = Scope::new_with_scope(self.current_stack_frame().unwrap().scope_cell(), object, context.gc_context);
let new_activation = self.current_stack_frame().unwrap().to_rescope(block, with_scope);
self.stack_frames.push(new_activation);
Ok(())
}

View File

@ -31,7 +31,11 @@ pub struct Activation<'gc> {
this: GcCell<'gc, Object<'gc>>,
/// The arguments this function was called by.
arguments: Option<GcCell<'gc, Object<'gc>>>
arguments: Option<GcCell<'gc, Object<'gc>>>,
/// Indicates if this activation object represents a function or embedded
/// block (e.g. ActionWith).
is_function: bool
}
unsafe impl<'gc> gc_arena::Collect for Activation<'gc> {
@ -51,7 +55,20 @@ impl<'gc> Activation<'gc> {
pc: 0,
scope: scope,
this: this,
arguments: arguments
arguments: arguments,
is_function: false
}
}
pub fn from_function(swf_version: u8, code: SwfSlice, scope: GcCell<'gc, Scope<'gc>>, this: GcCell<'gc, Object<'gc>>, arguments: Option<GcCell<'gc, Object<'gc>>>) -> Activation<'gc> {
Activation {
swf_version: swf_version,
data: code,
pc: 0,
scope: scope,
this: this,
arguments: arguments,
is_function: true
}
}
@ -63,7 +80,8 @@ impl<'gc> Activation<'gc> {
pc: 0,
scope: scope,
this: self.this,
arguments: self.arguments
arguments: self.arguments,
is_function: false
}
}
@ -113,6 +131,12 @@ impl<'gc> Activation<'gc> {
self.scope.clone()
}
/// Indicates whether or not the end of this scope should be handled as an
/// implicit function return or the end of a block.
pub fn can_implicit_return(&self) -> bool {
self.is_function
}
/// Resolve a particular named local variable within this activation.
pub fn resolve(&self, name: &str) -> Value<'gc> {
if name == "this" {

View File

@ -92,6 +92,29 @@ impl<'gc> Scope<'gc> {
}
}
/// Construct a with scope to be used as the scope during a with block.
///
/// A with block inserts the values of a particular object into the scope
/// of currently running code, while still maintaining the same local
/// scope. This requires some scope chain juggling.
pub fn new_with_scope(locals: GcCell<'gc, Self>,
with_object: GcCell<'gc, Object<'gc>>,
mc: MutationContext<'gc, '_>) -> GcCell<'gc, Self> {
let parent_scope = locals.read().parent.clone();
let local_values = locals.read().values.clone();
let with_scope = GcCell::allocate(mc, Scope {
parent: parent_scope,
class: ScopeClass::With,
values: with_object
});
GcCell::allocate(mc, Scope {
parent: Some(with_scope),
class: ScopeClass::Local,
values: local_values
})
}
/// Construct an arbitrary scope
pub fn new(parent: GcCell<'gc, Self>, class: ScopeClass, with_object: GcCell<'gc, Object<'gc>>) -> Scope<'gc> {
Scope {