Skip to content

Commit 1a5d7fe

Browse files
authored
avm2: Initial AVM2 interpreter (merge #404)
Initial work on the AVM2 interpreter.
2 parents 4967fb4 + 7adabc8 commit 1a5d7fe

File tree

175 files changed

+9543
-83
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

175 files changed

+9543
-83
lines changed

core/src/avm1/string.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
use gc_arena::{Collect, Gc, MutationContext};
2+
use std::cmp::{Eq, Ord, Ordering, PartialOrd};
23
use std::fmt;
4+
use std::hash::{Hash, Hasher};
35
use std::ops::Deref;
46

5-
#[derive(Debug, Clone, Collect)]
7+
#[derive(Debug, Clone, Copy, Collect)]
68
#[collect(no_drop)]
79
enum Source<'gc> {
810
Owned(Gc<'gc, String>),
911
Static(&'static str),
1012
}
1113

12-
#[derive(Debug, Clone, Collect)]
14+
#[derive(Debug, Clone, Copy, Collect)]
1315
#[collect(no_drop)]
1416
pub struct AvmString<'gc> {
1517
source: Source<'gc>,
@@ -78,6 +80,29 @@ impl<'gc> PartialEq<AvmString<'gc>> for AvmString<'gc> {
7880
}
7981
}
8082

83+
impl<'gc> Eq for AvmString<'gc> {}
84+
85+
impl<'gc> PartialOrd<AvmString<'gc>> for AvmString<'gc> {
86+
fn partial_cmp(&self, other: &AvmString<'gc>) -> Option<Ordering> {
87+
self.as_ref().partial_cmp(other.as_ref())
88+
}
89+
}
90+
91+
impl<'gc> Ord for AvmString<'gc> {
92+
fn cmp(&self, other: &AvmString<'gc>) -> Ordering {
93+
self.as_ref().cmp(other.as_ref())
94+
}
95+
}
96+
97+
impl<'gc> Hash for AvmString<'gc> {
98+
fn hash<H>(&self, state: &mut H)
99+
where
100+
H: Hasher,
101+
{
102+
self.as_ref().hash(state)
103+
}
104+
}
105+
81106
macro_rules! impl_eq {
82107
($lhs:ty, $rhs: ty) => {
83108
#[allow(unused_lifetimes)]

core/src/avm2.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//! ActionScript Virtual Machine 2 (AS3) support
2+
3+
use crate::avm2::activation::Activation;
4+
use crate::avm2::globals::SystemPrototypes;
5+
use crate::avm2::object::{Object, TObject};
6+
use crate::avm2::scope::Scope;
7+
use crate::avm2::script::Script;
8+
use crate::avm2::script::TranslationUnit;
9+
use crate::avm2::value::Value;
10+
use crate::context::UpdateContext;
11+
use crate::tag_utils::SwfSlice;
12+
use gc_arena::{Collect, GcCell, MutationContext};
13+
use std::rc::Rc;
14+
use swf::avm2::read::Reader;
15+
16+
#[macro_export]
17+
macro_rules! avm_debug {
18+
($($arg:tt)*) => (
19+
#[cfg(feature = "avm_debug")]
20+
log::debug!($($arg)*)
21+
)
22+
}
23+
24+
mod activation;
25+
mod class;
26+
mod function;
27+
mod globals;
28+
mod method;
29+
mod names;
30+
mod object;
31+
mod property;
32+
mod property_map;
33+
mod return_value;
34+
mod scope;
35+
mod script;
36+
mod script_object;
37+
mod slot;
38+
mod string;
39+
mod r#trait;
40+
mod value;
41+
42+
/// Boxed error alias.
43+
///
44+
/// As AVM2 is a far stricter VM than AVM1, this may eventually be replaced
45+
/// with a proper Avm2Error enum.
46+
type Error = Box<dyn std::error::Error>;
47+
48+
/// The state of an AVM2 interpreter.
49+
#[derive(Collect)]
50+
#[collect(no_drop)]
51+
pub struct Avm2<'gc> {
52+
/// Values currently present on the operand stack.
53+
stack: Vec<Value<'gc>>,
54+
55+
/// Global scope object.
56+
globals: Object<'gc>,
57+
58+
/// System prototypes.
59+
system_prototypes: SystemPrototypes<'gc>,
60+
}
61+
62+
impl<'gc> Avm2<'gc> {
63+
/// Construct a new AVM interpreter.
64+
pub fn new(mc: MutationContext<'gc, '_>) -> Self {
65+
let (globals, system_prototypes) = globals::construct_global_scope(mc);
66+
67+
Self {
68+
stack: Vec::new(),
69+
globals,
70+
system_prototypes,
71+
}
72+
}
73+
74+
/// Return the current set of system prototypes.
75+
pub fn prototypes(&self) -> &SystemPrototypes<'gc> {
76+
&self.system_prototypes
77+
}
78+
79+
/// Run a script's initializer method.
80+
pub fn run_script_initializer(
81+
&mut self,
82+
script: GcCell<'gc, Script<'gc>>,
83+
context: &mut UpdateContext<'_, 'gc, '_>,
84+
) -> Result<(), Error> {
85+
let mut init_activation = Activation::from_script(self, context, script, self.globals)?;
86+
87+
init_activation.run_stack_frame_for_script(context, script)
88+
}
89+
90+
/// Load an ABC file embedded in a `SwfSlice`.
91+
///
92+
/// The `SwfSlice` must resolve to the contents of an ABC file.
93+
pub fn load_abc(
94+
&mut self,
95+
abc: SwfSlice,
96+
_abc_name: &str,
97+
_lazy_init: bool,
98+
context: &mut UpdateContext<'_, 'gc, '_>,
99+
) -> Result<(), Error> {
100+
let mut read = Reader::new(abc.as_ref());
101+
102+
let abc_file = Rc::new(read.read()?);
103+
let tunit = TranslationUnit::from_abc(abc_file.clone(), context.gc_context);
104+
105+
for i in (0..abc_file.scripts.len()).rev() {
106+
let script = tunit.load_script(i as u32, context.gc_context)?;
107+
let mut globals = self.globals();
108+
let scope = Scope::push_scope(None, globals, context.gc_context);
109+
let mut null_activation = Activation::from_nothing(self, context);
110+
111+
// TODO: Lazyinit means we shouldn't do this until traits are
112+
// actually mentioned...
113+
for trait_entry in script.read().traits()?.iter() {
114+
globals.install_foreign_trait(
115+
&mut null_activation,
116+
context,
117+
trait_entry.clone(),
118+
Some(scope),
119+
globals,
120+
)?;
121+
}
122+
123+
drop(null_activation);
124+
125+
self.run_script_initializer(script, context)?;
126+
}
127+
128+
Ok(())
129+
}
130+
131+
pub fn globals(&self) -> Object<'gc> {
132+
self.globals
133+
}
134+
135+
/// Push a value onto the operand stack.
136+
fn push(&mut self, value: impl Into<Value<'gc>>) {
137+
let value = value.into();
138+
avm_debug!("Stack push {}: {:?}", self.stack.len(), value);
139+
self.stack.push(value);
140+
}
141+
142+
/// Retrieve the top-most value on the operand stack.
143+
#[allow(clippy::let_and_return)]
144+
fn pop(&mut self) -> Value<'gc> {
145+
let value = self.stack.pop().unwrap_or_else(|| {
146+
log::warn!("Avm1::pop: Stack underflow");
147+
Value::Undefined
148+
});
149+
150+
avm_debug!("Stack pop {}: {:?}", self.stack.len(), value);
151+
152+
value
153+
}
154+
155+
fn pop_args(&mut self, arg_count: u32) -> Vec<Value<'gc>> {
156+
let mut args = Vec::with_capacity(arg_count as usize);
157+
args.resize(arg_count as usize, Value::Undefined);
158+
for arg in args.iter_mut().rev() {
159+
*arg = self.pop();
160+
}
161+
162+
args
163+
}
164+
}

0 commit comments

Comments
 (0)