2020-02-04 02:22:44 +00:00
|
|
|
//! ActionScript Virtual Machine 2 (AS3) support
|
|
|
|
|
2020-02-18 22:54:39 +00:00
|
|
|
use crate::avm2::activation::{Activation, Avm2ScriptEntry};
|
2020-02-22 22:54:38 +00:00
|
|
|
use crate::avm2::function::{Avm2ClassEntry, Avm2MethodEntry, FunctionObject};
|
2020-02-19 03:26:08 +00:00
|
|
|
use crate::avm2::globals::SystemPrototypes;
|
2020-02-11 19:33:30 +00:00
|
|
|
use crate::avm2::names::{Multiname, Namespace, QName};
|
2020-02-11 04:58:15 +00:00
|
|
|
use crate::avm2::object::{Object, TObject};
|
|
|
|
use crate::avm2::return_value::ReturnValue;
|
2020-02-11 19:46:47 +00:00
|
|
|
use crate::avm2::scope::Scope;
|
2020-02-22 23:44:51 +00:00
|
|
|
use crate::avm2::script_object::ScriptObject;
|
2020-02-04 02:22:44 +00:00
|
|
|
use crate::avm2::value::Value;
|
2020-02-04 18:51:18 +00:00
|
|
|
use crate::context::UpdateContext;
|
|
|
|
use crate::tag_utils::SwfSlice;
|
2020-02-12 19:58:33 +00:00
|
|
|
use gc_arena::{Collect, GcCell, MutationContext};
|
2020-02-06 04:15:03 +00:00
|
|
|
use std::io::Cursor;
|
2020-02-07 00:28:54 +00:00
|
|
|
use std::rc::Rc;
|
2020-02-04 18:51:18 +00:00
|
|
|
use swf::avm2::read::Reader;
|
2020-02-11 04:58:15 +00:00
|
|
|
use swf::avm2::types::{
|
2020-02-22 22:54:38 +00:00
|
|
|
AbcFile, Class as AbcClass, Index, Method as AbcMethod, MethodBody, Multiname as AbcMultiname,
|
|
|
|
Namespace as AbcNamespace, Op, Script as AbcScript,
|
2020-02-11 04:58:15 +00:00
|
|
|
};
|
2020-02-06 04:15:03 +00:00
|
|
|
use swf::read::SwfRead;
|
2020-02-04 02:22:44 +00:00
|
|
|
|
2020-02-20 19:41:15 +00:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! avm_debug {
|
|
|
|
($($arg:tt)*) => (
|
|
|
|
#[cfg(feature = "avm_debug")]
|
|
|
|
log::debug!($($arg)*)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
mod activation;
|
|
|
|
mod function;
|
2020-02-12 19:58:33 +00:00
|
|
|
mod globals;
|
2020-02-05 18:52:11 +00:00
|
|
|
mod names;
|
|
|
|
mod object;
|
2020-02-15 01:30:19 +00:00
|
|
|
mod property;
|
2020-02-06 04:15:03 +00:00
|
|
|
mod return_value;
|
2020-02-10 19:54:55 +00:00
|
|
|
mod scope;
|
2020-02-05 18:52:11 +00:00
|
|
|
mod script_object;
|
2020-02-21 04:20:46 +00:00
|
|
|
mod slot;
|
2020-02-04 02:22:44 +00:00
|
|
|
mod value;
|
|
|
|
|
2020-02-04 18:51:18 +00:00
|
|
|
/// Boxed error alias.
|
|
|
|
///
|
|
|
|
/// As AVM2 is a far stricter VM than AVM1, this may eventually be replaced
|
|
|
|
/// with a proper Avm2Error enum.
|
|
|
|
type Error = Box<dyn std::error::Error>;
|
|
|
|
|
2020-02-04 02:22:44 +00:00
|
|
|
/// The state of an AVM2 interpreter.
|
|
|
|
#[derive(Collect)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct Avm2<'gc> {
|
2020-02-06 04:15:03 +00:00
|
|
|
/// All activation records for the current interpreter.
|
|
|
|
stack_frames: Vec<GcCell<'gc, Activation<'gc>>>,
|
|
|
|
|
2020-02-04 02:22:44 +00:00
|
|
|
/// Values currently present on the operand stack.
|
|
|
|
stack: Vec<Value<'gc>>,
|
2020-02-12 19:58:33 +00:00
|
|
|
|
|
|
|
/// Global scope object.
|
|
|
|
globals: Object<'gc>,
|
2020-02-19 03:26:08 +00:00
|
|
|
|
|
|
|
/// System prototypes.
|
|
|
|
system_prototypes: SystemPrototypes<'gc>,
|
2020-02-04 02:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> Avm2<'gc> {
|
|
|
|
/// Construct a new AVM interpreter.
|
2020-02-12 19:58:33 +00:00
|
|
|
pub fn new(mc: MutationContext<'gc, '_>) -> Self {
|
2020-02-19 03:26:08 +00:00
|
|
|
let (globals, system_prototypes) = globals::construct_global_scope(mc);
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
Self {
|
|
|
|
stack_frames: Vec::new(),
|
|
|
|
stack: Vec::new(),
|
2020-02-19 03:26:08 +00:00
|
|
|
globals,
|
|
|
|
system_prototypes,
|
2020-02-06 04:15:03 +00:00
|
|
|
}
|
2020-02-04 02:22:44 +00:00
|
|
|
}
|
2020-02-04 18:51:18 +00:00
|
|
|
|
|
|
|
/// Load an ABC file embedded in a `SwfSlice`.
|
|
|
|
///
|
|
|
|
/// The `SwfSlice` must resolve to the contents of an ABC file.
|
|
|
|
pub fn load_abc(
|
|
|
|
&mut self,
|
|
|
|
abc: SwfSlice,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut read = Reader::new(abc.as_ref());
|
|
|
|
|
2020-02-18 22:54:39 +00:00
|
|
|
let abc_file = Rc::new(read.read()?);
|
|
|
|
|
2020-02-23 21:32:16 +00:00
|
|
|
for i in (0..abc_file.scripts.len()).rev() {
|
|
|
|
let entrypoint_script: Index<AbcScript> = Index::new(i as u32);
|
2020-02-20 19:31:08 +00:00
|
|
|
let entrypoint: Result<Avm2ScriptEntry, Error> =
|
2020-02-23 21:32:16 +00:00
|
|
|
Avm2ScriptEntry::from_script_index(abc_file.clone(), entrypoint_script.clone())
|
|
|
|
.ok_or_else(|| {
|
|
|
|
format!("Script method {} does not exist", entrypoint_script.0).into()
|
|
|
|
});
|
2020-02-20 19:31:08 +00:00
|
|
|
let entrypoint = entrypoint?;
|
2020-02-19 01:08:13 +00:00
|
|
|
let scope = Scope::push_scope(None, self.globals(), context.gc_context);
|
|
|
|
|
|
|
|
for trait_entry in entrypoint.script().traits.iter() {
|
2020-02-20 04:10:21 +00:00
|
|
|
self.globals().install_trait(
|
|
|
|
self,
|
|
|
|
context,
|
2020-02-19 01:08:13 +00:00
|
|
|
entrypoint.abc(),
|
|
|
|
trait_entry,
|
|
|
|
Some(scope),
|
2020-02-19 03:26:08 +00:00
|
|
|
self.system_prototypes.function,
|
2020-02-19 01:08:13 +00:00
|
|
|
)?;
|
|
|
|
}
|
2020-02-18 22:54:39 +00:00
|
|
|
|
|
|
|
self.insert_stack_frame_for_script(context, entrypoint)?;
|
|
|
|
}
|
2020-02-04 18:51:18 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-06 04:15:03 +00:00
|
|
|
|
2020-02-12 19:58:33 +00:00
|
|
|
pub fn globals(&self) -> Object<'gc> {
|
|
|
|
self.globals
|
|
|
|
}
|
|
|
|
|
2020-02-08 03:42:04 +00:00
|
|
|
/// Get the current stack frame (`Activation` object).
|
2020-02-06 04:15:03 +00:00
|
|
|
pub fn current_stack_frame(&self) -> Option<GcCell<'gc, Activation<'gc>>> {
|
|
|
|
self.stack_frames.last().copied()
|
|
|
|
}
|
|
|
|
|
2020-02-08 03:42:04 +00:00
|
|
|
/// Add a new stack frame to the stack, which can represent any particular
|
|
|
|
/// operation you like that needs to execute AVM2 code.
|
|
|
|
pub fn insert_stack_frame(&mut self, frame: GcCell<'gc, Activation<'gc>>) {
|
|
|
|
self.stack_frames.push(frame)
|
|
|
|
}
|
|
|
|
|
2020-02-18 22:54:39 +00:00
|
|
|
/// Add a new stack frame for executing an entrypoint script.
|
|
|
|
pub fn insert_stack_frame_for_script(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
script: Avm2ScriptEntry,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
self.stack_frames.push(GcCell::allocate(
|
|
|
|
context.gc_context,
|
|
|
|
Activation::from_script(context, script, self.globals)?,
|
|
|
|
));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-23 22:28:28 +00:00
|
|
|
/// Destroy the current stack frame (if there is one) with a return value.
|
2020-02-08 21:59:59 +00:00
|
|
|
///
|
|
|
|
/// The given return value will be pushed on the stack if there is a
|
|
|
|
/// function to return it to. Otherwise, it will be discarded.
|
|
|
|
///
|
|
|
|
/// NOTE: This means that if you are starting a brand new AVM stack just to
|
|
|
|
/// get it's return value, you won't get that value. Instead, retain a cell
|
|
|
|
/// referencing the oldest activation frame and use that to retrieve the
|
|
|
|
/// return value.
|
|
|
|
fn retire_stack_frame(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
return_value: Value<'gc>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
if let Some(frame) = self.current_stack_frame() {
|
|
|
|
self.stack_frames.pop();
|
|
|
|
|
|
|
|
let can_return = !self.stack_frames.is_empty();
|
|
|
|
if can_return {
|
|
|
|
frame
|
|
|
|
.write(context.gc_context)
|
|
|
|
.set_return_value(return_value.clone());
|
|
|
|
|
|
|
|
self.push(return_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-23 22:28:28 +00:00
|
|
|
/// Destroy the current stack frame (if there is one) with an exception.
|
|
|
|
///
|
|
|
|
/// TODO: This function should allow exception recovery at some point in
|
|
|
|
/// the future.
|
|
|
|
///
|
|
|
|
/// NOTE: This means that if you are starting a brand new AVM stack just to
|
|
|
|
/// get it's return value, you won't get that value. Instead, retain a cell
|
|
|
|
/// referencing the oldest activation frame and use that to retrieve the
|
|
|
|
/// return value.
|
|
|
|
fn unwind_stack_frame(&mut self) {
|
|
|
|
if let Some(_frame) = self.current_stack_frame() {
|
|
|
|
self.stack_frames.pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
/// Perform some action with the current stack frame's reader.
|
|
|
|
///
|
|
|
|
/// This function constructs a reader based off the current stack frame's
|
|
|
|
/// reader. You are permitted to mutate the stack frame as you wish. If the
|
|
|
|
/// stack frame we started with still exists in the same location on the
|
|
|
|
/// stack, it's PC will be updated to the Reader's current PC.
|
|
|
|
///
|
|
|
|
/// Stack frame identity (for the purpose of the above paragraph) is
|
|
|
|
/// determined by the data pointed to by the `SwfSlice` of a given frame.
|
|
|
|
///
|
|
|
|
/// # Warnings
|
|
|
|
///
|
|
|
|
/// 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.
|
|
|
|
pub fn with_current_reader_mut<F, R>(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
func: F,
|
|
|
|
) -> Result<R, Error>
|
|
|
|
where
|
|
|
|
F: FnOnce(
|
|
|
|
&mut Self,
|
|
|
|
&mut Reader<Cursor<&[u8]>>,
|
|
|
|
&mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
) -> Result<R, Error>,
|
|
|
|
{
|
2020-02-12 23:52:00 +00:00
|
|
|
let (abc, frame_cell, method_body_index, pc) = {
|
2020-02-06 19:12:01 +00:00
|
|
|
let frame = self
|
|
|
|
.current_stack_frame()
|
|
|
|
.ok_or("No stack frame to read!")?;
|
2020-02-06 04:15:03 +00:00
|
|
|
let mut frame_ref = frame.write(context.gc_context);
|
|
|
|
frame_ref.lock()?;
|
|
|
|
|
2020-02-12 23:52:00 +00:00
|
|
|
let method = frame_ref.method();
|
|
|
|
let abc = method.abc.as_ref().clone();
|
2020-02-20 04:10:21 +00:00
|
|
|
let _method_index = method.abc_method;
|
2020-02-12 23:52:00 +00:00
|
|
|
let method_body_index = method.abc_method_body as usize;
|
|
|
|
|
|
|
|
(abc, frame, method_body_index, frame_ref.pc())
|
2020-02-06 04:15:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let method_body: Result<&MethodBody, Error> =
|
2020-02-06 19:12:01 +00:00
|
|
|
abc.method_bodies.get(method_body_index).ok_or_else(|| {
|
2020-02-06 04:15:03 +00:00
|
|
|
"Attempting to execute a method that does not exist"
|
|
|
|
.to_string()
|
2020-02-06 19:12:01 +00:00
|
|
|
.into()
|
|
|
|
});
|
2020-02-06 04:15:03 +00:00
|
|
|
|
|
|
|
let cursor = Cursor::new(method_body?.code.as_ref());
|
|
|
|
let mut read = Reader::new(cursor);
|
|
|
|
read.get_inner().set_position(pc as u64);
|
|
|
|
|
|
|
|
let r = func(self, &mut read, context);
|
|
|
|
|
|
|
|
let mut frame_ref = frame_cell.write(context.gc_context);
|
|
|
|
frame_ref.unlock_execution();
|
|
|
|
frame_ref.set_pc(read.get_inner().position() as usize);
|
|
|
|
|
|
|
|
r
|
|
|
|
}
|
|
|
|
|
2020-02-06 19:12:01 +00:00
|
|
|
/// Execute the AVM stack until it is exhausted.
|
|
|
|
pub fn run_stack_till_empty(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
while !self.stack_frames.is_empty() {
|
|
|
|
self.with_current_reader_mut(context, |this, r, context| {
|
|
|
|
this.do_next_opcode(context, r)
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Operand stack should be empty at this point.
|
|
|
|
// This is probably a bug on our part,
|
|
|
|
// although bytecode could in theory leave data on the stack.
|
|
|
|
if !self.stack.is_empty() {
|
|
|
|
log::warn!("Operand stack is not empty after execution");
|
|
|
|
self.stack.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-08 21:59:59 +00:00
|
|
|
/// Execute the AVM stack until a given activation returns.
|
|
|
|
pub fn run_current_frame(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
stop_frame: GcCell<'gc, Activation<'gc>>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut stop_frame_id = None;
|
|
|
|
for (index, frame) in self.stack_frames.iter().enumerate() {
|
|
|
|
if GcCell::ptr_eq(stop_frame, *frame) {
|
|
|
|
stop_frame_id = Some(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(stop_frame_id) = stop_frame_id {
|
|
|
|
while self
|
|
|
|
.stack_frames
|
|
|
|
.get(stop_frame_id)
|
|
|
|
.map(|fr| GcCell::ptr_eq(stop_frame, *fr))
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
|
|
|
self.with_current_reader_mut(context, |this, r, context| {
|
|
|
|
this.do_next_opcode(context, r)
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err("Attempted to run a frame not on the current interpreter stack".into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-07 00:28:54 +00:00
|
|
|
/// Push a value onto the operand stack.
|
|
|
|
fn push(&mut self, value: impl Into<Value<'gc>>) {
|
|
|
|
let value = value.into();
|
|
|
|
avm_debug!("Stack push {}: {:?}", self.stack.len(), value);
|
|
|
|
self.stack.push(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve the top-most value on the operand stack.
|
|
|
|
#[allow(clippy::let_and_return)]
|
|
|
|
fn pop(&mut self) -> Value<'gc> {
|
|
|
|
let value = self.stack.pop().unwrap_or_else(|| {
|
|
|
|
log::warn!("Avm1::pop: Stack underflow");
|
|
|
|
Value::Undefined
|
|
|
|
});
|
|
|
|
|
|
|
|
avm_debug!("Stack pop {}: {:?}", self.stack.len(), value);
|
|
|
|
|
|
|
|
value
|
|
|
|
}
|
|
|
|
|
2020-02-07 19:54:14 +00:00
|
|
|
fn register_value(&self, index: u32) -> Result<Value<'gc>, Error> {
|
|
|
|
self.current_stack_frame()
|
|
|
|
.and_then(|sf| sf.read().local_register(index))
|
|
|
|
.ok_or_else(|| format!("Out of bounds register read: {}", index).into())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_register_value(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: u32,
|
|
|
|
value: impl Into<Value<'gc>>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
match self.current_stack_frame().map(|sf| {
|
|
|
|
sf.write(context.gc_context)
|
|
|
|
.set_local_register(index, value, context.gc_context)
|
|
|
|
}) {
|
|
|
|
Some(true) => Ok(()),
|
|
|
|
_ => Err(format!("Out of bounds register write: {}", index).into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-07 00:28:54 +00:00
|
|
|
/// Retrieve the current constant pool for the currently executing function.
|
|
|
|
fn current_abc(&self) -> Option<Rc<AbcFile>> {
|
|
|
|
self.current_stack_frame()
|
2020-02-12 23:52:00 +00:00
|
|
|
.map(|sf| sf.read().method().abc.clone())
|
2020-02-07 00:28:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve a int from the current constant pool.
|
|
|
|
fn pool_int(&self, index: Index<i32>) -> Result<i32, Error> {
|
2020-02-09 21:45:51 +00:00
|
|
|
value::abc_int(&self.current_abc().unwrap(), index)
|
2020-02-07 00:28:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve a int from the current constant pool.
|
|
|
|
fn pool_uint(&self, index: Index<u32>) -> Result<u32, Error> {
|
2020-02-09 21:45:51 +00:00
|
|
|
value::abc_uint(&self.current_abc().unwrap(), index)
|
2020-02-07 00:28:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve a double from the current constant pool.
|
|
|
|
fn pool_double(&self, index: Index<f64>) -> Result<f64, Error> {
|
2020-02-09 21:45:51 +00:00
|
|
|
value::abc_double(&self.current_abc().unwrap(), index)
|
2020-02-07 00:28:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve a string from the current constant pool.
|
|
|
|
fn pool_string(&self, index: Index<String>) -> Result<String, Error> {
|
2020-02-09 21:45:51 +00:00
|
|
|
value::abc_string(&self.current_abc().unwrap(), index)
|
2020-02-07 00:28:54 +00:00
|
|
|
}
|
|
|
|
|
2020-02-10 02:25:42 +00:00
|
|
|
/// Retrieve a namespace from the current constant pool.
|
|
|
|
fn pool_namespace(&self, index: Index<AbcNamespace>) -> Result<Namespace, Error> {
|
|
|
|
Namespace::from_abc_namespace(&self.current_abc().unwrap(), index)
|
|
|
|
}
|
|
|
|
|
2020-02-22 20:17:33 +00:00
|
|
|
/// Retrieve a multiname from the current constant pool.
|
2020-02-11 04:58:15 +00:00
|
|
|
fn pool_multiname(&mut self, index: Index<AbcMultiname>) -> Result<Multiname, Error> {
|
|
|
|
Multiname::from_abc_multiname(&self.current_abc().unwrap(), index, self)
|
|
|
|
}
|
|
|
|
|
2020-02-22 20:17:33 +00:00
|
|
|
/// Retrieve a static, or non-runtime, multiname from the current constant
|
|
|
|
/// pool.
|
|
|
|
fn pool_multiname_static(&mut self, index: Index<AbcMultiname>) -> Result<Multiname, Error> {
|
|
|
|
Multiname::from_abc_multiname_static(&self.current_abc().unwrap(), index)
|
|
|
|
}
|
|
|
|
|
2020-02-22 22:54:38 +00:00
|
|
|
/// Retrieve a method entry from the current ABC file's method table.
|
|
|
|
fn table_method(&mut self, index: Index<AbcMethod>) -> Result<Avm2MethodEntry, Error> {
|
|
|
|
Avm2MethodEntry::from_method_index(self.current_abc().unwrap(), index.clone())
|
|
|
|
.ok_or_else(|| format!("Method index {} does not exist", index.0).into())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve a class entry from the current ABC file's method table.
|
|
|
|
fn table_class(&mut self, index: Index<AbcClass>) -> Result<Avm2ClassEntry, Error> {
|
|
|
|
Avm2ClassEntry::from_class_index(self.current_abc().unwrap(), index.clone())
|
|
|
|
.ok_or_else(|| format!("Class index {} does not exist", index.0).into())
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
/// Run a single action from a given action reader.
|
|
|
|
pub fn do_next_opcode(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
reader: &mut Reader<Cursor<&[u8]>>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
if let Some(op) = reader.read_op()? {
|
|
|
|
avm_debug!("Opcode: {:?}", op);
|
|
|
|
|
|
|
|
let result = match op {
|
2020-02-07 00:28:54 +00:00
|
|
|
Op::PushByte { value } => self.op_push_byte(value),
|
|
|
|
Op::PushDouble { value } => self.op_push_double(value),
|
|
|
|
Op::PushFalse => self.op_push_false(),
|
|
|
|
Op::PushInt { value } => self.op_push_int(value),
|
2020-02-10 02:25:42 +00:00
|
|
|
Op::PushNamespace { value } => self.op_push_namespace(value),
|
2020-02-07 00:28:54 +00:00
|
|
|
Op::PushNaN => self.op_push_nan(),
|
|
|
|
Op::PushNull => self.op_push_null(),
|
|
|
|
Op::PushShort { value } => self.op_push_short(value),
|
|
|
|
Op::PushString { value } => self.op_push_string(value),
|
|
|
|
Op::PushTrue => self.op_push_true(),
|
|
|
|
Op::PushUint { value } => self.op_push_uint(value),
|
|
|
|
Op::PushUndefined => self.op_push_undefined(),
|
2020-02-22 21:30:45 +00:00
|
|
|
Op::Dup => self.op_dup(),
|
2020-02-07 19:54:14 +00:00
|
|
|
Op::GetLocal { index } => self.op_get_local(index),
|
|
|
|
Op::SetLocal { index } => self.op_set_local(context, index),
|
2020-02-08 21:59:59 +00:00
|
|
|
Op::Call { num_args } => self.op_call(context, num_args),
|
2020-02-24 03:11:02 +00:00
|
|
|
Op::CallMethod { index, num_args } => self.op_call_method(context, index, num_args),
|
|
|
|
Op::CallProperty { index, num_args } => {
|
|
|
|
self.op_call_property(context, index, num_args)
|
|
|
|
}
|
|
|
|
Op::CallPropLex { index, num_args } => {
|
|
|
|
self.op_call_prop_lex(context, index, num_args)
|
|
|
|
}
|
|
|
|
Op::CallPropVoid { index, num_args } => {
|
|
|
|
self.op_call_prop_void(context, index, num_args)
|
|
|
|
}
|
|
|
|
Op::CallStatic { index, num_args } => self.op_call_static(context, index, num_args),
|
2020-02-08 21:59:59 +00:00
|
|
|
Op::ReturnValue => self.op_return_value(context),
|
|
|
|
Op::ReturnVoid => self.op_return_void(context),
|
2020-02-11 19:33:30 +00:00
|
|
|
Op::GetProperty { index } => self.op_get_property(context, index),
|
|
|
|
Op::SetProperty { index } => self.op_set_property(context, index),
|
2020-02-22 21:21:28 +00:00
|
|
|
Op::InitProperty { index } => self.op_init_property(context, index),
|
2020-02-21 19:52:24 +00:00
|
|
|
Op::DeleteProperty { index } => self.op_delete_property(context, index),
|
2020-02-11 19:46:47 +00:00
|
|
|
Op::PushScope => self.op_push_scope(context),
|
|
|
|
Op::PushWith => self.op_push_with(context),
|
|
|
|
Op::PopScope => self.op_pop_scope(context),
|
2020-02-22 04:31:30 +00:00
|
|
|
Op::GetScopeObject { index } => self.op_get_scope_object(index),
|
2020-02-22 23:14:07 +00:00
|
|
|
Op::GetGlobalScope => self.op_get_global_scope(),
|
2020-02-11 04:58:15 +00:00
|
|
|
Op::FindProperty { index } => self.op_find_property(context, index),
|
|
|
|
Op::FindPropStrict { index } => self.op_find_prop_strict(context, index),
|
|
|
|
Op::GetLex { index } => self.op_get_lex(context, index),
|
2020-02-19 19:17:33 +00:00
|
|
|
Op::GetSlot { index } => self.op_get_slot(index),
|
|
|
|
Op::SetSlot { index } => self.op_set_slot(context, index),
|
|
|
|
Op::GetGlobalSlot { index } => self.op_get_global_slot(index),
|
|
|
|
Op::SetGlobalSlot { index } => self.op_set_global_slot(context, index),
|
2020-02-19 23:53:21 +00:00
|
|
|
Op::Construct { num_args } => self.op_construct(context, num_args),
|
|
|
|
Op::ConstructProp { index, num_args } => {
|
|
|
|
self.op_construct_prop(context, index, num_args)
|
|
|
|
}
|
2020-02-22 23:44:51 +00:00
|
|
|
Op::NewActivation => self.op_new_activation(context),
|
|
|
|
Op::NewObject { num_args } => self.op_new_object(context, num_args),
|
2020-02-22 22:54:38 +00:00
|
|
|
Op::NewFunction { index } => self.op_new_function(context, index),
|
|
|
|
Op::NewClass { index } => self.op_new_class(context, index),
|
2020-02-24 03:24:23 +00:00
|
|
|
Op::CoerceA => self.op_coerce_a(),
|
2020-02-22 23:24:10 +00:00
|
|
|
Op::Debug {
|
|
|
|
is_local_register,
|
|
|
|
register_name,
|
|
|
|
register,
|
|
|
|
} => self.op_debug(is_local_register, register_name, register),
|
|
|
|
Op::DebugFile { file_name } => self.op_debug_file(file_name),
|
|
|
|
Op::DebugLine { line_num } => self.op_debug_line(line_num),
|
2020-02-07 00:28:54 +00:00
|
|
|
_ => self.unknown_op(op),
|
2020-02-06 04:15:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if let Err(ref e) = result {
|
|
|
|
log::error!("AVM2 error: {}", e);
|
2020-02-23 22:28:28 +00:00
|
|
|
self.unwind_stack_frame();
|
2020-02-06 04:15:03 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-07 00:28:54 +00:00
|
|
|
fn unknown_op(&mut self, op: swf::avm2::types::Op) -> Result<(), Error> {
|
2020-02-06 04:15:03 +00:00
|
|
|
log::error!("Unknown AVM2 opcode: {:?}", op);
|
|
|
|
Err("Unknown op".into())
|
|
|
|
}
|
2020-02-07 00:28:54 +00:00
|
|
|
|
|
|
|
fn op_push_byte(&mut self, value: u8) -> Result<(), Error> {
|
|
|
|
self.push(value);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_double(&mut self, value: Index<f64>) -> Result<(), Error> {
|
|
|
|
self.push(self.pool_double(value)?);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_false(&mut self) -> Result<(), Error> {
|
|
|
|
self.push(false);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_int(&mut self, value: Index<i32>) -> Result<(), Error> {
|
|
|
|
self.push(self.pool_int(value)?);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-10 02:25:42 +00:00
|
|
|
|
|
|
|
fn op_push_namespace(&mut self, value: Index<AbcNamespace>) -> Result<(), Error> {
|
|
|
|
self.push(self.pool_namespace(value)?);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-07 00:28:54 +00:00
|
|
|
|
|
|
|
fn op_push_nan(&mut self) -> Result<(), Error> {
|
|
|
|
self.push(std::f64::NAN);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_null(&mut self) -> Result<(), Error> {
|
|
|
|
self.push(Value::Null);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_short(&mut self, value: u32) -> Result<(), Error> {
|
|
|
|
self.push(value);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_string(&mut self, value: Index<String>) -> Result<(), Error> {
|
|
|
|
self.push(self.pool_string(value)?);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_true(&mut self) -> Result<(), Error> {
|
|
|
|
self.push(true);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_uint(&mut self, value: Index<u32>) -> Result<(), Error> {
|
|
|
|
self.push(self.pool_uint(value)?);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_undefined(&mut self) -> Result<(), Error> {
|
|
|
|
self.push(Value::Undefined);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-07 19:54:14 +00:00
|
|
|
|
2020-02-22 21:30:45 +00:00
|
|
|
fn op_dup(&mut self) -> Result<(), Error> {
|
|
|
|
self.push(self.stack.last().cloned().unwrap_or(Value::Undefined));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-07 19:54:14 +00:00
|
|
|
fn op_get_local(&mut self, register_index: u32) -> Result<(), Error> {
|
|
|
|
self.push(self.register_value(register_index)?);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_set_local(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
register_index: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let value = self.pop();
|
|
|
|
self.set_register_value(context, register_index, value)
|
|
|
|
}
|
2020-02-08 21:59:59 +00:00
|
|
|
|
|
|
|
fn op_call(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
arg_count: u32,
|
|
|
|
) -> Result<(), Error> {
|
2020-02-24 03:11:02 +00:00
|
|
|
let mut args = Vec::new();
|
|
|
|
for _ in 0..arg_count {
|
|
|
|
args.push(self.pop());
|
|
|
|
}
|
|
|
|
let receiver = self.pop().as_object().ok();
|
2020-02-08 21:59:59 +00:00
|
|
|
let function = self.pop().as_object()?;
|
2020-02-24 03:11:02 +00:00
|
|
|
|
|
|
|
function.call(receiver, &args, self, context)?.push(self);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_call_method(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMethod>,
|
|
|
|
arg_count: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut args = Vec::new();
|
|
|
|
for _ in 0..arg_count {
|
|
|
|
args.push(self.pop());
|
|
|
|
}
|
2020-02-08 21:59:59 +00:00
|
|
|
let receiver = self.pop().as_object()?;
|
2020-02-24 03:11:02 +00:00
|
|
|
let function: Result<Object<'gc>, Error> = receiver
|
|
|
|
.get_method(index.0)
|
|
|
|
.ok_or_else(|| format!("Object method {} does not exist", index.0).into());
|
|
|
|
|
|
|
|
function?
|
|
|
|
.call(Some(receiver), &args, self, context)?
|
|
|
|
.push(self);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_call_property(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
arg_count: u32,
|
|
|
|
) -> Result<(), Error> {
|
2020-02-08 21:59:59 +00:00
|
|
|
let mut args = Vec::new();
|
|
|
|
for _ in 0..arg_count {
|
|
|
|
args.push(self.pop());
|
|
|
|
}
|
2020-02-24 03:11:02 +00:00
|
|
|
let multiname = self.pool_multiname(index)?;
|
|
|
|
let receiver = self.pop().as_object()?;
|
|
|
|
let name: Result<QName, Error> = receiver
|
|
|
|
.resolve_multiname(&multiname)
|
|
|
|
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
|
|
|
let function = receiver
|
|
|
|
.get_property(&name?, self, context)?
|
|
|
|
.resolve(self, context)?
|
|
|
|
.as_object()?;
|
2020-02-08 21:59:59 +00:00
|
|
|
|
2020-02-24 03:11:02 +00:00
|
|
|
function
|
|
|
|
.call(Some(receiver), &args, self, context)?
|
|
|
|
.push(self);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_call_prop_lex(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
arg_count: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut args = Vec::new();
|
|
|
|
for _ in 0..arg_count {
|
|
|
|
args.push(self.pop());
|
|
|
|
}
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
|
|
|
let receiver = self.pop().as_object()?;
|
|
|
|
let name: Result<QName, Error> = receiver
|
|
|
|
.resolve_multiname(&multiname)
|
|
|
|
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
|
|
|
let function = receiver
|
|
|
|
.get_property(&name?, self, context)?
|
|
|
|
.resolve(self, context)?
|
|
|
|
.as_object()?;
|
|
|
|
|
|
|
|
function.call(None, &args, self, context)?.push(self);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_call_prop_void(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
arg_count: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut args = Vec::new();
|
|
|
|
for _ in 0..arg_count {
|
|
|
|
args.push(self.pop());
|
|
|
|
}
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
|
|
|
let receiver = self.pop().as_object()?;
|
|
|
|
let name: Result<QName, Error> = receiver
|
|
|
|
.resolve_multiname(&multiname)
|
|
|
|
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
|
|
|
let function = receiver
|
|
|
|
.get_property(&name?, self, context)?
|
|
|
|
.resolve(self, context)?
|
|
|
|
.as_object()?;
|
|
|
|
|
|
|
|
function
|
|
|
|
.call(Some(receiver), &args, self, context)?
|
|
|
|
.resolve(self, context)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_call_static(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMethod>,
|
|
|
|
arg_count: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut args = Vec::new();
|
|
|
|
for _ in 0..arg_count {
|
|
|
|
args.push(self.pop());
|
|
|
|
}
|
|
|
|
let receiver = self.pop().as_object()?;
|
|
|
|
let method = self.table_method(index)?;
|
|
|
|
let scope = self.current_stack_frame().unwrap().read().scope(); //TODO: Is this correct?
|
|
|
|
let function = FunctionObject::from_abc_method(
|
|
|
|
context.gc_context,
|
|
|
|
method,
|
|
|
|
scope,
|
|
|
|
self.system_prototypes.function,
|
|
|
|
);
|
|
|
|
|
|
|
|
function
|
|
|
|
.call(Some(receiver), &args, self, context)?
|
|
|
|
.push(self);
|
2020-02-08 21:59:59 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_return_value(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
|
|
|
let return_value = self.pop();
|
|
|
|
|
|
|
|
self.retire_stack_frame(context, return_value)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_return_void(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
|
|
|
self.retire_stack_frame(context, Value::Undefined)
|
|
|
|
}
|
2020-02-11 04:58:15 +00:00
|
|
|
|
2020-02-11 19:33:30 +00:00
|
|
|
fn op_get_property(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
|
|
|
let object = self.pop().as_object()?;
|
|
|
|
|
2020-02-21 00:05:33 +00:00
|
|
|
let name: Result<QName, Error> = object.resolve_multiname(&multiname).ok_or_else(|| {
|
|
|
|
format!("Could not resolve property {:?}", multiname.local_name()).into()
|
|
|
|
});
|
2020-02-11 19:33:30 +00:00
|
|
|
|
|
|
|
let value = object
|
|
|
|
.get_property(&name?, self, context)?
|
|
|
|
.resolve(self, context)?;
|
|
|
|
self.push(value);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_set_property(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let value = self.pop();
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
|
|
|
let object = self.pop().as_object()?;
|
|
|
|
|
|
|
|
if let Some(name) = object.resolve_multiname(&multiname) {
|
|
|
|
object.set_property(&name, value, self, context)
|
|
|
|
} else {
|
|
|
|
//TODO: Non-dynamic objects should fail
|
|
|
|
//TODO: This should only work if the public namespace is present
|
2020-02-21 00:05:33 +00:00
|
|
|
let local_name: Result<&str, Error> = multiname
|
|
|
|
.local_name()
|
|
|
|
.ok_or_else(|| "Cannot set property using any name".into());
|
|
|
|
let name = QName::dynamic_name(local_name?);
|
2020-02-11 19:33:30 +00:00
|
|
|
object.set_property(&name, value, self, context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-22 21:21:28 +00:00
|
|
|
fn op_init_property(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let value = self.pop();
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
|
|
|
let object = self.pop().as_object()?;
|
|
|
|
|
|
|
|
if let Some(name) = object.resolve_multiname(&multiname) {
|
|
|
|
object.init_property(&name, value, self, context)
|
|
|
|
} else {
|
|
|
|
//TODO: Non-dynamic objects should fail
|
|
|
|
//TODO: This should only work if the public namespace is present
|
|
|
|
let local_name: Result<&str, Error> = multiname
|
|
|
|
.local_name()
|
|
|
|
.ok_or_else(|| "Cannot set property using any name".into());
|
|
|
|
let name = QName::dynamic_name(local_name?);
|
|
|
|
object.init_property(&name, value, self, context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-21 19:52:24 +00:00
|
|
|
fn op_delete_property(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
|
|
|
let object = self.pop().as_object()?;
|
|
|
|
|
|
|
|
if let Some(name) = object.resolve_multiname(&multiname) {
|
|
|
|
self.push(object.delete_property(context.gc_context, &name))
|
|
|
|
} else {
|
|
|
|
self.push(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-11 19:46:47 +00:00
|
|
|
fn op_push_scope(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
|
|
|
let object = self.pop().as_object()?;
|
|
|
|
let activation = self.current_stack_frame().unwrap();
|
|
|
|
let mut write = activation.write(context.gc_context);
|
|
|
|
let scope_stack = write.scope();
|
|
|
|
let new_scope = Scope::push_scope(scope_stack, object, context.gc_context);
|
|
|
|
|
|
|
|
write.set_scope(Some(new_scope));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_push_with(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
|
|
|
let object = self.pop().as_object()?;
|
|
|
|
let activation = self.current_stack_frame().unwrap();
|
|
|
|
let mut write = activation.write(context.gc_context);
|
|
|
|
let scope_stack = write.scope();
|
|
|
|
let new_scope = Scope::push_with(scope_stack, object, context.gc_context);
|
|
|
|
|
|
|
|
write.set_scope(Some(new_scope));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_pop_scope(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
|
|
|
let activation = self.current_stack_frame().unwrap();
|
|
|
|
let mut write = activation.write(context.gc_context);
|
|
|
|
let scope_stack = write.scope();
|
|
|
|
let new_scope = scope_stack.and_then(|s| s.read().pop_scope());
|
|
|
|
|
|
|
|
write.set_scope(new_scope);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-22 04:31:30 +00:00
|
|
|
fn op_get_scope_object(&mut self, mut index: u8) -> Result<(), Error> {
|
|
|
|
let mut scope = self.current_stack_frame().unwrap().read().scope();
|
|
|
|
|
|
|
|
while index > 0 {
|
|
|
|
if let Some(child_scope) = scope {
|
|
|
|
scope = child_scope.read().parent_cell();
|
|
|
|
}
|
|
|
|
|
|
|
|
index -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.push(
|
|
|
|
scope
|
|
|
|
.map(|s| s.read().locals().clone().into())
|
|
|
|
.unwrap_or(Value::Undefined),
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-22 23:14:07 +00:00
|
|
|
fn op_get_global_scope(&mut self) -> Result<(), Error> {
|
|
|
|
let mut scope = self.current_stack_frame().unwrap().read().scope();
|
|
|
|
|
|
|
|
while let Some(this_scope) = scope {
|
|
|
|
let parent = this_scope.read().parent_cell();
|
|
|
|
if parent.is_none() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
scope = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.push(
|
|
|
|
scope
|
|
|
|
.map(|s| s.read().locals().clone().into())
|
|
|
|
.unwrap_or(Value::Undefined),
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-11 04:58:15 +00:00
|
|
|
fn op_find_property(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
2020-02-23 21:32:16 +00:00
|
|
|
avm_debug!("Resolving {:?}", multiname);
|
2020-02-23 00:02:53 +00:00
|
|
|
let result = self
|
|
|
|
.current_stack_frame()
|
|
|
|
.unwrap()
|
|
|
|
.read()
|
|
|
|
.scope()
|
|
|
|
.and_then(|scope| scope.read().find(&multiname, self, context));
|
2020-02-11 04:58:15 +00:00
|
|
|
|
|
|
|
self.push(result.map(|o| o.into()).unwrap_or(Value::Undefined));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_find_prop_strict(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
2020-02-23 21:32:16 +00:00
|
|
|
avm_debug!("Resolving {:?}", multiname);
|
2020-02-23 00:02:53 +00:00
|
|
|
let found: Result<Object<'gc>, Error> = self
|
|
|
|
.current_stack_frame()
|
|
|
|
.unwrap()
|
|
|
|
.read()
|
|
|
|
.scope()
|
|
|
|
.and_then(|scope| scope.read().find(&multiname, self, context))
|
2020-02-21 00:05:33 +00:00
|
|
|
.ok_or_else(|| format!("Property does not exist: {:?}", multiname.local_name()).into());
|
2020-02-11 04:58:15 +00:00
|
|
|
let result: Value<'gc> = found?.into();
|
|
|
|
|
|
|
|
self.push(result);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_get_lex(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
) -> Result<(), Error> {
|
2020-02-22 20:17:33 +00:00
|
|
|
let multiname = self.pool_multiname_static(index)?;
|
2020-02-23 21:32:16 +00:00
|
|
|
avm_debug!("Resolving {:?}", multiname);
|
2020-02-23 00:02:53 +00:00
|
|
|
let found: Result<Result<ReturnValue<'gc>, Error>, Error> = self
|
|
|
|
.current_stack_frame()
|
|
|
|
.unwrap()
|
|
|
|
.read()
|
|
|
|
.scope()
|
|
|
|
.and_then(|scope| scope.read().resolve(&multiname, self, context))
|
|
|
|
.ok_or_else(|| format!("Property does not exist: {:?}", multiname.local_name()).into());
|
|
|
|
let result: Value<'gc> = found??.resolve(self, context)?;
|
2020-02-11 04:58:15 +00:00
|
|
|
|
|
|
|
self.push(result);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-19 19:17:33 +00:00
|
|
|
|
|
|
|
fn op_get_slot(&mut self, index: u32) -> Result<(), Error> {
|
|
|
|
let object = self.pop().as_object()?;
|
|
|
|
let value = object.get_slot(index)?;
|
|
|
|
|
|
|
|
self.push(value);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_set_slot(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let object = self.pop().as_object()?;
|
|
|
|
let value = self.pop();
|
|
|
|
|
|
|
|
object.set_slot(index, value, context.gc_context)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_get_global_slot(&mut self, index: u32) -> Result<(), Error> {
|
|
|
|
let value = self.globals.get_slot(index)?;
|
|
|
|
|
|
|
|
self.push(value);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_set_global_slot(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let value = self.pop();
|
|
|
|
|
|
|
|
self.globals.set_slot(index, value, context.gc_context)
|
|
|
|
}
|
2020-02-19 23:53:21 +00:00
|
|
|
|
|
|
|
fn op_construct(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
arg_count: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut args = Vec::new();
|
|
|
|
for _ in 0..arg_count {
|
|
|
|
args.push(self.pop());
|
|
|
|
}
|
|
|
|
|
|
|
|
let ctor = self.pop().as_object()?;
|
|
|
|
let proto = ctor
|
|
|
|
.get_property(
|
|
|
|
&QName::new(Namespace::public_namespace(), "prototype"),
|
|
|
|
self,
|
|
|
|
context,
|
|
|
|
)?
|
|
|
|
.resolve(self, context)?
|
|
|
|
.as_object()?;
|
|
|
|
|
|
|
|
let object = proto.construct(self, context, &args)?;
|
2020-02-24 03:11:02 +00:00
|
|
|
ctor.call(Some(object), &args, self, context)?
|
2020-02-19 23:53:21 +00:00
|
|
|
.resolve(self, context)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_construct_prop(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMultiname>,
|
|
|
|
arg_count: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let mut args = Vec::new();
|
|
|
|
for _ in 0..arg_count {
|
|
|
|
args.push(self.pop());
|
|
|
|
}
|
|
|
|
|
|
|
|
let multiname = self.pool_multiname(index)?;
|
|
|
|
|
|
|
|
let source = self.pop().as_object()?;
|
2020-02-21 00:05:33 +00:00
|
|
|
let ctor_name: Result<QName, Error> =
|
|
|
|
source.resolve_multiname(&multiname).ok_or_else(|| {
|
|
|
|
format!("Could not resolve property {:?}", multiname.local_name()).into()
|
|
|
|
});
|
2020-02-19 23:53:21 +00:00
|
|
|
let ctor = source
|
|
|
|
.get_property(&ctor_name?, self, context)?
|
|
|
|
.resolve(self, context)?
|
|
|
|
.as_object()?;
|
|
|
|
let proto = ctor
|
|
|
|
.get_property(
|
|
|
|
&QName::new(Namespace::public_namespace(), "prototype"),
|
|
|
|
self,
|
|
|
|
context,
|
|
|
|
)?
|
|
|
|
.resolve(self, context)?
|
|
|
|
.as_object()?;
|
|
|
|
|
|
|
|
let object = proto.construct(self, context, &args)?;
|
2020-02-24 03:11:02 +00:00
|
|
|
ctor.call(Some(object), &args, self, context)?
|
2020-02-19 23:53:21 +00:00
|
|
|
.resolve(self, context)?;
|
|
|
|
|
2020-02-22 23:44:51 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_new_activation(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
|
|
|
self.push(ScriptObject::bare_object(context.gc_context));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_new_object(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
num_args: u32,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let object = ScriptObject::object(context.gc_context, self.system_prototypes.object);
|
|
|
|
|
|
|
|
for _ in 0..num_args {
|
|
|
|
let value = self.pop();
|
|
|
|
let name = self.pop();
|
|
|
|
|
|
|
|
object.set_property(
|
|
|
|
&QName::new(Namespace::public_namespace(), name.as_string()?),
|
|
|
|
value,
|
|
|
|
self,
|
|
|
|
context,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.push(object);
|
|
|
|
|
2020-02-19 23:53:21 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-22 22:54:38 +00:00
|
|
|
|
|
|
|
fn op_new_function(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcMethod>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let method_entry = self.table_method(index)?;
|
|
|
|
let scope = self.current_stack_frame().unwrap().read().scope();
|
|
|
|
|
|
|
|
let new_fn = FunctionObject::from_abc_method(
|
|
|
|
context.gc_context,
|
|
|
|
method_entry,
|
|
|
|
scope,
|
|
|
|
self.system_prototypes.function,
|
|
|
|
);
|
|
|
|
|
|
|
|
self.push(new_fn);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn op_new_class(
|
|
|
|
&mut self,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
index: Index<AbcClass>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let base_class = self.pop().as_object()?;
|
|
|
|
let class_entry = self.table_class(index)?;
|
|
|
|
let scope = self.current_stack_frame().unwrap().read().scope();
|
|
|
|
|
|
|
|
let new_class = FunctionObject::from_abc_class(
|
|
|
|
self,
|
|
|
|
context,
|
|
|
|
class_entry,
|
|
|
|
base_class,
|
|
|
|
scope,
|
|
|
|
self.system_prototypes.function,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
self.push(new_class);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-22 23:24:10 +00:00
|
|
|
|
2020-02-24 03:24:23 +00:00
|
|
|
fn op_coerce_a(&mut self) -> Result<(), Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-02-22 23:24:10 +00:00
|
|
|
#[allow(unused_variables)]
|
|
|
|
fn op_debug(
|
|
|
|
&mut self,
|
|
|
|
is_local_register: bool,
|
|
|
|
register_name: Index<String>,
|
|
|
|
register: u8,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
if is_local_register {
|
|
|
|
let register_name = self.pool_string(register_name)?;
|
|
|
|
let value = self.register_value(register as u32)?;
|
|
|
|
|
|
|
|
avm_debug!("Debug: {} = {:?}", register_name, value);
|
|
|
|
} else {
|
|
|
|
avm_debug!("Unknown debugging mode!");
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(unused_variables)]
|
|
|
|
fn op_debug_file(&mut self, file_name: Index<String>) -> Result<(), Error> {
|
|
|
|
let file_name = self.pool_string(file_name)?;
|
|
|
|
|
|
|
|
avm_debug!("File: {}", file_name);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(unused_variables)]
|
|
|
|
fn op_debug_line(&mut self, line_num: u32) -> Result<(), Error> {
|
|
|
|
avm_debug!("Line: {}", line_num);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-04 02:22:44 +00:00
|
|
|
}
|