Allow scope chain resolution to retrieve virtual properties

This commit is contained in:
David Wendt 2019-10-09 22:58:53 -04:00
parent a92190a456
commit 59dc35b8a4
6 changed files with 138 additions and 88 deletions

View File

@ -72,7 +72,7 @@ pub struct Avm1<'gc> {
globals: GcCell<'gc, Object<'gc>>,
/// All activation records for the current execution context.
stack_frames: Vec<Activation<'gc>>,
stack_frames: Vec<GcCell<'gc, Activation<'gc>>>,
/// The operand stack (shared across functions).
stack: Vec<Value<'gc>>,
@ -124,9 +124,10 @@ impl<'gc> Avm1<'gc> {
) -> HashMap<String, String> {
let mut form_values = HashMap::new();
let locals = self
.current_stack_frame_mut()
.current_stack_frame()
.unwrap()
.scope_mut(context.gc_context)
.read()
.scope()
.locals_cell();
for k in locals.read().get_keys() {
@ -161,37 +162,32 @@ impl<'gc> Avm1<'gc> {
action_context.gc_context,
Scope::new(global_scope, scope::ScopeClass::Target, clip_obj),
);
self.stack_frames.push(Activation::from_action(
swf_version,
code,
child_scope,
clip_obj,
None,
self.stack_frames.push(GcCell::allocate(
action_context.gc_context,
Activation::from_action(swf_version, code, child_scope, clip_obj, None),
));
}
/// Add a stack frame for any arbitrary code.
pub fn insert_stack_frame(&mut self, frame: Activation<'gc>) {
self.stack_frames.push(frame);
pub fn insert_stack_frame(
&mut self,
frame: Activation<'gc>,
context: &mut ActionContext<'_, 'gc, '_>,
) {
self.stack_frames
.push(GcCell::allocate(context.gc_context, frame));
}
/// Retrieve the current AVM execution frame.
///
/// Yields None if there is no stack frame.
pub fn current_stack_frame(&self) -> Option<&Activation<'gc>> {
self.stack_frames.last()
}
/// Retrieve the current AVM execution frame for mutation.
///
/// Yields None if there is no stack frame.
pub fn current_stack_frame_mut(&mut self) -> Option<&mut Activation<'gc>> {
self.stack_frames.last_mut()
pub fn current_stack_frame(&self) -> Option<GcCell<'gc, Activation<'gc>>> {
self.stack_frames.last().map(|ac| ac.clone())
}
/// Get the currently executing SWF version, if there is one.
fn current_swf_version(&self) -> Option<u8> {
self.current_stack_frame().map(|sf| sf.swf_version())
self.current_stack_frame().map(|sf| sf.read().swf_version())
}
/// Perform some action with the current stack frame's reader.
@ -209,9 +205,13 @@ impl<'gc> Avm1<'gc> {
/// It is incorrect to call this function multiple times in the same stack.
/// Doing so will result in any changes in duplicate readers being ignored.
/// Always pass the borrowed reader into functions that need it.
fn with_current_reader_mut<F, R>(&mut self, func: F) -> Option<R>
fn with_current_reader_mut<F, R>(
&mut self,
context: &mut ActionContext<'_, 'gc, '_>,
func: F,
) -> Option<R>
where
F: FnOnce(&mut Self, &mut Reader<'_>) -> R,
F: FnOnce(&mut Self, &mut Reader<'_>, &mut ActionContext<'_, 'gc, '_>) -> R,
{
if self.is_reading {
log::error!(
@ -220,21 +220,19 @@ impl<'gc> Avm1<'gc> {
}
self.is_reading = true;
let current_stack_id = self.stack_frames.len() - 1;
let (swf_version, data, pc) = self
.stack_frames
.last()
.map(|frame| (frame.swf_version(), frame.data(), frame.pc()))?;
let (frame_cell, swf_version, data, pc) = self.stack_frames.last().map(|frame| {
(
frame.clone(),
frame.read().swf_version(),
frame.read().data(),
frame.read().pc(),
)
})?;
let mut read = Reader::new(data.as_ref(), swf_version);
read.seek(pc.try_into().unwrap());
let r = func(self, &mut read);
//this took an hour of fighting borrowck to figure out was necessary
if let Some(new_stack) = self.stack_frames.get_mut(current_stack_id) {
if new_stack.is_identical_fn(&data) {
new_stack.set_pc(read.pos());
}
}
let r = func(self, &mut read, context);
frame_cell.write(context.gc_context).set_pc(read.pos());
self.is_reading = false;
@ -252,8 +250,10 @@ impl<'gc> Avm1<'gc> {
context: &mut ActionContext<'_, 'gc, '_>,
) -> Result<(), Error> {
while !self.stack_frames.is_empty() {
self.with_current_reader_mut(|this, r| this.do_next_action(context, r))
.unwrap()?;
self.with_current_reader_mut(context, |this, r, context| {
this.do_next_action(context, r)
})
.unwrap()?;
}
Ok(())
@ -265,11 +265,16 @@ impl<'gc> Avm1<'gc> {
context: &mut ActionContext<'_, 'gc, '_>,
reader: &mut Reader<'_>,
) -> Result<(), Error> {
let data = self.current_stack_frame().unwrap().data();
let data = self.current_stack_frame().unwrap().read().data();
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() {
if self
.current_stack_frame()
.unwrap()
.read()
.can_implicit_return()
{
self.push(Value::Undefined);
}
@ -396,7 +401,12 @@ impl<'gc> Avm1<'gc> {
}
} else {
//The explicit end opcode was encountered so return here
if self.current_stack_frame().unwrap().can_implicit_return() {
if self
.current_stack_frame()
.unwrap()
.read()
.can_implicit_return()
{
self.push(Value::Undefined);
}
@ -476,10 +486,13 @@ impl<'gc> Avm1<'gc> {
pub fn current_register(&self, id: u8) -> Value<'gc> {
if self
.current_stack_frame()
.map(|sf| sf.has_local_registers())
.map(|sf| sf.read().has_local_registers())
.unwrap_or(false)
{
self.current_stack_frame().unwrap().local_register(id)
self.current_stack_frame()
.unwrap()
.read()
.local_register(id)
} else {
self.registers
.get(id as usize)
@ -499,14 +512,13 @@ impl<'gc> Avm1<'gc> {
) {
if self
.current_stack_frame()
.map(|sf| sf.has_local_registers())
.map(|sf| sf.read().has_local_registers())
.unwrap_or(false)
{
self.current_stack_frame_mut().unwrap().set_local_register(
id,
value,
context.gc_context,
);
self.current_stack_frame()
.unwrap()
.write(context.gc_context)
.set_local_register(id, value, context.gc_context);
} else if let Some(v) = self.registers.get_mut(id as usize) {
*v = value;
}
@ -648,10 +660,11 @@ impl<'gc> Avm1<'gc> {
args.push(self.pop()?);
}
let target_fn = self
.current_stack_frame_mut()
.unwrap()
.resolve(fn_name.as_string()?);
let target_fn = self.stack_frames.last().unwrap().clone().read().resolve(
fn_name.as_string()?,
self,
context,
);
let this = context.active_clip.read().object().as_object()?.to_owned();
let return_value = target_fn.call(self, context, this, &args)?;
if let Some(instant_return) = return_value {
@ -741,15 +754,16 @@ impl<'gc> Avm1<'gc> {
params: &[&str],
actions: &[u8],
) -> Result<(), Error> {
let swf_version = self.current_stack_frame().unwrap().swf_version();
let swf_version = self.current_stack_frame().unwrap().read().swf_version();
let func_data = self
.current_stack_frame()
.unwrap()
.read()
.data()
.to_subslice(actions)
.unwrap();
let scope = Scope::new_closure_scope(
self.current_stack_frame().unwrap().scope_cell(),
self.current_stack_frame().unwrap().read().scope_cell(),
context.gc_context,
);
let func = Avm1Function::from_df1(swf_version, func_data, name, params, scope);
@ -760,8 +774,9 @@ impl<'gc> Avm1<'gc> {
if name == "" {
self.push(func_obj);
} else {
self.current_stack_frame_mut()
self.current_stack_frame()
.unwrap()
.read()
.define(name, func_obj, context.gc_context);
}
@ -773,15 +788,16 @@ impl<'gc> Avm1<'gc> {
context: &mut ActionContext<'_, 'gc, '_>,
action_func: &Function,
) -> Result<(), Error> {
let swf_version = self.current_stack_frame().unwrap().swf_version();
let swf_version = self.current_stack_frame().unwrap().read().swf_version();
let func_data = self
.current_stack_frame()
.unwrap()
.read()
.data()
.to_subslice(action_func.actions)
.unwrap();
let scope = Scope::new_closure_scope(
self.current_stack_frame().unwrap().scope_cell(),
self.current_stack_frame().unwrap().read().scope_cell(),
context.gc_context,
);
let func = Avm1Function::from_df2(swf_version, func_data, action_func, scope);
@ -792,7 +808,7 @@ impl<'gc> Avm1<'gc> {
if action_func.name == "" {
self.push(func_obj);
} else {
self.current_stack_frame_mut().unwrap().define(
self.current_stack_frame().unwrap().read().define(
action_func.name,
func_obj,
context.gc_context,
@ -808,7 +824,7 @@ impl<'gc> Avm1<'gc> {
) -> Result<(), Error> {
let value = self.pop()?;
let name = self.pop()?;
self.current_stack_frame_mut().unwrap().define(
self.current_stack_frame().unwrap().read().define(
name.as_string()?,
value,
context.gc_context,
@ -821,7 +837,7 @@ impl<'gc> Avm1<'gc> {
context: &mut ActionContext<'_, 'gc, '_>,
) -> Result<(), Error> {
let name = self.pop()?;
self.current_stack_frame_mut().unwrap().define(
self.current_stack_frame().unwrap().read().define(
name.as_string()?,
Value::Undefined,
context.gc_context,
@ -846,10 +862,11 @@ impl<'gc> Avm1<'gc> {
//Fun fact: This isn't in the Adobe SWF19 spec, but this opcode returns
//a boolean based on if the delete actually deleted something.
let did_exist = Value::Bool(self.current_stack_frame().unwrap().is_defined(name));
let did_exist = Value::Bool(self.current_stack_frame().unwrap().read().is_defined(name));
self.current_stack_frame()
.unwrap()
.read()
.scope()
.delete(name, context.gc_context);
self.push(did_exist);
@ -876,11 +893,16 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
fn action_enumerate(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
fn action_enumerate(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> {
let name = self.pop()?;
let name = name.as_string()?;
self.push(Value::Null); // Sentinel that indicates end of enumeration
let ob = match self.current_stack_frame().unwrap().resolve(name) {
let ob = match self
.current_stack_frame()
.unwrap()
.read()
.resolve(name, self, context)
{
Value::Object(ob) => ob,
_ => {
log::error!("Cannot enumerate properties of {}", name);
@ -936,7 +958,7 @@ impl<'gc> Avm1<'gc> {
let name_val = self.pop()?;
let name = name_val.into_string();
let object = self.pop()?.as_object()?;
let this = self.current_stack_frame().unwrap().this_cell();
let this = self.current_stack_frame().unwrap().read().this_cell();
let value = object.read().get(&name, self, context, this);
self.push(value);
@ -1030,8 +1052,13 @@ impl<'gc> Avm1<'gc> {
}
}
}
} else if self.current_stack_frame().unwrap().is_defined(path) {
result = Some(self.current_stack_frame().unwrap().resolve(path));
} else if self.current_stack_frame().unwrap().read().is_defined(path) {
result = Some(
self.current_stack_frame()
.unwrap()
.read()
.resolve(path, self, context),
);
}
self.push(result.unwrap_or(Value::Undefined));
@ -1455,7 +1482,7 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
fn action_return(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
fn action_return(&mut self, _context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> {
let result = self.pop()?;
if self.stack_frames.len() > 1 {
@ -1471,7 +1498,7 @@ impl<'gc> Avm1<'gc> {
let name_val = self.pop()?;
let name = name_val.as_string()?;
let object = self.pop()?.as_object()?;
let this = self.current_stack_frame().unwrap().this_cell();
let this = self.current_stack_frame().unwrap().read().this_cell();
object
.write(context.gc_context)
@ -1533,7 +1560,7 @@ impl<'gc> Avm1<'gc> {
var_path: &str,
value: Value<'gc>,
) -> Result<(), Error> {
let this = self.current_stack_frame().unwrap().this_cell();
let this = self.current_stack_frame().unwrap().read().this_cell();
let is_slashpath = Self::variable_name_is_slash_path(var_path);
if is_slashpath {
@ -1551,13 +1578,15 @@ impl<'gc> Avm1<'gc> {
}
}
} else {
let this = self.current_stack_frame().unwrap().this_cell();
let scope = self.current_stack_frame().unwrap().scope_cell();
let this = self.current_stack_frame().unwrap().read().this_cell();
let scope = self.current_stack_frame().unwrap().read().scope_cell();
let unused_value = scope.read().overwrite(var_path, value, self, context, this);
if let Some(value) = unused_value {
self.current_stack_frame()
.unwrap()
.define(var_path, value, context.gc_context);
self.current_stack_frame().unwrap().read().define(
var_path,
value,
context.gc_context,
);
}
}
@ -1592,7 +1621,7 @@ impl<'gc> Avm1<'gc> {
context.target_path = Value::Undefined;
}
let scope = self.current_stack_frame().unwrap().scope_cell();
let scope = self.current_stack_frame().unwrap().read().scope_cell();
let clip_obj = context
.active_clip
.read()
@ -1601,8 +1630,9 @@ impl<'gc> Avm1<'gc> {
.unwrap()
.to_owned();
self.current_stack_frame_mut()
self.current_stack_frame()
.unwrap()
.write(context.gc_context)
.set_scope(Scope::new_target_scope(scope, clip_obj, context.gc_context));
Ok(())
}
@ -1843,19 +1873,22 @@ impl<'gc> Avm1<'gc> {
let block = self
.current_stack_frame()
.unwrap()
.read()
.data()
.to_subslice(actions)
.unwrap();
let with_scope = Scope::new_with_scope(
self.current_stack_frame().unwrap().scope_cell(),
self.current_stack_frame().unwrap().read().scope_cell(),
object,
context.gc_context,
);
let new_activation = self
.current_stack_frame()
.unwrap()
.read()
.to_rescope(block, with_scope);
self.stack_frames.push(new_activation);
self.stack_frames
.push(GcCell::allocate(context.gc_context, new_activation));
Ok(())
}
}

View File

@ -2,7 +2,7 @@
use crate::avm1::object::Object;
use crate::avm1::scope::Scope;
use crate::avm1::Value;
use crate::avm1::{ActionContext, Avm1, Value};
use crate::tag_utils::SwfSlice;
use gc_arena::{GcCell, MutationContext};
use std::cell::{Ref, RefMut};
@ -201,7 +201,12 @@ impl<'gc> Activation<'gc> {
}
/// Resolve a particular named local variable within this activation.
pub fn resolve(&self, name: &str) -> Value<'gc> {
pub fn resolve(
&self,
name: &str,
avm: &mut Avm1<'gc>,
context: &mut ActionContext<'_, 'gc, '_>,
) -> Value<'gc> {
if name == "this" {
return Value::Object(self.this);
}
@ -210,7 +215,7 @@ impl<'gc> Activation<'gc> {
return Value::Object(self.arguments.unwrap());
}
self.scope().resolve(name)
self.scope().resolve(name, avm, context, self.this)
}
/// Check if a particular property in the scope chain is defined.

View File

@ -243,7 +243,7 @@ impl<'gc> Executable<'gc> {
if af.preload_parent {
frame.set_local_register(
preload_r,
child_scope.read().resolve("_parent"),
child_scope.read().resolve("_parent", avm, ac, this),
ac.gc_context,
);
preload_r += 1;
@ -267,7 +267,7 @@ impl<'gc> Executable<'gc> {
_ => {}
}
}
avm.insert_stack_frame(frame);
avm.insert_stack_frame(frame, ac);
None
}

View File

@ -172,7 +172,10 @@ mod tests {
};
let globals = avm.global_object_cell();
avm.insert_stack_frame(Activation::from_nothing(swf_version, globals, gc_context));
avm.insert_stack_frame(
Activation::from_nothing(swf_version, globals, gc_context),
&mut context,
);
test(&mut avm, &mut context)
})

View File

@ -417,7 +417,10 @@ mod tests {
let object = GcCell::allocate(gc_context, Object::object(gc_context));
let globals = avm.global_object_cell();
avm.insert_stack_frame(Activation::from_nothing(swf_version, globals, gc_context));
avm.insert_stack_frame(
Activation::from_nothing(swf_version, globals, gc_context),
&mut context,
);
test(&mut avm, &mut context, object)
})

View File

@ -236,12 +236,18 @@ impl<'gc> Scope<'gc> {
}
/// Resolve a particular value in the scope chain.
pub fn resolve(&self, name: &str) -> Value<'gc> {
pub fn resolve(
&self,
name: &str,
avm: &mut Avm1<'gc>,
context: &mut ActionContext<'_, 'gc, '_>,
this: GcCell<'gc, Object<'gc>>,
) -> Value<'gc> {
if self.locals().has_property(name) {
return self.locals().force_get(name);
return self.locals().get(name, avm, context, this);
}
if let Some(scope) = self.parent() {
return scope.resolve(name);
return scope.resolve(name, avm, context, this);
}
Value::Undefined