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:
parent
fc1ce7692b
commit
9b81a92516
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue