avm: Refactor Avm1, move execution to StackFrame (merge #767)

AVM1 refactoring: moving execution from Avm1 to StackFrame
This commit is contained in:
Mike Welsh 2020-07-01 15:56:09 -07:00 committed by GitHub
commit 4b4370b90d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 5994 additions and 5898 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
use crate::avm1::{Avm1, Object, ObjectPtr, TObject, Value}; use crate::avm1::activation::Activation;
use crate::avm1::{Object, ObjectPtr, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
#[allow(dead_code)] #[allow(dead_code)]
@ -23,11 +24,11 @@ impl<'a> VariableDumper<'a> {
pub fn dump<'gc>( pub fn dump<'gc>(
value: &Value<'gc>, value: &Value<'gc>,
indent: &str, indent: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> String { ) -> String {
let mut dumper = VariableDumper::new(indent); let mut dumper = VariableDumper::new(indent);
dumper.print_value(value, avm, context); dumper.print_value(value, activation, context);
dumper.output dumper.output
} }
@ -84,7 +85,7 @@ impl<'a> VariableDumper<'a> {
pub fn print_object<'gc>( pub fn print_object<'gc>(
&mut self, &mut self,
object: &Object<'gc>, object: &Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
let (id, new) = self.object_id(object); let (id, new) = self.object_id(object);
@ -93,7 +94,7 @@ impl<'a> VariableDumper<'a> {
self.output.push_str("]"); self.output.push_str("]");
if new { if new {
self.print_properties(object, avm, context); self.print_properties(object, activation, context);
} }
} }
@ -101,12 +102,12 @@ impl<'a> VariableDumper<'a> {
&mut self, &mut self,
object: &Object<'gc>, object: &Object<'gc>,
key: &str, key: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
match object.get(&key, avm, context) { match object.get(&key, activation, context) {
Ok(value) => { Ok(value) => {
self.print_value(&value, avm, context); self.print_value(&value, activation, context);
} }
Err(e) => { Err(e) => {
self.output.push_str("Error: \""); self.output.push_str("Error: \"");
@ -119,10 +120,10 @@ impl<'a> VariableDumper<'a> {
pub fn print_properties<'gc>( pub fn print_properties<'gc>(
&mut self, &mut self,
object: &Object<'gc>, object: &Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
let keys = object.get_keys(avm); let keys = object.get_keys(activation);
if keys.is_empty() { if keys.is_empty() {
self.output.push_str(" {}"); self.output.push_str(" {}");
} else { } else {
@ -133,7 +134,7 @@ impl<'a> VariableDumper<'a> {
self.indent(); self.indent();
self.output.push_str(&key); self.output.push_str(&key);
self.output.push_str(": "); self.output.push_str(": ");
self.print_property(object, &key, avm, context); self.print_property(object, &key, activation, context);
self.output.push_str("\n"); self.output.push_str("\n");
} }
@ -146,7 +147,7 @@ impl<'a> VariableDumper<'a> {
pub fn print_value<'gc>( pub fn print_value<'gc>(
&mut self, &mut self,
value: &Value<'gc>, value: &Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
match value { match value {
@ -158,7 +159,7 @@ impl<'a> VariableDumper<'a> {
self.print_string(value); self.print_string(value);
} }
Value::Object(object) => { Value::Object(object) => {
self.print_object(object, avm, context); self.print_object(object, activation, context);
} }
} }
} }
@ -168,10 +169,10 @@ impl<'a> VariableDumper<'a> {
header: &str, header: &str,
name: &str, name: &str,
object: &Object<'gc>, object: &Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
let keys = object.get_keys(avm); let keys = object.get_keys(activation);
if keys.is_empty() { if keys.is_empty() {
return; return;
} }
@ -183,7 +184,7 @@ impl<'a> VariableDumper<'a> {
for key in keys.into_iter() { for key in keys.into_iter() {
self.output.push_str(&format!("{}.{}", name, key)); self.output.push_str(&format!("{}.{}", name, key));
self.output.push_str(" = "); self.output.push_str(" = ");
self.print_property(object, &key, avm, context); self.print_property(object, &key, activation, context);
self.output.push_str("\n"); self.output.push_str("\n");
} }
@ -197,25 +198,24 @@ mod tests {
use super::*; use super::*;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::test_utils::with_avm; use crate::avm1::test_utils::with_avm;
use crate::avm1::ScriptObject; use crate::avm1::ScriptObject;
use enumset::EnumSet; use enumset::EnumSet;
fn throw_error<'gc>( fn throw_error<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Err(Error::PrototypeRecursionLimit) Err(Error::PrototypeRecursionLimit)
} }
#[test] #[test]
fn dump_undefined() { fn dump_undefined() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
assert_eq!( assert_eq!(
VariableDumper::dump(&Value::Undefined, " ", avm, context), VariableDumper::dump(&Value::Undefined, " ", activation, context),
"undefined" "undefined"
); );
Ok(()) Ok(())
@ -224,9 +224,9 @@ mod tests {
#[test] #[test]
fn dump_null() { fn dump_null() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
assert_eq!( assert_eq!(
VariableDumper::dump(&Value::Null, " ", avm, context), VariableDumper::dump(&Value::Null, " ", activation, context),
"null" "null"
); );
Ok(()) Ok(())
@ -235,13 +235,13 @@ mod tests {
#[test] #[test]
fn dump_bool() { fn dump_bool() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
assert_eq!( assert_eq!(
VariableDumper::dump(&Value::Bool(true), " ", avm, context), VariableDumper::dump(&Value::Bool(true), " ", activation, context),
"true" "true"
); );
assert_eq!( assert_eq!(
VariableDumper::dump(&Value::Bool(false), " ", avm, context), VariableDumper::dump(&Value::Bool(false), " ", activation, context),
"false" "false"
); );
Ok(()) Ok(())
@ -250,13 +250,13 @@ mod tests {
#[test] #[test]
fn dump_number() { fn dump_number() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
assert_eq!( assert_eq!(
VariableDumper::dump(&Value::Number(1000.0), " ", avm, context), VariableDumper::dump(&Value::Number(1000.0), " ", activation, context),
"1000" "1000"
); );
assert_eq!( assert_eq!(
VariableDumper::dump(&Value::Number(-0.05), " ", avm, context), VariableDumper::dump(&Value::Number(-0.05), " ", activation, context),
"-0.05" "-0.05"
); );
Ok(()) Ok(())
@ -265,13 +265,18 @@ mod tests {
#[test] #[test]
fn dump_string() { fn dump_string() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
assert_eq!( assert_eq!(
VariableDumper::dump(&Value::String("".to_string()), " ", avm, context), VariableDumper::dump(&Value::String("".to_string()), " ", activation, context),
"\"\"" "\"\""
); );
assert_eq!( assert_eq!(
VariableDumper::dump(&Value::String("HELLO WORLD".to_string()), " ", avm, context), VariableDumper::dump(
&Value::String("HELLO WORLD".to_string()),
" ",
activation,
context
),
"\"HELLO WORLD\"" "\"HELLO WORLD\""
); );
assert_eq!( assert_eq!(
@ -280,7 +285,7 @@ mod tests {
"Escape \"this\" string\nplease! \u{0008}\u{000C}\n\r\t\"\\".to_string() "Escape \"this\" string\nplease! \u{0008}\u{000C}\n\r\t\"\\".to_string()
), ),
" ", " ",
avm, activation,
context context
), ),
"\"Escape \\\"this\\\" string\\nplease! \\b\\f\\n\\r\\t\\\"\\\\\"" "\"Escape \\\"this\\\" string\\nplease! \\b\\f\\n\\r\\t\\\"\\\\\""
@ -291,10 +296,10 @@ mod tests {
#[test] #[test]
fn dump_empty_object() { fn dump_empty_object() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
let object = ScriptObject::object(context.gc_context, None); let object = ScriptObject::object(context.gc_context, None);
assert_eq!( assert_eq!(
VariableDumper::dump(&object.into(), " ", avm, context), VariableDumper::dump(&object.into(), " ", activation, context),
"[object #0] {}" "[object #0] {}"
); );
Ok(()) Ok(())
@ -303,16 +308,21 @@ mod tests {
#[test] #[test]
fn dump_object() { fn dump_object() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
let object = ScriptObject::object(context.gc_context, None); let object = ScriptObject::object(context.gc_context, None);
let child = ScriptObject::object(context.gc_context, None); let child = ScriptObject::object(context.gc_context, None);
object.set("self", object.into(), avm, context)?; object.set("self", object.into(), activation, context)?;
object.set("test", Value::String("value".to_string()), avm, context)?; object.set(
object.set("child", child.into(), avm, context)?; "test",
child.set("parent", object.into(), avm, context)?; Value::String("value".to_string()),
child.set("age", Value::Number(6.0), avm, context)?; activation,
context,
)?;
object.set("child", child.into(), activation, context)?;
child.set("parent", object.into(), activation, context)?;
child.set("age", Value::Number(6.0), activation, context)?;
assert_eq!( assert_eq!(
VariableDumper::dump(&object.into(), " ", avm, context), VariableDumper::dump(&object.into(), " ", activation, context),
"[object #0] {\n child: [object #1] {\n age: 6\n parent: [object #0]\n }\n test: \"value\"\n self: [object #0]\n}", "[object #0] {\n child: [object #1] {\n age: 6\n parent: [object #0]\n }\n test: \"value\"\n self: [object #0]\n}",
); );
Ok(()) Ok(())
@ -321,7 +331,7 @@ mod tests {
#[test] #[test]
fn dump_object_with_error() { fn dump_object_with_error() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
let object = ScriptObject::object(context.gc_context, None); let object = ScriptObject::object(context.gc_context, None);
object.add_property( object.add_property(
context.gc_context, context.gc_context,
@ -331,7 +341,7 @@ mod tests {
EnumSet::empty(), EnumSet::empty(),
); );
assert_eq!( assert_eq!(
VariableDumper::dump(&object.into(), " ", avm, context), VariableDumper::dump(&object.into(), " ", activation, context),
"[object #0] {\n broken_value: Error: \"Prototype recursion limit has been exceeded\"\n}" "[object #0] {\n broken_value: Error: \"Prototype recursion limit has been exceeded\"\n}"
); );
Ok(()) Ok(())
@ -340,16 +350,21 @@ mod tests {
#[test] #[test]
fn dump_variables() { fn dump_variables() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
let object = ScriptObject::object(context.gc_context, None); let object = ScriptObject::object(context.gc_context, None);
let child = ScriptObject::object(context.gc_context, None); let child = ScriptObject::object(context.gc_context, None);
object.set("self", object.into(), avm, context)?; object.set("self", object.into(), activation, context)?;
object.set("test", Value::String("value".to_string()), avm, context)?; object.set(
object.set("child", child.into(), avm, context)?; "test",
child.set("parent", object.into(), avm, context)?; Value::String("value".to_string()),
child.set("age", Value::Number(6.0), avm, context)?; activation,
context,
)?;
object.set("child", child.into(), activation, context)?;
child.set("parent", object.into(), activation, context)?;
child.set("age", Value::Number(6.0), activation, context)?;
let mut dumper = VariableDumper::new(" "); let mut dumper = VariableDumper::new(" ");
dumper.print_variables("Variables:", "object", &object.into(), avm, context); dumper.print_variables("Variables:", "object", &object.into(), activation, context);
assert_eq!( assert_eq!(
dumper.output, dumper.output,
"Variables:\nobject.child = [object #0] {\n age: 6\n parent: [object #1] {\n child: [object #0]\n test: \"value\"\n self: [object #1]\n }\n }\nobject.test = \"value\"\nobject.self = [object #1]\n\n" "Variables:\nobject.child = [object #0] {\n age: 6\n parent: [object #1] {\n child: [object #0]\n test: \"value\"\n self: [object #1]\n }\n }\nobject.test = \"value\"\nobject.self = [object #1]\n\n"

View File

@ -9,15 +9,6 @@ pub enum Error<'gc> {
#[error("Couldn't parse SWF. This may or may not be a bug in Ruffle, please help us by reporting it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")] #[error("Couldn't parse SWF. This may or may not be a bug in Ruffle, please help us by reporting it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
InvalidSwf(#[from] swf::error::Error), InvalidSwf(#[from] swf::error::Error),
#[error("No stack frame to execute. This is probably a bug in Ruffle, please report it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
NoStackFrame,
#[error("Attempted to run a frame not on the current interpreter stack. This is probably a bug in Ruffle, please report it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
FrameNotOnStack,
#[error("Attempted to execute the same frame twice. This is probably a bug in Ruffle, please report it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
AlreadyExecutingFrame,
#[error("A script has thrown a custom error.")] #[error("A script has thrown a custom error.")]
ThrownValue(Value<'gc>), ThrownValue(Value<'gc>),
} }
@ -27,9 +18,6 @@ impl Error<'_> {
match self { match self {
Error::PrototypeRecursionLimit => true, Error::PrototypeRecursionLimit => true,
Error::InvalidSwf(_) => true, Error::InvalidSwf(_) => true,
Error::NoStackFrame => true,
Error::FrameNotOnStack => true,
Error::AlreadyExecutingFrame => false,
Error::ThrownValue(_) => false, Error::ThrownValue(_) => false,
} }
} }

View File

@ -1,8 +1,8 @@
//! FSCommand handling //! FSCommand handling
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::{Avm1, UpdateContext}; use crate::avm1::UpdateContext;
/// Parse an FSCommand URL. /// Parse an FSCommand URL.
pub fn parse(url: &str) -> Option<&str> { pub fn parse(url: &str) -> Option<&str> {
log::info!("Checking {}", url); log::info!("Checking {}", url);
@ -16,7 +16,7 @@ pub fn parse(url: &str) -> Option<&str> {
/// TODO: FSCommand URL handling /// TODO: FSCommand URL handling
pub fn handle<'gc>( pub fn handle<'gc>(
fscommand: &str, fscommand: &str,
_avm: &mut Avm1, _activation: &mut Activation,
_ac: &mut UpdateContext, _ac: &mut UpdateContext,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
log::warn!("Unhandled FSCommand: {}", fscommand); log::warn!("Unhandled FSCommand: {}", fscommand);

View File

@ -3,11 +3,10 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::property::{Attribute, Attribute::*}; use crate::avm1::property::{Attribute, Attribute::*};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::scope::Scope; use crate::avm1::scope::Scope;
use crate::avm1::super_object::SuperObject; use crate::avm1::super_object::SuperObject;
use crate::avm1::value::Value; use crate::avm1::value::Value;
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, UpdateContext}; use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, UpdateContext};
use crate::display_object::{DisplayObject, TDisplayObject}; use crate::display_object::{DisplayObject, TDisplayObject};
use crate::tag_utils::SwfSlice; use crate::tag_utils::SwfSlice;
use enumset::EnumSet; use enumset::EnumSet;
@ -31,11 +30,11 @@ use swf::avm1::types::FunctionParam;
/// your function yields `None`, you must ensure that the top-most activation /// your function yields `None`, you must ensure that the top-most activation
/// in the AVM1 runtime will return with the value of this function. /// in the AVM1 runtime will return with the value of this function.
pub type NativeFunction<'gc> = fn( pub type NativeFunction<'gc> = fn(
&mut Avm1<'gc>, &mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>, &mut UpdateContext<'_, 'gc, '_>,
Object<'gc>, Object<'gc>,
&[Value<'gc>], &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>>; ) -> Result<Value<'gc>, Error<'gc>>;
/// Represents a function defined in the AVM1 runtime, either through /// Represents a function defined in the AVM1 runtime, either through
/// `DefineFunction` or `DefineFunction2`. /// `DefineFunction` or `DefineFunction2`.
@ -223,20 +222,21 @@ impl<'gc> Executable<'gc> {
/// create a new stack frame and execute the action data yourself. /// create a new stack frame and execute the action data yourself.
pub fn exec( pub fn exec(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
match self { match self {
Executable::Native(nf) => nf(avm, ac, this, args), Executable::Native(nf) => nf(activation, ac, this, args),
Executable::Action(af) => { Executable::Action(af) => {
let child_scope = GcCell::allocate( let child_scope = GcCell::allocate(
ac.gc_context, ac.gc_context,
Scope::new_local_scope(af.scope(), ac.gc_context), Scope::new_local_scope(af.scope(), ac.gc_context),
); );
let arguments = ScriptObject::object(ac.gc_context, Some(avm.prototypes().object)); let arguments =
ScriptObject::object(ac.gc_context, Some(activation.avm().prototypes().object));
if !af.suppress_arguments { if !af.suppress_arguments {
for i in 0..args.len() { for i in 0..args.len() {
arguments.define_value( arguments.define_value(
@ -261,7 +261,7 @@ impl<'gc> Executable<'gc> {
SuperObject::from_this_and_base_proto( SuperObject::from_this_and_base_proto(
this, this,
base_proto.unwrap_or(this), base_proto.unwrap_or(this),
avm, activation,
ac, ac,
)? )?
.into(), .into(),
@ -270,7 +270,7 @@ impl<'gc> Executable<'gc> {
None None
}; };
let effective_ver = if avm.current_swf_version() > 5 { let effective_ver = if activation.current_swf_version() > 5 {
af.swf_version() af.swf_version()
} else { } else {
this.as_display_object() this.as_display_object()
@ -278,19 +278,15 @@ impl<'gc> Executable<'gc> {
.unwrap_or(ac.player_version) .unwrap_or(ac.player_version)
}; };
let frame_cell = GcCell::allocate( let mut frame = Activation::from_action(
ac.gc_context, activation.avm(),
Activation::from_function( effective_ver,
effective_ver, child_scope,
af.data(), af.constant_pool,
child_scope, af.base_clip,
af.constant_pool, this,
af.base_clip, Some(argcell),
this,
Some(argcell),
),
); );
let mut frame = frame_cell.write(ac.gc_context);
frame.allocate_local_registers(af.register_count(), ac.gc_context); frame.allocate_local_registers(af.register_count(), ac.gc_context);
@ -341,7 +337,8 @@ impl<'gc> Executable<'gc> {
} }
if af.preload_global { if af.preload_global {
frame.set_local_register(preload_r, avm.global_object(ac), ac.gc_context); let global = frame.avm().global_object(ac);
frame.set_local_register(preload_r, global, ac.gc_context);
} }
//TODO: What happens if the argument registers clash with the //TODO: What happens if the argument registers clash with the
@ -357,9 +354,8 @@ impl<'gc> Executable<'gc> {
_ => {} _ => {}
} }
} }
avm.insert_stack_frame(frame_cell);
Ok(frame_cell.into()) Ok(frame.run_actions(ac, af.data.clone())?.value())
} }
} }
} }
@ -456,34 +452,33 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base.get_local(name, avm, context, this) self.base.get_local(name, activation, context, this)
} }
fn set( fn set(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base.set(name, value, avm, context) self.base.set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(exec) = self.as_executable() { if let Some(exec) = self.as_executable() {
exec.exec(avm, context, this, base_proto, args)? exec.exec(activation, context, this, base_proto, args)
.resolve(avm, context)
} else { } else {
Ok(Value::Undefined) Ok(Value::Undefined)
} }
@ -493,17 +488,16 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> { self.base.call_setter(name, value, activation, context)
self.base.call_setter(name, value, avm, context, this)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
prototype: Object<'gc>, prototype: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -525,11 +519,11 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base.delete(avm, gc_context, name) self.base.delete(activation, gc_context, name)
} }
fn proto(&self) -> Option<Object<'gc>> { fn proto(&self) -> Option<Object<'gc>> {
@ -575,7 +569,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -583,46 +577,46 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
attributes: EnumSet<Attribute>, attributes: EnumSet<Attribute>,
) { ) {
self.base self.base
.add_property_with_case(avm, gc_context, name, get, set, attributes) .add_property_with_case(activation, gc_context, name, get, set, attributes)
} }
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base.has_property(avm, context, name) self.base.has_property(activation, context, name)
} }
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base.has_own_property(avm, context, name) self.base.has_own_property(activation, context, name)
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base.has_own_virtual(avm, context, name) self.base.has_own_virtual(activation, context, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base.is_property_overwritable(avm, name) self.base.is_property_overwritable(activation, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base.is_property_enumerable(avm, name) self.base.is_property_enumerable(activation, name)
} }
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base.get_keys(avm) self.base.get_keys(activation)
} }
fn as_string(&self) -> Cow<str> { fn as_string(&self) -> Cow<str> {

View File

@ -1,9 +1,9 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::fscommand; use crate::avm1::fscommand;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::listeners::SystemListeners; use crate::avm1::listeners::SystemListeners;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::backend::navigator::NavigationMethod; use crate::backend::navigator::NavigationMethod;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
@ -40,21 +40,21 @@ mod xml;
#[allow(non_snake_case, unused_must_use)] //can't use errors yet #[allow(non_snake_case, unused_must_use)] //can't use errors yet
pub fn getURL<'a, 'gc>( pub fn getURL<'a, 'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'a, 'gc, '_>, context: &mut UpdateContext<'a, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
//TODO: Error behavior if no arguments are present //TODO: Error behavior if no arguments are present
if let Some(url_val) = args.get(0) { if let Some(url_val) = args.get(0) {
let url = url_val.coerce_to_string(avm, context)?; let url = url_val.coerce_to_string(activation, context)?;
if let Some(fscommand) = fscommand::parse(&url) { if let Some(fscommand) = fscommand::parse(&url) {
fscommand::handle(fscommand, avm, context); fscommand::handle(fscommand, activation, context);
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
let window = if let Some(window) = args.get(1) { let window = if let Some(window) = args.get(1) {
Some(window.coerce_to_string(avm, context)?.to_string()) Some(window.coerce_to_string(activation, context)?.to_string())
} else { } else {
None None
}; };
@ -63,64 +63,67 @@ pub fn getURL<'a, 'gc>(
Some(Value::String(s)) if s == "POST" => Some(NavigationMethod::POST), Some(Value::String(s)) if s == "POST" => Some(NavigationMethod::POST),
_ => None, _ => None,
}; };
let vars_method = method.map(|m| (m, avm.locals_into_form_values(context))); let vars_method = method.map(|m| (m, activation.locals_into_form_values(context)));
context context
.navigator .navigator
.navigate_to_url(url.to_string(), window, vars_method); .navigate_to_url(url.to_string(), window, vars_method);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn random<'gc>( pub fn random<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
match args.get(0) { match args.get(0) {
Some(Value::Number(max)) => Ok(action_context.rng.gen_range(0.0f64, max).floor().into()), Some(Value::Number(max)) => Ok(action_context.rng.gen_range(0.0f64, max).floor().into()),
_ => Ok(Value::Undefined.into()), //TODO: Shouldn't this be an error condition? _ => Ok(Value::Undefined), //TODO: Shouldn't this be an error condition?
} }
} }
pub fn is_nan<'gc>( pub fn is_nan<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(val) = args.get(0) { if let Some(val) = args.get(0) {
Ok(val.coerce_to_f64(avm, action_context)?.is_nan().into()) Ok(val
.coerce_to_f64(activation, action_context)?
.is_nan()
.into())
} else { } else {
Ok(true.into()) Ok(true.into())
} }
} }
pub fn get_infinity<'gc>( pub fn get_infinity<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() > 4 { if activation.current_swf_version() > 4 {
Ok(f64::INFINITY.into()) Ok(f64::INFINITY.into())
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
pub fn get_nan<'gc>( pub fn get_nan<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() > 4 { if activation.current_swf_version() > 4 {
Ok(f64::NAN.into()) Ok(f64::NAN.into())
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
@ -473,7 +476,10 @@ pub fn create_globals<'gc>(
mod tests { mod tests {
use super::*; use super::*;
fn setup<'gc>(_avm: &mut Avm1<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) -> Object<'gc> { fn setup<'gc>(
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Object<'gc> {
create_globals(context.gc_context).1 create_globals(context.gc_context).1
} }

View File

@ -1,10 +1,10 @@
//! Array class //! Array class
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
use smallvec::alloc::borrow::Cow; use smallvec::alloc::borrow::Cow;
@ -25,7 +25,12 @@ const DEFAULT_ORDERING: Ordering = Ordering::Equal;
// Compare function used by sort and sortOn. // Compare function used by sort and sortOn.
type CompareFn<'a, 'gc> = Box< type CompareFn<'a, 'gc> = Box<
dyn 'a dyn 'a
+ FnMut(&mut Avm1<'gc>, &mut UpdateContext<'_, 'gc, '_>, &Value<'gc>, &Value<'gc>) -> Ordering, + FnMut(
&mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>,
&Value<'gc>,
&Value<'gc>,
) -> Ordering,
>; >;
pub fn create_array_object<'gc>( pub fn create_array_object<'gc>(
@ -84,16 +89,16 @@ pub fn create_array_object<'gc>(
/// Implements `Array` /// Implements `Array`
pub fn constructor<'gc>( pub fn constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let mut consumed = false; let mut consumed = false;
if args.len() == 1 { if args.len() == 1 {
let arg = args.get(0).unwrap(); let arg = args.get(0).unwrap();
if let Ok(length) = arg.coerce_to_f64(avm, context) { if let Ok(length) = arg.coerce_to_f64(activation, context) {
if length >= 0.0 { if length >= 0.0 {
this.set_length(context.gc_context, length as usize); this.set_length(context.gc_context, length as usize);
consumed = true; consumed = true;
@ -115,15 +120,15 @@ pub fn constructor<'gc>(
this.set_length(context.gc_context, length); this.set_length(context.gc_context, length);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn push<'gc>( pub fn push<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let old_length = this.length(); let old_length = this.length();
let new_length = old_length + args.len(); let new_length = old_length + args.len();
this.set_length(context.gc_context, new_length); this.set_length(context.gc_context, new_length);
@ -140,11 +145,11 @@ pub fn push<'gc>(
} }
pub fn unshift<'gc>( pub fn unshift<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let old_length = this.length(); let old_length = this.length();
let new_length = old_length + args.len(); let new_length = old_length + args.len();
let offset = new_length - old_length; let offset = new_length - old_length;
@ -165,14 +170,14 @@ pub fn unshift<'gc>(
} }
pub fn shift<'gc>( pub fn shift<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let old_length = this.length(); let old_length = this.length();
if old_length == 0 { if old_length == 0 {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
let new_length = old_length - 1; let new_length = old_length - 1;
@ -184,41 +189,41 @@ pub fn shift<'gc>(
} }
this.delete_array_element(new_length, context.gc_context); this.delete_array_element(new_length, context.gc_context);
this.delete(avm, context.gc_context, &new_length.to_string()); this.delete(activation, context.gc_context, &new_length.to_string());
this.set_length(context.gc_context, new_length); this.set_length(context.gc_context, new_length);
Ok(removed.into()) Ok(removed)
} }
pub fn pop<'gc>( pub fn pop<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let old_length = this.length(); let old_length = this.length();
if old_length == 0 { if old_length == 0 {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
let new_length = old_length - 1; let new_length = old_length - 1;
let removed = this.array_element(new_length); let removed = this.array_element(new_length);
this.delete_array_element(new_length, context.gc_context); this.delete_array_element(new_length, context.gc_context);
this.delete(avm, context.gc_context, &new_length.to_string()); this.delete(activation, context.gc_context, &new_length.to_string());
this.set_length(context.gc_context, new_length); this.set_length(context.gc_context, new_length);
Ok(removed.into()) Ok(removed)
} }
pub fn reverse<'gc>( pub fn reverse<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let length = this.length(); let length = this.length();
let mut values = this.array().to_vec(); let mut values = this.array().to_vec();
@ -226,25 +231,25 @@ pub fn reverse<'gc>(
this.set_array_element(i, values.pop().unwrap(), context.gc_context); this.set_array_element(i, values.pop().unwrap(), context.gc_context);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn join<'gc>( pub fn join<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let separator = args let separator = args
.get(0) .get(0)
.and_then(|v| v.coerce_to_string(avm, context).ok()) .and_then(|v| v.coerce_to_string(activation, context).ok())
.unwrap_or_else(|| Cow::Borrowed(",")); .unwrap_or_else(|| Cow::Borrowed(","));
let values: Vec<Value<'gc>> = this.array(); let values: Vec<Value<'gc>> = this.array();
Ok(values Ok(values
.iter() .iter()
.map(|v| { .map(|v| {
v.coerce_to_string(avm, context) v.coerce_to_string(activation, context)
.unwrap_or_else(|_| Cow::Borrowed("undefined")) .unwrap_or_else(|_| Cow::Borrowed("undefined"))
}) })
.collect::<Vec<Cow<str>>>() .collect::<Vec<Cow<str>>>()
@ -264,23 +269,23 @@ fn make_index_absolute(mut index: i32, length: usize) -> usize {
} }
pub fn slice<'gc>( pub fn slice<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let start = args let start = args
.get(0) .get(0)
.and_then(|v| v.coerce_to_f64(avm, context).ok()) .and_then(|v| v.coerce_to_f64(activation, context).ok())
.map(|v| make_index_absolute(v as i32, this.length())) .map(|v| make_index_absolute(v as i32, this.length()))
.unwrap_or(0); .unwrap_or(0);
let end = args let end = args
.get(1) .get(1)
.and_then(|v| v.coerce_to_f64(avm, context).ok()) .and_then(|v| v.coerce_to_f64(activation, context).ok())
.map(|v| make_index_absolute(v as i32, this.length())) .map(|v| make_index_absolute(v as i32, this.length()))
.unwrap_or_else(|| this.length()); .unwrap_or_else(|| this.length());
let array = ScriptObject::array(context.gc_context, Some(avm.prototypes.array)); let array = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
if start < end { if start < end {
let length = end - start; let length = end - start;
@ -295,31 +300,31 @@ pub fn slice<'gc>(
} }
pub fn splice<'gc>( pub fn splice<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() { if args.is_empty() {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
let old_length = this.length(); let old_length = this.length();
let start = args let start = args
.get(0) .get(0)
.and_then(|v| v.coerce_to_f64(avm, context).ok()) .and_then(|v| v.coerce_to_f64(activation, context).ok())
.map(|v| make_index_absolute(v as i32, old_length)) .map(|v| make_index_absolute(v as i32, old_length))
.unwrap_or(0); .unwrap_or(0);
let count = args let count = args
.get(1) .get(1)
.and_then(|v| v.coerce_to_f64(avm, context).ok()) .and_then(|v| v.coerce_to_f64(activation, context).ok())
.map(|v| v as i32) .map(|v| v as i32)
.unwrap_or(old_length as i32); .unwrap_or(old_length as i32);
if count < 0 { if count < 0 {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
let removed = ScriptObject::array(context.gc_context, Some(avm.prototypes.array)); let removed = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
let to_remove = count.min(old_length as i32 - start as i32).max(0) as usize; let to_remove = count.min(old_length as i32 - start as i32).max(0) as usize;
let to_add = if args.len() > 2 { &args[2..] } else { &[] }; let to_add = if args.len() > 2 { &args[2..] } else { &[] };
let offset = to_remove as i32 - to_add.len() as i32; let offset = to_remove as i32 - to_add.len() as i32;
@ -358,7 +363,7 @@ pub fn splice<'gc>(
for i in new_length..old_length { for i in new_length..old_length {
this.delete_array_element(i, context.gc_context); this.delete_array_element(i, context.gc_context);
this.delete(avm, context.gc_context, &i.to_string()); this.delete(activation, context.gc_context, &i.to_string());
} }
this.set_length(context.gc_context, new_length); this.set_length(context.gc_context, new_length);
@ -367,17 +372,17 @@ pub fn splice<'gc>(
} }
pub fn concat<'gc>( pub fn concat<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let array = ScriptObject::array(context.gc_context, Some(avm.prototypes.array)); let array = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
let mut length = 0; let mut length = 0;
for i in 0..this.length() { for i in 0..this.length() {
let old = this let old = this
.get(&i.to_string(), avm, context) .get(&i.to_string(), activation, context)
.unwrap_or(Value::Undefined); .unwrap_or(Value::Undefined);
array.define_value( array.define_value(
context.gc_context, context.gc_context,
@ -393,11 +398,11 @@ pub fn concat<'gc>(
if let Value::Object(object) = arg { if let Value::Object(object) = arg {
let object = *object; let object = *object;
if avm.prototypes.array.is_prototype_of(object) { if activation.avm().prototypes.array.is_prototype_of(object) {
added = true; added = true;
for i in 0..object.length() { for i in 0..object.length() {
let old = object let old = object
.get(&i.to_string(), avm, context) .get(&i.to_string(), activation, context)
.unwrap_or(Value::Undefined); .unwrap_or(Value::Undefined);
array.define_value( array.define_value(
context.gc_context, context.gc_context,
@ -427,20 +432,20 @@ pub fn concat<'gc>(
} }
pub fn to_string<'gc>( pub fn to_string<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
join(avm, context, this, &[]) join(activation, context, this, &[])
} }
fn sort<'gc>( fn sort<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
// Overloads: // Overloads:
// 1) a.sort(flags: Number = 0): Sorts with the given flags. // 1) a.sort(flags: Number = 0): Sorts with the given flags.
// 2) a.sort(compare_fn: Object, flags: Number = 0): Sorts using the given compare function and flags. // 2) a.sort(compare_fn: Object, flags: Number = 0): Sorts using the given compare function and flags.
@ -453,7 +458,7 @@ fn sort<'gc>(
} }
[compare_fn @ Value::Object(_), ..] => (Some(compare_fn), 0), [compare_fn @ Value::Object(_), ..] => (Some(compare_fn), 0),
[] => (None, 0), [] => (None, 0),
_ => return Ok(Value::Undefined.into()), _ => return Ok(Value::Undefined),
}; };
let numeric = (flags & NUMERIC) != 0; let numeric = (flags & NUMERIC) != 0;
@ -466,10 +471,11 @@ fn sort<'gc>(
}; };
let compare_fn: CompareFn<'_, 'gc> = if let Some(f) = compare_fn { let compare_fn: CompareFn<'_, 'gc> = if let Some(f) = compare_fn {
let this = crate::avm1::value_object::ValueObject::boxed(avm, context, Value::Undefined); let this =
crate::avm1::value_object::ValueObject::boxed(activation, context, Value::Undefined);
// this is undefined in the compare function // this is undefined in the compare function
Box::new(move |avm, context, a: &Value<'gc>, b: &Value<'gc>| { Box::new(move |activation, context, a: &Value<'gc>, b: &Value<'gc>| {
sort_compare_custom(avm, context, this, a, b, &f) sort_compare_custom(activation, context, this, a, b, &f)
}) })
} else if numeric { } else if numeric {
Box::new(sort_compare_numeric(string_compare_fn)) Box::new(sort_compare_numeric(string_compare_fn))
@ -477,15 +483,15 @@ fn sort<'gc>(
Box::new(string_compare_fn) Box::new(string_compare_fn)
}; };
sort_with_function(avm, context, this, compare_fn, flags) sort_with_function(activation, context, this, compare_fn, flags)
} }
fn sort_on<'gc>( fn sort_on<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
// a.sortOn(field_name, flags: Number = 0): Sorts with the given flags. // a.sortOn(field_name, flags: Number = 0): Sorts with the given flags.
// a.sortOn(field_names: Array, flags: Number = 0): Sorts with fields in order of precedence with the given flags. // a.sortOn(field_names: Array, flags: Number = 0): Sorts with fields in order of precedence with the given flags.
// a.sortOn(field_names: Array, flags: Array: Sorts with fields in order of precedence with the given flags respectively. // a.sortOn(field_names: Array, flags: Array: Sorts with fields in order of precedence with the given flags respectively.
@ -494,15 +500,17 @@ fn sort_on<'gc>(
// Array of field names. // Array of field names.
let mut field_names = vec![]; let mut field_names = vec![];
for name in array.array() { for name in array.array() {
field_names.push(name.coerce_to_string(avm, context)?.to_string()); field_names.push(name.coerce_to_string(activation, context)?.to_string());
} }
field_names field_names
} }
Some(field_name) => { Some(field_name) => {
// Single field. // Single field.
vec![field_name.coerce_to_string(avm, context)?.to_string()] vec![field_name
.coerce_to_string(activation, context)?
.to_string()]
} }
None => return Ok(Value::Undefined.into()), None => return Ok(Value::Undefined),
}; };
// Bail out if we don't have any fields. // Bail out if we don't have any fields.
@ -516,7 +524,7 @@ fn sort_on<'gc>(
if array.length() == fields.len() { if array.length() == fields.len() {
let mut flags = vec![]; let mut flags = vec![];
for flag in array.array() { for flag in array.array() {
flags.push(flag.coerce_to_i32(avm, context)?); flags.push(flag.coerce_to_i32(activation, context)?);
} }
flags flags
} else { } else {
@ -526,7 +534,7 @@ fn sort_on<'gc>(
} }
Some(flags) => { Some(flags) => {
// Single field. // Single field.
let flags = flags.coerce_to_i32(avm, context)?; let flags = flags.coerce_to_i32(activation, context)?;
std::iter::repeat(flags).take(fields.len()).collect() std::iter::repeat(flags).take(fields.len()).collect()
} }
None => std::iter::repeat(0).take(fields.len()).collect(), None => std::iter::repeat(0).take(fields.len()).collect(),
@ -558,24 +566,24 @@ fn sort_on<'gc>(
let compare_fn = sort_compare_fields(fields, field_compare_fns); let compare_fn = sort_compare_fields(fields, field_compare_fns);
sort_with_function(avm, context, this, compare_fn, main_flags) sort_with_function(activation, context, this, compare_fn, main_flags)
} }
fn sort_with_function<'gc>( fn sort_with_function<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
mut compare_fn: impl FnMut( mut compare_fn: impl FnMut(
&mut Avm1<'gc>, &mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>, &mut UpdateContext<'_, 'gc, '_>,
&Value<'gc>, &Value<'gc>,
&Value<'gc>, &Value<'gc>,
) -> Ordering, ) -> Ordering,
flags: i32, flags: i32,
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let length = this.length(); let length = this.length();
let mut values: Vec<(usize, Value<'gc>)> = this.array().into_iter().enumerate().collect(); let mut values: Vec<(usize, Value<'gc>)> = this.array().into_iter().enumerate().collect();
let array_proto = avm.prototypes.array; let array_proto = activation.avm().prototypes.array;
let descending = (flags & DESCENDING) != 0; let descending = (flags & DESCENDING) != 0;
let unique_sort = (flags & UNIQUE_SORT) != 0; let unique_sort = (flags & UNIQUE_SORT) != 0;
@ -583,7 +591,7 @@ fn sort_with_function<'gc>(
let mut is_unique = true; let mut is_unique = true;
values.sort_unstable_by(|a, b| { values.sort_unstable_by(|a, b| {
let mut ret = compare_fn(avm, context, &a.1, &b.1); let mut ret = compare_fn(activation, context, &a.1, &b.1);
if descending { if descending {
ret = ret.reverse(); ret = ret.reverse();
} }
@ -710,13 +718,13 @@ pub fn create_proto<'gc>(
} }
fn sort_compare_string<'gc>( fn sort_compare_string<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
a: &Value<'gc>, a: &Value<'gc>,
b: &Value<'gc>, b: &Value<'gc>,
) -> Ordering { ) -> Ordering {
let a_str = a.coerce_to_string(avm, context); let a_str = a.coerce_to_string(activation, context);
let b_str = b.coerce_to_string(avm, context); let b_str = b.coerce_to_string(activation, context);
// TODO: Handle errors. // TODO: Handle errors.
if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) { if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) {
a_str.cmp(&b_str) a_str.cmp(&b_str)
@ -726,13 +734,13 @@ fn sort_compare_string<'gc>(
} }
fn sort_compare_string_ignore_case<'gc>( fn sort_compare_string_ignore_case<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
a: &Value<'gc>, a: &Value<'gc>,
b: &Value<'gc>, b: &Value<'gc>,
) -> Ordering { ) -> Ordering {
let a_str = a.coerce_to_string(avm, context); let a_str = a.coerce_to_string(activation, context);
let b_str = b.coerce_to_string(avm, context); let b_str = b.coerce_to_string(activation, context);
// TODO: Handle errors. // TODO: Handle errors.
if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) { if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) {
crate::string_utils::swf_string_cmp_ignore_case(&a_str, &b_str) crate::string_utils::swf_string_cmp_ignore_case(&a_str, &b_str)
@ -743,18 +751,22 @@ fn sort_compare_string_ignore_case<'gc>(
fn sort_compare_numeric<'gc>( fn sort_compare_numeric<'gc>(
mut string_compare_fn: impl FnMut( mut string_compare_fn: impl FnMut(
&mut Avm1<'gc>, &mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>, &mut UpdateContext<'_, 'gc, '_>,
&Value<'gc>, &Value<'gc>,
&Value<'gc>, &Value<'gc>,
) -> Ordering, ) -> Ordering,
) -> impl FnMut(&mut Avm1<'gc>, &mut UpdateContext<'_, 'gc, '_>, &Value<'gc>, &Value<'gc>) -> Ordering ) -> impl FnMut(
{ &mut Activation<'_, 'gc>,
move |avm, context, a, b| { &mut UpdateContext<'_, 'gc, '_>,
&Value<'gc>,
&Value<'gc>,
) -> Ordering {
move |activation, context, a, b| {
if let (Value::Number(a), Value::Number(b)) = (a, b) { if let (Value::Number(a), Value::Number(b)) = (a, b) {
a.partial_cmp(b).unwrap_or(DEFAULT_ORDERING) a.partial_cmp(b).unwrap_or(DEFAULT_ORDERING)
} else { } else {
string_compare_fn(avm, context, a, b) string_compare_fn(activation, context, a, b)
} }
} }
} }
@ -762,17 +774,22 @@ fn sort_compare_numeric<'gc>(
fn sort_compare_fields<'a, 'gc: 'a>( fn sort_compare_fields<'a, 'gc: 'a>(
field_names: Vec<String>, field_names: Vec<String>,
mut compare_fns: Vec<CompareFn<'a, 'gc>>, mut compare_fns: Vec<CompareFn<'a, 'gc>>,
) -> impl 'a + FnMut(&mut Avm1<'gc>, &mut UpdateContext<'_, 'gc, '_>, &Value<'gc>, &Value<'gc>) -> Ordering ) -> impl 'a
{ + FnMut(
&mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>,
&Value<'gc>,
&Value<'gc>,
) -> Ordering {
use crate::avm1::value_object::ValueObject; use crate::avm1::value_object::ValueObject;
move |avm, context, a, b| { move |activation, context, a, b| {
for (field_name, compare_fn) in field_names.iter().zip(compare_fns.iter_mut()) { for (field_name, compare_fn) in field_names.iter().zip(compare_fns.iter_mut()) {
let a_object = ValueObject::boxed(avm, context, a.clone()); let a_object = ValueObject::boxed(activation, context, a.clone());
let b_object = ValueObject::boxed(avm, context, b.clone()); let b_object = ValueObject::boxed(activation, context, b.clone());
let a_prop = a_object.get(field_name, avm, context).unwrap(); let a_prop = a_object.get(field_name, activation, context).unwrap();
let b_prop = b_object.get(field_name, avm, context).unwrap(); let b_prop = b_object.get(field_name, activation, context).unwrap();
let result = compare_fn(avm, context, &a_prop, &b_prop); let result = compare_fn(activation, context, &a_prop, &b_prop);
if result != Ordering::Equal { if result != Ordering::Equal {
return result; return result;
} }
@ -784,7 +801,7 @@ fn sort_compare_fields<'a, 'gc: 'a>(
// Returning an impl Trait here doesn't work yet because of https://github.com/rust-lang/rust/issues/65805 (?) // Returning an impl Trait here doesn't work yet because of https://github.com/rust-lang/rust/issues/65805 (?)
fn sort_compare_custom<'gc>( fn sort_compare_custom<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
a: &Value<'gc>, a: &Value<'gc>,
@ -794,7 +811,7 @@ fn sort_compare_custom<'gc>(
// TODO: Handle errors. // TODO: Handle errors.
let args = [a.clone(), b.clone()]; let args = [a.clone(), b.clone()];
let ret = compare_fn let ret = compare_fn
.call(avm, context, this, None, &args) .call(activation, context, this, None, &args)
.unwrap_or(Value::Undefined); .unwrap_or(Value::Undefined);
match ret { match ret {
Value::Number(n) if n > 0.0 => Ordering::Greater, Value::Number(n) if n > 0.0 => Ordering::Greater,

View File

@ -1,23 +1,23 @@
//! `Boolean` class impl //! `Boolean` class impl
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::value_object::ValueObject; use crate::avm1::value_object::ValueObject;
use crate::avm1::{Avm1, Object, TObject, Value}; use crate::avm1::{Object, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// `Boolean` constructor/function /// `Boolean` constructor/function
pub fn boolean<'gc>( pub fn boolean<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let (ret_value, cons_value) = if let Some(val) = args.get(0) { let (ret_value, cons_value) = if let Some(val) = args.get(0) {
let b = Value::Bool(val.as_bool(avm.current_swf_version())); let b = Value::Bool(val.as_bool(activation.current_swf_version()));
(b.clone(), b) (b.clone(), b)
} else { } else {
(Value::Undefined, Value::Bool(false)) (Value::Undefined, Value::Bool(false))
@ -30,7 +30,7 @@ pub fn boolean<'gc>(
// If called as a function, return the value. // If called as a function, return the value.
// Boolean() with no argument returns undefined. // Boolean() with no argument returns undefined.
Ok(ret_value.into()) Ok(ret_value)
} }
pub fn create_boolean_object<'gc>( pub fn create_boolean_object<'gc>(
@ -74,11 +74,11 @@ pub fn create_proto<'gc>(
} }
pub fn to_string<'gc>( pub fn to_string<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(vbox) = this.as_value_object() { if let Some(vbox) = this.as_value_object() {
// Must be a bool. // Must be a bool.
// Boolean.prototype.toString.call(x) returns undefined for non-bools. // Boolean.prototype.toString.call(x) returns undefined for non-bools.
@ -87,15 +87,15 @@ pub fn to_string<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn value_of<'gc>( pub fn value_of<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(vbox) = this.as_value_object() { if let Some(vbox) = this.as_value_object() {
// Must be a bool. // Must be a bool.
// Boolean.prototype.valueOf.call(x) returns undefined for non-bools. // Boolean.prototype.valueOf.call(x) returns undefined for non-bools.
@ -104,5 +104,5 @@ pub fn value_of<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }

View File

@ -1,9 +1,9 @@
//! Button/SimpleButton prototype //! Button/SimpleButton prototype
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::globals::display_object; use crate::avm1::globals::display_object;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
use gc_arena::MutationContext; use gc_arena::MutationContext;
pub fn create_proto<'gc>( pub fn create_proto<'gc>(
@ -20,10 +20,10 @@ pub fn create_proto<'gc>(
/// Implements `Button` constructor. /// Implements `Button` constructor.
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }

View File

@ -3,24 +3,24 @@
//! TODO: This should change when `ColorTransform` changes to match Flash's representation //! TODO: This should change when `ColorTransform` changes to match Flash's representation
//! (See GitHub #193) //! (See GitHub #193)
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::display_object::{DisplayObject, TDisplayObject}; use crate::display_object::{DisplayObject, TDisplayObject};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
pub fn constructor<'gc>( pub fn constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: Object<'gc>, mut this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
// The target display object that this color will modify. // The target display object that this color will modify.
let target = args.get(0).cloned().unwrap_or(Value::Undefined); let target = args.get(0).cloned().unwrap_or(Value::Undefined);
// Set undocumented `target` property // Set undocumented `target` property
this.set("target", target, avm, context)?; this.set("target", target, activation, context)?;
this.set_attributes( this.set_attributes(
context.gc_context, context.gc_context,
Some("target"), Some("target"),
@ -28,7 +28,7 @@ pub fn constructor<'gc>(
EnumSet::empty(), EnumSet::empty(),
); );
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create_proto<'gc>( pub fn create_proto<'gc>(
@ -75,75 +75,116 @@ pub fn create_proto<'gc>(
/// Gets the target display object of this color transform. /// Gets the target display object of this color transform.
fn target<'gc>( fn target<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Option<DisplayObject<'gc>>, Error<'gc>> { ) -> Result<Option<DisplayObject<'gc>>, Error<'gc>> {
// The target path resolves based on the active tellTarget clip of the stack frame. // The target path resolves based on the active tellTarget clip of the stack frame.
// This means calls on the same `Color` object could set the color of different clips // This means calls on the same `Color` object could set the color of different clips
// depending on which timeline its called from! // depending on which timeline its called from!
let target = this.get("target", avm, context)?; let target = this.get("target", activation, context)?;
// Undefined or empty target is no-op. // Undefined or empty target is no-op.
if target != Value::Undefined && !matches!(&target, &Value::String(ref s) if s.is_empty()) { if target != Value::Undefined && !matches!(&target, &Value::String(ref s) if s.is_empty()) {
let start_clip = avm.target_clip_or_root(); let start_clip = activation.target_clip_or_root();
avm.resolve_target_display_object(context, start_clip, target) activation.resolve_target_display_object(context, start_clip, target)
} else { } else {
Ok(None) Ok(None)
} }
} }
fn get_rgb<'gc>( fn get_rgb<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(target) = target(avm, context, this)? { if let Some(target) = target(activation, context, this)? {
let color_transform = target.color_transform(); let color_transform = target.color_transform();
let r = ((color_transform.r_add * 255.0) as i32) << 16; let r = ((color_transform.r_add * 255.0) as i32) << 16;
let g = ((color_transform.g_add * 255.0) as i32) << 8; let g = ((color_transform.g_add * 255.0) as i32) << 8;
let b = (color_transform.b_add * 255.0) as i32; let b = (color_transform.b_add * 255.0) as i32;
Ok((r | g | b).into()) Ok((r | g | b).into())
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
fn get_transform<'gc>( fn get_transform<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(target) = target(avm, context, this)? { if let Some(target) = target(activation, context, this)? {
let color_transform = target.color_transform(); let color_transform = target.color_transform();
let out = ScriptObject::object(context.gc_context, Some(avm.prototypes.object)); let out =
out.set("ra", (color_transform.r_mult * 100.0).into(), avm, context)?; ScriptObject::object(context.gc_context, Some(activation.avm().prototypes.object));
out.set("ga", (color_transform.g_mult * 100.0).into(), avm, context)?; out.set(
out.set("ba", (color_transform.b_mult * 100.0).into(), avm, context)?; "ra",
out.set("aa", (color_transform.a_mult * 100.0).into(), avm, context)?; (color_transform.r_mult * 100.0).into(),
out.set("rb", (color_transform.r_add * 255.0).into(), avm, context)?; activation,
out.set("gb", (color_transform.g_add * 255.0).into(), avm, context)?; context,
out.set("bb", (color_transform.b_add * 255.0).into(), avm, context)?; )?;
out.set("ab", (color_transform.a_add * 255.0).into(), avm, context)?; out.set(
"ga",
(color_transform.g_mult * 100.0).into(),
activation,
context,
)?;
out.set(
"ba",
(color_transform.b_mult * 100.0).into(),
activation,
context,
)?;
out.set(
"aa",
(color_transform.a_mult * 100.0).into(),
activation,
context,
)?;
out.set(
"rb",
(color_transform.r_add * 255.0).into(),
activation,
context,
)?;
out.set(
"gb",
(color_transform.g_add * 255.0).into(),
activation,
context,
)?;
out.set(
"bb",
(color_transform.b_add * 255.0).into(),
activation,
context,
)?;
out.set(
"ab",
(color_transform.a_add * 255.0).into(),
activation,
context,
)?;
Ok(out.into()) Ok(out.into())
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
fn set_rgb<'gc>( fn set_rgb<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(target) = target(avm, context, this)? { if let Some(target) = target(activation, context, this)? {
let mut color_transform = target.color_transform_mut(context.gc_context); let mut color_transform = target.color_transform_mut(context.gc_context);
let rgb = args let rgb = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)? as i32; .coerce_to_f64(activation, context)? as i32;
let r = (((rgb >> 16) & 0xff) as f32) / 255.0; let r = (((rgb >> 16) & 0xff) as f32) / 255.0;
let g = (((rgb >> 8) & 0xff) as f32) / 255.0; let g = (((rgb >> 8) & 0xff) as f32) / 255.0;
let b = ((rgb & 0xff) as f32) / 255.0; let b = ((rgb & 0xff) as f32) / 255.0;
@ -155,67 +196,115 @@ fn set_rgb<'gc>(
color_transform.g_add = g; color_transform.g_add = g;
color_transform.b_add = b; color_transform.b_add = b;
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn set_transform<'gc>( fn set_transform<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
// TODO: These map from the 0-100% range for mult and the -255-255 range for addition used by ActionScript // TODO: These map from the 0-100% range for mult and the -255-255 range for addition used by ActionScript
// to the 16-bit range used by the internal representations of the Flash Player. // to the 16-bit range used by the internal representations of the Flash Player.
// This will get slightly simpler when we change ColorTransform to the proper representation (see #193). // This will get slightly simpler when we change ColorTransform to the proper representation (see #193).
fn set_color_mult<'gc>( fn set_color_mult<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
transform: Object<'gc>, transform: Object<'gc>,
property: &str, property: &str,
out: &mut f32, out: &mut f32,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
// The parameters are set only if the property exists on the object itself (prototype excluded). // The parameters are set only if the property exists on the object itself (prototype excluded).
if transform.has_own_property(avm, context, property) { if transform.has_own_property(activation, context, property) {
let n = transform let n = transform
.get(property, avm, context)? .get(property, activation, context)?
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
*out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n * 2.56)) / 256.0 *out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n * 2.56)) / 256.0
} }
Ok(()) Ok(())
} }
fn set_color_add<'gc>( fn set_color_add<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
transform: Object<'gc>, transform: Object<'gc>,
property: &str, property: &str,
out: &mut f32, out: &mut f32,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
// The parameters are set only if the property exists on the object itself (prototype excluded). // The parameters are set only if the property exists on the object itself (prototype excluded).
if transform.has_own_property(avm, context, property) { if transform.has_own_property(activation, context, property) {
let n = transform let n = transform
.get(property, avm, context)? .get(property, activation, context)?
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
*out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n)) / 255.0 *out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n)) / 255.0
} }
Ok(()) Ok(())
} }
if let Some(target) = target(avm, context, this)? { if let Some(target) = target(activation, context, this)? {
let mut color_transform = target.color_transform_mut(context.gc_context); let mut color_transform = target.color_transform_mut(context.gc_context);
let transform = args let transform = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_object(avm, context); .coerce_to_object(activation, context);
set_color_mult(avm, context, transform, "ra", &mut color_transform.r_mult)?; set_color_mult(
set_color_mult(avm, context, transform, "ga", &mut color_transform.g_mult)?; activation,
set_color_mult(avm, context, transform, "ba", &mut color_transform.b_mult)?; context,
set_color_mult(avm, context, transform, "aa", &mut color_transform.a_mult)?; transform,
set_color_add(avm, context, transform, "rb", &mut color_transform.r_add)?; "ra",
set_color_add(avm, context, transform, "gb", &mut color_transform.g_add)?; &mut color_transform.r_mult,
set_color_add(avm, context, transform, "bb", &mut color_transform.b_add)?; )?;
set_color_add(avm, context, transform, "ab", &mut color_transform.a_add)?; set_color_mult(
activation,
context,
transform,
"ga",
&mut color_transform.g_mult,
)?;
set_color_mult(
activation,
context,
transform,
"ba",
&mut color_transform.b_mult,
)?;
set_color_mult(
activation,
context,
transform,
"aa",
&mut color_transform.a_mult,
)?;
set_color_add(
activation,
context,
transform,
"rb",
&mut color_transform.r_add,
)?;
set_color_add(
activation,
context,
transform,
"gb",
&mut color_transform.g_add,
)?;
set_color_add(
activation,
context,
transform,
"bb",
&mut color_transform.b_add,
)?;
set_color_add(
activation,
context,
transform,
"ab",
&mut color_transform.a_add,
)?;
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }

View File

@ -1,10 +1,10 @@
//! DisplayObject common methods //! DisplayObject common methods
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::display_object::{DisplayObject, TDisplayObject}; use crate::display_object::{DisplayObject, TDisplayObject};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
@ -24,11 +24,11 @@ macro_rules! with_display_object {
$( $(
$object.force_set_function( $object.force_set_function(
$name, $name,
|avm, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<ReturnValue<'gc>, Error<'gc>> { |activation, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
return $fn(display_object, avm, context, args); return $fn(display_object, activation, context, args);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} as crate::avm1::function::NativeFunction<'gc>, } as crate::avm1::function::NativeFunction<'gc>,
$gc_context, $gc_context,
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
@ -54,7 +54,9 @@ pub fn define_display_object_proto<'gc>(
object.add_property( object.add_property(
gc_context, gc_context,
"_global", "_global",
Executable::Native(|avm, context, _this, _args| Ok(avm.global_object(context).into())), Executable::Native(|activation, context, _this, _args| {
Ok(activation.avm().global_object(context))
}),
Some(Executable::Native(overwrite_global)), Some(Executable::Native(overwrite_global)),
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
); );
@ -62,7 +64,7 @@ pub fn define_display_object_proto<'gc>(
object.add_property( object.add_property(
gc_context, gc_context,
"_root", "_root",
Executable::Native(|avm, context, _this, _args| Ok(avm.root_object(context).into())), Executable::Native(|activation, context, _this, _args| Ok(activation.root_object(context))),
Some(Executable::Native(overwrite_root)), Some(Executable::Native(overwrite_root)),
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
); );
@ -77,60 +79,59 @@ pub fn define_display_object_proto<'gc>(
} }
pub fn get_parent<'gc>( pub fn get_parent<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(this Ok(this
.as_display_object() .as_display_object()
.and_then(|mc| mc.parent()) .and_then(|mc| mc.parent())
.map(|dn| dn.object().coerce_to_object(avm, context)) .map(|dn| dn.object().coerce_to_object(activation, context))
.map(Value::Object) .map(Value::Object)
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined))
.into())
} }
pub fn get_depth<'gc>( pub fn get_depth<'gc>(
display_object: DisplayObject<'gc>, display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() >= 6 { if activation.current_swf_version() >= 6 {
let depth = display_object.depth().wrapping_sub(AVM_DEPTH_BIAS); let depth = display_object.depth().wrapping_sub(AVM_DEPTH_BIAS);
Ok(depth.into()) Ok(depth.into())
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
pub fn overwrite_root<'gc>( pub fn overwrite_root<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let new_val = args let new_val = args
.get(0) .get(0)
.map(|v| v.to_owned()) .map(|v| v.to_owned())
.unwrap_or(Value::Undefined); .unwrap_or(Value::Undefined);
this.define_value(ac.gc_context, "_root", new_val, EnumSet::new()); this.define_value(ac.gc_context, "_root", new_val, EnumSet::new());
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn overwrite_global<'gc>( pub fn overwrite_global<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let new_val = args let new_val = args
.get(0) .get(0)
.map(|v| v.to_owned()) .map(|v| v.to_owned())
.unwrap_or(Value::Undefined); .unwrap_or(Value::Undefined);
this.define_value(ac.gc_context, "_global", new_val, EnumSet::new()); this.define_value(ac.gc_context, "_global", new_val, EnumSet::new());
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }

View File

@ -1,31 +1,31 @@
//! Function prototype //! Function prototype
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `Function` /// Implements `Function`
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Implements `Function.prototype.call` /// Implements `Function.prototype.call`
pub fn call<'gc>( pub fn call<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
func: Object<'gc>, func: Object<'gc>,
myargs: &[Value<'gc>], myargs: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this = match myargs.get(0) { let this = match myargs.get(0) {
Some(Value::Object(this)) => *this, Some(Value::Object(this)) => *this,
_ => avm.globals, _ => activation.avm().globals,
}; };
let empty = []; let empty = [];
let args = match myargs.len() { let args = match myargs.len() {
@ -35,52 +35,52 @@ pub fn call<'gc>(
}; };
match func.as_executable() { match func.as_executable() {
Some(exec) => exec.exec(avm, action_context, this, None, args), Some(exec) => exec.exec(activation, action_context, this, None, args),
_ => Ok(Value::Undefined.into()), _ => Ok(Value::Undefined),
} }
} }
/// Implements `Function.prototype.apply` /// Implements `Function.prototype.apply`
pub fn apply<'gc>( pub fn apply<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
func: Object<'gc>, func: Object<'gc>,
myargs: &[Value<'gc>], myargs: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this = match myargs.get(0) { let this = match myargs.get(0) {
Some(Value::Object(this)) => *this, Some(Value::Object(this)) => *this,
_ => avm.globals, _ => activation.avm().globals,
}; };
let mut child_args = Vec::new(); let mut child_args = Vec::new();
let args_object = myargs.get(1).cloned().unwrap_or(Value::Undefined); let args_object = myargs.get(1).cloned().unwrap_or(Value::Undefined);
let length = match args_object { let length = match args_object {
Value::Object(a) => a Value::Object(a) => a
.get("length", avm, action_context)? .get("length", activation, action_context)?
.coerce_to_f64(avm, action_context)? as usize, .coerce_to_f64(activation, action_context)? as usize,
_ => 0, _ => 0,
}; };
while child_args.len() < length { while child_args.len() < length {
let args = args_object.coerce_to_object(avm, action_context); let args = args_object.coerce_to_object(activation, action_context);
let next_arg = args.get(&format!("{}", child_args.len()), avm, action_context)?; let next_arg = args.get(&format!("{}", child_args.len()), activation, action_context)?;
child_args.push(next_arg); child_args.push(next_arg);
} }
match func.as_executable() { match func.as_executable() {
Some(exec) => exec.exec(avm, action_context, this, None, &child_args), Some(exec) => exec.exec(activation, action_context, this, None, &child_args),
_ => Ok(Value::Undefined.into()), _ => Ok(Value::Undefined),
} }
} }
/// Implements `Function.prototype.toString` /// Implements `Function.prototype.toString`
fn to_string<'gc>( fn to_string<'gc>(
_: &mut Avm1<'gc>, _: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>, _: &mut UpdateContext<'_, 'gc, '_>,
_: Object<'gc>, _: Object<'gc>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(ReturnValue::Immediate("[type Function]".into())) Ok("[type Function]".into())
} }
/// Partially construct `Function.prototype`. /// Partially construct `Function.prototype`.

View File

@ -1,21 +1,20 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::events::KeyCode; use crate::events::KeyCode;
use gc_arena::MutationContext; use gc_arena::MutationContext;
use std::convert::TryFrom; use std::convert::TryFrom;
pub fn is_down<'gc>( pub fn is_down<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(key) = args if let Some(key) = args
.get(0) .get(0)
.and_then(|v| v.coerce_to_f64(avm, context).ok()) .and_then(|v| v.coerce_to_f64(activation, context).ok())
.and_then(|k| KeyCode::try_from(k as u8).ok()) .and_then(|k| KeyCode::try_from(k as u8).ok())
{ {
Ok(context.input.is_key_down(key).into()) Ok(context.input.is_key_down(key).into())
@ -25,11 +24,11 @@ pub fn is_down<'gc>(
} }
pub fn get_code<'gc>( pub fn get_code<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let code: u8 = context.input.get_last_key_code().into(); let code: u8 = context.input.get_last_key_code().into();
Ok(code.into()) Ok(code.into())
} }

View File

@ -1,8 +1,8 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::MutationContext; use gc_arena::MutationContext;
use rand::Rng; use rand::Rng;
use std::f64::{INFINITY, NAN, NEG_INFINITY}; use std::f64::{INFINITY, NAN, NEG_INFINITY};
@ -12,9 +12,9 @@ macro_rules! wrap_std {
$( $(
$object.force_set_function( $object.force_set_function(
$name, $name,
|avm, context, _this, args| -> Result<ReturnValue<'gc>, Error<'gc>> { |activation, context, _this, args| -> Result<Value<'gc>, Error<'gc>> {
if let Some(input) = args.get(0) { if let Some(input) = args.get(0) {
Ok($std(input.coerce_to_f64(avm, context)?).into()) Ok($std(input.coerce_to_f64(activation, context)?).into())
} else { } else {
Ok(NAN.into()) Ok(NAN.into())
} }
@ -28,50 +28,50 @@ macro_rules! wrap_std {
} }
fn atan2<'gc>( fn atan2<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(y) = args.get(0) { if let Some(y) = args.get(0) {
if let Some(x) = args.get(1) { if let Some(x) = args.get(1) {
return Ok(y return Ok(y
.coerce_to_f64(avm, context)? .coerce_to_f64(activation, context)?
.atan2(x.coerce_to_f64(avm, context)?) .atan2(x.coerce_to_f64(activation, context)?)
.into()); .into());
} else { } else {
return Ok(y.coerce_to_f64(avm, context)?.atan2(0.0).into()); return Ok(y.coerce_to_f64(activation, context)?.atan2(0.0).into());
} }
} }
Ok(NAN.into()) Ok(NAN.into())
} }
fn pow<'gc>( fn pow<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(y) = args.get(0) { if let Some(y) = args.get(0) {
if let Some(x) = args.get(1) { if let Some(x) = args.get(1) {
let x = x.coerce_to_f64(avm, context)?; let x = x.coerce_to_f64(activation, context)?;
if x.is_nan() { if x.is_nan() {
return Ok(NAN.into()); return Ok(NAN.into());
} }
return Ok(y.coerce_to_f64(avm, context)?.powf(x).into()); return Ok(y.coerce_to_f64(activation, context)?.powf(x).into());
} }
} }
Ok(NAN.into()) Ok(NAN.into())
} }
fn round<'gc>( fn round<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(x) = args.get(0) { if let Some(x) = args.get(0) {
let x = x.coerce_to_f64(avm, context)?; let x = x.coerce_to_f64(activation, context)?;
// Note that Flash Math.round always rounds toward infinity, // Note that Flash Math.round always rounds toward infinity,
// unlike Rust f32::round which rounds away from zero. // unlike Rust f32::round which rounds away from zero.
let ret = (x + 0.5).floor(); let ret = (x + 0.5).floor();
@ -81,19 +81,19 @@ fn round<'gc>(
} }
fn max<'gc>( fn max<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(a) = args.get(0) { if let Some(a) = args.get(0) {
return if let Some(b) = args.get(1) { return if let Some(b) = args.get(1) {
match a.abstract_lt(b.to_owned(), avm, context)? { match a.abstract_lt(b.to_owned(), activation, context)? {
Value::Bool(value) => { Value::Bool(value) => {
if value { if value {
Ok(b.coerce_to_f64(avm, context)?.into()) Ok(b.coerce_to_f64(activation, context)?.into())
} else { } else {
Ok(a.coerce_to_f64(avm, context)?.into()) Ok(a.coerce_to_f64(activation, context)?.into())
} }
} }
_ => Ok(NAN.into()), _ => Ok(NAN.into()),
@ -106,19 +106,19 @@ fn max<'gc>(
} }
fn min<'gc>( fn min<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(a) = args.get(0) { if let Some(a) = args.get(0) {
return if let Some(b) = args.get(1) { return if let Some(b) = args.get(1) {
match a.abstract_lt(b.to_owned(), avm, context)? { match a.abstract_lt(b.to_owned(), activation, context)? {
Value::Bool(value) => { Value::Bool(value) => {
if value { if value {
Ok(a.coerce_to_f64(avm, context)?.into()) Ok(a.coerce_to_f64(activation, context)?.into())
} else { } else {
Ok(b.coerce_to_f64(avm, context)?.into()) Ok(b.coerce_to_f64(activation, context)?.into())
} }
} }
_ => Ok(NAN.into()), _ => Ok(NAN.into()),
@ -131,11 +131,11 @@ fn min<'gc>(
} }
pub fn random<'gc>( pub fn random<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(action_context.rng.gen_range(0.0f64, 1.0f64).into()) Ok(action_context.rng.gen_range(0.0f64, 1.0f64).into())
} }
@ -261,11 +261,14 @@ mod tests {
use super::*; use super::*;
use crate::avm1::test_utils::with_avm; use crate::avm1::test_utils::with_avm;
fn setup<'gc>(avm: &mut Avm1<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) -> Object<'gc> { fn setup<'gc>(
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Object<'gc> {
create( create(
context.gc_context, context.gc_context,
Some(avm.prototypes().object), Some(activation.avm().prototypes().object),
Some(avm.prototypes().function), Some(activation.avm().prototypes().function),
) )
} }
@ -479,24 +482,24 @@ mod tests {
#[test] #[test]
fn test_atan2_nan() { fn test_atan2_nan() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
let math = create( let math = create(
context.gc_context, context.gc_context,
Some(avm.prototypes().object), Some(activation.avm().prototypes().object),
Some(avm.prototypes().function), Some(activation.avm().prototypes().function),
); );
assert_eq!(atan2(avm, context, math, &[]).unwrap(), NAN.into()); assert_eq!(atan2(activation, context, math, &[]).unwrap(), NAN.into());
assert_eq!( assert_eq!(
atan2(avm, context, math, &[1.0.into(), Value::Null]).unwrap(), atan2(activation, context, math, &[1.0.into(), Value::Null]).unwrap(),
NAN.into() NAN.into()
); );
assert_eq!( assert_eq!(
atan2(avm, context, math, &[1.0.into(), Value::Undefined]).unwrap(), atan2(activation, context, math, &[1.0.into(), Value::Undefined]).unwrap(),
NAN.into() NAN.into()
); );
assert_eq!( assert_eq!(
atan2(avm, context, math, &[Value::Undefined, 1.0.into()]).unwrap(), atan2(activation, context, math, &[Value::Undefined, 1.0.into()]).unwrap(),
NAN.into() NAN.into()
); );
Ok(()) Ok(())
@ -505,19 +508,19 @@ mod tests {
#[test] #[test]
fn test_atan2_valid() { fn test_atan2_valid() {
with_avm(19, |avm, context, _root| -> Result<(), Error> { with_avm(19, |activation, context, _root| -> Result<(), Error> {
let math = create( let math = create(
context.gc_context, context.gc_context,
Some(avm.prototypes().object), Some(activation.avm().prototypes().object),
Some(avm.prototypes().function), Some(activation.avm().prototypes().function),
); );
assert_eq!( assert_eq!(
atan2(avm, context, math, &[10.0.into()]).unwrap(), atan2(activation, context, math, &[10.0.into()]).unwrap(),
std::f64::consts::FRAC_PI_2.into() std::f64::consts::FRAC_PI_2.into()
); );
assert_eq!( assert_eq!(
atan2(avm, context, math, &[1.0.into(), 2.0.into()]).unwrap(), atan2(activation, context, math, &[1.0.into(), 2.0.into()]).unwrap(),
f64::atan2(1.0, 2.0).into() f64::atan2(1.0, 2.0).into()
); );
Ok(()) Ok(())

View File

@ -1,9 +1,9 @@
//! flash.geom.Matrix //! flash.geom.Matrix
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
@ -11,36 +11,36 @@ use swf::{Matrix, Twips};
pub fn value_to_matrix<'gc>( pub fn value_to_matrix<'gc>(
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Matrix, Error<'gc>> { ) -> Result<Matrix, Error<'gc>> {
let a = value let a = value
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("a", avm, context)? .get("a", activation, context)?
.coerce_to_f64(avm, context)? as f32; .coerce_to_f64(activation, context)? as f32;
let b = value let b = value
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("b", avm, context)? .get("b", activation, context)?
.coerce_to_f64(avm, context)? as f32; .coerce_to_f64(activation, context)? as f32;
let c = value let c = value
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("c", avm, context)? .get("c", activation, context)?
.coerce_to_f64(avm, context)? as f32; .coerce_to_f64(activation, context)? as f32;
let d = value let d = value
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("d", avm, context)? .get("d", activation, context)?
.coerce_to_f64(avm, context)? as f32; .coerce_to_f64(activation, context)? as f32;
let tx = Twips::from_pixels( let tx = Twips::from_pixels(
value value
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("tx", avm, context)? .get("tx", activation, context)?
.coerce_to_f64(avm, context)?, .coerce_to_f64(activation, context)?,
); );
let ty = Twips::from_pixels( let ty = Twips::from_pixels(
value value
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("ty", avm, context)? .get("ty", activation, context)?
.coerce_to_f64(avm, context)?, .coerce_to_f64(activation, context)?,
); );
Ok(Matrix { a, b, c, d, tx, ty }) Ok(Matrix { a, b, c, d, tx, ty })
@ -48,19 +48,29 @@ pub fn value_to_matrix<'gc>(
pub fn gradient_object_to_matrix<'gc>( pub fn gradient_object_to_matrix<'gc>(
object: Object<'gc>, object: Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Matrix, Error<'gc>> { ) -> Result<Matrix, Error<'gc>> {
if object if object
.get("matrixType", avm, context)? .get("matrixType", activation, context)?
.coerce_to_string(avm, context)? .coerce_to_string(activation, context)?
== "box" == "box"
{ {
let width = object.get("w", avm, context)?.coerce_to_f64(avm, context)?; let width = object
let height = object.get("h", avm, context)?.coerce_to_f64(avm, context)?; .get("w", activation, context)?
let rotation = object.get("r", avm, context)?.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let tx = object.get("x", avm, context)?.coerce_to_f64(avm, context)?; let height = object
let ty = object.get("y", avm, context)?.coerce_to_f64(avm, context)?; .get("h", activation, context)?
.coerce_to_f64(activation, context)?;
let rotation = object
.get("r", activation, context)?
.coerce_to_f64(activation, context)?;
let tx = object
.get("x", activation, context)?
.coerce_to_f64(activation, context)?;
let ty = object
.get("y", activation, context)?
.coerce_to_f64(activation, context)?;
Ok(Matrix::create_gradient_box( Ok(Matrix::create_gradient_box(
width as f32, width as f32,
height as f32, height as f32,
@ -70,28 +80,36 @@ pub fn gradient_object_to_matrix<'gc>(
)) ))
} else { } else {
// TODO: You can apparently pass a 3x3 matrix here. Did anybody actually? How does it work? // TODO: You can apparently pass a 3x3 matrix here. Did anybody actually? How does it work?
object_to_matrix(object, avm, context) object_to_matrix(object, activation, context)
} }
} }
pub fn object_to_matrix<'gc>( pub fn object_to_matrix<'gc>(
object: Object<'gc>, object: Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Matrix, Error<'gc>> { ) -> Result<Matrix, Error<'gc>> {
let a = object.get("a", avm, context)?.coerce_to_f64(avm, context)? as f32; let a = object
let b = object.get("b", avm, context)?.coerce_to_f64(avm, context)? as f32; .get("a", activation, context)?
let c = object.get("c", avm, context)?.coerce_to_f64(avm, context)? as f32; .coerce_to_f64(activation, context)? as f32;
let d = object.get("d", avm, context)?.coerce_to_f64(avm, context)? as f32; let b = object
.get("b", activation, context)?
.coerce_to_f64(activation, context)? as f32;
let c = object
.get("c", activation, context)?
.coerce_to_f64(activation, context)? as f32;
let d = object
.get("d", activation, context)?
.coerce_to_f64(activation, context)? as f32;
let tx = Twips::from_pixels( let tx = Twips::from_pixels(
object object
.get("tx", avm, context)? .get("tx", activation, context)?
.coerce_to_f64(avm, context)?, .coerce_to_f64(activation, context)?,
); );
let ty = Twips::from_pixels( let ty = Twips::from_pixels(
object object
.get("ty", avm, context)? .get("ty", activation, context)?
.coerce_to_f64(avm, context)?, .coerce_to_f64(activation, context)?,
); );
Ok(Matrix { a, b, c, d, tx, ty }) Ok(Matrix { a, b, c, d, tx, ty })
@ -101,7 +119,7 @@ pub fn object_to_matrix<'gc>(
#[allow(dead_code)] #[allow(dead_code)]
pub fn matrix_to_object<'gc>( pub fn matrix_to_object<'gc>(
matrix: Matrix, matrix: Matrix,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
let proto = context.system_prototypes.matrix; let proto = context.system_prototypes.matrix;
@ -113,207 +131,207 @@ pub fn matrix_to_object<'gc>(
matrix.tx.to_pixels().into(), matrix.tx.to_pixels().into(),
matrix.ty.to_pixels().into(), matrix.ty.to_pixels().into(),
]; ];
let object = proto.new(avm, context, proto, &args)?; let object = proto.new(activation, context, proto, &args)?;
let _ = constructor(avm, context, object, &args)?; let _ = constructor(activation, context, object, &args)?;
Ok(object) Ok(object)
} }
pub fn apply_matrix_to_object<'gc>( pub fn apply_matrix_to_object<'gc>(
matrix: Matrix, matrix: Matrix,
object: Object<'gc>, object: Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
object.set("a", matrix.a.into(), avm, context)?; object.set("a", matrix.a.into(), activation, context)?;
object.set("b", matrix.b.into(), avm, context)?; object.set("b", matrix.b.into(), activation, context)?;
object.set("c", matrix.c.into(), avm, context)?; object.set("c", matrix.c.into(), activation, context)?;
object.set("d", matrix.d.into(), avm, context)?; object.set("d", matrix.d.into(), activation, context)?;
object.set("tx", matrix.tx.to_pixels().into(), avm, context)?; object.set("tx", matrix.tx.to_pixels().into(), activation, context)?;
object.set("ty", matrix.ty.to_pixels().into(), avm, context)?; object.set("ty", matrix.ty.to_pixels().into(), activation, context)?;
Ok(()) Ok(())
} }
fn constructor<'gc>( fn constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() { if args.is_empty() {
apply_matrix_to_object(Matrix::identity(), this, avm, context)?; apply_matrix_to_object(Matrix::identity(), this, activation, context)?;
} else { } else {
if let Some(a) = args.get(0) { if let Some(a) = args.get(0) {
this.set("a", a.clone(), avm, context)?; this.set("a", a.clone(), activation, context)?;
} }
if let Some(b) = args.get(1) { if let Some(b) = args.get(1) {
this.set("b", b.clone(), avm, context)?; this.set("b", b.clone(), activation, context)?;
} }
if let Some(c) = args.get(2) { if let Some(c) = args.get(2) {
this.set("c", c.clone(), avm, context)?; this.set("c", c.clone(), activation, context)?;
} }
if let Some(d) = args.get(3) { if let Some(d) = args.get(3) {
this.set("d", d.clone(), avm, context)?; this.set("d", d.clone(), activation, context)?;
} }
if let Some(tx) = args.get(4) { if let Some(tx) = args.get(4) {
this.set("tx", tx.clone(), avm, context)?; this.set("tx", tx.clone(), activation, context)?;
} }
if let Some(ty) = args.get(5) { if let Some(ty) = args.get(5) {
this.set("ty", ty.clone(), avm, context)?; this.set("ty", ty.clone(), activation, context)?;
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn identity<'gc>( fn identity<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
apply_matrix_to_object(Matrix::identity(), this, avm, context)?; apply_matrix_to_object(Matrix::identity(), this, activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn clone<'gc>( fn clone<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let proto = context.system_prototypes.matrix; let proto = context.system_prototypes.matrix;
let args = [ let args = [
this.get("a", avm, context)?, this.get("a", activation, context)?,
this.get("b", avm, context)?, this.get("b", activation, context)?,
this.get("c", avm, context)?, this.get("c", activation, context)?,
this.get("d", avm, context)?, this.get("d", activation, context)?,
this.get("tx", avm, context)?, this.get("tx", activation, context)?,
this.get("ty", avm, context)?, this.get("ty", activation, context)?,
]; ];
let cloned = proto.new(avm, context, proto, &args)?; let cloned = proto.new(activation, context, proto, &args)?;
let _ = constructor(avm, context, cloned, &args)?; let _ = constructor(activation, context, cloned, &args)?;
Ok(cloned.into()) Ok(cloned.into())
} }
fn scale<'gc>( fn scale<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let scale_x = args let scale_x = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let scale_y = args let scale_y = args
.get(1) .get(1)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let mut matrix = Matrix::scale(scale_x as f32, scale_y as f32); let mut matrix = Matrix::scale(scale_x as f32, scale_y as f32);
matrix *= object_to_matrix(this, avm, context)?; matrix *= object_to_matrix(this, activation, context)?;
apply_matrix_to_object(matrix, this, avm, context)?; apply_matrix_to_object(matrix, this, activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn rotate<'gc>( fn rotate<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let angle = args let angle = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let mut matrix = Matrix::rotate(angle as f32); let mut matrix = Matrix::rotate(angle as f32);
matrix *= object_to_matrix(this, avm, context)?; matrix *= object_to_matrix(this, activation, context)?;
apply_matrix_to_object(matrix, this, avm, context)?; apply_matrix_to_object(matrix, this, activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn translate<'gc>( fn translate<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let translate_x = args let translate_x = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let translate_y = args let translate_y = args
.get(1) .get(1)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let mut matrix = Matrix::translate( let mut matrix = Matrix::translate(
Twips::from_pixels(translate_x), Twips::from_pixels(translate_x),
Twips::from_pixels(translate_y), Twips::from_pixels(translate_y),
); );
matrix *= object_to_matrix(this, avm, context)?; matrix *= object_to_matrix(this, activation, context)?;
apply_matrix_to_object(matrix, this, avm, context)?; apply_matrix_to_object(matrix, this, activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn concat<'gc>( fn concat<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let mut matrix = object_to_matrix(this, avm, context)?; let mut matrix = object_to_matrix(this, activation, context)?;
let other = value_to_matrix( let other = value_to_matrix(
args.get(0).unwrap_or(&Value::Undefined).clone(), args.get(0).unwrap_or(&Value::Undefined).clone(),
avm, activation,
context, context,
)?; )?;
matrix = other * matrix; matrix = other * matrix;
apply_matrix_to_object(matrix, this, avm, context)?; apply_matrix_to_object(matrix, this, activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn invert<'gc>( fn invert<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let mut matrix = object_to_matrix(this, avm, context)?; let mut matrix = object_to_matrix(this, activation, context)?;
matrix.invert(); matrix.invert();
apply_matrix_to_object(matrix, this, avm, context)?; apply_matrix_to_object(matrix, this, activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn create_box<'gc>( fn create_box<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let scale_x = args let scale_x = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let scale_y = args let scale_y = args
.get(1) .get(1)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
// [NA] Docs say rotation is optional and defaults to 0, but that's wrong? // [NA] Docs say rotation is optional and defaults to 0, but that's wrong?
let rotation = args let rotation = args
.get(2) .get(2)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let translate_x = if let Some(value) = args.get(3) { let translate_x = if let Some(value) = args.get(3) {
value.coerce_to_f64(avm, context)? value.coerce_to_f64(activation, context)?
} else { } else {
0.0 0.0
}; };
let translate_y = if let Some(value) = args.get(4) { let translate_y = if let Some(value) = args.get(4) {
value.coerce_to_f64(avm, context)? value.coerce_to_f64(activation, context)?
} else { } else {
0.0 0.0
}; };
@ -325,37 +343,37 @@ fn create_box<'gc>(
Twips::from_pixels(translate_x), Twips::from_pixels(translate_x),
Twips::from_pixels(translate_y), Twips::from_pixels(translate_y),
); );
apply_matrix_to_object(matrix, this, avm, context)?; apply_matrix_to_object(matrix, this, activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn create_gradient_box<'gc>( fn create_gradient_box<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let width = args let width = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let height = args let height = args
.get(1) .get(1)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let rotation = if let Some(value) = args.get(2) { let rotation = if let Some(value) = args.get(2) {
value.coerce_to_f64(avm, context)? value.coerce_to_f64(activation, context)?
} else { } else {
0.0 0.0
}; };
let translate_x = if let Some(value) = args.get(3) { let translate_x = if let Some(value) = args.get(3) {
value.coerce_to_f64(avm, context)? value.coerce_to_f64(activation, context)?
} else { } else {
0.0 0.0
}; };
let translate_y = if let Some(value) = args.get(4) { let translate_y = if let Some(value) = args.get(4) {
value.coerce_to_f64(avm, context)? value.coerce_to_f64(activation, context)?
} else { } else {
0.0 0.0
}; };
@ -367,32 +385,32 @@ fn create_gradient_box<'gc>(
Twips::from_pixels(translate_x), Twips::from_pixels(translate_x),
Twips::from_pixels(translate_y), Twips::from_pixels(translate_y),
); );
apply_matrix_to_object(matrix, this, avm, context)?; apply_matrix_to_object(matrix, this, activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn to_string<'gc>( fn to_string<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let a = this.get("a", avm, context)?; let a = this.get("a", activation, context)?;
let b = this.get("b", avm, context)?; let b = this.get("b", activation, context)?;
let c = this.get("c", avm, context)?; let c = this.get("c", activation, context)?;
let d = this.get("d", avm, context)?; let d = this.get("d", activation, context)?;
let tx = this.get("tx", avm, context)?; let tx = this.get("tx", activation, context)?;
let ty = this.get("ty", avm, context)?; let ty = this.get("ty", activation, context)?;
Ok(format!( Ok(format!(
"(a={}, b={}, c={}, d={}, tx={}, ty={})", "(a={}, b={}, c={}, d={}, tx={}, ty={})",
a.coerce_to_string(avm, context)?, a.coerce_to_string(activation, context)?,
b.coerce_to_string(avm, context)?, b.coerce_to_string(activation, context)?,
c.coerce_to_string(avm, context)?, c.coerce_to_string(activation, context)?,
d.coerce_to_string(avm, context)?, d.coerce_to_string(activation, context)?,
tx.coerce_to_string(avm, context)?, tx.coerce_to_string(activation, context)?,
ty.coerce_to_string(avm, context)? ty.coerce_to_string(activation, context)?
) )
.into()) .into())
} }

View File

@ -1,17 +1,16 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::listeners::Listeners; use crate::avm1::listeners::Listeners;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::MutationContext; use gc_arena::MutationContext;
pub fn show_mouse<'gc>( pub fn show_mouse<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let was_visible = context.input.mouse_visible(); let was_visible = context.input.mouse_visible();
context.input.show_mouse(); context.input.show_mouse();
if was_visible { if was_visible {
@ -22,11 +21,11 @@ pub fn show_mouse<'gc>(
} }
pub fn hide_mouse<'gc>( pub fn hide_mouse<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let was_visible = context.input.mouse_visible(); let was_visible = context.input.mouse_visible();
context.input.hide_mouse(); context.input.hide_mouse();
if was_visible { if was_visible {

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +1,45 @@
//! `MovieClipLoader` impl //! `MovieClipLoader` impl
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::object::TObject; use crate::avm1::object::TObject;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::script_object::ScriptObject; use crate::avm1::script_object::ScriptObject;
use crate::avm1::{Avm1, Object, UpdateContext, Value}; use crate::avm1::{Object, UpdateContext, Value};
use crate::backend::navigator::RequestOptions; use crate::backend::navigator::RequestOptions;
use crate::display_object::{DisplayObject, TDisplayObject}; use crate::display_object::{DisplayObject, TDisplayObject};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
pub fn constructor<'gc>( pub fn constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let listeners = ScriptObject::array(context.gc_context, Some(avm.prototypes().array)); let listeners = ScriptObject::array(
context.gc_context,
Some(activation.avm().prototypes().array),
);
this.define_value( this.define_value(
context.gc_context, context.gc_context,
"_listeners", "_listeners",
Value::Object(listeners.into()), Value::Object(listeners.into()),
Attribute::DontEnum.into(), Attribute::DontEnum.into(),
); );
listeners.set("0", Value::Object(this), avm, context)?; listeners.set("0", Value::Object(this), activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn add_listener<'gc>( pub fn add_listener<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let new_listener = args.get(0).cloned().unwrap_or(Value::Undefined); let new_listener = args.get(0).cloned().unwrap_or(Value::Undefined);
let listeners = this.get("_listeners", avm, context)?; let listeners = this.get("_listeners", activation, context)?;
if let Value::Object(listeners) = listeners { if let Value::Object(listeners) = listeners {
let length = listeners.length(); let length = listeners.length();
@ -48,20 +51,20 @@ pub fn add_listener<'gc>(
} }
pub fn remove_listener<'gc>( pub fn remove_listener<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let old_listener = args.get(0).cloned().unwrap_or(Value::Undefined); let old_listener = args.get(0).cloned().unwrap_or(Value::Undefined);
let listeners = this.get("_listeners", avm, context)?; let listeners = this.get("_listeners", activation, context)?;
if let Value::Object(listeners) = listeners { if let Value::Object(listeners) = listeners {
let length = listeners.length(); let length = listeners.length();
let mut position = None; let mut position = None;
for i in 0..length { for i in 0..length {
let other_listener = listeners.get(&format!("{}", i), avm, context)?; let other_listener = listeners.get(&format!("{}", i), activation, context)?;
if old_listener == other_listener { if old_listener == other_listener {
position = Some(i); position = Some(i);
break; break;
@ -80,7 +83,7 @@ pub fn remove_listener<'gc>(
} }
listeners.delete_array_element(new_length, context.gc_context); listeners.delete_array_element(new_length, context.gc_context);
listeners.delete(avm, context.gc_context, &new_length.to_string()); listeners.delete(activation, context.gc_context, &new_length.to_string());
listeners.set_length(context.gc_context, new_length); listeners.set_length(context.gc_context, new_length);
} }
@ -91,37 +94,37 @@ pub fn remove_listener<'gc>(
} }
pub fn broadcast_message<'gc>( pub fn broadcast_message<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let event_name_val = args.get(0).cloned().unwrap_or(Value::Undefined); let event_name_val = args.get(0).cloned().unwrap_or(Value::Undefined);
let event_name = event_name_val.coerce_to_string(avm, context)?; let event_name = event_name_val.coerce_to_string(activation, context)?;
let call_args = &args[0..]; let call_args = &args[0..];
let listeners = this.get("_listeners", avm, context)?; let listeners = this.get("_listeners", activation, context)?;
if let Value::Object(listeners) = listeners { if let Value::Object(listeners) = listeners {
for i in 0..listeners.length() { for i in 0..listeners.length() {
let listener = listeners.get(&format!("{}", i), avm, context)?; let listener = listeners.get(&format!("{}", i), activation, context)?;
if let Value::Object(listener) = listener { if let Value::Object(listener) = listener {
listener.call_method(&event_name, call_args, avm, context)?; listener.call_method(&event_name, call_args, activation, context)?;
} }
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn load_clip<'gc>( pub fn load_clip<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let url_val = args.get(0).cloned().unwrap_or(Value::Undefined); let url_val = args.get(0).cloned().unwrap_or(Value::Undefined);
let url = url_val.coerce_to_string(avm, context)?; let url = url_val.coerce_to_string(activation, context)?;
let target = args.get(1).cloned().unwrap_or(Value::Undefined); let target = args.get(1).cloned().unwrap_or(Value::Undefined);
if let Value::Object(target) = target { if let Value::Object(target) = target {
@ -147,11 +150,11 @@ pub fn load_clip<'gc>(
} }
pub fn unload_clip<'gc>( pub fn unload_clip<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let target = args.get(0).cloned().unwrap_or(Value::Undefined); let target = args.get(0).cloned().unwrap_or(Value::Undefined);
if let Value::Object(target) = target { if let Value::Object(target) = target {
@ -170,11 +173,11 @@ pub fn unload_clip<'gc>(
} }
pub fn get_progress<'gc>( pub fn get_progress<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let target = args.get(0).cloned().unwrap_or(Value::Undefined); let target = args.get(0).cloned().unwrap_or(Value::Undefined);
if let Value::Object(target) = target { if let Value::Object(target) = target {
@ -206,7 +209,7 @@ pub fn get_progress<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create_proto<'gc>( pub fn create_proto<'gc>(

View File

@ -1,24 +1,24 @@
//! `Number` class impl //! `Number` class impl
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::value_object::ValueObject; use crate::avm1::value_object::ValueObject;
use crate::avm1::{Avm1, Object, TObject, Value}; use crate::avm1::{Object, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// `Number` constructor/function /// `Number` constructor/function
pub fn number<'gc>( pub fn number<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let value = if let Some(val) = args.get(0) { let value = if let Some(val) = args.get(0) {
val.coerce_to_f64(avm, context)? val.coerce_to_f64(activation, context)?
} else { } else {
0.0 0.0
}; };
@ -113,27 +113,27 @@ pub fn create_proto<'gc>(
} }
fn to_string<'gc>( fn to_string<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
// Boxed value must be a number. No coercion. // Boxed value must be a number. No coercion.
let this = if let Some(vbox) = this.as_value_object() { let this = if let Some(vbox) = this.as_value_object() {
if let Value::Number(n) = vbox.unbox() { if let Value::Number(n) = vbox.unbox() {
n n
} else { } else {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
} else { } else {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
}; };
let radix = { let radix = {
let radix = args let radix = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
if radix >= 2.0 && radix <= 36.0 { if radix >= 2.0 && radix <= 36.0 {
radix as u32 radix as u32
} else { } else {
@ -143,7 +143,9 @@ fn to_string<'gc>(
if radix == 10 { if radix == 10 {
// Output number as floating-point decimal. // Output number as floating-point decimal.
Ok(Value::from(this).coerce_to_string(avm, context)?.into()) Ok(Value::from(this)
.coerce_to_string(activation, context)?
.into())
} else if this > -2_147_483_648.0 && this < 2_147_483_648.0 { } else if this > -2_147_483_648.0 && this < 2_147_483_648.0 {
// Output truncated integer in specified base. // Output truncated integer in specified base.
let n = this as i32; let n = this as i32;
@ -184,18 +186,18 @@ fn to_string<'gc>(
} }
fn value_of<'gc>( fn value_of<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(vbox) = this.as_value_object() { if let Some(vbox) = this.as_value_object() {
if let Value::Number(n) = vbox.unbox() { if let Value::Number(n) = vbox.unbox() {
return Ok(n.into()); return Ok(n.into());
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
// The values returned by `NaN.toString(radix)` in Flash Player v7+ // The values returned by `NaN.toString(radix)` in Flash Player v7+

View File

@ -1,9 +1,9 @@
//! Object prototype //! Object prototype
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute::{self, *}; use crate::avm1::property::Attribute::{self, *};
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, TObject, UpdateContext, Value};
use crate::character::Character; use crate::character::Character;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
@ -11,24 +11,24 @@ use std::borrow::Cow;
/// Implements `Object` /// Implements `Object`
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Implements `Object.prototype.addProperty` /// Implements `Object.prototype.addProperty`
pub fn add_property<'gc>( pub fn add_property<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let name = args let name = args
.get(0) .get(0)
.and_then(|v| v.coerce_to_string(avm, context).ok()) .and_then(|v| v.coerce_to_string(activation, context).ok())
.unwrap_or_else(|| Cow::Borrowed("undefined")); .unwrap_or_else(|| Cow::Borrowed("undefined"));
let getter = args.get(1).unwrap_or(&Value::Undefined); let getter = args.get(1).unwrap_or(&Value::Undefined);
let setter = args.get(2).unwrap_or(&Value::Undefined); let setter = args.get(2).unwrap_or(&Value::Undefined);
@ -39,7 +39,7 @@ pub fn add_property<'gc>(
if let Value::Object(set) = setter { if let Value::Object(set) = setter {
if let Some(set_func) = set.as_executable() { if let Some(set_func) = set.as_executable() {
this.add_property_with_case( this.add_property_with_case(
avm, activation,
context.gc_context, context.gc_context,
&name, &name,
get_func.clone(), get_func.clone(),
@ -51,7 +51,7 @@ pub fn add_property<'gc>(
} }
} else if let Value::Null = setter { } else if let Value::Null = setter {
this.add_property_with_case( this.add_property_with_case(
avm, activation,
context.gc_context, context.gc_context,
&name, &name,
get_func.clone(), get_func.clone(),
@ -71,14 +71,16 @@ pub fn add_property<'gc>(
/// Implements `Object.prototype.hasOwnProperty` /// Implements `Object.prototype.hasOwnProperty`
pub fn has_own_property<'gc>( pub fn has_own_property<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(value) = args.get(0) { if let Some(value) = args.get(0) {
let name = value.coerce_to_string(avm, context)?; let name = value.coerce_to_string(activation, context)?;
Ok(Value::Bool(this.has_own_property(avm, context, &name)).into()) Ok(Value::Bool(
this.has_own_property(activation, context, &name),
))
} else { } else {
Ok(false.into()) Ok(false.into())
} }
@ -86,62 +88,62 @@ pub fn has_own_property<'gc>(
/// Implements `Object.prototype.toString` /// Implements `Object.prototype.toString`
fn to_string<'gc>( fn to_string<'gc>(
_: &mut Avm1<'gc>, _: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>, _: &mut UpdateContext<'_, 'gc, '_>,
_: Object<'gc>, _: Object<'gc>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(ReturnValue::Immediate("[object Object]".into())) Ok("[object Object]".into())
} }
/// Implements `Object.prototype.isPropertyEnumerable` /// Implements `Object.prototype.isPropertyEnumerable`
fn is_property_enumerable<'gc>( fn is_property_enumerable<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>, _: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
match args.get(0) { match args.get(0) {
Some(Value::String(name)) => Ok(Value::Bool(this.is_property_enumerable(avm, name)).into()), Some(Value::String(name)) => Ok(Value::Bool(this.is_property_enumerable(activation, name))),
_ => Ok(Value::Bool(false).into()), _ => Ok(Value::Bool(false)),
} }
} }
/// Implements `Object.prototype.isPrototypeOf` /// Implements `Object.prototype.isPrototypeOf`
fn is_prototype_of<'gc>( fn is_prototype_of<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
match args.get(0) { match args.get(0) {
Some(val) => { Some(val) => {
let ob = val.coerce_to_object(avm, context); let ob = val.coerce_to_object(activation, context);
Ok(Value::Bool(this.is_prototype_of(ob)).into()) Ok(Value::Bool(this.is_prototype_of(ob)))
} }
_ => Ok(Value::Bool(false).into()), _ => Ok(Value::Bool(false)),
} }
} }
/// Implements `Object.prototype.valueOf` /// Implements `Object.prototype.valueOf`
fn value_of<'gc>( fn value_of<'gc>(
_: &mut Avm1<'gc>, _: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>, _: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(ReturnValue::Immediate(this.into())) Ok(this.into())
} }
/// Implements `Object.registerClass` /// Implements `Object.registerClass`
pub fn register_class<'gc>( pub fn register_class<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(class_name) = args.get(0).cloned() { if let Some(class_name) = args.get(0).cloned() {
let class_name = class_name.coerce_to_string(avm, context)?; let class_name = class_name.coerce_to_string(activation, context)?;
if let Some(Character::MovieClip(movie_clip)) = context if let Some(Character::MovieClip(movie_clip)) = context
.library .library
.library_for_movie_mut(context.swf.clone()) .library_for_movie_mut(context.swf.clone())
@ -150,14 +152,14 @@ pub fn register_class<'gc>(
if let Some(constructor) = args.get(1) { if let Some(constructor) = args.get(1) {
movie_clip.set_avm1_constructor( movie_clip.set_avm1_constructor(
context.gc_context, context.gc_context,
Some(constructor.coerce_to_object(avm, context)), Some(constructor.coerce_to_object(activation, context)),
); );
} else { } else {
movie_clip.set_avm1_constructor(context.gc_context, None); movie_clip.set_avm1_constructor(context.gc_context, None);
} }
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Partially construct `Object.prototype`. /// Partially construct `Object.prototype`.
@ -224,16 +226,16 @@ pub fn fill_proto<'gc>(
/// declare the property flags of a given property. It's not part of /// declare the property flags of a given property. It's not part of
/// `Object.prototype`, and I suspect that's a deliberate omission. /// `Object.prototype`, and I suspect that's a deliberate omission.
pub fn as_set_prop_flags<'gc>( pub fn as_set_prop_flags<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
_: Object<'gc>, _: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let mut object = if let Some(object) = args.get(0).map(|v| v.coerce_to_object(avm, ac)) { let mut object = if let Some(object) = args.get(0).map(|v| v.coerce_to_object(activation, ac)) {
object object
} else { } else {
log::warn!("ASSetPropFlags called without object to apply to!"); log::warn!("ASSetPropFlags called without object to apply to!");
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
}; };
let properties = match args.get(1) { let properties = match args.get(1) {
@ -241,11 +243,13 @@ pub fn as_set_prop_flags<'gc>(
//Convert to native array. //Convert to native array.
//TODO: Can we make this an iterator? //TODO: Can we make this an iterator?
let mut array = vec![]; let mut array = vec![];
let length = ob.get("length", avm, ac)?.coerce_to_f64(avm, ac)? as usize; let length = ob
.get("length", activation, ac)?
.coerce_to_f64(activation, ac)? as usize;
for i in 0..length { for i in 0..length {
array.push( array.push(
ob.get(&format!("{}", i), avm, ac)? ob.get(&format!("{}", i), activation, ac)?
.coerce_to_string(avm, ac)? .coerce_to_string(activation, ac)?
.to_string(), .to_string(),
) )
} }
@ -256,20 +260,20 @@ pub fn as_set_prop_flags<'gc>(
Some(_) => None, Some(_) => None,
None => { None => {
log::warn!("ASSetPropFlags called without object list!"); log::warn!("ASSetPropFlags called without object list!");
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
}; };
let set_attributes = EnumSet::<Attribute>::from_u128( let set_attributes = EnumSet::<Attribute>::from_u128(
args.get(2) args.get(2)
.unwrap_or(&Value::Number(0.0)) .unwrap_or(&Value::Number(0.0))
.coerce_to_f64(avm, ac)? as u128, .coerce_to_f64(activation, ac)? as u128,
); );
let clear_attributes = EnumSet::<Attribute>::from_u128( let clear_attributes = EnumSet::<Attribute>::from_u128(
args.get(3) args.get(3)
.unwrap_or(&Value::Number(0.0)) .unwrap_or(&Value::Number(0.0))
.coerce_to_f64(avm, ac)? as u128, .coerce_to_f64(activation, ac)? as u128,
); );
match properties { match properties {
@ -286,7 +290,7 @@ pub fn as_set_prop_flags<'gc>(
None => object.set_attributes(ac.gc_context, None, set_attributes, clear_attributes), None => object.set_attributes(ac.gc_context, None, set_attributes, clear_attributes),
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create_object_object<'gc>( pub fn create_object_object<'gc>(

View File

@ -1,10 +1,10 @@
//! flash.geom.Point //! flash.geom.Point
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
@ -12,103 +12,110 @@ use std::f64::NAN;
pub fn point_to_object<'gc>( pub fn point_to_object<'gc>(
point: (f64, f64), point: (f64, f64),
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
let args = [point.0.into(), point.1.into()]; let args = [point.0.into(), point.1.into()];
construct_new_point(&args, avm, context) construct_new_point(&args, activation, context)
} }
pub fn construct_new_point<'gc>( pub fn construct_new_point<'gc>(
args: &[Value<'gc>], args: &[Value<'gc>],
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
let proto = context.system_prototypes.point; let proto = context.system_prototypes.point;
let object = proto.new(avm, context, proto, &args)?; let object = proto.new(activation, context, proto, &args)?;
let _ = constructor(avm, context, object, &args)?; let _ = constructor(activation, context, object, &args)?;
Ok(object) Ok(object)
} }
pub fn value_to_point<'gc>( pub fn value_to_point<'gc>(
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(f64, f64), Error<'gc>> { ) -> Result<(f64, f64), Error<'gc>> {
let x = value let x = value
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("x", avm, context)? .get("x", activation, context)?
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let y = value let y = value
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("y", avm, context)? .get("y", activation, context)?
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
Ok((x, y)) Ok((x, y))
} }
pub fn object_to_point<'gc>( pub fn object_to_point<'gc>(
object: Object<'gc>, object: Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(f64, f64), Error<'gc>> { ) -> Result<(f64, f64), Error<'gc>> {
let x = object.get("x", avm, context)?.coerce_to_f64(avm, context)?; let x = object
let y = object.get("y", avm, context)?.coerce_to_f64(avm, context)?; .get("x", activation, context)?
.coerce_to_f64(activation, context)?;
let y = object
.get("y", activation, context)?
.coerce_to_f64(activation, context)?;
Ok((x, y)) Ok((x, y))
} }
fn constructor<'gc>( fn constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() { if args.is_empty() {
this.set("x", 0.into(), avm, context)?; this.set("x", 0.into(), activation, context)?;
this.set("y", 0.into(), avm, context)?; this.set("y", 0.into(), activation, context)?;
} else { } else {
this.set( this.set(
"x", "x",
args.get(0).unwrap_or(&Value::Undefined).to_owned(), args.get(0).unwrap_or(&Value::Undefined).to_owned(),
avm, activation,
context, context,
)?; )?;
this.set( this.set(
"y", "y",
args.get(1).unwrap_or(&Value::Undefined).to_owned(), args.get(1).unwrap_or(&Value::Undefined).to_owned(),
avm, activation,
context, context,
)?; )?;
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn clone<'gc>( fn clone<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let proto = context.system_prototypes.point; let proto = context.system_prototypes.point;
let args = [this.get("x", avm, context)?, this.get("y", avm, context)?]; let args = [
let cloned = proto.new(avm, context, proto, &args)?; this.get("x", activation, context)?,
let _ = constructor(avm, context, cloned, &args)?; this.get("y", activation, context)?,
];
let cloned = proto.new(activation, context, proto, &args)?;
let _ = constructor(activation, context, cloned, &args)?;
Ok(cloned.into()) Ok(cloned.into())
} }
fn equals<'gc>( fn equals<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(other) = args.get(0) { if let Some(other) = args.get(0) {
let this_x = this.get("x", avm, context)?; let this_x = this.get("x", activation, context)?;
let this_y = this.get("y", avm, context)?; let this_y = this.get("y", activation, context)?;
let other = other.coerce_to_object(avm, context); let other = other.coerce_to_object(activation, context);
let other_x = other.get("x", avm, context)?; let other_x = other.get("x", activation, context)?;
let other_y = other.get("y", avm, context)?; let other_y = other.get("y", activation, context)?;
return Ok((this_x == other_x && this_y == other_y).into()); return Ok((this_x == other_x && this_y == other_y).into());
} }
@ -116,45 +123,53 @@ fn equals<'gc>(
} }
fn add<'gc>( fn add<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this_x = this.get("x", avm, context)?.coerce_to_f64(avm, context)?; let this_x = this
let this_y = this.get("y", avm, context)?.coerce_to_f64(avm, context)?; .get("x", activation, context)?
.coerce_to_f64(activation, context)?;
let this_y = this
.get("y", activation, context)?
.coerce_to_f64(activation, context)?;
let other = value_to_point( let other = value_to_point(
args.get(0).unwrap_or(&Value::Undefined).to_owned(), args.get(0).unwrap_or(&Value::Undefined).to_owned(),
avm, activation,
context, context,
)?; )?;
let object = point_to_object((this_x + other.0, this_y + other.1), avm, context)?; let object = point_to_object((this_x + other.0, this_y + other.1), activation, context)?;
Ok(object.into()) Ok(object.into())
} }
fn subtract<'gc>( fn subtract<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this_x = this.get("x", avm, context)?.coerce_to_f64(avm, context)?; let this_x = this
let this_y = this.get("y", avm, context)?.coerce_to_f64(avm, context)?; .get("x", activation, context)?
.coerce_to_f64(activation, context)?;
let this_y = this
.get("y", activation, context)?
.coerce_to_f64(activation, context)?;
let other = value_to_point( let other = value_to_point(
args.get(0).unwrap_or(&Value::Undefined).to_owned(), args.get(0).unwrap_or(&Value::Undefined).to_owned(),
avm, activation,
context, context,
)?; )?;
let object = point_to_object((this_x - other.0, this_y - other.1), avm, context)?; let object = point_to_object((this_x - other.0, this_y - other.1), activation, context)?;
Ok(object.into()) Ok(object.into())
} }
fn distance<'gc>( fn distance<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if args.len() < 2 { if args.len() < 2 {
return Ok(NAN.into()); return Ok(NAN.into());
} }
@ -162,93 +177,96 @@ fn distance<'gc>(
let a = args let a = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_object(avm, context); .coerce_to_object(activation, context);
let b = args.get(1).unwrap_or(&Value::Undefined); let b = args.get(1).unwrap_or(&Value::Undefined);
let delta = a.call_method("subtract", &[b.to_owned()], avm, context)?; let delta = a.call_method("subtract", &[b.to_owned()], activation, context)?;
Ok(delta Ok(delta
.coerce_to_object(avm, context) .coerce_to_object(activation, context)
.get("length", avm, context)? .get("length", activation, context)?)
.into())
} }
fn polar<'gc>( fn polar<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let length = args let length = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let angle = args let angle = args
.get(1) .get(1)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let point = point_to_object((length * angle.cos(), length * angle.sin()), avm, context)?; let point = point_to_object(
(length * angle.cos(), length * angle.sin()),
activation,
context,
)?;
Ok(point.into()) Ok(point.into())
} }
fn interpolate<'gc>( fn interpolate<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if args.len() < 3 { if args.len() < 3 {
return Ok(point_to_object((NAN, NAN), avm, context)?.into()); return Ok(point_to_object((NAN, NAN), activation, context)?.into());
} }
let a = value_to_point(args.get(0).unwrap().to_owned(), avm, context)?; let a = value_to_point(args.get(0).unwrap().to_owned(), activation, context)?;
let b = value_to_point(args.get(1).unwrap().to_owned(), avm, context)?; let b = value_to_point(args.get(1).unwrap().to_owned(), activation, context)?;
let f = args.get(2).unwrap().coerce_to_f64(avm, context)?; let f = args.get(2).unwrap().coerce_to_f64(activation, context)?;
let result = (b.0 - (b.0 - a.0) * f, b.1 - (b.1 - a.1) * f); let result = (b.0 - (b.0 - a.0) * f, b.1 - (b.1 - a.1) * f);
Ok(point_to_object(result, avm, context)?.into()) Ok(point_to_object(result, activation, context)?.into())
} }
fn to_string<'gc>( fn to_string<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let x = this.get("x", avm, context)?; let x = this.get("x", activation, context)?;
let y = this.get("y", avm, context)?; let y = this.get("y", activation, context)?;
Ok(format!( Ok(format!(
"(x={}, y={})", "(x={}, y={})",
x.coerce_to_string(avm, context)?, x.coerce_to_string(activation, context)?,
y.coerce_to_string(avm, context)? y.coerce_to_string(activation, context)?
) )
.into()) .into())
} }
fn length<'gc>( fn length<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let point = value_to_point(this.into(), avm, context)?; let point = value_to_point(this.into(), activation, context)?;
let length = (point.0 * point.0 + point.1 * point.1).sqrt(); let length = (point.0 * point.0 + point.1 * point.1).sqrt();
Ok(length.into()) Ok(length.into())
} }
fn normalize<'gc>( fn normalize<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let current_length = this let current_length = this
.get("length", avm, context)? .get("length", activation, context)?
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
if current_length.is_finite() { if current_length.is_finite() {
let point = object_to_point(this, avm, context)?; let point = object_to_point(this, activation, context)?;
let new_length = args let new_length = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let (x, y) = if current_length == 0.0 { let (x, y) = if current_length == 0.0 {
(point.0 * new_length, point.1 * new_length) (point.0 * new_length, point.1 * new_length)
} else { } else {
@ -258,33 +276,33 @@ fn normalize<'gc>(
) )
}; };
this.set("x", x.into(), avm, context)?; this.set("x", x.into(), activation, context)?;
this.set("y", y.into(), avm, context)?; this.set("y", y.into(), activation, context)?;
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn offset<'gc>( fn offset<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let point = value_to_point(this.into(), avm, context)?; let point = value_to_point(this.into(), activation, context)?;
let dx = args let dx = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let dy = args let dy = args
.get(1) .get(1)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
this.set("x", (point.0 + dx).into(), avm, context)?; this.set("x", (point.0 + dx).into(), activation, context)?;
this.set("y", (point.1 + dy).into(), avm, context)?; this.set("y", (point.1 + dy).into(), activation, context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create_point_object<'gc>( pub fn create_point_object<'gc>(

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, TObject, Value};
use crate::avm1::{Avm1, Object, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
@ -11,36 +11,36 @@ use crate::avm1::shared_object::SharedObject;
use json::JsonValue; use json::JsonValue;
pub fn delete_all<'gc>( pub fn delete_all<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.deleteAll() not implemented"); log::warn!("SharedObject.deleteAll() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_disk_usage<'gc>( pub fn get_disk_usage<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.getDiskUsage() not implemented"); log::warn!("SharedObject.getDiskUsage() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Serialize an Object and any children to a JSON object /// Serialize an Object and any children to a JSON object
/// It would be best if this was implemented via serde but due to avm and context it can't /// It would be best if this was implemented via serde but due to avm and context it can't
/// Undefined fields aren't serialized /// Undefined fields aren't serialized
fn recursive_serialize<'gc>( fn recursive_serialize<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
obj: Object<'gc>, obj: Object<'gc>,
json_obj: &mut JsonValue, json_obj: &mut JsonValue,
) { ) {
for k in &obj.get_keys(avm) { for k in &obj.get_keys(activation) {
if let Ok(elem) = obj.get(k, avm, action_context) { if let Ok(elem) = obj.get(k, activation, action_context) {
match elem { match elem {
Value::Undefined => {} Value::Undefined => {}
Value::Null => json_obj[k] = JsonValue::Null, Value::Null => json_obj[k] = JsonValue::Null,
@ -49,12 +49,13 @@ fn recursive_serialize<'gc>(
Value::String(s) => json_obj[k] = s.into(), Value::String(s) => json_obj[k] = s.into(),
Value::Object(o) => { Value::Object(o) => {
// Don't attempt to serialize functions // Don't attempt to serialize functions
let function = activation.avm().prototypes.function;
if !o if !o
.is_instance_of(avm, action_context, o, avm.prototypes.function) .is_instance_of(activation, action_context, o, function)
.unwrap_or_default() .unwrap_or_default()
{ {
let mut sub_data_json = JsonValue::new_object(); let mut sub_data_json = JsonValue::new_object();
recursive_serialize(avm, action_context, o, &mut sub_data_json); recursive_serialize(activation, action_context, o, &mut sub_data_json);
json_obj[k] = sub_data_json; json_obj[k] = sub_data_json;
} }
} }
@ -68,7 +69,7 @@ fn recursive_serialize<'gc>(
/// Undefined fields aren't deserialized /// Undefined fields aren't deserialized
fn recursive_deserialize<'gc>( fn recursive_deserialize<'gc>(
json_obj: JsonValue, json_obj: JsonValue,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
object: Object<'gc>, object: Object<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
@ -112,10 +113,11 @@ fn recursive_deserialize<'gc>(
); );
} }
JsonValue::Object(o) => { JsonValue::Object(o) => {
let so = avm.prototypes.object; let so = activation.avm().prototypes.object;
let obj = so.new(avm, context, so, &[]).unwrap(); let obj = so.new(activation, context, so, &[]).unwrap();
let _ = crate::avm1::globals::object::constructor(avm, context, obj, &[]).unwrap(); let _ = crate::avm1::globals::object::constructor(activation, context, obj, &[])
recursive_deserialize(JsonValue::Object(o.clone()), avm, obj, context); .unwrap();
recursive_deserialize(JsonValue::Object(o.clone()), activation, obj, context);
object.define_value( object.define_value(
context.gc_context, context.gc_context,
@ -130,21 +132,21 @@ fn recursive_deserialize<'gc>(
} }
pub fn get_local<'gc>( pub fn get_local<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let name = args let name = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.to_owned() .to_owned()
.coerce_to_string(avm, action_context)? .coerce_to_string(activation, action_context)?
.to_string(); .to_string();
//Check if this is referencing an existing shared object //Check if this is referencing an existing shared object
if let Some(so) = action_context.shared_objects.get(&name) { if let Some(so) = action_context.shared_objects.get(&name) {
return Ok(Value::Object(*so).into()); return Ok(Value::Object(*so));
} }
if args.len() > 1 { if args.len() > 1 {
@ -152,23 +154,23 @@ pub fn get_local<'gc>(
} }
// Data property only should exist when created with getLocal/Remote // Data property only should exist when created with getLocal/Remote
let so = avm.prototypes.shared_object; let so = activation.avm().prototypes.shared_object;
let this = so.new(avm, action_context, so, &[])?; let this = so.new(activation, action_context, so, &[])?;
let _ = constructor(avm, action_context, this, &[])?; let _ = constructor(activation, action_context, this, &[])?;
// Set the internal name // Set the internal name
let obj_so = this.as_shared_object().unwrap(); let obj_so = this.as_shared_object().unwrap();
obj_so.set_name(action_context.gc_context, name.to_string()); obj_so.set_name(action_context.gc_context, name.to_string());
// Create the data object // Create the data object
let data_proto = avm.prototypes.object; let data_proto = activation.avm().prototypes.object;
let data = data_proto.new(avm, action_context, so, &[])?; let data = data_proto.new(activation, action_context, so, &[])?;
let _ = crate::avm1::globals::object::constructor(avm, action_context, data, &[])?; let _ = crate::avm1::globals::object::constructor(activation, action_context, data, &[])?;
// Load the data object from storage if it existed prior // Load the data object from storage if it existed prior
if let Some(saved) = action_context.storage.get_string(&name) { if let Some(saved) = action_context.storage.get_string(&name) {
if let Ok(json_data) = json::parse(&saved) { if let Ok(json_data) = json::parse(&saved) {
recursive_deserialize(json_data, avm, data, action_context); recursive_deserialize(json_data, activation, data, action_context);
} }
} }
@ -185,43 +187,43 @@ pub fn get_local<'gc>(
} }
pub fn get_remote<'gc>( pub fn get_remote<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.getRemote() not implemented"); log::warn!("SharedObject.getRemote() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_max_size<'gc>( pub fn get_max_size<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.getMaxSize() not implemented"); log::warn!("SharedObject.getMaxSize() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn add_listener<'gc>( pub fn add_listener<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.addListener() not implemented"); log::warn!("SharedObject.addListener() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn remove_listener<'gc>( pub fn remove_listener<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.removeListener() not implemented"); log::warn!("SharedObject.removeListener() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create_shared_object_object<'gc>( pub fn create_shared_object_object<'gc>(
@ -297,17 +299,17 @@ pub fn create_shared_object_object<'gc>(
} }
pub fn clear<'gc>( pub fn clear<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let data = this let data = this
.get("data", avm, action_context)? .get("data", activation, action_context)?
.coerce_to_object(avm, action_context); .coerce_to_object(activation, action_context);
for k in &data.get_keys(avm) { for k in &data.get_keys(activation) {
data.delete(avm, action_context.gc_context, k); data.delete(activation, action_context.gc_context, k);
} }
let so = this.as_shared_object().unwrap(); let so = this.as_shared_object().unwrap();
@ -315,41 +317,41 @@ pub fn clear<'gc>(
action_context.storage.remove_key(&name); action_context.storage.remove_key(&name);
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn close<'gc>( pub fn close<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.close() not implemented"); log::warn!("SharedObject.close() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn connect<'gc>( pub fn connect<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.connect() not implemented"); log::warn!("SharedObject.connect() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn flush<'gc>( pub fn flush<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let data = this let data = this
.get("data", avm, action_context)? .get("data", activation, action_context)?
.coerce_to_object(avm, action_context); .coerce_to_object(activation, action_context);
let mut data_json = JsonValue::new_object(); let mut data_json = JsonValue::new_object();
recursive_serialize(avm, action_context, data, &mut data_json); recursive_serialize(activation, action_context, data, &mut data_json);
let this_obj = this.as_shared_object().unwrap(); let this_obj = this.as_shared_object().unwrap();
let name = this_obj.get_name(); let name = this_obj.get_name();
@ -361,53 +363,53 @@ pub fn flush<'gc>(
} }
pub fn get_size<'gc>( pub fn get_size<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.getSize() not implemented"); log::warn!("SharedObject.getSize() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn send<'gc>( pub fn send<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.send() not implemented"); log::warn!("SharedObject.send() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_fps<'gc>( pub fn set_fps<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.setFps() not implemented"); log::warn!("SharedObject.setFps() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn on_status<'gc>( pub fn on_status<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.onStatus() not implemented"); log::warn!("SharedObject.onStatus() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn on_sync<'gc>( pub fn on_sync<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.onSync() not implemented"); log::warn!("SharedObject.onSync() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create_proto<'gc>( pub fn create_proto<'gc>(
@ -470,10 +472,10 @@ pub fn create_proto<'gc>(
} }
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }

View File

@ -1,27 +1,27 @@
//! AVM1 Sound object //! AVM1 Sound object
//! TODO: Sound position, transform, loadSound //! TODO: Sound position, transform, loadSound
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, SoundObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, SoundObject, TObject, UpdateContext, Value};
use crate::character::Character; use crate::character::Character;
use crate::display_object::TDisplayObject; use crate::display_object::TDisplayObject;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `Sound` /// Implements `Sound`
pub fn constructor<'gc>( pub fn constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
// 1st parameter is the movie clip that "owns" all sounds started by this object. // 1st parameter is the movie clip that "owns" all sounds started by this object.
// `Sound.setTransform`, `Sound.stop`, etc. will affect all sounds owned by this clip. // `Sound.setTransform`, `Sound.stop`, etc. will affect all sounds owned by this clip.
let owner = args let owner = args
.get(0) .get(0)
.map(|o| o.coerce_to_object(avm, context)) .map(|o| o.coerce_to_object(activation, context))
.and_then(|o| o.as_display_object()); .and_then(|o| o.as_display_object());
let sound = this.as_sound_object().unwrap(); let sound = this.as_sound_object().unwrap();
@ -161,14 +161,14 @@ pub fn create_proto<'gc>(
} }
fn attach_sound<'gc>( fn attach_sound<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let name = args.get(0).unwrap_or(&Value::Undefined); let name = args.get(0).unwrap_or(&Value::Undefined);
if let Some(sound_object) = this.as_sound_object() { if let Some(sound_object) = this.as_sound_object() {
let name = name.coerce_to_string(avm, context)?; let name = name.coerce_to_string(activation, context)?;
let movie = sound_object let movie = sound_object
.owner() .owner()
.or_else(|| context.levels.get(&0).copied()) .or_else(|| context.levels.get(&0).copied())
@ -197,16 +197,16 @@ fn attach_sound<'gc>(
} else { } else {
log::warn!("Sound.attachSound: this is not a Sound"); log::warn!("Sound.attachSound: this is not a Sound");
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn duration<'gc>( fn duration<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() >= 6 { if activation.current_swf_version() >= 6 {
if let Some(sound_object) = this.as_sound_object() { if let Some(sound_object) = this.as_sound_object() {
return Ok(sound_object.duration().into()); return Ok(sound_object.duration().into());
} else { } else {
@ -214,98 +214,98 @@ fn duration<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn get_bytes_loaded<'gc>( fn get_bytes_loaded<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() >= 6 { if activation.current_swf_version() >= 6 {
log::warn!("Sound.getBytesLoaded: Unimplemented"); log::warn!("Sound.getBytesLoaded: Unimplemented");
Ok(1.into()) Ok(1.into())
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
fn get_bytes_total<'gc>( fn get_bytes_total<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() >= 6 { if activation.current_swf_version() >= 6 {
log::warn!("Sound.getBytesTotal: Unimplemented"); log::warn!("Sound.getBytesTotal: Unimplemented");
Ok(1.into()) Ok(1.into())
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
fn get_pan<'gc>( fn get_pan<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Sound.getPan: Unimplemented"); log::warn!("Sound.getPan: Unimplemented");
Ok(0.into()) Ok(0.into())
} }
fn get_transform<'gc>( fn get_transform<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Sound.getTransform: Unimplemented"); log::warn!("Sound.getTransform: Unimplemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn get_volume<'gc>( fn get_volume<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Sound.getVolume: Unimplemented"); log::warn!("Sound.getVolume: Unimplemented");
Ok(100.into()) Ok(100.into())
} }
fn id3<'gc>( fn id3<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() >= 6 { if activation.current_swf_version() >= 6 {
log::warn!("Sound.id3: Unimplemented"); log::warn!("Sound.id3: Unimplemented");
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn load_sound<'gc>( fn load_sound<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() >= 6 { if activation.current_swf_version() >= 6 {
log::warn!("Sound.loadSound: Unimplemented"); log::warn!("Sound.loadSound: Unimplemented");
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn position<'gc>( fn position<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if avm.current_swf_version() >= 6 { if activation.current_swf_version() >= 6 {
if let Some(sound_object) = this.as_sound_object() { if let Some(sound_object) = this.as_sound_object() {
// TODO: The position is "sticky"; even if the sound is no longer playing, it should return // TODO: The position is "sticky"; even if the sound is no longer playing, it should return
// the previous valid position. // the previous valid position.
@ -320,53 +320,53 @@ fn position<'gc>(
log::warn!("Sound.position: this is not a Sound"); log::warn!("Sound.position: this is not a Sound");
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn set_pan<'gc>( fn set_pan<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Sound.setPan: Unimplemented"); log::warn!("Sound.setPan: Unimplemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn set_transform<'gc>( fn set_transform<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Sound.setTransform: Unimplemented"); log::warn!("Sound.setTransform: Unimplemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn set_volume<'gc>( fn set_volume<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Sound.setVolume: Unimplemented"); log::warn!("Sound.setVolume: Unimplemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn start<'gc>( fn start<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let start_offset = args let start_offset = args
.get(0) .get(0)
.unwrap_or(&Value::Number(0.0)) .unwrap_or(&Value::Number(0.0))
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let loops = args let loops = args
.get(1) .get(1)
.unwrap_or(&Value::Number(1.0)) .unwrap_or(&Value::Number(1.0))
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let loops = if loops >= 1.0 && loops <= f64::from(std::i16::MAX) { let loops = if loops >= 1.0 && loops <= f64::from(std::i16::MAX) {
loops as u16 loops as u16
@ -401,19 +401,19 @@ fn start<'gc>(
log::warn!("Sound.start: Invalid sound"); log::warn!("Sound.start: Invalid sound");
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn stop<'gc>( fn stop<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(sound) = this.as_sound_object() { if let Some(sound) = this.as_sound_object() {
if let Some(name) = args.get(0) { if let Some(name) = args.get(0) {
// Usage 1: Stop all instances of a particular sound, using the name parameter. // Usage 1: Stop all instances of a particular sound, using the name parameter.
let name = name.coerce_to_string(avm, context)?; let name = name.coerce_to_string(activation, context)?;
let movie = sound let movie = sound
.owner() .owner()
.or_else(|| context.levels.get(&0).copied()) .or_else(|| context.levels.get(&0).copied())
@ -449,5 +449,5 @@ fn stop<'gc>(
log::warn!("Sound.stop: this is not a Sound"); log::warn!("Sound.stop: this is not a Sound");
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }

View File

@ -1,12 +1,11 @@
//! Stage object //! Stage object
//! //!
//! TODO: This is a very rough stub with not much implementation. //! TODO: This is a very rough stub with not much implementation.
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::MutationContext; use gc_arena::MutationContext;
pub fn create_stage_object<'gc>( pub fn create_stage_object<'gc>(
@ -77,99 +76,99 @@ pub fn create_stage_object<'gc>(
} }
fn add_listener<'gc>( fn add_listener<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.addListener: unimplemented"); log::warn!("Stage.addListener: unimplemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn align<'gc>( fn align<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.align: unimplemented"); log::warn!("Stage.align: unimplemented");
Ok("".into()) Ok("".into())
} }
fn set_align<'gc>( fn set_align<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.align: unimplemented"); log::warn!("Stage.align: unimplemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn height<'gc>( fn height<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.stage_size.1.to_pixels().into()) Ok(context.stage_size.1.to_pixels().into())
} }
fn remove_listener<'gc>( fn remove_listener<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.removeListener: unimplemented"); log::warn!("Stage.removeListener: unimplemented");
Ok("".into()) Ok("".into())
} }
fn scale_mode<'gc>( fn scale_mode<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.scaleMode: unimplemented"); log::warn!("Stage.scaleMode: unimplemented");
Ok("noScale".into()) Ok("noScale".into())
} }
fn set_scale_mode<'gc>( fn set_scale_mode<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.scaleMode: unimplemented"); log::warn!("Stage.scaleMode: unimplemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn show_menu<'gc>( fn show_menu<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.showMenu: unimplemented"); log::warn!("Stage.showMenu: unimplemented");
Ok(true.into()) Ok(true.into())
} }
fn set_show_menu<'gc>( fn set_show_menu<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("Stage.showMenu: unimplemented"); log::warn!("Stage.showMenu: unimplemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn width<'gc>( fn width<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.stage_size.0.to_pixels().into()) Ok(context.stage_size.0.to_pixels().into())
} }

View File

@ -1,11 +1,11 @@
//! `String` class impl //! `String` class impl
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::value_object::ValueObject; use crate::avm1::value_object::ValueObject;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value}; use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::string_utils; use crate::string_utils;
use enumset::EnumSet; use enumset::EnumSet;
@ -13,14 +13,14 @@ use gc_arena::MutationContext;
/// `String` constructor /// `String` constructor
pub fn string<'gc>( pub fn string<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let value = match args.get(0).cloned() { let value = match args.get(0).cloned() {
Some(Value::String(s)) => s, Some(Value::String(s)) => s,
Some(v) => v.coerce_to_string(avm, ac)?.to_string(), Some(v) => v.coerce_to_string(activation, ac)?.to_string(),
_ => String::new(), _ => String::new(),
}; };
@ -171,19 +171,19 @@ pub fn create_proto<'gc>(
} }
fn char_at<'gc>( fn char_at<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
// TODO: Will return REPLACEMENT_CHAR if this indexes a character outside the BMP, losing info about the surrogate. // TODO: Will return REPLACEMENT_CHAR if this indexes a character outside the BMP, losing info about the surrogate.
// When we improve our string representation, the unpaired surrogate should be returned. // When we improve our string representation, the unpaired surrogate should be returned.
let this_val = Value::from(this); let this_val = Value::from(this);
let string = this_val.coerce_to_string(avm, context)?; let string = this_val.coerce_to_string(activation, context)?;
let i = args let i = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(avm, context)?; .coerce_to_i32(activation, context)?;
let ret = if i >= 0 { let ret = if i >= 0 {
string string
.encode_utf16() .encode_utf16()
@ -197,17 +197,17 @@ fn char_at<'gc>(
} }
fn char_code_at<'gc>( fn char_code_at<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(avm, context)?; let this = this_val.coerce_to_string(activation, context)?;
let i = args let i = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(avm, context)?; .coerce_to_i32(activation, context)?;
let ret = if i >= 0 { let ret = if i >= 0 {
this.encode_utf16() this.encode_utf16()
.nth(i as usize) .nth(i as usize)
@ -220,31 +220,31 @@ fn char_code_at<'gc>(
} }
fn concat<'gc>( fn concat<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let mut ret = Value::from(this) let mut ret = Value::from(this)
.coerce_to_string(avm, context)? .coerce_to_string(activation, context)?
.to_string(); .to_string();
for arg in args { for arg in args {
let s = arg.coerce_to_string(avm, context)?; let s = arg.coerce_to_string(activation, context)?;
ret.push_str(&s) ret.push_str(&s)
} }
Ok(ret.into()) Ok(ret.into())
} }
fn from_char_code<'gc>( fn from_char_code<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
// TODO: Unpaired surrogates will be replace with Unicode replacement char. // TODO: Unpaired surrogates will be replace with Unicode replacement char.
let mut out = String::with_capacity(args.len()); let mut out = String::with_capacity(args.len());
for arg in args { for arg in args {
let i = arg.coerce_to_u16(avm, context)?; let i = arg.coerce_to_u16(activation, context)?;
if i == 0 { if i == 0 {
// Stop at a null-terminator. // Stop at a null-terminator.
break; break;
@ -255,20 +255,20 @@ fn from_char_code<'gc>(
} }
fn index_of<'gc>( fn index_of<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this = Value::from(this) let this = Value::from(this)
.coerce_to_string(avm, context)? .coerce_to_string(activation, context)?
.encode_utf16() .encode_utf16()
.collect::<Vec<u16>>(); .collect::<Vec<u16>>();
let pattern = match args.get(0) { let pattern = match args.get(0) {
None | Some(Value::Undefined) => return Ok(Value::Undefined.into()), None | Some(Value::Undefined) => return Ok(Value::Undefined),
Some(s) => s Some(s) => s
.clone() .clone()
.coerce_to_string(avm, context)? .coerce_to_string(activation, context)?
.encode_utf16() .encode_utf16()
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
}; };
@ -276,7 +276,7 @@ fn index_of<'gc>(
let n = args let n = args
.get(1) .get(1)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(avm, context)?; .coerce_to_i32(activation, context)?;
if n >= 0 { if n >= 0 {
n as usize n as usize
} else { } else {
@ -303,27 +303,27 @@ fn index_of<'gc>(
} }
fn last_index_of<'gc>( fn last_index_of<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this = Value::from(this) let this = Value::from(this)
.coerce_to_string(avm, context)? .coerce_to_string(activation, context)?
.encode_utf16() .encode_utf16()
.collect::<Vec<u16>>(); .collect::<Vec<u16>>();
let pattern = match args.get(0) { let pattern = match args.get(0) {
None | Some(Value::Undefined) => return Ok(Value::Undefined.into()), None | Some(Value::Undefined) => return Ok(Value::Undefined),
Some(s) => s Some(s) => s
.clone() .clone()
.coerce_to_string(avm, context)? .coerce_to_string(activation, context)?
.encode_utf16() .encode_utf16()
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
}; };
let start_index = match args.get(1) { let start_index = match args.get(1) {
None | Some(Value::Undefined) => this.len(), None | Some(Value::Undefined) => this.len(),
Some(n) => { Some(n) => {
let n = n.coerce_to_i32(avm, context)?; let n = n.coerce_to_i32(activation, context)?;
if n >= 0 { if n >= 0 {
let n = n as usize; let n = n as usize;
if n <= this.len() { if n <= this.len() {
@ -355,28 +355,28 @@ fn last_index_of<'gc>(
} }
fn slice<'gc>( fn slice<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() { if args.is_empty() {
// No args returns undefined immediately. // No args returns undefined immediately.
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(avm, context)?; let this = this_val.coerce_to_string(activation, context)?;
let this_len = this.encode_utf16().count(); let this_len = this.encode_utf16().count();
let start_index = string_wrapping_index( let start_index = string_wrapping_index(
args.get(0) args.get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(avm, context)?, .coerce_to_i32(activation, context)?,
this_len, this_len,
); );
let end_index = match args.get(1) { let end_index = match args.get(1) {
None | Some(Value::Undefined) => this_len, None | Some(Value::Undefined) => this_len,
Some(n) => string_wrapping_index(n.coerce_to_i32(avm, context)?, this_len), Some(n) => string_wrapping_index(n.coerce_to_i32(activation, context)?, this_len),
}; };
if start_index < end_index { if start_index < end_index {
let ret = utf16_iter_to_string( let ret = utf16_iter_to_string(
@ -391,20 +391,20 @@ fn slice<'gc>(
} }
fn split<'gc>( fn split<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(avm, context)?; let this = this_val.coerce_to_string(activation, context)?;
let delimiter_val = args.get(0).unwrap_or(&Value::Undefined); let delimiter_val = args.get(0).unwrap_or(&Value::Undefined);
let delimiter = delimiter_val.coerce_to_string(avm, context)?; let delimiter = delimiter_val.coerce_to_string(activation, context)?;
let limit = match args.get(1) { let limit = match args.get(1) {
None | Some(Value::Undefined) => std::usize::MAX, None | Some(Value::Undefined) => std::usize::MAX,
Some(n) => std::cmp::max(0, n.coerce_to_i32(avm, context)?) as usize, Some(n) => std::cmp::max(0, n.coerce_to_i32(activation, context)?) as usize,
}; };
let array = ScriptObject::array(context.gc_context, Some(avm.prototypes.array)); let array = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
if !delimiter.is_empty() { if !delimiter.is_empty() {
for (i, token) in this.split(delimiter.as_ref()).take(limit).enumerate() { for (i, token) in this.split(delimiter.as_ref()).take(limit).enumerate() {
array.set_array_element(i, token.to_string().into(), context.gc_context); array.set_array_element(i, token.to_string().into(), context.gc_context);
@ -421,24 +421,26 @@ fn split<'gc>(
} }
fn substr<'gc>( fn substr<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() { if args.is_empty() {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(avm, context)?; let this = this_val.coerce_to_string(activation, context)?;
let this_len = this.encode_utf16().count(); let this_len = this.encode_utf16().count();
let start_index = let start_index = string_wrapping_index(
string_wrapping_index(args.get(0).unwrap().coerce_to_i32(avm, context)?, this_len); args.get(0).unwrap().coerce_to_i32(activation, context)?,
this_len,
);
let len = match args.get(1) { let len = match args.get(1) {
None | Some(Value::Undefined) => this_len, None | Some(Value::Undefined) => this_len,
Some(n) => string_index(n.coerce_to_i32(avm, context)?, this_len), Some(n) => string_index(n.coerce_to_i32(activation, context)?, this_len),
}; };
let ret = utf16_iter_to_string(this.encode_utf16().skip(start_index).take(len)); let ret = utf16_iter_to_string(this.encode_utf16().skip(start_index).take(len));
@ -446,23 +448,26 @@ fn substr<'gc>(
} }
fn substring<'gc>( fn substring<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() { if args.is_empty() {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(avm, context)?; let this = this_val.coerce_to_string(activation, context)?;
let this_len = this.encode_utf16().count(); let this_len = this.encode_utf16().count();
let mut start_index = string_index(args.get(0).unwrap().coerce_to_i32(avm, context)?, this_len); let mut start_index = string_index(
args.get(0).unwrap().coerce_to_i32(activation, context)?,
this_len,
);
let mut end_index = match args.get(1) { let mut end_index = match args.get(1) {
None | Some(Value::Undefined) => this_len, None | Some(Value::Undefined) => this_len,
Some(n) => string_index(n.coerce_to_i32(avm, context)?, this_len), Some(n) => string_index(n.coerce_to_i32(activation, context)?, this_len),
}; };
// substring automatically swaps the start/end if they are flipped. // substring automatically swaps the start/end if they are flipped.
@ -478,13 +483,13 @@ fn substring<'gc>(
} }
fn to_lower_case<'gc>( fn to_lower_case<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(avm, context)?; let this = this_val.coerce_to_string(activation, context)?;
Ok(this Ok(this
.chars() .chars()
.map(string_utils::swf_char_to_lowercase) .map(string_utils::swf_char_to_lowercase)
@ -494,11 +499,11 @@ fn to_lower_case<'gc>(
/// `String.toString` / `String.valueOf` impl /// `String.toString` / `String.valueOf` impl
pub fn to_string_value_of<'gc>( pub fn to_string_value_of<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(vbox) = this.as_value_object() { if let Some(vbox) = this.as_value_object() {
if let Value::String(s) = vbox.unbox() { if let Value::String(s) = vbox.unbox() {
return Ok(s.into()); return Ok(s.into());
@ -508,17 +513,17 @@ pub fn to_string_value_of<'gc>(
//TODO: This normally falls back to `[object Object]` or `[type Function]`, //TODO: This normally falls back to `[object Object]` or `[type Function]`,
//implying that `toString` and `valueOf` are inherent object properties and //implying that `toString` and `valueOf` are inherent object properties and
//not just methods. //not just methods.
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn to_upper_case<'gc>( fn to_upper_case<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this_val = Value::from(this); let this_val = Value::from(this);
let this = this_val.coerce_to_string(avm, context)?; let this = this_val.coerce_to_string(activation, context)?;
Ok(this Ok(this
.chars() .chars()
.map(string_utils::swf_char_to_uppercase) .map(string_utils::swf_char_to_uppercase)

View File

@ -1,8 +1,8 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{ScriptObject, TObject, Value};
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use core::fmt; use core::fmt;
use enumset::{EnumSet, EnumSetType}; use enumset::{EnumSet, EnumSetType};
@ -273,11 +273,11 @@ pub struct SystemProperties {
} }
impl SystemProperties { impl SystemProperties {
pub fn get_version_string(&self, avm: &Avm1) -> String { pub fn get_version_string(&self, activation: &mut Activation) -> String {
format!( format!(
"{} {},0,0,0", "{} {},0,0,0",
self.manufacturer.get_platform_name(), self.manufacturer.get_platform_name(),
avm.player_version activation.avm().player_version
) )
} }
@ -305,7 +305,7 @@ impl SystemProperties {
percent_encoding::utf8_percent_encode(s, percent_encoding::NON_ALPHANUMERIC).to_string() percent_encoding::utf8_percent_encode(s, percent_encoding::NON_ALPHANUMERIC).to_string()
} }
pub fn get_server_string(&self, avm: &Avm1) -> String { pub fn get_server_string(&self, activation: &mut Activation) -> String {
url::form_urlencoded::Serializer::new(String::new()) url::form_urlencoded::Serializer::new(String::new())
.append_pair("A", self.encode_capability(SystemCapabilities::Audio)) .append_pair("A", self.encode_capability(SystemCapabilities::Audio))
.append_pair( .append_pair(
@ -347,7 +347,7 @@ impl SystemProperties {
"M", "M",
&self.encode_string( &self.encode_string(
self.manufacturer self.manufacturer
.get_manufacturer_string(avm.player_version) .get_manufacturer_string(activation.avm().player_version)
.as_str(), .as_str(),
), ),
) )
@ -358,7 +358,11 @@ impl SystemProperties {
.append_pair("COL", &self.screen_color.to_string()) .append_pair("COL", &self.screen_color.to_string())
.append_pair("AR", &self.aspect_ratio.to_string()) .append_pair("AR", &self.aspect_ratio.to_string())
.append_pair("OS", &self.encode_string(&self.os.to_string())) .append_pair("OS", &self.encode_string(&self.os.to_string()))
.append_pair("L", self.language.get_language_code(avm.player_version)) .append_pair(
"L",
self.language
.get_language_code(activation.avm().player_version),
)
.append_pair("IME", self.encode_capability(SystemCapabilities::IME)) .append_pair("IME", self.encode_capability(SystemCapabilities::IME))
.append_pair("PT", &self.player_type.to_string()) .append_pair("PT", &self.player_type.to_string())
.append_pair( .append_pair(
@ -399,102 +403,102 @@ impl Default for SystemProperties {
} }
pub fn set_clipboard<'gc>( pub fn set_clipboard<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let new_content = args let new_content = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_string(avm, action_context)? .coerce_to_string(activation, action_context)?
.to_string(); .to_string();
action_context.input.set_clipboard_content(new_content); action_context.input.set_clipboard_content(new_content);
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn show_settings<'gc>( pub fn show_settings<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
//TODO: should default to the last panel displayed //TODO: should default to the last panel displayed
let last_panel_pos = 0; let last_panel_pos = 0;
let panel_pos = args let panel_pos = args
.get(0) .get(0)
.unwrap_or(&Value::Number(last_panel_pos as f64)) .unwrap_or(&Value::Number(last_panel_pos as f64))
.coerce_to_i32(avm, action_context)?; .coerce_to_i32(activation, action_context)?;
let panel = SettingsPanel::try_from(panel_pos as u8).unwrap_or(SettingsPanel::Privacy); let panel = SettingsPanel::try_from(panel_pos as u8).unwrap_or(SettingsPanel::Privacy);
log::warn!("System.showSettings({:?}) not not implemented", panel); log::warn!("System.showSettings({:?}) not not implemented", panel);
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_use_code_page<'gc>( pub fn set_use_code_page<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let value = args let value = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.to_owned() .to_owned()
.as_bool(avm.current_swf_version()); .as_bool(activation.current_swf_version());
action_context.system.use_codepage = value; action_context.system.use_codepage = value;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_use_code_page<'gc>( pub fn get_use_code_page<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(action_context.system.use_codepage.into()) Ok(action_context.system.use_codepage.into())
} }
pub fn set_exact_settings<'gc>( pub fn set_exact_settings<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let value = args let value = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.to_owned() .to_owned()
.as_bool(avm.current_swf_version()); .as_bool(activation.current_swf_version());
action_context.system.exact_settings = value; action_context.system.exact_settings = value;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_exact_settings<'gc>( pub fn get_exact_settings<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>, action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(action_context.system.exact_settings.into()) Ok(action_context.system.exact_settings.into())
} }
pub fn on_status<'gc>( pub fn on_status<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("System.onStatus() not implemented"); log::warn!("System.onStatus() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create<'gc>( pub fn create<'gc>(

View File

@ -1,9 +1,9 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::globals::system::SystemCapabilities; use crate::avm1::globals::system::SystemCapabilities;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{ScriptObject, TObject, Value};
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
@ -11,11 +11,11 @@ use gc_arena::MutationContext;
macro_rules! capabilities_func { macro_rules! capabilities_func {
($func_name: ident, $capability: expr) => { ($func_name: ident, $capability: expr) => {
pub fn $func_name<'gc>( pub fn $func_name<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.has_capability($capability).into()) Ok(context.system.has_capability($capability).into())
} }
}; };
@ -24,11 +24,11 @@ macro_rules! capabilities_func {
macro_rules! inverse_capabilities_func { macro_rules! inverse_capabilities_func {
($func_name: ident, $capability: expr) => { ($func_name: ident, $capability: expr) => {
pub fn $func_name<'gc>( pub fn $func_name<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok((!context.system.has_capability($capability)).into()) Ok((!context.system.has_capability($capability)).into())
} }
}; };
@ -77,127 +77,127 @@ inverse_capabilities_func!(get_is_av_hardware_disabled, SystemCapabilities::AvHa
inverse_capabilities_func!(get_is_windowless_disabled, SystemCapabilities::WindowLess); inverse_capabilities_func!(get_is_windowless_disabled, SystemCapabilities::WindowLess);
pub fn get_player_type<'gc>( pub fn get_player_type<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.player_type.to_string().into()) Ok(context.system.player_type.to_string().into())
} }
pub fn get_screen_color<'gc>( pub fn get_screen_color<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.screen_color.to_string().into()) Ok(context.system.screen_color.to_string().into())
} }
pub fn get_language<'gc>( pub fn get_language<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context Ok(context
.system .system
.language .language
.get_language_code(avm.player_version) .get_language_code(activation.avm().player_version)
.into()) .into())
} }
pub fn get_screen_resolution_x<'gc>( pub fn get_screen_resolution_x<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.screen_resolution.0.into()) Ok(context.system.screen_resolution.0.into())
} }
pub fn get_screen_resolution_y<'gc>( pub fn get_screen_resolution_y<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.screen_resolution.1.into()) Ok(context.system.screen_resolution.1.into())
} }
pub fn get_pixel_aspect_ratio<'gc>( pub fn get_pixel_aspect_ratio<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.aspect_ratio.into()) Ok(context.system.aspect_ratio.into())
} }
pub fn get_screen_dpi<'gc>( pub fn get_screen_dpi<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.dpi.into()) Ok(context.system.dpi.into())
} }
pub fn get_manufacturer<'gc>( pub fn get_manufacturer<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context Ok(context
.system .system
.manufacturer .manufacturer
.get_manufacturer_string(avm.player_version) .get_manufacturer_string(activation.avm().player_version)
.into()) .into())
} }
pub fn get_os_name<'gc>( pub fn get_os_name<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.os.to_string().into()) Ok(context.system.os.to_string().into())
} }
pub fn get_version<'gc>( pub fn get_version<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.get_version_string(avm).into()) Ok(context.system.get_version_string(activation).into())
} }
pub fn get_server_string<'gc>( pub fn get_server_string<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.get_server_string(avm).into()) Ok(context.system.get_server_string(activation).into())
} }
pub fn get_cpu_architecture<'gc>( pub fn get_cpu_architecture<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.cpu_architecture.to_string().into()) Ok(context.system.cpu_architecture.to_string().into())
} }
pub fn get_max_idc_level<'gc>( pub fn get_max_idc_level<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.idc_level.clone().into()) Ok(context.system.idc_level.clone().into())
} }

View File

@ -1,74 +1,74 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::listeners::Listeners; use crate::avm1::listeners::Listeners;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::property::Attribute::{DontDelete, DontEnum, ReadOnly}; use crate::avm1::property::Attribute::{DontDelete, DontEnum, ReadOnly};
use crate::avm1::return_value::ReturnValue; use crate::avm1::{ScriptObject, TObject, Value};
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
use std::convert::Into; use std::convert::Into;
fn on_ime_composition<'gc>( fn on_ime_composition<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into()) Ok(false.into())
} }
fn do_conversion<'gc>( fn do_conversion<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(true.into()) Ok(true.into())
} }
fn get_conversion_mode<'gc>( fn get_conversion_mode<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok("KOREAN".into()) Ok("KOREAN".into())
} }
fn get_enabled<'gc>( fn get_enabled<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into()) Ok(false.into())
} }
fn set_composition_string<'gc>( fn set_composition_string<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into()) Ok(false.into())
} }
fn set_conversion_mode<'gc>( fn set_conversion_mode<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into()) Ok(false.into())
} }
fn set_enabled<'gc>( fn set_enabled<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into()) Ok(false.into())
} }

View File

@ -1,80 +1,80 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{ScriptObject, TObject, Value};
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
use std::convert::Into; use std::convert::Into;
fn allow_domain<'gc>( fn allow_domain<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("System.security.allowDomain() not implemented"); log::warn!("System.security.allowDomain() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn allow_insecure_domain<'gc>( fn allow_insecure_domain<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("System.security.allowInsecureDomain() not implemented"); log::warn!("System.security.allowInsecureDomain() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn load_policy_file<'gc>( fn load_policy_file<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("System.security.allowInsecureDomain() not implemented"); log::warn!("System.security.allowInsecureDomain() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn escape_domain<'gc>( fn escape_domain<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("System.security.escapeDomain() not implemented"); log::warn!("System.security.escapeDomain() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn get_sandbox_type<'gc>( fn get_sandbox_type<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.sandbox_type.to_string().into()) Ok(context.system.sandbox_type.to_string().into())
} }
fn get_choose_local_swf_path<'gc>( fn get_choose_local_swf_path<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("System.security.chooseLocalSwfPath() not implemented"); log::warn!("System.security.chooseLocalSwfPath() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn policy_file_resolver<'gc>( fn policy_file_resolver<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("System.security.chooseLocalSwfPath() not implemented"); log::warn!("System.security.chooseLocalSwfPath() not implemented");
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create<'gc>( pub fn create<'gc>(

View File

@ -1,94 +1,95 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::globals::display_object; use crate::avm1::globals::display_object;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject}; use crate::display_object::{AutoSizeMode, EditText, TDisplayObject};
use crate::html::TextFormat; use crate::html::TextFormat;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `TextField` /// Implements `TextField`
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_text<'gc>( pub fn get_text<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
return Ok(text_field.text().into()); return Ok(text_field.text().into());
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_text<'gc>( pub fn set_text<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
if let Some(value) = args.get(0) { if let Some(value) = args.get(0) {
if let Err(err) = if let Err(err) = text_field.set_text(
text_field.set_text(value.coerce_to_string(avm, context)?.to_string(), context) value.coerce_to_string(activation, context)?.to_string(),
{ context,
) {
log::error!("Error when setting TextField.text: {}", err); log::error!("Error when setting TextField.text: {}", err);
} }
text_field.propagate_text_binding(avm, context); text_field.propagate_text_binding(activation, context);
} }
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_html<'gc>( pub fn get_html<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
return Ok(text_field.is_html().into()); return Ok(text_field.is_html().into());
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_html<'gc>( pub fn set_html<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
if let Some(value) = args.get(0) { if let Some(value) = args.get(0) {
text_field.set_is_html(context, value.as_bool(avm.current_swf_version())); text_field.set_is_html(context, value.as_bool(activation.current_swf_version()));
} }
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_html_text<'gc>( pub fn get_html_text<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
if let Ok(text) = text_field.html_text(context) { if let Ok(text) = text_field.html_text(context) {
@ -96,104 +97,104 @@ pub fn get_html_text<'gc>(
} }
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_html_text<'gc>( pub fn set_html_text<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
let text = args let text = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_string(avm, context)?; .coerce_to_string(activation, context)?;
let _ = text_field.set_html_text(text.into_owned(), context); let _ = text_field.set_html_text(text.into_owned(), context);
// Changing the htmlText does NOT update variable bindings (does not call EditText::propagate_text_binding). // Changing the htmlText does NOT update variable bindings (does not call EditText::propagate_text_binding).
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_border<'gc>( pub fn get_border<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
return Ok(text_field.has_border().into()); return Ok(text_field.has_border().into());
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_border<'gc>( pub fn set_border<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
if let Some(value) = args.get(0) { if let Some(value) = args.get(0) {
let has_border = value.as_bool(avm.current_swf_version()); let has_border = value.as_bool(activation.current_swf_version());
text_field.set_has_border(context.gc_context, has_border); text_field.set_has_border(context.gc_context, has_border);
} }
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_embed_fonts<'gc>( pub fn get_embed_fonts<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
return Ok((!text_field.is_device_font()).into()); return Ok((!text_field.is_device_font()).into());
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_embed_fonts<'gc>( pub fn set_embed_fonts<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
if let Some(value) = args.get(0) { if let Some(value) = args.get(0) {
let embed_fonts = value.as_bool(avm.current_swf_version()); let embed_fonts = value.as_bool(activation.current_swf_version());
text_field.set_is_device_font(context, !embed_fonts); text_field.set_is_device_font(context, !embed_fonts);
} }
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn get_length<'gc>( pub fn get_length<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
return Ok((text_field.text_length() as f64).into()); return Ok((text_field.text_length() as f64).into());
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
macro_rules! with_text_field { macro_rules! with_text_field {
@ -201,13 +202,13 @@ macro_rules! with_text_field {
$( $(
$object.force_set_function( $object.force_set_function(
$name, $name,
|avm, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<ReturnValue<'gc>, Error<'gc>> { |activation, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<Value<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() { if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() { if let Some(text_field) = display_object.as_edit_text() {
return $fn(text_field, avm, context, args); return $fn(text_field, activation, context, args);
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} as crate::avm1::function::NativeFunction<'gc>, } as crate::avm1::function::NativeFunction<'gc>,
$gc_context, $gc_context,
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
@ -218,11 +219,11 @@ macro_rules! with_text_field {
} }
pub fn text_width<'gc>( pub fn text_width<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
@ -232,15 +233,15 @@ pub fn text_width<'gc>(
return Ok(metrics.0.to_pixels().into()); return Ok(metrics.0.to_pixels().into());
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn text_height<'gc>( pub fn text_height<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
@ -250,15 +251,15 @@ pub fn text_height<'gc>(
return Ok(metrics.1.to_pixels().into()); return Ok(metrics.1.to_pixels().into());
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn multiline<'gc>( pub fn multiline<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
@ -266,20 +267,20 @@ pub fn multiline<'gc>(
return Ok(etext.is_multiline().into()); return Ok(etext.is_multiline().into());
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_multiline<'gc>( pub fn set_multiline<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let is_multiline = args let is_multiline = args
.get(0) .get(0)
.cloned() .cloned()
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)
.as_bool(avm.current_swf_version()); .as_bool(activation.current_swf_version());
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
@ -288,15 +289,15 @@ pub fn set_multiline<'gc>(
etext.set_multiline(is_multiline, context); etext.set_multiline(is_multiline, context);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn variable<'gc>( fn variable<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
@ -307,36 +308,36 @@ fn variable<'gc>(
} }
// Unset `variable` retuns null, not undefined // Unset `variable` retuns null, not undefined
Ok(Value::Null.into()) Ok(Value::Null)
} }
fn set_variable<'gc>( fn set_variable<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let variable = match args.get(0) { let variable = match args.get(0) {
None | Some(Value::Undefined) | Some(Value::Null) => None, None | Some(Value::Undefined) | Some(Value::Null) => None,
Some(v) => Some(v.coerce_to_string(avm, context)?), Some(v) => Some(v.coerce_to_string(activation, context)?),
}; };
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
{ {
etext.set_variable(variable.map(|v| v.into_owned()), avm, context); etext.set_variable(variable.map(|v| v.into_owned()), activation, context);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn word_wrap<'gc>( pub fn word_wrap<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
@ -344,20 +345,20 @@ pub fn word_wrap<'gc>(
return Ok(etext.is_word_wrap().into()); return Ok(etext.is_word_wrap().into());
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_word_wrap<'gc>( pub fn set_word_wrap<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let is_word_wrap = args let is_word_wrap = args
.get(0) .get(0)
.cloned() .cloned()
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)
.as_bool(avm.current_swf_version()); .as_bool(activation.current_swf_version());
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
@ -366,15 +367,15 @@ pub fn set_word_wrap<'gc>(
etext.set_word_wrap(is_word_wrap, context); etext.set_word_wrap(is_word_wrap, context);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn auto_size<'gc>( pub fn auto_size<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
@ -387,15 +388,15 @@ pub fn auto_size<'gc>(
}); });
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn set_auto_size<'gc>( pub fn set_auto_size<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this if let Some(etext) = this
.as_display_object() .as_display_object()
.and_then(|dobj| dobj.as_edit_text()) .and_then(|dobj| dobj.as_edit_text())
@ -412,7 +413,7 @@ pub fn set_auto_size<'gc>(
); );
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn create_proto<'gc>( pub fn create_proto<'gc>(
@ -527,44 +528,44 @@ pub fn attach_virtual_properties<'gc>(gc_context: MutationContext<'gc, '_>, obje
fn get_new_text_format<'gc>( fn get_new_text_format<'gc>(
text_field: EditText<'gc>, text_field: EditText<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let tf = text_field.new_text_format(); let tf = text_field.new_text_format();
Ok(tf.as_avm1_object(avm, context)?.into()) Ok(tf.as_avm1_object(activation, context)?.into())
} }
fn set_new_text_format<'gc>( fn set_new_text_format<'gc>(
text_field: EditText<'gc>, text_field: EditText<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let tf = args.get(0).cloned().unwrap_or(Value::Undefined); let tf = args.get(0).cloned().unwrap_or(Value::Undefined);
if let Value::Object(tf) = tf { if let Value::Object(tf) = tf {
let tf_parsed = TextFormat::from_avm1_object(tf, avm, context)?; let tf_parsed = TextFormat::from_avm1_object(tf, activation, context)?;
text_field.set_new_text_format(tf_parsed, context); text_field.set_new_text_format(tf_parsed, context);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn get_text_format<'gc>( fn get_text_format<'gc>(
text_field: EditText<'gc>, text_field: EditText<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let (from, to) = match (args.get(0), args.get(1)) { let (from, to) = match (args.get(0), args.get(1)) {
(Some(f), Some(t)) => ( (Some(f), Some(t)) => (
f.coerce_to_f64(avm, context)? as usize, f.coerce_to_f64(activation, context)? as usize,
t.coerce_to_f64(avm, context)? as usize, t.coerce_to_f64(activation, context)? as usize,
), ),
(Some(f), None) => { (Some(f), None) => {
let v = f.coerce_to_f64(avm, context)? as usize; let v = f.coerce_to_f64(activation, context)? as usize;
(v, v.saturating_add(1)) (v, v.saturating_add(1))
} }
_ => (0, text_field.text_length()), _ => (0, text_field.text_length()),
@ -572,28 +573,28 @@ fn get_text_format<'gc>(
Ok(text_field Ok(text_field
.text_format(from, to) .text_format(from, to)
.as_avm1_object(avm, context)? .as_avm1_object(activation, context)?
.into()) .into())
} }
fn set_text_format<'gc>( fn set_text_format<'gc>(
text_field: EditText<'gc>, text_field: EditText<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let tf = args.last().cloned().unwrap_or(Value::Undefined); let tf = args.last().cloned().unwrap_or(Value::Undefined);
if let Value::Object(tf) = tf { if let Value::Object(tf) = tf {
let tf_parsed = TextFormat::from_avm1_object(tf, avm, context)?; let tf_parsed = TextFormat::from_avm1_object(tf, activation, context)?;
let (from, to) = match (args.get(0), args.get(1)) { let (from, to) = match (args.get(0), args.get(1)) {
(Some(f), Some(t)) if args.len() > 2 => ( (Some(f), Some(t)) if args.len() > 2 => (
f.coerce_to_f64(avm, context)? as usize, f.coerce_to_f64(activation, context)? as usize,
t.coerce_to_f64(avm, context)? as usize, t.coerce_to_f64(activation, context)? as usize,
), ),
(Some(f), _) if args.len() > 1 => { (Some(f), _) if args.len() > 1 => {
let v = f.coerce_to_f64(avm, context)? as usize; let v = f.coerce_to_f64(activation, context)? as usize;
(v, v.saturating_add(1)) (v, v.saturating_add(1))
} }
_ => (0, text_field.text_length()), _ => (0, text_field.text_length()),
@ -602,33 +603,33 @@ fn set_text_format<'gc>(
text_field.set_text_format(from, to, tf_parsed, context); text_field.set_text_format(from, to, tf_parsed, context);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
fn replace_text<'gc>( fn replace_text<'gc>(
text_field: EditText<'gc>, text_field: EditText<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let from = args let from = args
.get(0) .get(0)
.cloned() .cloned()
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let to = args let to = args
.get(1) .get(1)
.cloned() .cloned()
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)
.coerce_to_f64(avm, context)?; .coerce_to_f64(activation, context)?;
let text = args let text = args
.get(2) .get(2)
.cloned() .cloned()
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)
.coerce_to_string(avm, context)? .coerce_to_string(activation, context)?
.into_owned(); .into_owned();
text_field.replace_text(from as usize, to as usize, &text, context); text_field.replace_text(from as usize, to as usize, &text, context);
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }

View File

@ -1,14 +1,14 @@
//! `TextFormat` impl //! `TextFormat` impl
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::MutationContext; use gc_arena::MutationContext;
fn map_defined_to_string<'gc>( fn map_defined_to_string<'gc>(
name: &str, name: &str,
this: Object<'gc>, this: Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
val: Option<Value<'gc>>, val: Option<Value<'gc>>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
@ -16,10 +16,10 @@ fn map_defined_to_string<'gc>(
Some(Value::Undefined) => Value::Null, Some(Value::Undefined) => Value::Null,
Some(Value::Null) => Value::Null, Some(Value::Null) => Value::Null,
None => Value::Null, None => Value::Null,
Some(v) => v.coerce_to_string(avm, ac)?.into(), Some(v) => v.coerce_to_string(activation, ac)?.into(),
}; };
this.set(name, val, avm, ac)?; this.set(name, val, activation, ac)?;
Ok(()) Ok(())
} }
@ -27,7 +27,7 @@ fn map_defined_to_string<'gc>(
fn map_defined_to_number<'gc>( fn map_defined_to_number<'gc>(
name: &str, name: &str,
this: Object<'gc>, this: Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
val: Option<Value<'gc>>, val: Option<Value<'gc>>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
@ -35,10 +35,10 @@ fn map_defined_to_number<'gc>(
Some(Value::Undefined) => Value::Null, Some(Value::Undefined) => Value::Null,
Some(Value::Null) => Value::Null, Some(Value::Null) => Value::Null,
None => Value::Null, None => Value::Null,
Some(v) => v.coerce_to_f64(avm, ac)?.into(), Some(v) => v.coerce_to_f64(activation, ac)?.into(),
}; };
this.set(name, val, avm, ac)?; this.set(name, val, activation, ac)?;
Ok(()) Ok(())
} }
@ -46,7 +46,7 @@ fn map_defined_to_number<'gc>(
fn map_defined_to_bool<'gc>( fn map_defined_to_bool<'gc>(
name: &str, name: &str,
this: Object<'gc>, this: Object<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
val: Option<Value<'gc>>, val: Option<Value<'gc>>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
@ -54,36 +54,36 @@ fn map_defined_to_bool<'gc>(
Some(Value::Undefined) => Value::Null, Some(Value::Undefined) => Value::Null,
Some(Value::Null) => Value::Null, Some(Value::Null) => Value::Null,
None => Value::Null, None => Value::Null,
Some(v) => v.as_bool(avm.current_swf_version()).into(), Some(v) => v.as_bool(activation.current_swf_version()).into(),
}; };
this.set(name, val, avm, ac)?; this.set(name, val, activation, ac)?;
Ok(()) Ok(())
} }
/// `TextFormat` constructor /// `TextFormat` constructor
pub fn constructor<'gc>( pub fn constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
map_defined_to_string("font", this, avm, ac, args.get(0).cloned())?; map_defined_to_string("font", this, activation, ac, args.get(0).cloned())?;
map_defined_to_number("size", this, avm, ac, args.get(1).cloned())?; map_defined_to_number("size", this, activation, ac, args.get(1).cloned())?;
map_defined_to_number("color", this, avm, ac, args.get(2).cloned())?; map_defined_to_number("color", this, activation, ac, args.get(2).cloned())?;
map_defined_to_bool("bold", this, avm, ac, args.get(3).cloned())?; map_defined_to_bool("bold", this, activation, ac, args.get(3).cloned())?;
map_defined_to_bool("italic", this, avm, ac, args.get(4).cloned())?; map_defined_to_bool("italic", this, activation, ac, args.get(4).cloned())?;
map_defined_to_bool("underline", this, avm, ac, args.get(5).cloned())?; map_defined_to_bool("underline", this, activation, ac, args.get(5).cloned())?;
map_defined_to_string("url", this, avm, ac, args.get(6).cloned())?; map_defined_to_string("url", this, activation, ac, args.get(6).cloned())?;
map_defined_to_string("target", this, avm, ac, args.get(7).cloned())?; map_defined_to_string("target", this, activation, ac, args.get(7).cloned())?;
map_defined_to_string("align", this, avm, ac, args.get(8).cloned())?; map_defined_to_string("align", this, activation, ac, args.get(8).cloned())?;
map_defined_to_number("leftMargin", this, avm, ac, args.get(9).cloned())?; map_defined_to_number("leftMargin", this, activation, ac, args.get(9).cloned())?;
map_defined_to_number("rightMargin", this, avm, ac, args.get(10).cloned())?; map_defined_to_number("rightMargin", this, activation, ac, args.get(10).cloned())?;
map_defined_to_number("indent", this, avm, ac, args.get(11).cloned())?; map_defined_to_number("indent", this, activation, ac, args.get(11).cloned())?;
map_defined_to_number("leading", this, avm, ac, args.get(12).cloned())?; map_defined_to_number("leading", this, activation, ac, args.get(12).cloned())?;
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// `TextFormat.prototype` constructor /// `TextFormat.prototype` constructor

View File

@ -1,12 +1,12 @@
//! XML/XMLNode global classes //! XML/XMLNode global classes
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::script_object::ScriptObject; use crate::avm1::script_object::ScriptObject;
use crate::avm1::xml_object::XMLObject; use crate::avm1::xml_object::XMLObject;
use crate::avm1::{Avm1, Object, TObject, UpdateContext, Value}; use crate::avm1::{Object, TObject, UpdateContext, Value};
use crate::backend::navigator::RequestOptions; use crate::backend::navigator::RequestOptions;
use crate::xml; use crate::xml;
use crate::xml::{XMLDocument, XMLNode}; use crate::xml::{XMLDocument, XMLNode};
@ -42,17 +42,17 @@ fn is_as2_compatible(node: XMLNode<'_>) -> bool {
/// XMLNode constructor /// XMLNode constructor
pub fn xmlnode_constructor<'gc>( pub fn xmlnode_constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let blank_document = XMLDocument::new(ac.gc_context); let blank_document = XMLDocument::new(ac.gc_context);
match ( match (
args.get(0) args.get(0)
.map(|v| v.coerce_to_f64(avm, ac).map(|v| v as u32)), .map(|v| v.coerce_to_f64(activation, ac).map(|v| v as u32)),
args.get(1).map(|v| v.coerce_to_string(avm, ac)), args.get(1).map(|v| v.coerce_to_string(activation, ac)),
this.as_xml_node(), this.as_xml_node(),
) { ) {
(Some(Ok(1)), Some(Ok(ref strval)), Some(ref mut this_node)) => { (Some(Ok(1)), Some(Ok(ref strval)), Some(ref mut this_node)) => {
@ -69,19 +69,19 @@ pub fn xmlnode_constructor<'gc>(
_ => {} _ => {}
}; };
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_append_child<'gc>( pub fn xmlnode_append_child<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(mut xmlnode), Some(child_xmlnode)) = ( if let (Some(mut xmlnode), Some(child_xmlnode)) = (
this.as_xml_node(), this.as_xml_node(),
args.get(0) args.get(0)
.and_then(|n| n.coerce_to_object(avm, ac).as_xml_node()), .and_then(|n| n.coerce_to_object(activation, ac).as_xml_node()),
) { ) {
if let Ok(None) = child_xmlnode.parent() { if let Ok(None) = child_xmlnode.parent() {
let position = xmlnode.children_len(); let position = xmlnode.children_len();
@ -91,21 +91,21 @@ pub fn xmlnode_append_child<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_insert_before<'gc>( pub fn xmlnode_insert_before<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(mut xmlnode), Some(child_xmlnode), Some(insertpoint_xmlnode)) = ( if let (Some(mut xmlnode), Some(child_xmlnode), Some(insertpoint_xmlnode)) = (
this.as_xml_node(), this.as_xml_node(),
args.get(0) args.get(0)
.and_then(|n| n.coerce_to_object(avm, ac).as_xml_node()), .and_then(|n| n.coerce_to_object(activation, ac).as_xml_node()),
args.get(1) args.get(1)
.and_then(|n| n.coerce_to_object(avm, ac).as_xml_node()), .and_then(|n| n.coerce_to_object(activation, ac).as_xml_node()),
) { ) {
if let Ok(None) = child_xmlnode.parent() { if let Ok(None) = child_xmlnode.parent() {
if let Some(position) = xmlnode.child_position(insertpoint_xmlnode) { if let Some(position) = xmlnode.child_position(insertpoint_xmlnode) {
@ -119,91 +119,91 @@ pub fn xmlnode_insert_before<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_clone_node<'gc>( pub fn xmlnode_clone_node<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(xmlnode), deep) = ( if let (Some(xmlnode), deep) = (
this.as_xml_node(), this.as_xml_node(),
args.get(0) args.get(0)
.map(|v| v.as_bool(avm.current_swf_version())) .map(|v| v.as_bool(activation.current_swf_version()))
.unwrap_or(false), .unwrap_or(false),
) { ) {
let mut clone_node = xmlnode.duplicate(ac.gc_context, deep); let mut clone_node = xmlnode.duplicate(ac.gc_context, deep);
return Ok(Value::Object( return Ok(Value::Object(clone_node.script_object(
clone_node.script_object(ac.gc_context, Some(avm.prototypes.xml_node)), ac.gc_context,
) Some(activation.avm().prototypes.xml_node),
.into()); )));
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_get_namespace_for_prefix<'gc>( pub fn xmlnode_get_namespace_for_prefix<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(xmlnode), Some(prefix_string)) = ( if let (Some(xmlnode), Some(prefix_string)) = (
this.as_xml_node(), this.as_xml_node(),
args.get(0).map(|v| v.coerce_to_string(avm, ac)), args.get(0).map(|v| v.coerce_to_string(activation, ac)),
) { ) {
if let Some(uri) = xmlnode.lookup_uri_for_namespace(&prefix_string?) { if let Some(uri) = xmlnode.lookup_uri_for_namespace(&prefix_string?) {
Ok(uri.into()) Ok(uri.into())
} else { } else {
Ok(Value::Null.into()) Ok(Value::Null)
} }
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
pub fn xmlnode_get_prefix_for_namespace<'gc>( pub fn xmlnode_get_prefix_for_namespace<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(xmlnode), Some(uri_string)) = ( if let (Some(xmlnode), Some(uri_string)) = (
this.as_xml_node(), this.as_xml_node(),
args.get(0).map(|v| v.coerce_to_string(avm, ac)), args.get(0).map(|v| v.coerce_to_string(activation, ac)),
) { ) {
if let Some(prefix) = xmlnode.lookup_namespace_for_uri(&uri_string?) { if let Some(prefix) = xmlnode.lookup_namespace_for_uri(&uri_string?) {
Ok(prefix.into()) Ok(prefix.into())
} else { } else {
Ok(Value::Null.into()) Ok(Value::Null)
} }
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
pub fn xmlnode_has_child_nodes<'gc>( pub fn xmlnode_has_child_nodes<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(xmlnode) = this.as_xml_node() { if let Some(xmlnode) = this.as_xml_node() {
Ok((xmlnode.children_len() > 0).into()) Ok((xmlnode.children_len() > 0).into())
} else { } else {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
} }
pub fn xmlnode_remove_node<'gc>( pub fn xmlnode_remove_node<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
if let Ok(Some(mut parent)) = node.parent() { if let Ok(Some(mut parent)) = node.parent() {
if let Err(e) = parent.remove_child(ac.gc_context, node) { if let Err(e) = parent.remove_child(ac.gc_context, node) {
@ -212,15 +212,15 @@ pub fn xmlnode_remove_node<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_to_string<'gc>( pub fn xmlnode_to_string<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
let result = node.into_string(&mut is_as2_compatible); let result = node.into_string(&mut is_as2_compatible);
@ -236,37 +236,37 @@ pub fn xmlnode_to_string<'gc>(
} }
pub fn xmlnode_local_name<'gc>( pub fn xmlnode_local_name<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(this Ok(this
.as_xml_node() .as_xml_node()
.and_then(|n| n.tag_name()) .and_then(|n| n.tag_name())
.map(|n| n.local_name().to_string().into()) .map(|n| n.local_name().to_string().into())
.unwrap_or_else(|| Value::Null.into())) .unwrap_or_else(|| Value::Null))
} }
pub fn xmlnode_node_name<'gc>( pub fn xmlnode_node_name<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(this Ok(this
.as_xml_node() .as_xml_node()
.and_then(|n| n.tag_name()) .and_then(|n| n.tag_name())
.map(|n| n.node_name().into()) .map(|n| n.node_name().into())
.unwrap_or_else(|| Value::Null.into())) .unwrap_or_else(|| Value::Null))
} }
pub fn xmlnode_node_type<'gc>( pub fn xmlnode_node_type<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(this Ok(this
.as_xml_node() .as_xml_node()
.map(|n| { .map(|n| {
@ -278,28 +278,28 @@ pub fn xmlnode_node_type<'gc>(
} }
.into() .into()
}) })
.unwrap_or_else(|| Value::Undefined.into())) .unwrap_or_else(|| Value::Undefined))
} }
pub fn xmlnode_node_value<'gc>( pub fn xmlnode_node_value<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(this Ok(this
.as_xml_node() .as_xml_node()
.and_then(|n| n.node_value()) .and_then(|n| n.node_value())
.map(|n| n.into()) .map(|n| n.into())
.unwrap_or_else(|| Value::Null.into())) .unwrap_or_else(|| Value::Null))
} }
pub fn xmlnode_prefix<'gc>( pub fn xmlnode_prefix<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(this Ok(this
.as_xml_node() .as_xml_node()
.and_then(|n| n.tag_name()) .and_then(|n| n.tag_name())
@ -308,17 +308,17 @@ pub fn xmlnode_prefix<'gc>(
.map(|n| n.to_string().into()) .map(|n| n.to_string().into())
.unwrap_or_else(|| "".to_string().into()) .unwrap_or_else(|| "".to_string().into())
}) })
.unwrap_or_else(|| Value::Null.into())) .unwrap_or_else(|| Value::Null))
} }
pub fn xmlnode_child_nodes<'gc>( pub fn xmlnode_child_nodes<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
let array = ScriptObject::array(ac.gc_context, Some(avm.prototypes.array)); let array = ScriptObject::array(ac.gc_context, Some(activation.avm().prototypes.array));
if let Some(children) = node.children() { if let Some(children) = node.children() {
let mut compatible_nodes = 0; let mut compatible_nodes = 0;
for mut child in children { for mut child in children {
@ -329,7 +329,7 @@ pub fn xmlnode_child_nodes<'gc>(
array.set_array_element( array.set_array_element(
compatible_nodes as usize, compatible_nodes as usize,
child child
.script_object(ac.gc_context, Some(avm.prototypes.xml_node)) .script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.into(), .into(),
ac.gc_context, ac.gc_context,
); );
@ -341,80 +341,80 @@ pub fn xmlnode_child_nodes<'gc>(
return Ok(array.into()); return Ok(array.into());
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_first_child<'gc>( pub fn xmlnode_first_child<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
if let Some(mut children) = node.children() { if let Some(mut children) = node.children() {
return Ok(children return Ok(children
.next() .next()
.map(|mut child| { .map(|mut child| {
child child
.script_object(ac.gc_context, Some(avm.prototypes.xml_node)) .script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.into() .into()
}) })
.unwrap_or_else(|| Value::Null.into())); .unwrap_or_else(|| Value::Null));
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_last_child<'gc>( pub fn xmlnode_last_child<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
if let Some(mut children) = node.children() { if let Some(mut children) = node.children() {
return Ok(children return Ok(children
.next_back() .next_back()
.map(|mut child| { .map(|mut child| {
child child
.script_object(ac.gc_context, Some(avm.prototypes.xml_node)) .script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.into() .into()
}) })
.unwrap_or_else(|| Value::Null.into())); .unwrap_or_else(|| Value::Null));
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_parent_node<'gc>( pub fn xmlnode_parent_node<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
return Ok(node return Ok(node
.parent() .parent()
.unwrap_or(None) .unwrap_or(None)
.map(|mut parent| { .map(|mut parent| {
parent parent
.script_object(ac.gc_context, Some(avm.prototypes.xml_node)) .script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.into() .into()
}) })
.unwrap_or_else(|| Value::Null.into())); .unwrap_or_else(|| Value::Null));
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_previous_sibling<'gc>( pub fn xmlnode_previous_sibling<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
let mut prev = node.prev_sibling().unwrap_or(None); let mut prev = node.prev_sibling().unwrap_or(None);
while let Some(my_prev) = prev { while let Some(my_prev) = prev {
@ -427,21 +427,21 @@ pub fn xmlnode_previous_sibling<'gc>(
return Ok(prev return Ok(prev
.map(|mut prev| { .map(|mut prev| {
prev.script_object(ac.gc_context, Some(avm.prototypes.xml_node)) prev.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.into() .into()
}) })
.unwrap_or_else(|| Value::Null.into())); .unwrap_or_else(|| Value::Null));
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_next_sibling<'gc>( pub fn xmlnode_next_sibling<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
let mut next = node.next_sibling().unwrap_or(None); let mut next = node.next_sibling().unwrap_or(None);
while let Some(my_next) = next { while let Some(my_next) = next {
@ -454,37 +454,37 @@ pub fn xmlnode_next_sibling<'gc>(
return Ok(next return Ok(next
.map(|mut next| { .map(|mut next| {
next.script_object(ac.gc_context, Some(avm.prototypes.xml_node)) next.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.into() .into()
}) })
.unwrap_or_else(|| Value::Null.into())); .unwrap_or_else(|| Value::Null));
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_attributes<'gc>( pub fn xmlnode_attributes<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(mut node) = this.as_xml_node() { if let Some(mut node) = this.as_xml_node() {
return Ok(node return Ok(node
.attribute_script_object(ac.gc_context) .attribute_script_object(ac.gc_context)
.map(|o| o.into()) .map(|o| o.into())
.unwrap_or_else(|| Value::Undefined.into())); .unwrap_or_else(|| Value::Undefined));
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xmlnode_namespace_uri<'gc>( pub fn xmlnode_namespace_uri<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
if let Some(name) = node.tag_name() { if let Some(name) = node.tag_name() {
return Ok(node return Ok(node
@ -493,10 +493,10 @@ pub fn xmlnode_namespace_uri<'gc>(
.unwrap_or_else(|| "".into())); .unwrap_or_else(|| "".into()));
} }
return Ok(Value::Null.into()); return Ok(Value::Null);
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Construct the prototype for `XMLNode`. /// Construct the prototype for `XMLNode`.
@ -684,13 +684,13 @@ pub fn create_xmlnode_proto<'gc>(
/// XML (document) constructor /// XML (document) constructor
pub fn xml_constructor<'gc>( pub fn xml_constructor<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
match ( match (
args.get(0).map(|v| v.coerce_to_string(avm, ac)), args.get(0).map(|v| v.coerce_to_string(activation, ac)),
this.as_xml_node(), this.as_xml_node(),
) { ) {
(Some(Ok(ref string)), Some(ref mut this_node)) => { (Some(Ok(ref string)), Some(ref mut this_node)) => {
@ -713,15 +713,15 @@ pub fn xml_constructor<'gc>(
_ => {} _ => {}
}; };
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xml_create_element<'gc>( pub fn xml_create_element<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let document = if let Some(node) = this.as_xml_node() { let document = if let Some(node) = this.as_xml_node() {
node.document() node.document()
} else { } else {
@ -730,10 +730,14 @@ pub fn xml_create_element<'gc>(
let nodename = args let nodename = args
.get(0) .get(0)
.map(|v| v.coerce_to_string(avm, ac).unwrap_or_default()) .map(|v| v.coerce_to_string(activation, ac).unwrap_or_default())
.unwrap_or_default(); .unwrap_or_default();
let mut xml_node = XMLNode::new_element(ac.gc_context, &nodename, document); let mut xml_node = XMLNode::new_element(ac.gc_context, &nodename, document);
let object = XMLObject::from_xml_node(ac.gc_context, xml_node, Some(avm.prototypes().xml_node)); let object = XMLObject::from_xml_node(
ac.gc_context,
xml_node,
Some(activation.avm().prototypes().xml_node),
);
xml_node.introduce_script_object(ac.gc_context, object); xml_node.introduce_script_object(ac.gc_context, object);
@ -741,11 +745,11 @@ pub fn xml_create_element<'gc>(
} }
pub fn xml_create_text_node<'gc>( pub fn xml_create_text_node<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let document = if let Some(node) = this.as_xml_node() { let document = if let Some(node) = this.as_xml_node() {
node.document() node.document()
} else { } else {
@ -754,10 +758,14 @@ pub fn xml_create_text_node<'gc>(
let text_node = args let text_node = args
.get(0) .get(0)
.map(|v| v.coerce_to_string(avm, ac).unwrap_or_default()) .map(|v| v.coerce_to_string(activation, ac).unwrap_or_default())
.unwrap_or_default(); .unwrap_or_default();
let mut xml_node = XMLNode::new_text(ac.gc_context, &text_node, document); let mut xml_node = XMLNode::new_text(ac.gc_context, &text_node, document);
let object = XMLObject::from_xml_node(ac.gc_context, xml_node, Some(avm.prototypes().xml_node)); let object = XMLObject::from_xml_node(
ac.gc_context,
xml_node,
Some(activation.avm().prototypes().xml_node),
);
xml_node.introduce_script_object(ac.gc_context, object); xml_node.introduce_script_object(ac.gc_context, object);
@ -765,14 +773,14 @@ pub fn xml_create_text_node<'gc>(
} }
pub fn xml_parse_xml<'gc>( pub fn xml_parse_xml<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(mut node) = this.as_xml_node() { if let Some(mut node) = this.as_xml_node() {
let xmlstring = let xmlstring =
if let Some(Ok(xmlstring)) = args.get(0).map(|s| s.coerce_to_string(avm, ac)) { if let Some(Ok(xmlstring)) = args.get(0).map(|s| s.coerce_to_string(activation, ac)) {
xmlstring xmlstring
} else { } else {
Cow::Borrowed("") Cow::Borrowed("")
@ -783,7 +791,7 @@ pub fn xml_parse_xml<'gc>(
let result = node.remove_child(ac.gc_context, child); let result = node.remove_child(ac.gc_context, child);
if let Err(e) = result { if let Err(e) = result {
log::warn!("XML.parseXML: Error removing node contents: {}", e); log::warn!("XML.parseXML: Error removing node contents: {}", e);
return Ok(Value::Undefined.into()); return Ok(Value::Undefined);
} }
} }
} }
@ -794,15 +802,15 @@ pub fn xml_parse_xml<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xml_load<'gc>( pub fn xml_load<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let url = args.get(0).cloned().unwrap_or(Value::Undefined); let url = args.get(0).cloned().unwrap_or(Value::Undefined);
if let Value::Null = url { if let Value::Null = url {
@ -810,12 +818,12 @@ pub fn xml_load<'gc>(
} }
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
let url = url.coerce_to_string(avm, ac)?; let url = url.coerce_to_string(activation, ac)?;
this.set("loaded", false.into(), avm, ac)?; this.set("loaded", false.into(), activation, ac)?;
let fetch = ac.navigator.fetch(&url, RequestOptions::get()); let fetch = ac.navigator.fetch(&url, RequestOptions::get());
let target_clip = avm.target_clip_or_root(); let target_clip = activation.target_clip_or_root();
let process = ac.load_manager.load_xml_into_node( let process = ac.load_manager.load_xml_into_node(
ac.player.clone().unwrap(), ac.player.clone().unwrap(),
node, node,
@ -832,33 +840,33 @@ pub fn xml_load<'gc>(
} }
pub fn xml_on_data<'gc>( pub fn xml_on_data<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let src = args.get(0).cloned().unwrap_or(Value::Undefined); let src = args.get(0).cloned().unwrap_or(Value::Undefined);
if let Value::Undefined = src { if let Value::Undefined = src {
this.call_method("onLoad", &[false.into()], avm, ac)?; this.call_method("onLoad", &[false.into()], activation, ac)?;
} else { } else {
let src = src.coerce_to_string(avm, ac)?; let src = src.coerce_to_string(activation, ac)?;
this.call_method("parseXML", &[src.into()], avm, ac)?; this.call_method("parseXML", &[src.into()], activation, ac)?;
this.set("loaded", true.into(), avm, ac)?; this.set("loaded", true.into(), activation, ac)?;
this.call_method("onLoad", &[true.into()], avm, ac)?; this.call_method("onLoad", &[true.into()], activation, ac)?;
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xml_doc_type_decl<'gc>( pub fn xml_doc_type_decl<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
if let Some(doctype) = node.document().doctype() { if let Some(doctype) = node.document().doctype() {
let result = doctype.into_string(&mut |_| true); let result = doctype.into_string(&mut |_| true);
@ -872,15 +880,15 @@ pub fn xml_doc_type_decl<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xml_xml_decl<'gc>( pub fn xml_xml_decl<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
let result = node.document().xmldecl_string(); let result = node.document().xmldecl_string();
@ -891,55 +899,49 @@ pub fn xml_xml_decl<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xml_id_map<'gc>( pub fn xml_id_map<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>, ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
return Ok(node.document().idmap_script_object(ac.gc_context).into()); return Ok(node.document().idmap_script_object(ac.gc_context).into());
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
pub fn xml_status<'gc>( pub fn xml_status<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>, _ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() { if let Some(node) = this.as_xml_node() {
return match node.document().last_parse_error() { return match node.document().last_parse_error() {
None => Ok(XML_NO_ERROR.into()), None => Ok(XML_NO_ERROR.into()),
Some(err) => match err.ref_error() { Some(err) => match err.ref_error() {
ParseError::UnexpectedEof(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()), ParseError::UnexpectedEof(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
ParseError::EndEventMismatch { .. } => Ok(Value::Number(XML_MISMATCHED_END).into()), ParseError::EndEventMismatch { .. } => Ok(Value::Number(XML_MISMATCHED_END)),
ParseError::XmlDeclWithoutVersion(_) => { ParseError::XmlDeclWithoutVersion(_) => Ok(Value::Number(XML_DECL_NOT_TERMINATED)),
Ok(Value::Number(XML_DECL_NOT_TERMINATED).into()) ParseError::NameWithQuote(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
} ParseError::NoEqAfterName(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
ParseError::NameWithQuote(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()), ParseError::UnquotedValue(_) => Ok(Value::Number(XML_ATTRIBUTE_NOT_TERMINATED)),
ParseError::NoEqAfterName(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()), ParseError::DuplicatedAttribute(_, _) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
ParseError::UnquotedValue(_) => { _ => Ok(Value::Number(XML_OUT_OF_MEMORY)), //Not accounted for:
Ok(Value::Number(XML_ATTRIBUTE_NOT_TERMINATED).into()) //ParseError::UnexpectedToken(_)
} //ParseError::UnexpectedBang
ParseError::DuplicatedAttribute(_, _) => { //ParseError::TextNotFound
Ok(Value::Number(XML_ELEMENT_MALFORMED).into()) //ParseError::EscapeError(_)
}
_ => Ok(Value::Number(XML_OUT_OF_MEMORY).into()), //Not accounted for:
//ParseError::UnexpectedToken(_)
//ParseError::UnexpectedBang
//ParseError::TextNotFound
//ParseError::EscapeError(_)
}, },
}; };
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Construct the prototype for `XML`. /// Construct the prototype for `XML`.

View File

@ -1,7 +1,6 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::{Collect, MutationContext}; use gc_arena::{Collect, MutationContext};
#[derive(Clone, Collect, Debug, Copy)] #[derive(Clone, Collect, Debug, Copy)]
@ -11,24 +10,26 @@ pub struct Listeners<'gc>(Object<'gc>);
macro_rules! register_listener { macro_rules! register_listener {
( $gc_context: ident, $object:ident, $listener: ident, $fn_proto: ident, $system_listeners_key: ident ) => {{ ( $gc_context: ident, $object:ident, $listener: ident, $fn_proto: ident, $system_listeners_key: ident ) => {{
pub fn add_listener<'gc>( pub fn add_listener<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
avm.system_listeners activation
.avm()
.system_listeners
.$system_listeners_key .$system_listeners_key
.add_listener(context, args) .add_listener(context, args)
} }
pub fn remove_listener<'gc>( pub fn remove_listener<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let listener = avm.system_listeners.$system_listeners_key; let listener = activation.avm().system_listeners.$system_listeners_key;
listener.remove_listener(avm, context, args) listener.remove_listener(activation, context, args)
} }
$object.define_value( $object.define_value(
@ -65,7 +66,7 @@ impl<'gc> Listeners<'gc> {
&self, &self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let listeners = self.0; let listeners = self.0;
let listener = args.get(0).unwrap_or(&Value::Undefined).to_owned(); let listener = args.get(0).unwrap_or(&Value::Undefined).to_owned();
for i in 0..listeners.length() { for i in 0..listeners.length() {
@ -80,10 +81,10 @@ impl<'gc> Listeners<'gc> {
pub fn remove_listener( pub fn remove_listener(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let listeners = self.0; let listeners = self.0;
let listener = args.get(0).unwrap_or(&Value::Undefined).to_owned(); let listener = args.get(0).unwrap_or(&Value::Undefined).to_owned();
for index in 0..listeners.length() { for index in 0..listeners.length() {
@ -99,7 +100,7 @@ impl<'gc> Listeners<'gc> {
} }
listeners.delete_array_element(new_length, context.gc_context); listeners.delete_array_element(new_length, context.gc_context);
listeners.delete(avm, context.gc_context, &new_length.to_string()); listeners.delete(activation, context.gc_context, &new_length.to_string());
listeners.set_length(context.gc_context, new_length); listeners.set_length(context.gc_context, new_length);
return Ok(true.into()); return Ok(true.into());
@ -111,7 +112,7 @@ impl<'gc> Listeners<'gc> {
pub fn prepare_handlers( pub fn prepare_handlers(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
method: &str, method: &str,
) -> Vec<(Object<'gc>, Value<'gc>)> { ) -> Vec<(Object<'gc>, Value<'gc>)> {
@ -119,8 +120,10 @@ impl<'gc> Listeners<'gc> {
let mut handlers = Vec::with_capacity(listeners.length()); let mut handlers = Vec::with_capacity(listeners.length());
for i in 0..listeners.length() { for i in 0..listeners.length() {
let listener = listeners.array_element(i).coerce_to_object(avm, context); let listener = listeners
if let Ok(handler) = listener.get(method, avm, context) { .array_element(i)
.coerce_to_object(activation, context);
if let Ok(handler) = listener.get(method, activation, context) {
handlers.push((listener, handler)); handlers.push((listener, handler));
} }
} }

View File

@ -3,15 +3,15 @@
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::shared_object::SharedObject; use crate::avm1::shared_object::SharedObject;
use crate::avm1::super_object::SuperObject; use crate::avm1::super_object::SuperObject;
use crate::avm1::value_object::ValueObject; use crate::avm1::value_object::ValueObject;
use crate::avm1::activation::Activation;
use crate::avm1::xml_attributes_object::XMLAttributesObject; use crate::avm1::xml_attributes_object::XMLAttributesObject;
use crate::avm1::xml_idmap_object::XMLIDMapObject; use crate::avm1::xml_idmap_object::XMLIDMapObject;
use crate::avm1::xml_object::XMLObject; use crate::avm1::xml_object::XMLObject;
use crate::avm1::{Avm1, ScriptObject, SoundObject, StageObject, UpdateContext, Value}; use crate::avm1::{ScriptObject, SoundObject, StageObject, UpdateContext, Value};
use crate::display_object::DisplayObject; use crate::display_object::DisplayObject;
use crate::xml::XMLNode; use crate::xml::XMLNode;
use enumset::EnumSet; use enumset::EnumSet;
@ -50,7 +50,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>>; ) -> Result<Value<'gc>, Error<'gc>>;
@ -59,15 +59,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
fn get( fn get(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if self.has_own_property(avm, context, name) { if self.has_own_property(activation, context, name) {
self.get_local(name, avm, context, (*self).into()) self.get_local(name, activation, context, (*self).into())
} else { } else {
search_prototype(self.proto(), name, avm, context, (*self).into())? Ok(search_prototype(self.proto(), name, activation, context, (*self).into())?.0)
.0
.resolve(avm, context)
} }
} }
@ -76,7 +74,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>>; ) -> Result<(), Error<'gc>>;
@ -87,7 +85,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// it can be changed by `Function.apply`/`Function.call`. /// it can be changed by `Function.apply`/`Function.call`.
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
@ -105,24 +103,29 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
&self, &self,
name: &str, name: &str,
args: &[Value<'gc>], args: &[Value<'gc>],
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let (method, base_proto) = let (method, base_proto) = search_prototype(
search_prototype(Some((*self).into()), name, avm, context, (*self).into())?; Some((*self).into()),
let method = method.resolve(avm, context)?; name,
activation,
context,
(*self).into(),
)?;
let method = method;
if let Value::Object(_) = method { if let Value::Object(_) = method {
} else { } else {
log::warn!("Object method {} is not callable", name); log::warn!("Object method {} is not callable", name);
} }
method.call(avm, context, (*self).into(), base_proto, args) method.call(activation, context, (*self).into(), base_proto, args)
} }
/// Call a setter defined in this object. /// Call a setter defined in this object.
/// ///
/// This function returns the `ReturnValue` of the called function; it /// This function may return a `Executable` of the function to call; it
/// should be resolved and discarded. Attempts to call a non-virtual setter /// should be resolved and discarded. Attempts to call a non-virtual setter
/// or non-existent setter fail silently. /// or non-existent setter fail silently.
/// ///
@ -133,10 +136,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>>;
) -> Result<ReturnValue<'gc>, Error<'gc>>;
/// Construct a host object of some kind and return it's cell. /// Construct a host object of some kind and return it's cell.
/// ///
@ -151,7 +153,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// purely so that host objects can be constructed by the VM. /// purely so that host objects can be constructed by the VM.
fn new( fn new(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
@ -160,8 +162,12 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// Delete a named property from the object. /// Delete a named property from the object.
/// ///
/// Returns false if the property cannot be deleted. /// Returns false if the property cannot be deleted.
fn delete(&self, avm: &mut Avm1<'gc>, gc_context: MutationContext<'gc, '_>, name: &str) fn delete(
-> bool; &self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool;
/// Retrieve the `__proto__` of a given object. /// Retrieve the `__proto__` of a given object.
/// ///
@ -242,7 +248,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// as `__proto__`. /// as `__proto__`.
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -253,7 +259,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// Checks if the object has a given named property. /// Checks if the object has a given named property.
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool; ) -> bool;
@ -262,7 +268,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// say, the object's prototype or superclass) /// say, the object's prototype or superclass)
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool; ) -> bool;
@ -271,19 +277,19 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// virtual. /// virtual.
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool; ) -> bool;
/// Checks if a named property can be overwritten. /// Checks if a named property can be overwritten.
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool; fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool;
/// Checks if a named property appears when enumerating the object. /// Checks if a named property appears when enumerating the object.
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool; fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool;
/// Enumerate the object. /// Enumerate the object.
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String>; fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String>;
/// Coerce the object into a string. /// Coerce the object into a string.
fn as_string(&self) -> Cow<str>; fn as_string(&self) -> Cow<str>;
@ -314,7 +320,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// somehow could this would support that, too. /// somehow could this would support that, too.
fn is_instance_of( fn is_instance_of(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
constructor: Object<'gc>, constructor: Object<'gc>,
prototype: Object<'gc>, prototype: Object<'gc>,
@ -333,13 +339,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
proto_stack.push(p); proto_stack.push(p);
} }
if avm.current_swf_version() >= 7 { if activation.current_swf_version() >= 7 {
for interface in this_proto.interfaces() { for interface in this_proto.interfaces() {
if Object::ptr_eq(interface, constructor) { if Object::ptr_eq(interface, constructor) {
return Ok(true); return Ok(true);
} }
if let Value::Object(o) = interface.get("prototype", avm, context)? { if let Value::Object(o) = interface.get("prototype", activation, context)? {
proto_stack.push(o); proto_stack.push(o);
} }
} }
@ -464,10 +470,10 @@ impl<'gc> Object<'gc> {
pub fn search_prototype<'gc>( pub fn search_prototype<'gc>(
mut proto: Option<Object<'gc>>, mut proto: Option<Object<'gc>>,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<(ReturnValue<'gc>, Option<Object<'gc>>), Error<'gc>> { ) -> Result<(Value<'gc>, Option<Object<'gc>>), Error<'gc>> {
let mut depth = 0; let mut depth = 0;
while proto.is_some() { while proto.is_some() {
@ -475,9 +481,9 @@ pub fn search_prototype<'gc>(
return Err(Error::PrototypeRecursionLimit); return Err(Error::PrototypeRecursionLimit);
} }
if proto.unwrap().has_own_property(avm, context, name) { if proto.unwrap().has_own_property(activation, context, name) {
return Ok(( return Ok((
proto.unwrap().get_local(name, avm, context, this)?.into(), proto.unwrap().get_local(name, activation, context, this)?,
proto, proto,
)); ));
} }
@ -486,5 +492,5 @@ pub fn search_prototype<'gc>(
depth += 1; depth += 1;
} }
Ok((Value::Undefined.into(), None)) Ok((Value::Undefined, None))
} }

View File

@ -1,10 +1,8 @@
//! User-defined properties //! User-defined properties
use self::Attribute::*; use self::Attribute::*;
use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::return_value::ReturnValue; use crate::avm1::Value;
use crate::avm1::{Avm1, Object, UpdateContext, Value};
use core::fmt; use core::fmt;
use enumset::{EnumSet, EnumSetType}; use enumset::{EnumSet, EnumSetType};
@ -32,42 +30,18 @@ pub enum Property<'gc> {
} }
impl<'gc> Property<'gc> { impl<'gc> Property<'gc> {
/// Get the value of a property slot.
///
/// This function yields `ReturnValue` because some properties may be
/// user-defined.
pub fn get(
&self,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
) -> Result<ReturnValue<'gc>, Error<'gc>> {
match self {
Property::Virtual { get, .. } => get.exec(avm, context, this, base_proto, &[]),
Property::Stored { value, .. } => Ok(value.to_owned().into()),
}
}
/// Set a property slot. /// Set a property slot.
/// ///
/// This function returns the `ReturnValue` of the property's virtual /// This function may return an `Executable` of the property's virtual
/// function, if any happen to exist. It should be resolved, and it's value /// function, if any happen to exist. It should be resolved, and it's value
/// discarded. /// discarded.
pub fn set( pub fn set(&mut self, new_value: impl Into<Value<'gc>>) -> Option<Executable<'gc>> {
&mut self,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
new_value: impl Into<Value<'gc>>,
) -> Result<ReturnValue<'gc>, Error<'gc>> {
match self { match self {
Property::Virtual { set, .. } => { Property::Virtual { set, .. } => {
if let Some(function) = set { if let Some(function) = set {
function.exec(avm, context, this, base_proto, &[new_value.into()]) Some(function.to_owned())
} else { } else {
Ok(Value::Undefined.into()) None
} }
} }
Property::Stored { Property::Stored {
@ -77,7 +51,7 @@ impl<'gc> Property<'gc> {
*value = new_value.into(); *value = new_value.into();
} }
Ok(Value::Undefined.into()) None
} }
} }
} }

View File

@ -1,203 +0,0 @@
//! Return value enum
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::{Avm1, Object, Value};
use crate::context::UpdateContext;
use gc_arena::{Collect, GcCell};
use std::borrow::Cow;
use std::fmt;
/// Represents the return value of a function call.
///
/// Since function calls can result in invoking native code or adding a new
/// activation onto the AVM stack, you need another type to represent how the
/// return value will be delivered to you.
///
/// This function contains a handful of utility methods for deciding what to do
/// with a given value regardless of how it is delivered to the calling
/// function.
///
/// It is `must_use` - failing to use a return value is a compiler warning. We
/// provide a helper function specifically to indicate that you aren't
/// interested in the result of a call.
#[must_use = "Return values must be used"]
#[derive(Clone)]
pub enum ReturnValue<'gc> {
/// Indicates that the return value is available immediately.
Immediate(Value<'gc>),
/// Indicates that the return value is the result of a given user-defined
/// function call. The activation record returned is the frame that needs
/// to return to get your value.
ResultOf(GcCell<'gc, Activation<'gc>>),
}
unsafe impl<'gc> Collect for ReturnValue<'gc> {
#[inline]
fn trace(&self, cc: gc_arena::CollectionContext) {
use ReturnValue::*;
match self {
Immediate(value) => value.trace(cc),
ResultOf(frame) => frame.trace(cc),
}
}
}
impl PartialEq for ReturnValue<'_> {
fn eq(&self, other: &Self) -> bool {
use ReturnValue::*;
match (self, other) {
(Immediate(val1), Immediate(val2)) => val1 == val2,
(ResultOf(frame1), ResultOf(frame2)) => GcCell::ptr_eq(*frame1, *frame2),
_ => false,
}
}
}
impl fmt::Debug for ReturnValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ReturnValue::*;
match self {
Immediate(val) => write!(f, "Immediate({:?})", val),
ResultOf(_frame) => write!(f, "ResultOf(<activation frame>)"),
}
}
}
impl<'gc> ReturnValue<'gc> {
/// Mark a given return value as intended to be pushed onto the stack.
///
/// The natural result of a stack frame retiring is to be pushed, so this
/// only ensures that Immediate values are pushed.
pub fn push(self, avm: &mut Avm1<'gc>) {
use ReturnValue::*;
match self {
Immediate(val) => avm.push(val),
ResultOf(_frame) => {}
};
}
/// Force a return value to resolve on the Rust stack by recursing back
/// into the AVM.
pub fn resolve(
self,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> {
use ReturnValue::*;
match self {
Immediate(val) => Ok(val),
ResultOf(frame) => match avm.run_current_frame(context, frame) {
Ok(_) => Ok(avm.pop()),
Err(e) => {
avm.retire_stack_frame(context, Value::Undefined);
Err(e)
}
},
}
}
pub fn is_immediate(&self) -> bool {
use ReturnValue::*;
if let Immediate(_v) = self {
true
} else {
false
}
}
/// Panic if a value is not immediate.
///
/// This should only be used in test assertions.
#[cfg(test)]
pub fn unwrap_immediate(self) -> Value<'gc> {
use ReturnValue::*;
match self {
Immediate(val) => val,
_ => panic!("Unwrapped a non-immediate return value"),
}
}
}
impl<'gc> From<Value<'gc>> for ReturnValue<'gc> {
fn from(val: Value<'gc>) -> Self {
ReturnValue::Immediate(val)
}
}
impl<'gc> From<String> for ReturnValue<'gc> {
fn from(string: String) -> Self {
ReturnValue::Immediate(Value::String(string))
}
}
impl<'gc> From<&str> for ReturnValue<'gc> {
fn from(string: &str) -> Self {
ReturnValue::Immediate(Value::String(string.to_owned()))
}
}
impl<'gc> From<Cow<'_, str>> for ReturnValue<'gc> {
fn from(string: Cow<str>) -> Self {
ReturnValue::Immediate(Value::String(string.to_string()))
}
}
impl<'gc> From<bool> for ReturnValue<'gc> {
fn from(value: bool) -> Self {
ReturnValue::Immediate(Value::Bool(value))
}
}
impl<'gc, T> From<T> for ReturnValue<'gc>
where
Object<'gc>: From<T>,
{
fn from(value: T) -> Self {
ReturnValue::Immediate(Value::Object(Object::from(value)))
}
}
impl<'gc> From<f64> for ReturnValue<'gc> {
fn from(value: f64) -> Self {
ReturnValue::Immediate(Value::Number(value))
}
}
impl<'gc> From<f32> for ReturnValue<'gc> {
fn from(value: f32) -> Self {
ReturnValue::Immediate(Value::Number(f64::from(value)))
}
}
impl<'gc> From<u8> for ReturnValue<'gc> {
fn from(value: u8) -> Self {
ReturnValue::Immediate(Value::Number(f64::from(value)))
}
}
impl<'gc> From<i32> for ReturnValue<'gc> {
fn from(value: i32) -> Self {
ReturnValue::Immediate(Value::Number(f64::from(value)))
}
}
impl<'gc> From<u32> for ReturnValue<'gc> {
fn from(value: u32) -> Self {
ReturnValue::Immediate(Value::Number(f64::from(value)))
}
}
impl<'gc> From<GcCell<'gc, Activation<'gc>>> for ReturnValue<'gc> {
fn from(frame: GcCell<'gc, Activation<'gc>>) -> Self {
ReturnValue::ResultOf(frame)
}
}

View File

@ -1,8 +1,8 @@
//! Represents AVM1 scope chain resolution. //! Represents AVM1 scope chain resolution.
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{GcCell, MutationContext}; use gc_arena::{GcCell, MutationContext};
use std::cell::Ref; use std::cell::Ref;
@ -207,6 +207,11 @@ impl<'gc> Scope<'gc> {
&self.values &self.values
} }
/// Returns a reference to the current local scope object.
pub fn locals_cell(&self) -> Object<'gc> {
self.values
}
/// Returns a reference to the current local scope object for mutation. /// Returns a reference to the current local scope object for mutation.
#[allow(dead_code)] #[allow(dead_code)]
pub fn locals_mut(&mut self) -> &mut Object<'gc> { pub fn locals_mut(&mut self) -> &mut Object<'gc> {
@ -234,34 +239,34 @@ impl<'gc> Scope<'gc> {
pub fn resolve( pub fn resolve(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if self.locals().has_property(avm, context, name) { if self.locals().has_property(activation, context, name) {
return Ok(self.locals().get(name, avm, context)?.into()); return Ok(self.locals().get(name, activation, context)?);
} }
if let Some(scope) = self.parent() { if let Some(scope) = self.parent() {
return scope.resolve(name, avm, context, this); return scope.resolve(name, activation, context, this);
} }
//TODO: Should undefined variables halt execution? //TODO: Should undefined variables halt execution?
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Check if a particular property in the scope chain is defined. /// Check if a particular property in the scope chain is defined.
pub fn is_defined( pub fn is_defined(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
if self.locals().has_property(avm, context, name) { if self.locals().has_property(activation, context, name) {
return true; return true;
} }
if let Some(scope) = self.parent() { if let Some(scope) = self.parent() {
return scope.is_defined(avm, context, name); return scope.is_defined(activation, context, name);
} }
false false
@ -277,26 +282,26 @@ impl<'gc> Scope<'gc> {
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if self.class == ScopeClass::Target if self.class == ScopeClass::Target
|| (self.locals().has_property(avm, context, name) || (self.locals().has_property(activation, context, name)
&& self.locals().is_property_overwritable(avm, name)) && self.locals().is_property_overwritable(activation, name))
{ {
// Value found on this object, so overwrite it. // Value found on this object, so overwrite it.
// Or we've hit the executing movie clip, so create it here. // Or we've hit the executing movie clip, so create it here.
self.locals().set(name, value, avm, context) self.locals().set(name, value, activation, context)
} else if let Some(scope) = self.parent() { } else if let Some(scope) = self.parent() {
// Traverse the scope chain in search of the value. // Traverse the scope chain in search of the value.
scope.set(name, value, avm, context, this) scope.set(name, value, activation, context, this)
} else { } else {
// This probably shouldn't happen -- all AVM1 code runs in reference to some movieclip, // This probably shouldn't happen -- all AVM1 code runs in reference to some movieclip,
// so we should always have a movieclip scope. // so we should always have a movieclip scope.
// Define on the top-level scope. // Define on the top-level scope.
debug_assert!(false, "Scope::set: No top-level movie clip scope"); debug_assert!(false, "Scope::set: No top-level movie clip scope");
self.locals().set(name, value, avm, context) self.locals().set(name, value, activation, context)
} }
} }
@ -314,17 +319,17 @@ impl<'gc> Scope<'gc> {
/// Delete a value from scope /// Delete a value from scope
pub fn delete( pub fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
mc: MutationContext<'gc, '_>, mc: MutationContext<'gc, '_>,
) -> bool { ) -> bool {
if self.locals().has_property(avm, context, name) { if self.locals().has_property(activation, context, name) {
return self.locals().delete(avm, mc, name); return self.locals().delete(activation, mc, name);
} }
if let Some(scope) = self.parent() { if let Some(scope) = self.parent() {
return scope.delete(avm, context, name, mc); return scope.delete(activation, context, name, mc);
} }
false false

View File

@ -1,8 +1,8 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject, NativeFunction}; use crate::avm1::function::{Executable, FunctionObject, NativeFunction};
use crate::avm1::property::{Attribute, Property}; use crate::avm1::property::{Attribute, Property};
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ObjectPtr, TObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ObjectPtr, TObject, UpdateContext, Value};
use crate::property_map::{Entry, PropertyMap}; use crate::property_map::{Entry, PropertyMap};
use core::fmt; use core::fmt;
use enumset::EnumSet; use enumset::EnumSet;
@ -197,19 +197,20 @@ impl<'gc> ScriptObject<'gc> {
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if name == "__proto__" { if name == "__proto__" {
self.0.write(context.gc_context).prototype = Some(value.coerce_to_object(avm, context)); self.0.write(context.gc_context).prototype =
Some(value.coerce_to_object(activation, context));
} else if let Ok(index) = name.parse::<usize>() { } else if let Ok(index) = name.parse::<usize>() {
self.set_array_element(index, value.to_owned(), context.gc_context); self.set_array_element(index, value.to_owned(), context.gc_context);
} else if !name.is_empty() { } else if !name.is_empty() {
if name == "length" { if name == "length" {
let length = value let length = value
.coerce_to_f64(avm, context) .coerce_to_f64(activation, context)
.map(|v| v.abs() as i32) .map(|v| v.abs() as i32)
.unwrap_or(0); .unwrap_or(0);
if length > 0 { if length > 0 {
@ -226,14 +227,14 @@ impl<'gc> ScriptObject<'gc> {
.0 .0
.read() .read()
.values .values
.contains_key(name, avm.is_case_sensitive()); .contains_key(name, activation.is_case_sensitive());
let mut rval = None; let mut worked = false;
if is_vacant { if is_vacant {
let mut proto: Option<Object<'gc>> = Some((*self).into()); let mut proto: Option<Object<'gc>> = Some((*self).into());
while let Some(this_proto) = proto { while let Some(this_proto) = proto {
if this_proto.has_own_virtual(avm, context, name) if this_proto.has_own_virtual(activation, context, name)
&& this_proto.is_property_overwritable(avm, name) && this_proto.is_property_overwritable(activation, name)
{ {
break; break;
} }
@ -242,42 +243,45 @@ impl<'gc> ScriptObject<'gc> {
} }
if let Some(this_proto) = proto { if let Some(this_proto) = proto {
rval = Some(this_proto.call_setter( if let Some(rval) =
name, this_proto.call_setter(name, value.clone(), activation, context)
value.clone(), {
avm, let _ = rval.exec(
context, activation,
(*self).into(), context,
)?); this,
Some(this_proto),
&[value.clone()],
)?;
worked = true;
}
} }
} }
//No `rval` signals we didn't call a virtual setter above. Normally, //This signals we didn't call a virtual setter above. Normally,
//we'd resolve and return up there, but we have borrows that need //we'd resolve and return up there, but we have borrows that need
//to end before we can do so. //to end before we can do so.
if rval.is_none() { if !worked {
rval = match self let rval = match self
.0 .0
.write(context.gc_context) .write(context.gc_context)
.values .values
.entry(name.to_owned(), avm.is_case_sensitive()) .entry(name.to_owned(), activation.is_case_sensitive())
{ {
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => entry.get_mut().set(value.clone()),
Some(entry.get_mut().set(avm, context, this, base_proto, value)?)
}
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
entry.insert(Property::Stored { entry.insert(Property::Stored {
value, value: value.clone(),
attributes: Default::default(), attributes: Default::default(),
}); });
None None
} }
}; };
}
if let Some(rval) = rval { if let Some(rval) = rval {
rval.resolve(avm, context)?; let _ = rval.exec(activation, context, this, base_proto, &[value])?;
}
} }
} }
@ -297,7 +301,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -305,13 +309,25 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
return Ok(self.proto().map_or(Value::Undefined, Value::Object)); return Ok(self.proto().map_or(Value::Undefined, Value::Object));
} }
if let Some(value) = self.0.read().values.get(name, avm.is_case_sensitive()) { let mut exec = None;
return value
.get(avm, context, this, Some((*self).into()))? if let Some(value) = self
.resolve(avm, context); .0
.read()
.values
.get(name, activation.is_case_sensitive())
{
match value {
Property::Virtual { get, .. } => exec = Some(get.to_owned()),
Property::Stored { value, .. } => return Ok(value.to_owned()),
}
} }
Ok(Value::Undefined) if let Some(get) = exec {
get.exec(activation, context, this, Some((*self).into()), &[])
} else {
Ok(Value::Undefined)
}
} }
/// Set a named property on the object. /// Set a named property on the object.
@ -323,13 +339,13 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.internal_set( self.internal_set(
name, name,
value, value,
avm, activation,
context, context,
(*self).into(), (*self).into(),
Some((*self).into()), Some((*self).into()),
@ -343,7 +359,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
/// overrides that may need to interact with the underlying object. /// overrides that may need to interact with the underlying object.
fn call( fn call(
&self, &self,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_base_proto: Option<Object<'gc>>, _base_proto: Option<Object<'gc>>,
@ -356,27 +372,24 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> {
match self match self
.0 .0
.write(context.gc_context) .write(context.gc_context)
.values .values
.get_mut(name, avm.is_case_sensitive()) .get_mut(name, activation.is_case_sensitive())
{ {
Some(propref) if propref.is_virtual() => { Some(propref) if propref.is_virtual() => propref.set(value),
propref.set(avm, context, this, Some((*self).into()), value) _ => None,
}
_ => Ok(Value::Undefined.into()),
} }
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -396,14 +409,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
/// Returns false if the property cannot be deleted. /// Returns false if the property cannot be deleted.
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
let mut object = self.0.write(gc_context); let mut object = self.0.write(gc_context);
if let Some(prop) = object.values.get(name, avm.is_case_sensitive()) { if let Some(prop) = object.values.get(name, activation.is_case_sensitive()) {
if prop.can_delete() { if prop.can_delete() {
object.values.remove(name, avm.is_case_sensitive()); object.values.remove(name, activation.is_case_sensitive());
return true; return true;
} }
} }
@ -432,7 +445,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -446,7 +459,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
set, set,
attributes, attributes,
}, },
avm.is_case_sensitive(), activation.is_case_sensitive(),
); );
} }
@ -499,22 +512,22 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
/// Checks if the object has a given named property. /// Checks if the object has a given named property.
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.has_own_property(avm, context, name) self.has_own_property(activation, context, name)
|| self || self
.proto() .proto()
.as_ref() .as_ref()
.map_or(false, |p| p.has_property(avm, context, name)) .map_or(false, |p| p.has_property(activation, context, name))
} }
/// Checks if the object has a given named property on itself (and not, /// Checks if the object has a given named property on itself (and not,
/// say, the object's prototype or superclass) /// say, the object's prototype or superclass)
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
@ -524,34 +537,44 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
self.0 self.0
.read() .read()
.values .values
.contains_key(name, avm.is_case_sensitive()) .contains_key(name, activation.is_case_sensitive())
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
if let Some(slot) = self.0.read().values.get(name, avm.is_case_sensitive()) { if let Some(slot) = self
.0
.read()
.values
.get(name, activation.is_case_sensitive())
{
slot.is_virtual() slot.is_virtual()
} else { } else {
false false
} }
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0 self.0
.read() .read()
.values .values
.get(name, avm.is_case_sensitive()) .get(name, activation.is_case_sensitive())
.map(|p| p.is_overwritable()) .map(|p| p.is_overwritable())
.unwrap_or(false) .unwrap_or(false)
} }
/// Checks if a named property appears when enumerating the object. /// Checks if a named property appears when enumerating the object.
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
if let Some(prop) = self.0.read().values.get(name, avm.is_case_sensitive()) { if let Some(prop) = self
.0
.read()
.values
.get(name, activation.is_case_sensitive())
{
prop.is_enumerable() prop.is_enumerable()
} else { } else {
false false
@ -559,17 +582,19 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
} }
/// Enumerate the object. /// Enumerate the object.
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
let proto_keys = self.proto().map_or_else(Vec::new, |p| p.get_keys(avm)); let proto_keys = self
.proto()
.map_or_else(Vec::new, |p| p.get_keys(activation));
let mut out_keys = vec![]; let mut out_keys = vec![];
let object = self.0.read(); let object = self.0.read();
// Prototype keys come first. // Prototype keys come first.
out_keys.extend( out_keys.extend(proto_keys.into_iter().filter(|k| {
proto_keys !object
.into_iter() .values
.filter(|k| !object.values.contains_key(k, avm.is_case_sensitive())), .contains_key(k, activation.is_case_sensitive())
); }));
// Then our own keys. // Then our own keys.
out_keys.extend(self.0.read().values.iter().filter_map(move |(k, p)| { out_keys.extend(self.0.read().values.iter().filter_map(move |(k, p)| {
@ -710,9 +735,9 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
mod tests { mod tests {
use super::*; use super::*;
use crate::avm1::activation::Activation;
use crate::avm1::globals::system::SystemProperties; use crate::avm1::globals::system::SystemProperties;
use crate::avm1::property::Attribute::*; use crate::avm1::property::Attribute::*;
use crate::avm1::Avm1;
use crate::backend::audio::NullAudioBackend; use crate::backend::audio::NullAudioBackend;
use crate::backend::input::NullInputBackend; use crate::backend::input::NullInputBackend;
use crate::backend::navigator::NullNavigatorBackend; use crate::backend::navigator::NullNavigatorBackend;
@ -730,7 +755,11 @@ mod tests {
fn with_object<F, R>(swf_version: u8, test: F) -> R fn with_object<F, R>(swf_version: u8, test: F) -> R
where where
F: for<'a, 'gc> FnOnce(&mut Avm1<'gc>, &mut UpdateContext<'a, 'gc, '_>, Object<'gc>) -> R, F: for<'a, 'gc> FnOnce(
&mut Activation<'_, 'gc>,
&mut UpdateContext<'a, 'gc, '_>,
Object<'gc>,
) -> R,
{ {
rootless_arena(|gc_context| { rootless_arena(|gc_context| {
let mut avm = Avm1::new(gc_context, swf_version); let mut avm = Avm1::new(gc_context, swf_version);
@ -780,20 +809,23 @@ mod tests {
let object = ScriptObject::object(gc_context, Some(avm.prototypes().object)).into(); let object = ScriptObject::object(gc_context, Some(avm.prototypes().object)).into();
let globals = avm.global_object_cell(); let globals = avm.global_object_cell();
avm.insert_stack_frame(GcCell::allocate( let mut activation = Activation::from_nothing(
gc_context, &mut avm,
Activation::from_nothing(swf_version, globals, gc_context, root), context.swf.version(),
)); globals,
context.gc_context,
*context.levels.get(&0).unwrap(),
);
test(&mut avm, &mut context, object) test(&mut activation, &mut context, object)
}) })
} }
#[test] #[test]
fn test_get_undefined() { fn test_get_undefined() {
with_object(0, |avm, context, object| { with_object(0, |activation, context, object| {
assert_eq!( assert_eq!(
object.get("not_defined", avm, context).unwrap(), object.get("not_defined", activation, context).unwrap(),
Value::Undefined Value::Undefined
); );
}) })
@ -801,7 +833,7 @@ mod tests {
#[test] #[test]
fn test_set_get() { fn test_set_get() {
with_object(0, |avm, context, object| { with_object(0, |activation, context, object| {
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"forced", "forced",
@ -809,12 +841,15 @@ mod tests {
EnumSet::empty(), EnumSet::empty(),
); );
object object
.set("natural", "natural".into(), avm, context) .set("natural", "natural".into(), activation, context)
.unwrap(); .unwrap();
assert_eq!(object.get("forced", avm, context).unwrap(), "forced".into());
assert_eq!( assert_eq!(
object.get("natural", avm, context).unwrap(), object.get("forced", activation, context).unwrap(),
"forced".into()
);
assert_eq!(
object.get("natural", activation, context).unwrap(),
"natural".into() "natural".into()
); );
}) })
@ -822,7 +857,7 @@ mod tests {
#[test] #[test]
fn test_set_readonly() { fn test_set_readonly() {
with_object(0, |avm, context, object| { with_object(0, |activation, context, object| {
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"normal", "normal",
@ -837,18 +872,18 @@ mod tests {
); );
object object
.set("normal", "replaced".into(), avm, context) .set("normal", "replaced".into(), activation, context)
.unwrap(); .unwrap();
object object
.set("readonly", "replaced".into(), avm, context) .set("readonly", "replaced".into(), activation, context)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
object.get("normal", avm, context).unwrap(), object.get("normal", activation, context).unwrap(),
"replaced".into() "replaced".into()
); );
assert_eq!( assert_eq!(
object.get("readonly", avm, context).unwrap(), object.get("readonly", activation, context).unwrap(),
"initial".into() "initial".into()
); );
}) })
@ -856,7 +891,7 @@ mod tests {
#[test] #[test]
fn test_deletable_not_readonly() { fn test_deletable_not_readonly() {
with_object(0, |avm, context, object| { with_object(0, |activation, context, object| {
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
"test", "test",
@ -864,26 +899,30 @@ mod tests {
DontDelete.into(), DontDelete.into(),
); );
assert_eq!(object.delete(avm, context.gc_context, "test"), false); assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!(object.get("test", avm, context).unwrap(), "initial".into()); assert_eq!(
object.get("test", activation, context).unwrap(),
"initial".into()
);
object object
.as_script_object() .as_script_object()
.unwrap() .unwrap()
.set("test", "replaced".into(), avm, context) .set("test", "replaced".into(), activation, context)
.unwrap(); .unwrap();
assert_eq!(object.delete(avm, context.gc_context, "test"), false); assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!(object.get("test", avm, context).unwrap(), "replaced".into()); assert_eq!(
object.get("test", activation, context).unwrap(),
"replaced".into()
);
}) })
} }
#[test] #[test]
fn test_virtual_get() { fn test_virtual_get() {
with_object(0, |avm, context, object| { with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| { let getter = Executable::Native(|_avm, _context, _this, _args| Ok("Virtual!".into()));
Ok(ReturnValue::Immediate("Virtual!".into()))
});
object.as_script_object().unwrap().add_property( object.as_script_object().unwrap().add_property(
context.gc_context, context.gc_context,
@ -893,20 +932,26 @@ mod tests {
EnumSet::empty(), EnumSet::empty(),
); );
assert_eq!(object.get("test", avm, context).unwrap(), "Virtual!".into()); assert_eq!(
object.get("test", activation, context).unwrap(),
"Virtual!".into()
);
// This set should do nothing // This set should do nothing
object.set("test", "Ignored!".into(), avm, context).unwrap(); object
assert_eq!(object.get("test", avm, context).unwrap(), "Virtual!".into()); .set("test", "Ignored!".into(), activation, context)
.unwrap();
assert_eq!(
object.get("test", activation, context).unwrap(),
"Virtual!".into()
);
}) })
} }
#[test] #[test]
fn test_delete() { fn test_delete() {
with_object(0, |avm, context, object| { with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| { let getter = Executable::Native(|_avm, _context, _this, _args| Ok("Virtual!".into()));
Ok(ReturnValue::Immediate("Virtual!".into()))
});
object.as_script_object().unwrap().add_property( object.as_script_object().unwrap().add_property(
context.gc_context, context.gc_context,
@ -935,29 +980,41 @@ mod tests {
DontDelete.into(), DontDelete.into(),
); );
assert_eq!(object.delete(avm, context.gc_context, "virtual"), true);
assert_eq!(object.delete(avm, context.gc_context, "virtual_un"), false);
assert_eq!(object.delete(avm, context.gc_context, "stored"), true);
assert_eq!(object.delete(avm, context.gc_context, "stored_un"), false);
assert_eq!( assert_eq!(
object.delete(avm, context.gc_context, "non_existent"), object.delete(activation, context.gc_context, "virtual"),
true
);
assert_eq!(
object.delete(activation, context.gc_context, "virtual_un"),
false
);
assert_eq!(
object.delete(activation, context.gc_context, "stored"),
true
);
assert_eq!(
object.delete(activation, context.gc_context, "stored_un"),
false
);
assert_eq!(
object.delete(activation, context.gc_context, "non_existent"),
false false
); );
assert_eq!( assert_eq!(
object.get("virtual", avm, context).unwrap(), object.get("virtual", activation, context).unwrap(),
Value::Undefined Value::Undefined
); );
assert_eq!( assert_eq!(
object.get("virtual_un", avm, context).unwrap(), object.get("virtual_un", activation, context).unwrap(),
"Virtual!".into() "Virtual!".into()
); );
assert_eq!( assert_eq!(
object.get("stored", avm, context).unwrap(), object.get("stored", activation, context).unwrap(),
Value::Undefined Value::Undefined
); );
assert_eq!( assert_eq!(
object.get("stored_un", avm, context).unwrap(), object.get("stored_un", activation, context).unwrap(),
"Stored!".into() "Stored!".into()
); );
}) })
@ -965,10 +1022,8 @@ mod tests {
#[test] #[test]
fn test_iter_values() { fn test_iter_values() {
with_object(0, |avm, context, object| { with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| { let getter = Executable::Native(|_avm, _context, _this, _args| Ok(Value::Null));
Ok(ReturnValue::Immediate(Value::Null))
});
object.as_script_object().unwrap().define_value( object.as_script_object().unwrap().define_value(
context.gc_context, context.gc_context,
@ -997,7 +1052,7 @@ mod tests {
DontEnum.into(), DontEnum.into(),
); );
let keys: Vec<_> = object.get_keys(avm); let keys: Vec<_> = object.get_keys(activation);
assert_eq!(keys.len(), 2); assert_eq!(keys.len(), 2);
assert_eq!(keys.contains(&"stored".to_string()), true); assert_eq!(keys.contains(&"stored".to_string()), true);
assert_eq!(keys.contains(&"stored_hidden".to_string()), false); assert_eq!(keys.contains(&"stored_hidden".to_string()), false);

View File

@ -1,9 +1,9 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::sound_object::SoundObject; use crate::avm1::sound_object::SoundObject;
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, Value}; use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::display_object::DisplayObject; use crate::display_object::DisplayObject;
use enumset::EnumSet; use enumset::EnumSet;
@ -73,66 +73,67 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base().get_local(name, avm, context, this) self.base().get_local(name, activation, context, this)
} }
fn set( fn set(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base().set(name, value, avm, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base().call(avm, context, this, base_proto, args) self.base()
.call(activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> { self.base().call_setter(name, value, activation, context)
self.base().call_setter(name, value, avm, context, this)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
Ok( Ok(SharedObject::empty_shared_obj(
SharedObject::empty_shared_obj(context.gc_context, Some(avm.prototypes.shared_object)) context.gc_context,
.into(), Some(activation.avm().prototypes.shared_object),
) )
.into())
} }
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().delete(avm, gc_context, name) self.base().delete(activation, gc_context, name)
} }
fn proto(&self) -> Option<Object<'gc>> { fn proto(&self) -> Option<Object<'gc>> {
@ -179,7 +180,7 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -187,46 +188,46 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
attributes: EnumSet<Attribute>, attributes: EnumSet<Attribute>,
) { ) {
self.base() self.base()
.add_property_with_case(avm, gc_context, name, get, set, attributes) .add_property_with_case(activation, gc_context, name, get, set, attributes)
} }
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_property(avm, context, name) self.base().has_property(activation, context, name)
} }
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_own_property(avm, context, name) self.base().has_own_property(activation, context, name)
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_own_virtual(avm, context, name) self.base().has_own_virtual(activation, context, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_overwritable(avm, name) self.base().is_property_overwritable(activation, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(avm, name) self.base().is_property_enumerable(activation, name)
} }
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(avm) self.base().get_keys(activation)
} }
fn as_string(&self) -> Cow<str> { fn as_string(&self) -> Cow<str> {

View File

@ -1,10 +1,10 @@
//! AVM1 object type to represent Sound objects. //! AVM1 object type to represent Sound objects.
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, Value};
use crate::backend::audio::{SoundHandle, SoundInstanceHandle}; use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::display_object::DisplayObject; use crate::display_object::DisplayObject;
@ -134,63 +134,66 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base().get_local(name, avm, context, this) self.base().get_local(name, activation, context, this)
} }
fn set( fn set(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base().set(name, value, avm, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base().call(avm, context, this, base_proto, args) self.base()
.call(activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> { self.base().call_setter(name, value, activation, context)
self.base().call_setter(name, value, avm, context, this)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
Ok(SoundObject::empty_sound(context.gc_context, Some(avm.prototypes.sound)).into()) Ok(
SoundObject::empty_sound(context.gc_context, Some(activation.avm().prototypes.sound))
.into(),
)
} }
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().delete(avm, gc_context, name) self.base().delete(activation, gc_context, name)
} }
fn proto(&self) -> Option<Object<'gc>> { fn proto(&self) -> Option<Object<'gc>> {
@ -237,7 +240,7 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -245,46 +248,46 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
attributes: EnumSet<Attribute>, attributes: EnumSet<Attribute>,
) { ) {
self.base() self.base()
.add_property_with_case(avm, gc_context, name, get, set, attributes) .add_property_with_case(activation, gc_context, name, get, set, attributes)
} }
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_property(avm, context, name) self.base().has_property(activation, context, name)
} }
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_own_property(avm, context, name) self.base().has_own_property(activation, context, name)
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_own_virtual(avm, context, name) self.base().has_own_virtual(activation, context, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_overwritable(avm, name) self.base().is_property_overwritable(activation, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(avm, name) self.base().is_property_enumerable(activation, name)
} }
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(avm) self.base().get_keys(activation)
} }
fn as_string(&self) -> Cow<str> { fn as_string(&self) -> Cow<str> {

View File

@ -1,11 +1,11 @@
//! AVM1 object type to represent objects on the stage. //! AVM1 object type to represent objects on the stage.
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::search_prototype; use crate::avm1::object::search_prototype;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value};
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::display_object::{DisplayObject, EditText, MovieClip}; use crate::display_object::{DisplayObject, EditText, MovieClip};
use crate::property_map::PropertyMap; use crate::property_map::PropertyMap;
@ -127,19 +127,19 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn get( fn get(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let obj = self.0.read(); let obj = self.0.read();
let props = avm.display_properties; let props = activation.avm().display_properties;
let case_sensitive = avm.is_case_sensitive(); let case_sensitive = activation.is_case_sensitive();
// Property search order for DisplayObjects: // Property search order for DisplayObjects:
if self.has_own_property(avm, context, name) { if self.has_own_property(activation, context, name) {
// 1) Actual properties on the underlying object // 1) Actual properties on the underlying object
self.get_local(name, avm, context, (*self).into()) self.get_local(name, activation, context, (*self).into())
} else if let Some(property) = props.read().get_by_name(&name) { } else if let Some(property) = props.read().get_by_name(&name) {
// 2) Display object properties such as _x, _y // 2) Display object properties such as _x, _y
let val = property.get(avm, context, obj.display_object)?; let val = property.get(activation, context, obj.display_object)?;
Ok(val) Ok(val)
} else if let Some(child) = obj.display_object.get_child_by_name(name, case_sensitive) { } else if let Some(child) = obj.display_object.get_child_by_name(name, case_sensitive) {
// 3) Child display objects with the given instance name // 3) Child display objects with the given instance name
@ -152,9 +152,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
Ok(level.object()) Ok(level.object())
} else { } else {
// 5) Prototype // 5) Prototype
search_prototype(self.proto(), name, avm, context, (*self).into())? Ok(search_prototype(self.proto(), name, activation, context, (*self).into())?.0)
.0
.resolve(avm, context)
} }
// 6) TODO: __resolve? // 6) TODO: __resolve?
} }
@ -162,22 +160,25 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.0.read().base.get_local(name, avm, context, this) self.0
.read()
.base
.get_local(name, activation, context, this)
} }
fn set( fn set(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
let obj = self.0.read(); let obj = self.0.read();
let props = avm.display_properties; let props = activation.avm().display_properties;
// Check if a text field is bound to this property and update the text if so. // Check if a text field is bound to this property and update the text if so.
for binding in obj for binding in obj
@ -185,31 +186,32 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
.iter() .iter()
.filter(|binding| binding.variable_name == name) .filter(|binding| binding.variable_name == name)
{ {
let _ = binding let _ = binding.text_field.set_html_text(
.text_field value.coerce_to_string(activation, context)?.into_owned(),
.set_html_text(value.coerce_to_string(avm, context)?.into_owned(), context); context,
);
} }
if obj.base.has_own_property(avm, context, name) { if obj.base.has_own_property(activation, context, name) {
// 1) Actual proeprties on the underlying object // 1) Actual proeprties on the underlying object
obj.base.internal_set( obj.base.internal_set(
name, name,
value, value,
avm, activation,
context, context,
(*self).into(), (*self).into(),
Some((*self).into()), Some((*self).into()),
) )
} else if let Some(property) = props.read().get_by_name(&name) { } else if let Some(property) = props.read().get_by_name(&name) {
// 2) Display object properties such as _x, _y // 2) Display object properties such as _x, _y
property.set(avm, context, obj.display_object, value)?; property.set(activation, context, obj.display_object, value)?;
Ok(()) Ok(())
} else { } else {
// 3) TODO: Prototype // 3) TODO: Prototype
obj.base.internal_set( obj.base.internal_set(
name, name,
value, value,
avm, activation,
context, context,
(*self).into(), (*self).into(),
Some((*self).into()), Some((*self).into()),
@ -219,7 +221,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
@ -228,42 +230,41 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
self.0 self.0
.read() .read()
.base .base
.call(avm, context, this, base_proto, args) .call(activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> {
self.0 self.0
.read() .read()
.base .base
.call_setter(name, value, avm, context, this) .call_setter(name, value, activation, context)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
//TODO: Create a StageObject of some kind //TODO: Create a StageObject of some kind
self.0.read().base.new(avm, context, this, args) self.0.read().base.new(activation, context, this, args)
} }
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().base.delete(avm, gc_context, name) self.0.read().base.delete(activation, gc_context, name)
} }
fn proto(&self) -> Option<Object<'gc>> { fn proto(&self) -> Option<Object<'gc>> {
@ -318,7 +319,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -328,21 +329,21 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
self.0 self.0
.read() .read()
.base .base
.add_property_with_case(avm, gc_context, name, get, set, attributes) .add_property_with_case(activation, gc_context, name, get, set, attributes)
} }
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
let obj = self.0.read(); let obj = self.0.read();
if obj.base.has_property(avm, context, name) { if obj.base.has_property(activation, context, name) {
return true; return true;
} }
let case_sensitive = avm.is_case_sensitive(); let case_sensitive = activation.is_case_sensitive();
if obj if obj
.display_object .display_object
.get_child_by_name(name, case_sensitive) .get_child_by_name(name, case_sensitive)
@ -364,36 +365,45 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
// Note that `hasOwnProperty` does NOT return true for child display objects. // Note that `hasOwnProperty` does NOT return true for child display objects.
self.0.read().base.has_own_property(avm, context, name) self.0
.read()
.base
.has_own_property(activation, context, name)
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().base.has_own_virtual(avm, context, name) self.0
.read()
.base
.has_own_virtual(activation, context, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0.read().base.is_property_enumerable(avm, name) self.0.read().base.is_property_enumerable(activation, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0.read().base.is_property_overwritable(avm, name) self.0
.read()
.base
.is_property_overwritable(activation, name)
} }
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
// Keys from the underlying object are listed first, followed by // Keys from the underlying object are listed first, followed by
// child display objects in order from highest depth to lowest depth. // child display objects in order from highest depth to lowest depth.
let obj = self.0.read(); let obj = self.0.read();
let mut keys = obj.base.get_keys(avm); let mut keys = obj.base.get_keys(activation);
keys.extend( keys.extend(
obj.display_object obj.display_object
.children() .children()
@ -486,13 +496,13 @@ pub struct DisplayProperty<'gc> {
} }
pub type DisplayGetter<'gc> = fn( pub type DisplayGetter<'gc> = fn(
&mut Avm1<'gc>, &mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>, &mut UpdateContext<'_, 'gc, '_>,
DisplayObject<'gc>, DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>>; ) -> Result<Value<'gc>, Error<'gc>>;
pub type DisplaySetter<'gc> = fn( pub type DisplaySetter<'gc> = fn(
&mut Avm1<'gc>, &mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>, &mut UpdateContext<'_, 'gc, '_>,
DisplayObject<'gc>, DisplayObject<'gc>,
Value<'gc>, Value<'gc>,
@ -501,22 +511,22 @@ pub type DisplaySetter<'gc> = fn(
impl<'gc> DisplayProperty<'gc> { impl<'gc> DisplayProperty<'gc> {
pub fn get( pub fn get(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
(self.get)(avm, context, this) (self.get)(activation, context, this)
} }
pub fn set( pub fn set(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
value: Value<'gc>, value: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.set self.set
.map(|f| f(avm, context, this, value)) .map(|f| f(activation, context, this, value))
.unwrap_or(Ok(())) .unwrap_or(Ok(()))
} }
} }
@ -593,7 +603,7 @@ impl<'gc> DisplayPropertyMap<'gc> {
} }
fn x<'gc>( fn x<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -601,19 +611,19 @@ fn x<'gc>(
} }
fn set_x<'gc>( fn set_x<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if let Some(val) = property_coerce_to_number(avm, context, val)? { if let Some(val) = property_coerce_to_number(activation, context, val)? {
this.set_x(context.gc_context, val); this.set_x(context.gc_context, val);
} }
Ok(()) Ok(())
} }
fn y<'gc>( fn y<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -621,19 +631,19 @@ fn y<'gc>(
} }
fn set_y<'gc>( fn set_y<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if let Some(val) = property_coerce_to_number(avm, context, val)? { if let Some(val) = property_coerce_to_number(activation, context, val)? {
this.set_y(context.gc_context, val); this.set_y(context.gc_context, val);
} }
Ok(()) Ok(())
} }
fn x_scale<'gc>( fn x_scale<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -642,19 +652,19 @@ fn x_scale<'gc>(
} }
fn set_x_scale<'gc>( fn set_x_scale<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if let Some(val) = property_coerce_to_number(avm, context, val)? { if let Some(val) = property_coerce_to_number(activation, context, val)? {
this.set_scale_x(context.gc_context, val / 100.0); this.set_scale_x(context.gc_context, val / 100.0);
} }
Ok(()) Ok(())
} }
fn y_scale<'gc>( fn y_scale<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -663,19 +673,19 @@ fn y_scale<'gc>(
} }
fn set_y_scale<'gc>( fn set_y_scale<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if let Some(val) = property_coerce_to_number(avm, context, val)? { if let Some(val) = property_coerce_to_number(activation, context, val)? {
this.set_scale_y(context.gc_context, val / 100.0); this.set_scale_y(context.gc_context, val / 100.0);
} }
Ok(()) Ok(())
} }
fn current_frame<'gc>( fn current_frame<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -687,7 +697,7 @@ fn current_frame<'gc>(
} }
fn total_frames<'gc>( fn total_frames<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -699,7 +709,7 @@ fn total_frames<'gc>(
} }
fn alpha<'gc>( fn alpha<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -708,19 +718,19 @@ fn alpha<'gc>(
} }
fn set_alpha<'gc>( fn set_alpha<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if let Some(val) = property_coerce_to_number(avm, context, val)? { if let Some(val) = property_coerce_to_number(activation, context, val)? {
this.set_alpha(context.gc_context, val / 100.0); this.set_alpha(context.gc_context, val / 100.0);
} }
Ok(()) Ok(())
} }
fn visible<'gc>( fn visible<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -729,21 +739,21 @@ fn visible<'gc>(
} }
fn set_visible<'gc>( fn set_visible<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
// Because this property dates to the era of Flash 4, this is actually coerced to an integer. // Because this property dates to the era of Flash 4, this is actually coerced to an integer.
// `_visible = "false";` coerces to NaN and has no effect. // `_visible = "false";` coerces to NaN and has no effect.
if let Some(n) = property_coerce_to_number(avm, context, val)? { if let Some(n) = property_coerce_to_number(activation, context, val)? {
this.set_visible(context.gc_context, n != 0.0); this.set_visible(context.gc_context, n != 0.0);
} }
Ok(()) Ok(())
} }
fn width<'gc>( fn width<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -751,19 +761,19 @@ fn width<'gc>(
} }
fn set_width<'gc>( fn set_width<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if let Some(val) = property_coerce_to_number(avm, context, val)? { if let Some(val) = property_coerce_to_number(activation, context, val)? {
this.set_width(context.gc_context, val); this.set_width(context.gc_context, val);
} }
Ok(()) Ok(())
} }
fn height<'gc>( fn height<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -771,19 +781,19 @@ fn height<'gc>(
} }
fn set_height<'gc>( fn set_height<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if let Some(val) = property_coerce_to_number(avm, context, val)? { if let Some(val) = property_coerce_to_number(activation, context, val)? {
this.set_height(context.gc_context, val); this.set_height(context.gc_context, val);
} }
Ok(()) Ok(())
} }
fn rotation<'gc>( fn rotation<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -791,12 +801,12 @@ fn rotation<'gc>(
} }
fn set_rotation<'gc>( fn set_rotation<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
degrees: Value<'gc>, degrees: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
if let Some(mut degrees) = property_coerce_to_number(avm, context, degrees)? { if let Some(mut degrees) = property_coerce_to_number(activation, context, degrees)? {
// Normalize into the range of [-180, 180]. // Normalize into the range of [-180, 180].
degrees %= 360.0; degrees %= 360.0;
if degrees < -180.0 { if degrees < -180.0 {
@ -810,7 +820,7 @@ fn set_rotation<'gc>(
} }
fn target<'gc>( fn target<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -818,7 +828,7 @@ fn target<'gc>(
} }
fn frames_loaded<'gc>( fn frames_loaded<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -830,7 +840,7 @@ fn frames_loaded<'gc>(
} }
fn name<'gc>( fn name<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -838,18 +848,18 @@ fn name<'gc>(
} }
fn set_name<'gc>( fn set_name<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>, mut this: DisplayObject<'gc>,
val: Value<'gc>, val: Value<'gc>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
let name = val.coerce_to_string(avm, context)?; let name = val.coerce_to_string(activation, context)?;
this.set_name(context.gc_context, &name); this.set_name(context.gc_context, &name);
Ok(()) Ok(())
} }
fn drop_target<'gc>( fn drop_target<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -858,7 +868,7 @@ fn drop_target<'gc>(
} }
fn url<'gc>( fn url<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -867,7 +877,7 @@ fn url<'gc>(
} }
fn high_quality<'gc>( fn high_quality<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -876,7 +886,7 @@ fn high_quality<'gc>(
} }
fn set_high_quality<'gc>( fn set_high_quality<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
_val: Value<'gc>, _val: Value<'gc>,
@ -886,7 +896,7 @@ fn set_high_quality<'gc>(
} }
fn focus_rect<'gc>( fn focus_rect<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -895,7 +905,7 @@ fn focus_rect<'gc>(
} }
fn set_focus_rect<'gc>( fn set_focus_rect<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
_val: Value<'gc>, _val: Value<'gc>,
@ -905,7 +915,7 @@ fn set_focus_rect<'gc>(
} }
fn sound_buf_time<'gc>( fn sound_buf_time<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -914,7 +924,7 @@ fn sound_buf_time<'gc>(
} }
fn set_sound_buf_time<'gc>( fn set_sound_buf_time<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
_val: Value<'gc>, _val: Value<'gc>,
@ -924,7 +934,7 @@ fn set_sound_buf_time<'gc>(
} }
fn quality<'gc>( fn quality<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -933,7 +943,7 @@ fn quality<'gc>(
} }
fn set_quality<'gc>( fn set_quality<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>, _this: DisplayObject<'gc>,
_val: Value<'gc>, _val: Value<'gc>,
@ -943,7 +953,7 @@ fn set_quality<'gc>(
} }
fn x_mouse<'gc>( fn x_mouse<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -952,7 +962,7 @@ fn x_mouse<'gc>(
} }
fn y_mouse<'gc>( fn y_mouse<'gc>(
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>, this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -961,12 +971,12 @@ fn y_mouse<'gc>(
} }
fn property_coerce_to_number<'gc>( fn property_coerce_to_number<'gc>(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
value: Value<'gc>, value: Value<'gc>,
) -> Result<Option<f64>, Error<'gc>> { ) -> Result<Option<f64>, Error<'gc>> {
if value != Value::Undefined && value != Value::Null { if value != Value::Undefined && value != Value::Null {
let n = value.coerce_to_f64(avm, context)?; let n = value.coerce_to_f64(activation, context)?;
if n.is_finite() { if n.is_finite() {
return Ok(Some(n)); return Ok(Some(n));
} }

View File

@ -1,12 +1,12 @@
//! Special object that implements `super` //! Special object that implements `super`
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::search_prototype; use crate::avm1::object::search_prototype;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::script_object::TYPE_OF_OBJECT; use crate::avm1::script_object::TYPE_OF_OBJECT;
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, Value}; use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::display_object::DisplayObject; use crate::display_object::DisplayObject;
use enumset::EnumSet; use enumset::EnumSet;
@ -45,7 +45,7 @@ impl<'gc> SuperObject<'gc> {
pub fn from_this_and_base_proto( pub fn from_this_and_base_proto(
this: Object<'gc>, this: Object<'gc>,
base_proto: Object<'gc>, base_proto: Object<'gc>,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Self, Error<'gc>> { ) -> Result<Self, Error<'gc>> {
Ok(Self(GcCell::allocate( Ok(Self(GcCell::allocate(
@ -65,14 +65,14 @@ impl<'gc> SuperObject<'gc> {
/// Retrieve the constructor associated with the super proto. /// Retrieve the constructor associated with the super proto.
fn super_constr( fn super_constr(
self, self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Option<Object<'gc>>, Error<'gc>> { ) -> Result<Option<Object<'gc>>, Error<'gc>> {
if let Some(super_proto) = self.super_proto() { if let Some(super_proto) = self.super_proto() {
Ok(Some( Ok(Some(
super_proto super_proto
.get("__constructor__", avm, context)? .get("__constructor__", activation, context)?
.coerce_to_object(avm, context), .coerce_to_object(activation, context),
)) ))
} else { } else {
Ok(None) Ok(None)
@ -84,7 +84,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
fn get_local( fn get_local(
&self, &self,
_name: &str, _name: &str,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -95,7 +95,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
&self, &self,
_name: &str, _name: &str,
_value: Value<'gc>, _value: Value<'gc>,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
//TODO: What happens if you set `super.__proto__`? //TODO: What happens if you set `super.__proto__`?
@ -104,14 +104,20 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_base_proto: Option<Object<'gc>>, _base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(constr) = self.super_constr(avm, context)? { if let Some(constr) = self.super_constr(activation, context)? {
constr.call(avm, context, self.0.read().child, self.super_proto(), args) constr.call(
activation,
context,
self.0.read().child,
self.super_proto(),
args,
)
} else { } else {
Ok(Value::Undefined) Ok(Value::Undefined)
} }
@ -121,56 +127,55 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
&self, &self,
name: &str, name: &str,
args: &[Value<'gc>], args: &[Value<'gc>],
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let child = self.0.read().child; let child = self.0.read().child;
let super_proto = self.super_proto(); let super_proto = self.super_proto();
let (method, base_proto) = search_prototype(super_proto, name, avm, context, child)?; let (method, base_proto) = search_prototype(super_proto, name, activation, context, child)?;
let method = method.resolve(avm, context)?; let method = method;
if let Value::Object(_) = method { if let Value::Object(_) = method {
} else { } else {
log::warn!("Super method {} is not callable", name); log::warn!("Super method {} is not callable", name);
} }
method.call(avm, context, child, base_proto, args) method.call(activation, context, child, base_proto, args)
} }
fn call_setter( fn call_setter(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> {
self.0 self.0
.read() .read()
.child .child
.call_setter(name, value, avm, context, this) .call_setter(name, value, activation, context)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
if let Some(proto) = self.proto() { if let Some(proto) = self.proto() {
proto.new(avm, context, this, args) proto.new(activation, context, this, args)
} else { } else {
// TODO: What happens when you `new super` but there's no // TODO: What happens when you `new super` but there's no
// super? Is this code even reachable?! // super? Is this code even reachable?!
self.0.read().child.new(avm, context, this, args) self.0.read().child.new(activation, context, this, args)
} }
} }
fn delete( fn delete(
&self, &self,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_gc_context: MutationContext<'gc, '_>, _gc_context: MutationContext<'gc, '_>,
_name: &str, _name: &str,
) -> bool { ) -> bool {
@ -221,7 +226,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_gc_context: MutationContext<'gc, '_>, _gc_context: MutationContext<'gc, '_>,
_name: &str, _name: &str,
_get: Executable<'gc>, _get: Executable<'gc>,
@ -233,40 +238,49 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().child.has_property(avm, context, name) self.0.read().child.has_property(activation, context, name)
} }
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().child.has_own_property(avm, context, name) self.0
.read()
.child
.has_own_property(activation, context, name)
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().child.has_own_virtual(avm, context, name) self.0
.read()
.child
.has_own_virtual(activation, context, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0.read().child.is_property_enumerable(avm, name) self.0.read().child.is_property_enumerable(activation, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0.read().child.is_property_overwritable(avm, name) self.0
.read()
.child
.is_property_overwritable(activation, name)
} }
fn get_keys(&self, _avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, _activation: &mut Activation<'_, 'gc>) -> Vec<String> {
vec![] vec![]
} }

View File

@ -13,7 +13,7 @@ use crate::library::Library;
use crate::loader::LoadManager; use crate::loader::LoadManager;
use crate::prelude::*; use crate::prelude::*;
use crate::tag_utils::{SwfMovie, SwfSlice}; use crate::tag_utils::{SwfMovie, SwfSlice};
use gc_arena::{rootless_arena, GcCell, MutationContext}; use gc_arena::{rootless_arena, MutationContext};
use rand::{rngs::SmallRng, SeedableRng}; use rand::{rngs::SmallRng, SeedableRng};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::sync::Arc; use std::sync::Arc;
@ -21,22 +21,22 @@ use std::sync::Arc;
pub fn with_avm<F>(swf_version: u8, test: F) pub fn with_avm<F>(swf_version: u8, test: F)
where where
F: for<'a, 'gc> FnOnce( F: for<'a, 'gc> FnOnce(
&mut Avm1<'gc>, &mut Activation<'_, 'gc>,
&mut UpdateContext<'a, 'gc, '_>, &mut UpdateContext<'a, 'gc, '_>,
Object<'gc>, Object<'gc>,
) -> Result<(), Error<'gc>>, ) -> Result<(), Error<'gc>>,
{ {
fn in_the_arena<'gc, F>(swf_version: u8, test: F, gc_context: MutationContext<'gc, '_>) fn in_the_arena<'a, 'gc: 'a, F>(swf_version: u8, test: F, gc_context: MutationContext<'gc, '_>)
where where
F: for<'a> FnOnce( F: FnOnce(
&mut Avm1<'gc>, &mut Activation<'_, 'gc>,
&mut UpdateContext<'a, 'gc, '_>, &mut UpdateContext<'_, 'gc, '_>,
Object<'gc>, Object<'gc>,
) -> Result<(), Error<'gc>>, ) -> Result<(), Error<'gc>>,
{ {
let mut avm = Avm1::new(gc_context, swf_version); let mut avm = Avm1::new(gc_context, swf_version);
let swf = Arc::new(SwfMovie::empty(swf_version)); let swf = Arc::new(SwfMovie::empty(swf_version));
let mut root: DisplayObject<'_> = let mut root: DisplayObject<'gc> =
MovieClip::new(SwfSlice::empty(swf.clone()), gc_context).into(); MovieClip::new(SwfSlice::empty(swf.clone()), gc_context).into();
root.set_depth(gc_context, 0); root.set_depth(gc_context, 0);
let mut levels = BTreeMap::new(); let mut levels = BTreeMap::new();
@ -77,17 +77,35 @@ where
root.post_instantiation(&mut avm, &mut context, root, None, false); root.post_instantiation(&mut avm, &mut context, root, None, false);
root.set_name(context.gc_context, ""); root.set_name(context.gc_context, "");
let globals = avm.global_object_cell(); fn run_test<'a, 'gc: 'a, F>(
avm.insert_stack_frame(GcCell::allocate( activation: &mut Activation<'_, 'gc>,
gc_context, context: &mut UpdateContext<'_, 'gc, '_>,
Activation::from_nothing(swf_version, globals, gc_context, root), root: DisplayObject<'gc>,
)); test: F,
) where
let this = root.object().coerce_to_object(&mut avm, &mut context); F: FnOnce(
&mut Activation<'_, 'gc>,
if let Err(e) = test(&mut avm, &mut context, this) { &mut UpdateContext<'_, 'gc, '_>,
panic!("Encountered exception during test: {}", e); Object<'gc>,
) -> Result<(), Error<'gc>>,
{
let this = root.object().coerce_to_object(activation, context);
let result = test(activation, context, this);
if let Err(e) = result {
panic!("Encountered exception during test: {}", e);
}
} }
let globals = avm.global_object_cell();
let mut activation = Activation::from_nothing(
&mut avm,
context.swf.version(),
globals,
context.gc_context,
*context.levels.get(&0).unwrap(),
);
run_test(&mut activation, &mut context, root, test)
} }
rootless_arena(|gc_context| in_the_arena(swf_version, test, gc_context)) rootless_arena(|gc_context| in_the_arena(swf_version, test, gc_context))
@ -100,9 +118,9 @@ macro_rules! test_method {
use $crate::avm1::test_utils::*; use $crate::avm1::test_utils::*;
$( $(
for version in &$versions { for version in &$versions {
with_avm(*version, |avm, context, _root| -> Result<(), Error> { with_avm(*version, |activation, context, _root| -> Result<(), Error> {
let object = $object(avm, context); let object = $object(activation, context);
let function = object.get($name, avm, context)?; let function = object.get($name, activation, context)?;
$( $(
#[allow(unused_mut)] #[allow(unused_mut)]
@ -110,7 +128,7 @@ macro_rules! test_method {
$( $(
args.push($arg.into()); args.push($arg.into());
)* )*
assert_eq!(function.call(avm, context, object, None, &args)?, $out.into(), "{:?} => {:?} in swf {}", args, $out, version); assert_eq!(function.call(activation, context, object, None, &args)?, $out.into(), "{:?} => {:?} in swf {}", args, $out, version);
)* )*
Ok(()) Ok(())

View File

@ -1,28 +1,18 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::test_utils::with_avm; use crate::avm1::test_utils::with_avm;
use crate::avm1::TObject; use crate::avm1::TObject;
use gc_arena::GcCell;
#[test] #[test]
fn locals_into_form_values() { fn locals_into_form_values() {
with_avm(19, |avm, context, _this| -> Result<(), Error> { with_avm(19, |activation, context, _this| -> Result<(), Error> {
let my_activation = Activation::from_nothing( let my_locals = activation.scope().locals().to_owned();
19,
avm.global_object_cell(),
context.gc_context,
*context.levels.get(&0).expect("_level0 in test"),
);
let my_locals = my_activation.scope().locals().to_owned();
my_locals my_locals
.set("value1", "string".into(), avm, context) .set("value1", "string".into(), activation, context)
.unwrap(); .unwrap();
my_locals.set("value2", 2.0.into(), avm, context).unwrap(); my_locals
.set("value2", 2.0.into(), activation, context)
avm.insert_stack_frame(GcCell::allocate(context.gc_context, my_activation)); .unwrap();
let my_local_values = activation.locals_into_form_values(context);
let my_local_values = avm.locals_into_form_values(context);
assert_eq!(my_local_values.len(), 2); assert_eq!(my_local_values.len(), 2);
assert_eq!(my_local_values.get("value1"), Some(&"string".to_string())); assert_eq!(my_local_values.get("value1"), Some(&"string".to_string()));

View File

@ -1,6 +1,7 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::value_object::ValueObject; use crate::avm1::value_object::ValueObject;
use crate::avm1::{Avm1, Object, TObject, UpdateContext}; use crate::avm1::{Object, TObject, UpdateContext};
use std::borrow::Cow; use std::borrow::Cow;
use std::f64::NAN; use std::f64::NAN;
@ -157,19 +158,19 @@ impl<'gc> Value<'gc> {
/// * In SWF5 and lower, hexadecimal is unsupported. /// * In SWF5 and lower, hexadecimal is unsupported.
fn primitive_as_number( fn primitive_as_number(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
) -> f64 { ) -> f64 {
match self { match self {
Value::Undefined if avm.current_swf_version() < 7 => 0.0, Value::Undefined if activation.current_swf_version() < 7 => 0.0,
Value::Null if avm.current_swf_version() < 7 => 0.0, Value::Null if activation.current_swf_version() < 7 => 0.0,
Value::Undefined => NAN, Value::Undefined => NAN,
Value::Null => NAN, Value::Null => NAN,
Value::Bool(false) => 0.0, Value::Bool(false) => 0.0,
Value::Bool(true) => 1.0, Value::Bool(true) => 1.0,
Value::Number(v) => *v, Value::Number(v) => *v,
Value::String(v) => match v.as_str() { Value::String(v) => match v.as_str() {
v if avm.current_swf_version() >= 6 && v.starts_with("0x") => { v if activation.current_swf_version() >= 6 && v.starts_with("0x") => {
let mut n: u32 = 0; let mut n: u32 = 0;
for c in v[2..].bytes() { for c in v[2..].bytes() {
n = n.wrapping_shl(4); n = n.wrapping_shl(4);
@ -195,7 +196,7 @@ impl<'gc> Value<'gc> {
} }
f64::from(n as i32) f64::from(n as i32)
} }
v if avm.current_swf_version() >= 6 v if activation.current_swf_version() >= 6
&& (v.starts_with('0') || v.starts_with("+0") || v.starts_with("-0")) && (v.starts_with('0') || v.starts_with("+0") || v.starts_with("-0"))
&& v[1..].bytes().all(|c| c >= b'0' && c <= b'7') => && v[1..].bytes().all(|c| c >= b'0' && c <= b'7') =>
{ {
@ -223,14 +224,14 @@ impl<'gc> Value<'gc> {
/// ECMA-262 2nd edition s. 9.3 ToNumber /// ECMA-262 2nd edition s. 9.3 ToNumber
pub fn coerce_to_f64( pub fn coerce_to_f64(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<f64, Error<'gc>> { ) -> Result<f64, Error<'gc>> {
Ok(match self { Ok(match self {
Value::Object(_) => self Value::Object(_) => self
.to_primitive_num(avm, context)? .to_primitive_num(activation, context)?
.primitive_as_number(avm, context), .primitive_as_number(activation, context),
val => val.primitive_as_number(avm, context), val => val.primitive_as_number(activation, context),
}) })
} }
@ -247,11 +248,11 @@ impl<'gc> Value<'gc> {
/// return `undefined` rather than yielding a runtime error. /// return `undefined` rather than yielding a runtime error.
pub fn to_primitive_num( pub fn to_primitive_num(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(match self { Ok(match self {
Value::Object(object) => object.call_method("valueOf", &[], avm, context)?, Value::Object(object) => object.call_method("valueOf", &[], activation, context)?,
val => val.to_owned(), val => val.to_owned(),
}) })
} }
@ -261,18 +262,18 @@ impl<'gc> Value<'gc> {
pub fn abstract_lt( pub fn abstract_lt(
&self, &self,
other: Value<'gc>, other: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let prim_self = self.to_primitive_num(avm, context)?; let prim_self = self.to_primitive_num(activation, context)?;
let prim_other = other.to_primitive_num(avm, context)?; let prim_other = other.to_primitive_num(activation, context)?;
if let (Value::String(a), Value::String(b)) = (&prim_self, &prim_other) { if let (Value::String(a), Value::String(b)) = (&prim_self, &prim_other) {
return Ok(a.to_string().bytes().lt(b.to_string().bytes()).into()); return Ok(a.to_string().bytes().lt(b.to_string().bytes()).into());
} }
let num_self = prim_self.primitive_as_number(avm, context); let num_self = prim_self.primitive_as_number(activation, context);
let num_other = prim_other.primitive_as_number(avm, context); let num_other = prim_other.primitive_as_number(activation, context);
if num_self.is_nan() || num_other.is_nan() { if num_self.is_nan() || num_other.is_nan() {
return Ok(Value::Undefined); return Ok(Value::Undefined);
@ -301,7 +302,7 @@ impl<'gc> Value<'gc> {
pub fn abstract_eq( pub fn abstract_eq(
&self, &self,
other: Value<'gc>, other: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
coerced: bool, coerced: bool,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -327,62 +328,62 @@ impl<'gc> Value<'gc> {
(Value::Bool(a), Value::Bool(b)) => Ok((a == b).into()), (Value::Bool(a), Value::Bool(b)) => Ok((a == b).into()),
(Value::Object(a), Value::Object(b)) => Ok(Object::ptr_eq(*a, *b).into()), (Value::Object(a), Value::Object(b)) => Ok(Object::ptr_eq(*a, *b).into()),
(Value::Object(a), Value::Null) | (Value::Object(a), Value::Undefined) => { (Value::Object(a), Value::Null) | (Value::Object(a), Value::Undefined) => {
Ok(Object::ptr_eq(*a, avm.global_object_cell()).into()) Ok(Object::ptr_eq(*a, activation.avm().global_object_cell()).into())
} }
(Value::Null, Value::Object(b)) | (Value::Undefined, Value::Object(b)) => { (Value::Null, Value::Object(b)) | (Value::Undefined, Value::Object(b)) => {
Ok(Object::ptr_eq(*b, avm.global_object_cell()).into()) Ok(Object::ptr_eq(*b, activation.avm().global_object_cell()).into())
} }
(Value::Undefined, Value::Null) => Ok(true.into()), (Value::Undefined, Value::Null) => Ok(true.into()),
(Value::Null, Value::Undefined) => Ok(true.into()), (Value::Null, Value::Undefined) => Ok(true.into()),
(Value::Number(_), Value::String(_)) => Ok(self.abstract_eq( (Value::Number(_), Value::String(_)) => Ok(self.abstract_eq(
Value::Number(other.coerce_to_f64(avm, context)?), Value::Number(other.coerce_to_f64(activation, context)?),
avm, activation,
context, context,
true, true,
)?), )?),
(Value::String(_), Value::Number(_)) => { (Value::String(_), Value::Number(_)) => {
Ok(Value::Number(self.coerce_to_f64(avm, context)?) Ok(Value::Number(self.coerce_to_f64(activation, context)?)
.abstract_eq(other, avm, context, true)?) .abstract_eq(other, activation, context, true)?)
} }
(Value::Bool(_), _) => Ok(Value::Number(self.coerce_to_f64(avm, context)?) (Value::Bool(_), _) => Ok(Value::Number(self.coerce_to_f64(activation, context)?)
.abstract_eq(other, avm, context, true)?), .abstract_eq(other, activation, context, true)?),
(_, Value::Bool(_)) => Ok(self.abstract_eq( (_, Value::Bool(_)) => Ok(self.abstract_eq(
Value::Number(other.coerce_to_f64(avm, context)?), Value::Number(other.coerce_to_f64(activation, context)?),
avm, activation,
context, context,
true, true,
)?), )?),
(Value::String(_), Value::Object(_)) => { (Value::String(_), Value::Object(_)) => {
let non_obj_other = other.to_primitive_num(avm, context)?; let non_obj_other = other.to_primitive_num(activation, context)?;
if let Value::Object(_) = non_obj_other { if let Value::Object(_) = non_obj_other {
return Ok(false.into()); return Ok(false.into());
} }
Ok(self.abstract_eq(non_obj_other, avm, context, true)?) Ok(self.abstract_eq(non_obj_other, activation, context, true)?)
} }
(Value::Number(_), Value::Object(_)) => { (Value::Number(_), Value::Object(_)) => {
let non_obj_other = other.to_primitive_num(avm, context)?; let non_obj_other = other.to_primitive_num(activation, context)?;
if let Value::Object(_) = non_obj_other { if let Value::Object(_) = non_obj_other {
return Ok(false.into()); return Ok(false.into());
} }
Ok(self.abstract_eq(non_obj_other, avm, context, true)?) Ok(self.abstract_eq(non_obj_other, activation, context, true)?)
} }
(Value::Object(_), Value::String(_)) => { (Value::Object(_), Value::String(_)) => {
let non_obj_self = self.to_primitive_num(avm, context)?; let non_obj_self = self.to_primitive_num(activation, context)?;
if let Value::Object(_) = non_obj_self { if let Value::Object(_) = non_obj_self {
return Ok(false.into()); return Ok(false.into());
} }
Ok(non_obj_self.abstract_eq(other, avm, context, true)?) Ok(non_obj_self.abstract_eq(other, activation, context, true)?)
} }
(Value::Object(_), Value::Number(_)) => { (Value::Object(_), Value::Number(_)) => {
let non_obj_self = self.to_primitive_num(avm, context)?; let non_obj_self = self.to_primitive_num(activation, context)?;
if let Value::Object(_) = non_obj_self { if let Value::Object(_) = non_obj_self {
return Ok(false.into()); return Ok(false.into());
} }
Ok(non_obj_self.abstract_eq(other, avm, context, true)?) Ok(non_obj_self.abstract_eq(other, activation, context, true)?)
} }
_ => Ok(false.into()), _ => Ok(false.into()),
} }
@ -408,10 +409,11 @@ impl<'gc> Value<'gc> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn coerce_to_u16( pub fn coerce_to_u16(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<u16, Error<'gc>> { ) -> Result<u16, Error<'gc>> {
self.coerce_to_f64(avm, context).map(f64_to_wrapping_u16) self.coerce_to_f64(activation, context)
.map(f64_to_wrapping_u16)
} }
/// Coerce a number to an `i16` following the wrapping behavior ECMAScript specifications. /// Coerce a number to an `i16` following the wrapping behavior ECMAScript specifications.
@ -420,10 +422,11 @@ impl<'gc> Value<'gc> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn coerce_to_i16( pub fn coerce_to_i16(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<i16, Error<'gc>> { ) -> Result<i16, Error<'gc>> {
self.coerce_to_f64(avm, context).map(f64_to_wrapping_i16) self.coerce_to_f64(activation, context)
.map(f64_to_wrapping_i16)
} }
/// Coerce a number to an `i32` following the ECMAScript specifications for `ToInt32`. /// Coerce a number to an `i32` following the ECMAScript specifications for `ToInt32`.
@ -433,10 +436,11 @@ impl<'gc> Value<'gc> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn coerce_to_i32( pub fn coerce_to_i32(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<i32, Error<'gc>> { ) -> Result<i32, Error<'gc>> {
self.coerce_to_f64(avm, context).map(f64_to_wrapping_i32) self.coerce_to_f64(activation, context)
.map(f64_to_wrapping_i32)
} }
/// Coerce a number to an `u32` following the ECMAScript specifications for `ToUInt32`. /// Coerce a number to an `u32` following the ECMAScript specifications for `ToUInt32`.
@ -445,25 +449,28 @@ impl<'gc> Value<'gc> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn coerce_to_u32( pub fn coerce_to_u32(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<u32, Error<'gc>> { ) -> Result<u32, Error<'gc>> {
self.coerce_to_f64(avm, context).map(f64_to_wrapping_u32) self.coerce_to_f64(activation, context)
.map(f64_to_wrapping_u32)
} }
/// Coerce a value to a string. /// Coerce a value to a string.
pub fn coerce_to_string<'a>( pub fn coerce_to_string<'a>(
&'a self, &'a self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Cow<'a, str>, Error<'gc>> { ) -> Result<Cow<'a, str>, Error<'gc>> {
Ok(match self { Ok(match self {
Value::Object(object) => match object.call_method("toString", &[], avm, context)? { Value::Object(object) => {
Value::String(s) => Cow::Owned(s), match object.call_method("toString", &[], activation, context)? {
_ => Cow::Borrowed("[type Object]"), Value::String(s) => Cow::Owned(s),
}, _ => Cow::Borrowed("[type Object]"),
}
}
Value::Undefined => { Value::Undefined => {
if avm.current_swf_version() >= 7 { if activation.current_swf_version() >= 7 {
Cow::Borrowed("undefined") Cow::Borrowed("undefined")
} else { } else {
Cow::Borrowed("") Cow::Borrowed("")
@ -510,22 +517,22 @@ impl<'gc> Value<'gc> {
pub fn coerce_to_object( pub fn coerce_to_object(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Object<'gc> { ) -> Object<'gc> {
ValueObject::boxed(avm, context, self.to_owned()) ValueObject::boxed(activation, context, self.to_owned())
} }
pub fn call( pub fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Value::Object(object) = self { if let Value::Object(object) = self {
object.call(avm, context, this, base_proto, args) object.call(activation, context, this, base_proto, args)
} else { } else {
Ok(Value::Undefined) Ok(Value::Undefined)
} }
@ -593,48 +600,54 @@ pub fn f64_to_wrapping_i32(n: f64) -> i32 {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::globals::create_globals; use crate::avm1::globals::create_globals;
use crate::avm1::object::{Object, TObject}; use crate::avm1::object::{Object, TObject};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::script_object::ScriptObject; use crate::avm1::script_object::ScriptObject;
use crate::avm1::test_utils::with_avm; use crate::avm1::test_utils::with_avm;
use crate::avm1::{Avm1, Value}; use crate::avm1::Value;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use enumset::EnumSet; use enumset::EnumSet;
use std::f64::{INFINITY, NAN, NEG_INFINITY}; use std::f64::{INFINITY, NAN, NEG_INFINITY};
#[test] #[test]
fn to_primitive_num() { fn to_primitive_num() {
with_avm(6, |avm, context, _this| -> Result<(), Error> { with_avm(6, |activation, context, _this| -> Result<(), Error> {
let true_value = Value::Bool(true); let true_value = Value::Bool(true);
let undefined = Value::Undefined; let undefined = Value::Undefined;
let false_value = Value::Bool(false); let false_value = Value::Bool(false);
let null = Value::Null; let null = Value::Null;
assert_eq!( assert_eq!(
true_value.to_primitive_num(avm, context).unwrap(), true_value.to_primitive_num(activation, context).unwrap(),
true_value true_value
); );
assert_eq!(undefined.to_primitive_num(avm, context).unwrap(), undefined);
assert_eq!( assert_eq!(
false_value.to_primitive_num(avm, context).unwrap(), undefined.to_primitive_num(activation, context).unwrap(),
undefined
);
assert_eq!(
false_value.to_primitive_num(activation, context).unwrap(),
false_value false_value
); );
assert_eq!(null.to_primitive_num(avm, context).unwrap(), null); assert_eq!(null.to_primitive_num(activation, context).unwrap(), null);
let (protos, global, _) = create_globals(context.gc_context); let (protos, global, _) = create_globals(context.gc_context);
let vglobal = Value::Object(global); let vglobal = Value::Object(global);
assert_eq!(vglobal.to_primitive_num(avm, context).unwrap(), undefined); assert_eq!(
vglobal.to_primitive_num(activation, context).unwrap(),
undefined
);
fn value_of_impl<'gc>( fn value_of_impl<'gc>(
_: &mut Avm1<'gc>, _: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>, _: &mut UpdateContext<'_, 'gc, '_>,
_: Object<'gc>, _: Object<'gc>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
Ok(5.0.into()) Ok(5.0.into())
} }
@ -654,7 +667,9 @@ mod test {
); );
assert_eq!( assert_eq!(
Value::Object(o).to_primitive_num(avm, context).unwrap(), Value::Object(o)
.to_primitive_num(activation, context)
.unwrap(),
Value::Number(5.0) Value::Number(5.0)
); );
@ -665,20 +680,20 @@ mod test {
#[test] #[test]
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
fn to_number_swf7() { fn to_number_swf7() {
with_avm(7, |avm, context, _this| -> Result<(), Error> { with_avm(7, |activation, context, _this| -> Result<(), Error> {
let t = Value::Bool(true); let t = Value::Bool(true);
let u = Value::Undefined; let u = Value::Undefined;
let f = Value::Bool(false); let f = Value::Bool(false);
let n = Value::Null; let n = Value::Null;
assert_eq!(t.coerce_to_f64(avm, context).unwrap(), 1.0); assert_eq!(t.coerce_to_f64(activation, context).unwrap(), 1.0);
assert!(u.coerce_to_f64(avm, context).unwrap().is_nan()); assert!(u.coerce_to_f64(activation, context).unwrap().is_nan());
assert_eq!(f.coerce_to_f64(avm, context).unwrap(), 0.0); assert_eq!(f.coerce_to_f64(activation, context).unwrap(), 0.0);
assert!(n.coerce_to_f64(avm, context).unwrap().is_nan()); assert!(n.coerce_to_f64(activation, context).unwrap().is_nan());
let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into()); let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into());
assert!(bo.coerce_to_f64(avm, context).unwrap().is_nan()); assert!(bo.coerce_to_f64(activation, context).unwrap().is_nan());
Ok(()) Ok(())
}); });
@ -687,20 +702,20 @@ mod test {
#[test] #[test]
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
fn to_number_swf6() { fn to_number_swf6() {
with_avm(6, |avm, context, _this| -> Result<(), Error> { with_avm(6, |activation, context, _this| -> Result<(), Error> {
let t = Value::Bool(true); let t = Value::Bool(true);
let u = Value::Undefined; let u = Value::Undefined;
let f = Value::Bool(false); let f = Value::Bool(false);
let n = Value::Null; let n = Value::Null;
assert_eq!(t.coerce_to_f64(avm, context).unwrap(), 1.0); assert_eq!(t.coerce_to_f64(activation, context).unwrap(), 1.0);
assert_eq!(u.coerce_to_f64(avm, context).unwrap(), 0.0); assert_eq!(u.coerce_to_f64(activation, context).unwrap(), 0.0);
assert_eq!(f.coerce_to_f64(avm, context).unwrap(), 0.0); assert_eq!(f.coerce_to_f64(activation, context).unwrap(), 0.0);
assert_eq!(n.coerce_to_f64(avm, context).unwrap(), 0.0); assert_eq!(n.coerce_to_f64(activation, context).unwrap(), 0.0);
let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into()); let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into());
assert_eq!(bo.coerce_to_f64(avm, context).unwrap(), 0.0); assert_eq!(bo.coerce_to_f64(activation, context).unwrap(), 0.0);
Ok(()) Ok(())
}); });
@ -708,27 +723,36 @@ mod test {
#[test] #[test]
fn abstract_lt_num() { fn abstract_lt_num() {
with_avm(8, |avm, context, _this| -> Result<(), Error> { with_avm(8, |activation, context, _this| -> Result<(), Error> {
let a = Value::Number(1.0); let a = Value::Number(1.0);
let b = Value::Number(2.0); let b = Value::Number(2.0);
assert_eq!(a.abstract_lt(b, avm, context).unwrap(), Value::Bool(true)); assert_eq!(
a.abstract_lt(b, activation, context).unwrap(),
Value::Bool(true)
);
let nan = Value::Number(NAN); let nan = Value::Number(NAN);
assert_eq!(a.abstract_lt(nan, avm, context).unwrap(), Value::Undefined); assert_eq!(
a.abstract_lt(nan, activation, context).unwrap(),
Value::Undefined
);
let inf = Value::Number(INFINITY); let inf = Value::Number(INFINITY);
assert_eq!(a.abstract_lt(inf, avm, context).unwrap(), Value::Bool(true)); assert_eq!(
a.abstract_lt(inf, activation, context).unwrap(),
Value::Bool(true)
);
let neg_inf = Value::Number(NEG_INFINITY); let neg_inf = Value::Number(NEG_INFINITY);
assert_eq!( assert_eq!(
a.abstract_lt(neg_inf, avm, context).unwrap(), a.abstract_lt(neg_inf, activation, context).unwrap(),
Value::Bool(false) Value::Bool(false)
); );
let zero = Value::Number(0.0); let zero = Value::Number(0.0);
assert_eq!( assert_eq!(
a.abstract_lt(zero, avm, context).unwrap(), a.abstract_lt(zero, activation, context).unwrap(),
Value::Bool(false) Value::Bool(false)
); );
@ -738,36 +762,36 @@ mod test {
#[test] #[test]
fn abstract_gt_num() { fn abstract_gt_num() {
with_avm(8, |avm, context, _this| -> Result<(), Error> { with_avm(8, |activation, context, _this| -> Result<(), Error> {
let a = Value::Number(1.0); let a = Value::Number(1.0);
let b = Value::Number(2.0); let b = Value::Number(2.0);
assert_eq!( assert_eq!(
b.abstract_lt(a.clone(), avm, context).unwrap(), b.abstract_lt(a.clone(), activation, context).unwrap(),
Value::Bool(false) Value::Bool(false)
); );
let nan = Value::Number(NAN); let nan = Value::Number(NAN);
assert_eq!( assert_eq!(
nan.abstract_lt(a.clone(), avm, context).unwrap(), nan.abstract_lt(a.clone(), activation, context).unwrap(),
Value::Undefined Value::Undefined
); );
let inf = Value::Number(INFINITY); let inf = Value::Number(INFINITY);
assert_eq!( assert_eq!(
inf.abstract_lt(a.clone(), avm, context).unwrap(), inf.abstract_lt(a.clone(), activation, context).unwrap(),
Value::Bool(false) Value::Bool(false)
); );
let neg_inf = Value::Number(NEG_INFINITY); let neg_inf = Value::Number(NEG_INFINITY);
assert_eq!( assert_eq!(
neg_inf.abstract_lt(a.clone(), avm, context).unwrap(), neg_inf.abstract_lt(a.clone(), activation, context).unwrap(),
Value::Bool(true) Value::Bool(true)
); );
let zero = Value::Number(0.0); let zero = Value::Number(0.0);
assert_eq!( assert_eq!(
zero.abstract_lt(a, avm, context).unwrap(), zero.abstract_lt(a, activation, context).unwrap(),
Value::Bool(true) Value::Bool(true)
); );
@ -777,11 +801,14 @@ mod test {
#[test] #[test]
fn abstract_lt_str() { fn abstract_lt_str() {
with_avm(8, |avm, context, _this| -> Result<(), Error> { with_avm(8, |activation, context, _this| -> Result<(), Error> {
let a = Value::String("a".to_owned()); let a = Value::String("a".to_owned());
let b = Value::String("b".to_owned()); let b = Value::String("b".to_owned());
assert_eq!(a.abstract_lt(b, avm, context).unwrap(), Value::Bool(true)); assert_eq!(
a.abstract_lt(b, activation, context).unwrap(),
Value::Bool(true)
);
Ok(()) Ok(())
}) })
@ -789,11 +816,14 @@ mod test {
#[test] #[test]
fn abstract_gt_str() { fn abstract_gt_str() {
with_avm(8, |avm, context, _this| -> Result<(), Error> { with_avm(8, |activation, context, _this| -> Result<(), Error> {
let a = Value::String("a".to_owned()); let a = Value::String("a".to_owned());
let b = Value::String("b".to_owned()); let b = Value::String("b".to_owned());
assert_eq!(b.abstract_lt(a, avm, context).unwrap(), Value::Bool(false)); assert_eq!(
b.abstract_lt(a, activation, context).unwrap(),
Value::Bool(false)
);
Ok(()) Ok(())
}) })

View File

@ -1,11 +1,11 @@
//! Object impl for boxed values //! Object impl for boxed values
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::{ObjectPtr, TObject}; use crate::avm1::object::{ObjectPtr, TObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::borrow::Cow; use std::borrow::Cow;
@ -40,7 +40,7 @@ impl<'gc> ValueObject<'gc> {
/// If a class exists for a given value type, this function automatically /// If a class exists for a given value type, this function automatically
/// selects the correct prototype for it from the system prototypes list. /// selects the correct prototype for it from the system prototypes list.
pub fn boxed( pub fn boxed(
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
value: Value<'gc>, value: Value<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
@ -48,9 +48,9 @@ impl<'gc> ValueObject<'gc> {
ob ob
} else { } else {
let proto = match &value { let proto = match &value {
Value::Bool(_) => Some(avm.prototypes.boolean), Value::Bool(_) => Some(activation.avm().prototypes.boolean),
Value::Number(_) => Some(avm.prototypes.number), Value::Number(_) => Some(activation.avm().prototypes.number),
Value::String(_) => Some(avm.prototypes.string), Value::String(_) => Some(activation.avm().prototypes.string),
_ => None, _ => None,
}; };
@ -65,16 +65,28 @@ impl<'gc> ValueObject<'gc> {
// Constructor populates the boxed object with the value. // Constructor populates the boxed object with the value.
match &value { match &value {
Value::Bool(_) => { Value::Bool(_) => {
let _ = let _ = crate::avm1::globals::boolean::boolean(
crate::avm1::globals::boolean::boolean(avm, context, obj.into(), &[value]); activation,
context,
obj.into(),
&[value],
);
} }
Value::Number(_) => { Value::Number(_) => {
let _ = let _ = crate::avm1::globals::number::number(
crate::avm1::globals::number::number(avm, context, obj.into(), &[value]); activation,
context,
obj.into(),
&[value],
);
} }
Value::String(_) => { Value::String(_) => {
let _ = let _ = crate::avm1::globals::string::string(
crate::avm1::globals::string::string(avm, context, obj.into(), &[value]); activation,
context,
obj.into(),
&[value],
);
} }
_ => (), _ => (),
} }
@ -123,26 +135,29 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.0.read().base.get_local(name, avm, context, this) self.0
.read()
.base
.get_local(name, activation, context, this)
} }
fn set( fn set(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.0.read().base.set(name, value, avm, context) self.0.read().base.set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
@ -151,27 +166,26 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
self.0 self.0
.read() .read()
.base .base
.call(avm, context, this, base_proto, args) .call(activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> {
self.0 self.0
.read() .read()
.base .base
.call_setter(name, value, avm, context, this) .call_setter(name, value, activation, context)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -181,11 +195,11 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().base.delete(avm, gc_context, name) self.0.read().base.delete(activation, gc_context, name)
} }
fn add_property( fn add_property(
@ -204,7 +218,7 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -214,7 +228,7 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
self.0 self.0
.read() .read()
.base .base
.add_property_with_case(avm, gc_context, name, get, set, attributes) .add_property_with_case(activation, gc_context, name, get, set, attributes)
} }
fn define_value( fn define_value(
@ -258,41 +272,50 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().base.has_property(avm, context, name) self.0.read().base.has_property(activation, context, name)
} }
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().base.has_own_property(avm, context, name) self.0
.read()
.base
.has_own_property(activation, context, name)
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.0.read().base.has_own_virtual(avm, context, name) self.0
.read()
.base
.has_own_virtual(activation, context, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0.read().base.is_property_overwritable(avm, name) self.0
.read()
.base
.is_property_overwritable(activation, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0.read().base.is_property_enumerable(avm, name) self.0.read().base.is_property_enumerable(activation, name)
} }
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.0.read().base.get_keys(avm) self.0.read().base.get_keys(activation)
} }
fn as_string(&self) -> Cow<str> { fn as_string(&self) -> Cow<str> {

View File

@ -1,11 +1,11 @@
//! AVM1 object type to represent the attributes of XML nodes //! AVM1 object type to represent the attributes of XML nodes
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::{ObjectPtr, TObject}; use crate::avm1::object::{ObjectPtr, TObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
use crate::xml::{XMLName, XMLNode}; use crate::xml::{XMLName, XMLNode};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Collect, MutationContext}; use gc_arena::{Collect, MutationContext};
@ -60,7 +60,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
@ -75,61 +75,61 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.node().set_attribute_value( self.node().set_attribute_value(
context.gc_context, context.gc_context,
&XMLName::from_str(name), &XMLName::from_str(name),
&value.coerce_to_string(avm, context)?, &value.coerce_to_string(activation, context)?,
); );
self.base().set(name, value, avm, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base().call(avm, context, this, base_proto, args) self.base()
.call(activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> { self.base().call_setter(name, value, activation, context)
self.base().call_setter(name, value, avm, context, this)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
//TODO: `new xmlnode.attributes()` returns undefined, not an object //TODO: `new xmlnode.attributes()` returns undefined, not an object
log::warn!("Cannot create new XML Attributes object"); log::warn!("Cannot create new XML Attributes object");
Ok(Value::Undefined.coerce_to_object(avm, context)) Ok(Value::Undefined.coerce_to_object(activation, context))
} }
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.node() self.node()
.delete_attribute(gc_context, &XMLName::from_str(name)); .delete_attribute(gc_context, &XMLName::from_str(name));
self.base().delete(avm, gc_context, name) self.base().delete(activation, gc_context, name)
} }
fn add_property( fn add_property(
@ -146,7 +146,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -154,7 +154,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
attributes: EnumSet<Attribute>, attributes: EnumSet<Attribute>,
) { ) {
self.base() self.base()
.add_property_with_case(avm, gc_context, name, get, set, attributes) .add_property_with_case(activation, gc_context, name, get, set, attributes)
} }
fn define_value( fn define_value(
@ -189,16 +189,16 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_property(avm, context, name) self.base().has_property(activation, context, name)
} }
fn has_own_property( fn has_own_property(
&self, &self,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
@ -209,23 +209,23 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_own_virtual(avm, context, name) self.base().has_own_virtual(activation, context, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_overwritable(avm, name) self.base().is_property_overwritable(activation, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(avm, name) self.base().is_property_enumerable(activation, name)
} }
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(avm) self.base().get_keys(activation)
} }
fn as_string(&self) -> Cow<str> { fn as_string(&self) -> Cow<str> {

View File

@ -1,11 +1,11 @@
//! AVM1 object type to represent the attributes of XML nodes //! AVM1 object type to represent the attributes of XML nodes
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::{ObjectPtr, TObject}; use crate::avm1::object::{ObjectPtr, TObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
use crate::xml::{XMLDocument, XMLNode}; use crate::xml::{XMLDocument, XMLNode};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Collect, MutationContext}; use gc_arena::{Collect, MutationContext};
@ -60,16 +60,19 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(mut node) = self.document().get_node_by_id(name) { if let Some(mut node) = self.document().get_node_by_id(name) {
Ok(node Ok(node
.script_object(context.gc_context, Some(avm.prototypes().xml_node)) .script_object(
context.gc_context,
Some(activation.avm().prototypes().xml_node),
)
.into()) .into())
} else { } else {
self.base().get_local(name, avm, context, this) self.base().get_local(name, activation, context, this)
} }
} }
@ -77,54 +80,54 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base().set(name, value, avm, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base().call(avm, context, this, base_proto, args) self.base()
.call(activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> { self.base().call_setter(name, value, activation, context)
self.base().call_setter(name, value, avm, context, this)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
//TODO: `new xmlnode.attributes()` returns undefined, not an object //TODO: `new xmlnode.attributes()` returns undefined, not an object
log::warn!("Cannot create new XML Attributes object"); log::warn!("Cannot create new XML Attributes object");
Ok(Value::Undefined.coerce_to_object(avm, context)) Ok(Value::Undefined.coerce_to_object(activation, context))
} }
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().delete(avm, gc_context, name) self.base().delete(activation, gc_context, name)
} }
fn add_property( fn add_property(
@ -141,7 +144,7 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -149,7 +152,7 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
attributes: EnumSet<Attribute>, attributes: EnumSet<Attribute>,
) { ) {
self.base() self.base()
.add_property_with_case(avm, gc_context, name, get, set, attributes) .add_property_with_case(activation, gc_context, name, get, set, attributes)
} }
fn define_value( fn define_value(
@ -184,42 +187,42 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_property(avm, context, name) self.base().has_property(activation, context, name)
} }
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.document().get_node_by_id(name).is_some() self.document().get_node_by_id(name).is_some()
|| self.base().has_own_property(avm, context, name) || self.base().has_own_property(activation, context, name)
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_own_virtual(avm, context, name) self.base().has_own_virtual(activation, context, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_overwritable(avm, name) self.base().is_property_overwritable(activation, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(avm, name) self.base().is_property_enumerable(activation, name)
} }
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
let mut keys = self.base().get_keys(avm); let mut keys = self.base().get_keys(activation);
keys.extend(self.document().get_node_ids().into_iter()); keys.extend(self.document().get_node_ids().into_iter());
keys keys
} }

View File

@ -1,11 +1,11 @@
//! AVM1 object type to represent XML nodes //! AVM1 object type to represent XML nodes
use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::Executable; use crate::avm1::function::Executable;
use crate::avm1::object::{ObjectPtr, TObject}; use crate::avm1::object::{ObjectPtr, TObject};
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue; use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
use crate::xml::{XMLDocument, XMLNode}; use crate::xml::{XMLDocument, XMLNode};
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::{Collect, MutationContext}; use gc_arena::{Collect, MutationContext};
@ -61,49 +61,49 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
fn get_local( fn get_local(
&self, &self,
name: &str, name: &str,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base().get_local(name, avm, context, this) self.base().get_local(name, activation, context, this)
} }
fn set( fn set(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
self.base().set(name, value, avm, context) self.base().set(name, value, activation, context)
} }
fn call( fn call(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
self.base().call(avm, context, this, base_proto, args) self.base()
.call(activation, context, this, base_proto, args)
} }
fn call_setter( fn call_setter(
&self, &self,
name: &str, name: &str,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, ) -> Option<Executable<'gc>> {
) -> Result<ReturnValue<'gc>, Error<'gc>> { self.base().call_setter(name, value, activation, context)
self.base().call_setter(name, value, avm, context, this)
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
fn new( fn new(
&self, &self,
_avm: &mut Avm1<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -113,11 +113,11 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
fn delete( fn delete(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().delete(avm, gc_context, name) self.base().delete(activation, gc_context, name)
} }
fn add_property( fn add_property(
@ -134,7 +134,7 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
fn add_property_with_case( fn add_property_with_case(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
name: &str, name: &str,
get: Executable<'gc>, get: Executable<'gc>,
@ -142,7 +142,7 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
attributes: EnumSet<Attribute>, attributes: EnumSet<Attribute>,
) { ) {
self.base() self.base()
.add_property_with_case(avm, gc_context, name, get, set, attributes) .add_property_with_case(activation, gc_context, name, get, set, attributes)
} }
fn define_value( fn define_value(
@ -177,41 +177,41 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
fn has_property( fn has_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_property(avm, context, name) self.base().has_property(activation, context, name)
} }
fn has_own_property( fn has_own_property(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_own_property(avm, context, name) self.base().has_own_property(activation, context, name)
} }
fn has_own_virtual( fn has_own_virtual(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
name: &str, name: &str,
) -> bool { ) -> bool {
self.base().has_own_virtual(avm, context, name) self.base().has_own_virtual(activation, context, name)
} }
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_overwritable(avm, name) self.base().is_property_overwritable(activation, name)
} }
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(avm, name) self.base().is_property_enumerable(activation, name)
} }
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> { fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(avm) self.base().get_keys(activation)
} }
fn as_string(&self) -> Cow<str> { fn as_string(&self) -> Cow<str> {

View File

@ -20,6 +20,7 @@ mod morph_shape;
mod movie_clip; mod movie_clip;
mod text; mod text;
use crate::avm1::activation::Activation;
use crate::events::{ClipEvent, ClipEventResult}; use crate::events::{ClipEvent, ClipEventResult};
pub use bitmap::Bitmap; pub use bitmap::Bitmap;
pub use button::Button; pub use button::Button;
@ -913,7 +914,7 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>>
fn bind_text_field_variables( fn bind_text_field_variables(
&self, &self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
// Check all unbound text fields to see if they apply to this object. // Check all unbound text fields to see if they apply to this object.
@ -921,7 +922,9 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>>
let mut i = 0; let mut i = 0;
let mut len = context.unbound_text_fields.len(); let mut len = context.unbound_text_fields.len();
while i < len { while i < len {
if context.unbound_text_fields[i].try_bind_text_field_variable(avm, context, false) { if context.unbound_text_fields[i]
.try_bind_text_field_variable(activation, context, false)
{
context.unbound_text_fields.swap_remove(i); context.unbound_text_fields.swap_remove(i);
len -= 1; len -= 1;
} else { } else {

View File

@ -1,4 +1,5 @@
//! `EditText` display object and support code. //! `EditText` display object and support code.
use crate::avm1::activation::Activation;
use crate::avm1::globals::text_field::attach_virtual_properties; use crate::avm1::globals::text_field::attach_virtual_properties;
use crate::avm1::{Avm1, Object, StageObject, TObject, Value}; use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
use crate::context::{RenderContext, UpdateContext}; use crate::context::{RenderContext, UpdateContext};
@ -502,7 +503,7 @@ impl<'gc> EditText<'gc> {
pub fn set_variable( pub fn set_variable(
self, self,
variable: Option<String>, variable: Option<String>,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
// Clear previous binding. // Clear previous binding.
@ -526,7 +527,7 @@ impl<'gc> EditText<'gc> {
let _ = self.set_text(text, context); let _ = self.set_text(text, context);
self.0.write(context.gc_context).variable = variable; self.0.write(context.gc_context).variable = variable;
self.try_bind_text_field_variable(avm, context, true); self.try_bind_text_field_variable(activation, context, true);
} }
/// Construct a base text transform for this `EditText`, to be used for /// Construct a base text transform for this `EditText`, to be used for
@ -698,7 +699,7 @@ impl<'gc> EditText<'gc> {
/// This is called when the text field is created, and, if the text field is in the unbound list, anytime a display object is created. /// This is called when the text field is created, and, if the text field is in the unbound list, anytime a display object is created.
pub fn try_bind_text_field_variable( pub fn try_bind_text_field_variable(
self, self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
set_initial_value: bool, set_initial_value: bool,
) -> bool { ) -> bool {
@ -714,41 +715,46 @@ impl<'gc> EditText<'gc> {
let parent = self.parent().unwrap(); let parent = self.parent().unwrap();
avm.insert_stack_frame_for_display_object( activation.run_with_child_frame_for_display_object(
parent, parent,
context.swf.header().version, context.swf.header().version,
context, context,
); |activation, context| {
if let Ok(Some((object, property))) =
activation.resolve_text_field_variable_path(context, parent, &variable)
{
// If this text field was just created, we immediately propagate the text to the variable (or vice versa).
if set_initial_value {
// If the property exists on the object, we overwrite the text with the property's value.
if object.has_property(activation, context, property) {
let value = object.get(property, activation, context).unwrap();
let _ = self.set_text(
value
.coerce_to_string(activation, context)
.unwrap_or_default()
.into_owned(),
context,
);
} else {
// Otherwise, we initialize the proprty with the text field's text.
let _ =
object.set(property, self.text().into(), activation, context);
}
}
if let Ok(Some((object, property))) = if let Some(stage_object) = object.as_stage_object() {
avm.resolve_text_field_variable_path(context, parent, &variable) self.0.write(context.gc_context).bound_stage_object =
{ Some(stage_object);
// If this text field was just created, we immediately propagate the text to the variable (or vice versa). stage_object.register_text_field_binding(
if set_initial_value { context.gc_context,
// If the property exists on the object, we overwrite the text with the property's value. self,
if object.has_property(avm, context, property) { property,
let value = object.get(property, avm, context).unwrap(); );
let _ = self.set_text( bound = true;
value }
.coerce_to_string(avm, context)
.unwrap_or_default()
.into_owned(),
context,
);
} else {
// Otherwise, we initialize the proprty with the text field's text.
let _ = object.set(property, self.text().into(), avm, context);
} }
} },
);
if let Some(stage_object) = object.as_stage_object() {
self.0.write(context.gc_context).bound_stage_object = Some(stage_object);
stage_object.register_text_field_binding(context.gc_context, self, property);
bound = true;
}
}
avm.retire_stack_frame(context, Value::Undefined);
} }
bound bound
@ -765,7 +771,7 @@ impl<'gc> EditText<'gc> {
/// ///
pub fn propagate_text_binding( pub fn propagate_text_binding(
self, self,
avm: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) { ) {
if !self.0.read().firing_variable_binding { if !self.0.read().firing_variable_binding {
@ -776,7 +782,7 @@ impl<'gc> EditText<'gc> {
let variable_path = variable.to_string(); let variable_path = variable.to_string();
drop(variable); drop(variable);
if let Ok(Some((object, property))) = avm.resolve_text_field_variable_path( if let Ok(Some((object, property))) = activation.resolve_text_field_variable_path(
context, context,
self.parent().unwrap(), self.parent().unwrap(),
&variable_path, &variable_path,
@ -791,13 +797,14 @@ impl<'gc> EditText<'gc> {
// Note that this can call virtual setters, even though the opposite direction won't work // Note that this can call virtual setters, even though the opposite direction won't work
// (virtual property changes do not affect the text field) // (virtual property changes do not affect the text field)
avm.insert_stack_frame_for_display_object( activation.run_with_child_frame_for_display_object(
self.parent().unwrap(), self.parent().unwrap(),
context.swf.header().version, context.swf.header().version,
context, context,
|activation, context| {
let _ = object.set(property, text.into(), activation, context);
},
); );
let _ = object.set(property, text.into(), avm, context);
avm.retire_stack_frame(context, Value::Undefined);
} }
} }
self.0.write(context.gc_context).firing_variable_binding = false; self.0.write(context.gc_context).firing_variable_binding = false;
@ -861,12 +868,18 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
drop(text); drop(text);
// If this text field has a variable set, initialize text field binding. // If this text field has a variable set, initialize text field binding.
if !self.try_bind_text_field_variable(avm, context, true) { avm.run_with_stack_frame_for_display_object(
context.unbound_text_fields.push(*self); (*self).into(),
} context.swf.version(),
context,
// People can bind to properties of TextFields the same as other display objects. |activation, context| {
self.bind_text_field_variables(avm, context); if !self.try_bind_text_field_variable(activation, context, true) {
context.unbound_text_fields.push(*self);
}
// People can bind to properties of TextFields the same as other display objects.
self.bind_text_field_variables(activation, context);
},
);
} }
fn object(&self) -> Value<'gc> { fn object(&self) -> Value<'gc> {

View File

@ -2,6 +2,7 @@
use crate::avm1::{Avm1, Object, StageObject, TObject, Value}; use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
use crate::backend::audio::AudioStreamHandle; use crate::backend::audio::AudioStreamHandle;
use crate::avm1::activation::Activation;
use crate::character::Character; use crate::character::Character;
use crate::context::{ActionType, RenderContext, UpdateContext}; use crate::context::{ActionType, RenderContext, UpdateContext};
use crate::display_object::{ use crate::display_object::{
@ -373,14 +374,12 @@ impl<'gc> MovieClip<'gc> {
) )
})?; })?;
avm.insert_stack_frame_for_init_action( avm.run_stack_frame_for_init_action(
*context.levels.get(&0).unwrap(), *context.levels.get(&0).unwrap(),
context.swf.header().version, context.swf.header().version,
slice, slice,
context, context,
); );
let frame = avm.current_stack_frame().unwrap();
let _ = avm.run_current_frame(context, frame);
Ok(()) Ok(())
} }
@ -976,10 +975,18 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
return Some(self_node); return Some(self_node);
} }
let object = self.object().coerce_to_object(avm, context); let mut activation = Activation::from_nothing(
avm,
context.swf.version(),
avm.global_object_cell(),
context.gc_context,
*context.levels.get(&0).unwrap(),
);
let object = self.object().coerce_to_object(&mut activation, context);
if ClipEvent::BUTTON_EVENT_METHODS if ClipEvent::BUTTON_EVENT_METHODS
.iter() .iter()
.any(|handler| object.has_property(avm, context, handler)) .any(|handler| object.has_property(&mut activation, context, handler))
{ {
return Some(self_node); return Some(self_node);
} }
@ -1033,11 +1040,18 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
// If we are running within the AVM, this must be an immediate action. // If we are running within the AVM, this must be an immediate action.
// If we are not, then this must be queued to be ran first-thing // If we are not, then this must be queued to be ran first-thing
if instantiated_from_avm && self.0.read().avm1_constructor.is_some() { if instantiated_from_avm && self.0.read().avm1_constructor.is_some() {
let constructor = self.0.read().avm1_constructor.unwrap(); let mut activation = Activation::from_nothing(
avm,
context.swf.version(),
avm.global_object_cell(),
context.gc_context,
*context.levels.get(&0).unwrap(),
);
let constructor = self.0.read().avm1_constructor.unwrap();
if let Ok(prototype) = constructor if let Ok(prototype) = constructor
.get("prototype", avm, context) .get("prototype", &mut activation, context)
.map(|v| v.coerce_to_object(avm, context)) .map(|v| v.coerce_to_object(&mut activation, context))
{ {
let object: Object<'gc> = StageObject::for_display_object( let object: Object<'gc> = StageObject::for_display_object(
context.gc_context, context.gc_context,
@ -1046,16 +1060,17 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
) )
.into(); .into();
if let Some(init_object) = init_object { if let Some(init_object) = init_object {
for key in init_object.get_keys(avm) { for key in init_object.get_keys(&mut activation) {
if let Ok(value) = init_object.get(&key, avm, context) { if let Ok(value) = init_object.get(&key, &mut activation, context) {
let _ = object.set(&key, value, avm, context); let _ = object.set(&key, value, &mut activation, context);
} }
} }
} }
self.0.write(context.gc_context).object = Some(object); self.0.write(context.gc_context).object = Some(object);
let _ = constructor.call(avm, context, object, None, &[]); let _ = constructor.call(&mut activation, context, object, None, &[]);
return;
} }
return;
} }
let object = StageObject::for_display_object( let object = StageObject::for_display_object(
@ -1064,9 +1079,17 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
Some(context.system_prototypes.movie_clip), Some(context.system_prototypes.movie_clip),
); );
if let Some(init_object) = init_object { if let Some(init_object) = init_object {
for key in init_object.get_keys(avm) { let mut activation = Activation::from_nothing(
if let Ok(value) = init_object.get(&key, avm, context) { avm,
let _ = object.set(&key, value, avm, context); context.swf.version(),
avm.global_object_cell(),
context.gc_context,
*context.levels.get(&0).unwrap(),
);
for key in init_object.get_keys(&mut activation) {
if let Ok(value) = init_object.get(&key, &mut activation, context) {
let _ = object.set(&key, value, &mut activation, context);
} }
} }
} }
@ -1093,7 +1116,15 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
); );
} }
self.bind_text_field_variables(avm, context); // If this text field has a variable set, initialize text field binding.
avm.run_with_stack_frame_for_display_object(
(*self).into(),
context.swf.version(),
context,
|activation, context| {
self.bind_text_field_variables(activation, context);
},
);
} }
fn object(&self) -> Value<'gc> { fn object(&self) -> Value<'gc> {

View File

@ -1,5 +1,6 @@
//! Classes that store formatting options //! Classes that store formatting options
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value}; use crate::avm1::activation::Activation;
use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::html::iterators::TextSpanIter; use crate::html::iterators::TextSpanIter;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
@ -85,57 +86,57 @@ pub struct TextFormat {
fn getstr_from_avm1_object<'gc>( fn getstr_from_avm1_object<'gc>(
object: Object<'gc>, object: Object<'gc>,
name: &str, name: &str,
avm1: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, 'gc, '_>, uc: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Option<String>, crate::avm1::error::Error<'gc>> { ) -> Result<Option<String>, crate::avm1::error::Error<'gc>> {
Ok(match object.get(name, avm1, uc)? { Ok(match object.get(name, activation, uc)? {
Value::Undefined => None, Value::Undefined => None,
Value::Null => None, Value::Null => None,
v => Some(v.coerce_to_string(avm1, uc)?.to_string()), v => Some(v.coerce_to_string(activation, uc)?.to_string()),
}) })
} }
fn getfloat_from_avm1_object<'gc>( fn getfloat_from_avm1_object<'gc>(
object: Object<'gc>, object: Object<'gc>,
name: &str, name: &str,
avm1: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, 'gc, '_>, uc: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Option<f64>, crate::avm1::error::Error<'gc>> { ) -> Result<Option<f64>, crate::avm1::error::Error<'gc>> {
Ok(match object.get(name, avm1, uc)? { Ok(match object.get(name, activation, uc)? {
Value::Undefined => None, Value::Undefined => None,
Value::Null => None, Value::Null => None,
v => Some(v.coerce_to_f64(avm1, uc)?), v => Some(v.coerce_to_f64(activation, uc)?),
}) })
} }
fn getbool_from_avm1_object<'gc>( fn getbool_from_avm1_object<'gc>(
object: Object<'gc>, object: Object<'gc>,
name: &str, name: &str,
avm1: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, 'gc, '_>, uc: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Option<bool>, crate::avm1::error::Error<'gc>> { ) -> Result<Option<bool>, crate::avm1::error::Error<'gc>> {
Ok(match object.get(name, avm1, uc)? { Ok(match object.get(name, activation, uc)? {
Value::Undefined => None, Value::Undefined => None,
Value::Null => None, Value::Null => None,
v => Some(v.as_bool(avm1.current_swf_version())), v => Some(v.as_bool(activation.current_swf_version())),
}) })
} }
fn getfloatarray_from_avm1_object<'gc>( fn getfloatarray_from_avm1_object<'gc>(
object: Object<'gc>, object: Object<'gc>,
name: &str, name: &str,
avm1: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, 'gc, '_>, uc: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Option<Vec<f64>>, crate::avm1::error::Error<'gc>> { ) -> Result<Option<Vec<f64>>, crate::avm1::error::Error<'gc>> {
Ok(match object.get(name, avm1, uc)? { Ok(match object.get(name, activation, uc)? {
Value::Undefined => None, Value::Undefined => None,
Value::Null => None, Value::Null => None,
v => { v => {
let mut output = Vec::new(); let mut output = Vec::new();
let v = v.coerce_to_object(avm1, uc); let v = v.coerce_to_object(activation, uc);
for i in 0..v.length() { for i in 0..v.length() {
output.push(v.array_element(i).coerce_to_f64(avm1, uc)?); output.push(v.array_element(i).coerce_to_f64(activation, uc)?);
} }
Some(output) Some(output)
@ -197,37 +198,38 @@ impl TextFormat {
/// Construct a `TextFormat` from an object that is /// Construct a `TextFormat` from an object that is
pub fn from_avm1_object<'gc>( pub fn from_avm1_object<'gc>(
object1: Object<'gc>, object1: Object<'gc>,
avm1: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, 'gc, '_>, uc: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Self, crate::avm1::error::Error<'gc>> { ) -> Result<Self, crate::avm1::error::Error<'gc>> {
Ok(Self { Ok(Self {
font: getstr_from_avm1_object(object1, "font", avm1, uc)?, font: getstr_from_avm1_object(object1, "font", activation, uc)?,
size: getfloat_from_avm1_object(object1, "size", avm1, uc)?, size: getfloat_from_avm1_object(object1, "size", activation, uc)?,
color: getfloat_from_avm1_object(object1, "color", avm1, uc)? color: getfloat_from_avm1_object(object1, "color", activation, uc)?
.map(|v| swf::Color::from_rgb(v as u32, 0xFF)), .map(|v| swf::Color::from_rgb(v as u32, 0xFF)),
align: getstr_from_avm1_object(object1, "align", avm1, uc)?.and_then(|v| { align: getstr_from_avm1_object(object1, "align", activation, uc)?.and_then(|v| match v
match v.to_lowercase().as_str() { .to_lowercase()
"left" => Some(swf::TextAlign::Left), .as_str()
"center" => Some(swf::TextAlign::Center), {
"right" => Some(swf::TextAlign::Right), "left" => Some(swf::TextAlign::Left),
"justify" => Some(swf::TextAlign::Justify), "center" => Some(swf::TextAlign::Center),
_ => None, "right" => Some(swf::TextAlign::Right),
} "justify" => Some(swf::TextAlign::Justify),
_ => None,
}), }),
bold: getbool_from_avm1_object(object1, "bold", avm1, uc)?, bold: getbool_from_avm1_object(object1, "bold", activation, uc)?,
italic: getbool_from_avm1_object(object1, "italic", avm1, uc)?, italic: getbool_from_avm1_object(object1, "italic", activation, uc)?,
underline: getbool_from_avm1_object(object1, "underline", avm1, uc)?, underline: getbool_from_avm1_object(object1, "underline", activation, uc)?,
left_margin: getfloat_from_avm1_object(object1, "leftMargin", avm1, uc)?, left_margin: getfloat_from_avm1_object(object1, "leftMargin", activation, uc)?,
right_margin: getfloat_from_avm1_object(object1, "rightMargin", avm1, uc)?, right_margin: getfloat_from_avm1_object(object1, "rightMargin", activation, uc)?,
indent: getfloat_from_avm1_object(object1, "indent", avm1, uc)?, indent: getfloat_from_avm1_object(object1, "indent", activation, uc)?,
block_indent: getfloat_from_avm1_object(object1, "blockIndent", avm1, uc)?, block_indent: getfloat_from_avm1_object(object1, "blockIndent", activation, uc)?,
kerning: getbool_from_avm1_object(object1, "kerning", avm1, uc)?, kerning: getbool_from_avm1_object(object1, "kerning", activation, uc)?,
leading: getfloat_from_avm1_object(object1, "leading", avm1, uc)?, leading: getfloat_from_avm1_object(object1, "leading", activation, uc)?,
letter_spacing: getfloat_from_avm1_object(object1, "letterSpacing", avm1, uc)?, letter_spacing: getfloat_from_avm1_object(object1, "letterSpacing", activation, uc)?,
tab_stops: getfloatarray_from_avm1_object(object1, "tabStops", avm1, uc)?, tab_stops: getfloatarray_from_avm1_object(object1, "tabStops", activation, uc)?,
bullet: getbool_from_avm1_object(object1, "bullet", avm1, uc)?, bullet: getbool_from_avm1_object(object1, "bullet", activation, uc)?,
url: getstr_from_avm1_object(object1, "url", avm1, uc)?, url: getstr_from_avm1_object(object1, "url", activation, uc)?,
target: getstr_from_avm1_object(object1, "target", avm1, uc)?, target: getstr_from_avm1_object(object1, "target", activation, uc)?,
}) })
} }
@ -350,21 +352,24 @@ impl TextFormat {
/// Construct a `TextFormat` AVM1 object from this text format object. /// Construct a `TextFormat` AVM1 object from this text format object.
pub fn as_avm1_object<'gc>( pub fn as_avm1_object<'gc>(
&self, &self,
avm1: &mut Avm1<'gc>, activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, 'gc, '_>, uc: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Object<'gc>, crate::avm1::error::Error<'gc>> { ) -> Result<Object<'gc>, crate::avm1::error::Error<'gc>> {
let object = ScriptObject::object(uc.gc_context, Some(avm1.prototypes().text_format)); let object = ScriptObject::object(
uc.gc_context,
Some(activation.avm().prototypes().text_format),
);
object.set( object.set(
"font", "font",
self.font.clone().map(|v| v.into()).unwrap_or(Value::Null), self.font.clone().map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"size", "size",
self.size.map(|v| v.into()).unwrap_or(Value::Null), self.size.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
@ -373,7 +378,7 @@ impl TextFormat {
.clone() .clone()
.map(|v| (((v.r as u32) << 16) + ((v.g as u32) << 8) + v.b as u32).into()) .map(|v| (((v.r as u32) << 16) + ((v.g as u32) << 8) + v.b as u32).into())
.unwrap_or(Value::Null), .unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
@ -389,90 +394,91 @@ impl TextFormat {
.into() .into()
}) })
.unwrap_or(Value::Null), .unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"bold", "bold",
self.bold.map(|v| v.into()).unwrap_or(Value::Null), self.bold.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"italic", "italic",
self.italic.map(|v| v.into()).unwrap_or(Value::Null), self.italic.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"underline", "underline",
self.underline.map(|v| v.into()).unwrap_or(Value::Null), self.underline.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"leftMargin", "leftMargin",
self.left_margin.map(|v| v.into()).unwrap_or(Value::Null), self.left_margin.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"rightMargin", "rightMargin",
self.right_margin.map(|v| v.into()).unwrap_or(Value::Null), self.right_margin.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"indent", "indent",
self.indent.map(|v| v.into()).unwrap_or(Value::Null), self.indent.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"blockIndent", "blockIndent",
self.block_indent.map(|v| v.into()).unwrap_or(Value::Null), self.block_indent.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"kerning", "kerning",
self.kerning.map(|v| v.into()).unwrap_or(Value::Null), self.kerning.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"leading", "leading",
self.leading.map(|v| v.into()).unwrap_or(Value::Null), self.leading.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"letterSpacing", "letterSpacing",
self.letter_spacing.map(|v| v.into()).unwrap_or(Value::Null), self.letter_spacing.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"bullet", "bullet",
self.bullet.map(|v| v.into()).unwrap_or(Value::Null), self.bullet.map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"url", "url",
self.url.clone().map(|v| v.into()).unwrap_or(Value::Null), self.url.clone().map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
object.set( object.set(
"target", "target",
self.target.clone().map(|v| v.into()).unwrap_or(Value::Null), self.target.clone().map(|v| v.into()).unwrap_or(Value::Null),
avm1, activation,
uc, uc,
)?; )?;
if let Some(ts) = &self.tab_stops { if let Some(ts) = &self.tab_stops {
let tab_stops = ScriptObject::array(uc.gc_context, Some(avm1.prototypes().array)); let tab_stops =
ScriptObject::array(uc.gc_context, Some(activation.avm().prototypes().array));
tab_stops.set_length(uc.gc_context, ts.len()); tab_stops.set_length(uc.gc_context, ts.len());
@ -480,9 +486,9 @@ impl TextFormat {
tab_stops.set_array_element(index, (*tab).into(), uc.gc_context); tab_stops.set_array_element(index, (*tab).into(), uc.gc_context);
} }
object.set("tabStops", tab_stops.into(), avm1, uc)?; object.set("tabStops", tab_stops.into(), activation, uc)?;
} else { } else {
object.set("tabStops", Value::Null, avm1, uc)?; object.set("tabStops", Value::Null, activation, uc)?;
} }
Ok(object.into()) Ok(object.into())

View File

@ -1,5 +1,6 @@
//! Management of async loaders //! Management of async loaders
use crate::avm1::activation::Activation;
use crate::avm1::{Object, TObject, Value}; use crate::avm1::{Object, TObject, Value};
use crate::backend::navigator::OwnedFuture; use crate::backend::navigator::OwnedFuture;
use crate::context::{ActionQueue, ActionType}; use crate::context::{ActionQueue, ActionType};
@ -314,7 +315,7 @@ impl<'gc> Loader<'gc> {
.replace_with_movie(uc.gc_context, None); .replace_with_movie(uc.gc_context, None);
if let Some(broadcaster) = broadcaster { if let Some(broadcaster) = broadcaster {
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
clip, clip,
broadcaster, broadcaster,
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
@ -322,7 +323,6 @@ impl<'gc> Loader<'gc> {
"broadcastMessage", "broadcastMessage",
&["onLoadStart".into(), Value::Object(broadcaster)], &["onLoadStart".into(), Value::Object(broadcaster)],
); );
avm.run_stack_till_empty(uc)?;
} }
Ok(()) Ok(())
@ -348,7 +348,7 @@ impl<'gc> Loader<'gc> {
}; };
if let Some(broadcaster) = broadcaster { if let Some(broadcaster) = broadcaster {
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
clip, clip,
broadcaster, broadcaster,
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
@ -361,7 +361,6 @@ impl<'gc> Loader<'gc> {
length.into(), length.into(),
], ],
); );
avm.run_stack_till_empty(uc)?;
} }
let mut mc = clip let mut mc = clip
@ -386,7 +385,7 @@ impl<'gc> Loader<'gc> {
} }
if let Some(broadcaster) = broadcaster { if let Some(broadcaster) = broadcaster {
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
clip, clip,
broadcaster, broadcaster,
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
@ -394,7 +393,6 @@ impl<'gc> Loader<'gc> {
"broadcastMessage", "broadcastMessage",
&["onLoadComplete".into(), Value::Object(broadcaster)], &["onLoadComplete".into(), Value::Object(broadcaster)],
); );
avm.run_stack_till_empty(uc)?;
} }
if let Some(Loader::Movie { load_complete, .. }) = if let Some(Loader::Movie { load_complete, .. }) =
@ -424,7 +422,7 @@ impl<'gc> Loader<'gc> {
}; };
if let Some(broadcaster) = broadcaster { if let Some(broadcaster) = broadcaster {
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
clip, clip,
broadcaster, broadcaster,
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
@ -436,7 +434,6 @@ impl<'gc> Loader<'gc> {
"LoadNeverCompleted".into(), "LoadNeverCompleted".into(),
], ],
); );
avm.run_stack_till_empty(uc)?;
} }
if let Some(Loader::Movie { load_complete, .. }) = if let Some(Loader::Movie { load_complete, .. }) =
@ -477,8 +474,15 @@ impl<'gc> Loader<'gc> {
_ => return Err(Error::NotMovieLoader), _ => return Err(Error::NotMovieLoader),
}; };
let mut activation = Activation::from_nothing(
avm,
uc.swf.version(),
avm.global_object_cell(),
uc.gc_context,
*uc.levels.get(&0).unwrap(),
);
for (k, v) in form_urlencoded::parse(&data) { for (k, v) in form_urlencoded::parse(&data) {
that.set(&k, v.into_owned().into(), avm, uc)?; that.set(&k, v.into_owned().into(), &mut activation, uc)?;
} }
Ok(()) Ok(())
@ -562,7 +566,7 @@ impl<'gc> Loader<'gc> {
let object = let object =
node.script_object(uc.gc_context, Some(avm.prototypes().xml_node)); node.script_object(uc.gc_context, Some(avm.prototypes().xml_node));
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
active_clip, active_clip,
object, object,
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
@ -570,9 +574,8 @@ impl<'gc> Loader<'gc> {
"onHTTPStatus", "onHTTPStatus",
&[200.into()], &[200.into()],
); );
avm.run_stack_till_empty(uc)?;
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
active_clip, active_clip,
object, object,
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
@ -580,7 +583,6 @@ impl<'gc> Loader<'gc> {
"onData", "onData",
&[xmlstring.into()], &[xmlstring.into()],
); );
avm.run_stack_till_empty(uc)?;
Ok(()) Ok(())
}, },
@ -600,7 +602,7 @@ impl<'gc> Loader<'gc> {
let object = let object =
node.script_object(uc.gc_context, Some(avm.prototypes().xml_node)); node.script_object(uc.gc_context, Some(avm.prototypes().xml_node));
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
active_clip, active_clip,
object, object,
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
@ -608,9 +610,8 @@ impl<'gc> Loader<'gc> {
"onHTTPStatus", "onHTTPStatus",
&[404.into()], &[404.into()],
); );
avm.run_stack_till_empty(uc)?;
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
active_clip, active_clip,
object, object,
NEWEST_PLAYER_VERSION, NEWEST_PLAYER_VERSION,
@ -618,7 +619,6 @@ impl<'gc> Loader<'gc> {
"onData", "onData",
&[], &[],
); );
avm.run_stack_till_empty(uc)?;
Ok(()) Ok(())
}, },

View File

@ -1,8 +1,9 @@
use crate::avm1::activation::Activation;
use crate::avm1::debug::VariableDumper; use crate::avm1::debug::VariableDumper;
use crate::avm1::globals::system::SystemProperties; use crate::avm1::globals::system::SystemProperties;
use crate::avm1::listeners::SystemListener; use crate::avm1::listeners::SystemListener;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::{Activation, Avm1, TObject, Value}; use crate::avm1::{Avm1, TObject, Value};
use crate::backend::input::{InputBackend, MouseCursor}; use crate::backend::input::{InputBackend, MouseCursor};
use crate::backend::storage::StorageBackend; use crate::backend::storage::StorageBackend;
use crate::backend::{ use crate::backend::{
@ -271,11 +272,18 @@ impl Player {
root.set_name(context.gc_context, ""); root.set_name(context.gc_context, "");
context.levels.insert(0, root); context.levels.insert(0, root);
let object = root.object().coerce_to_object(avm, context); let mut activation = Activation::from_nothing(
avm,
context.swf.version(),
avm.global_object_cell(),
context.gc_context,
*context.levels.get(&0).unwrap(),
);
let object = root.object().coerce_to_object(&mut activation, context);
object.define_value( object.define_value(
context.gc_context, context.gc_context,
"$version", "$version",
context.system.get_version_string(avm).into(), context.system.get_version_string(&mut activation).into(),
EnumSet::empty(), EnumSet::empty(),
); );
}); });
@ -379,21 +387,32 @@ impl Player {
if self.input.is_key_down(KeyCode::Control) && self.input.is_key_down(KeyCode::Alt) { if self.input.is_key_down(KeyCode::Control) && self.input.is_key_down(KeyCode::Alt) {
self.mutate_with_update_context(|avm, context| { self.mutate_with_update_context(|avm, context| {
let mut dumper = VariableDumper::new(" "); let mut dumper = VariableDumper::new(" ");
let mut activation = Activation::from_nothing(
avm,
context.swf.version(),
avm.global_object_cell(),
context.gc_context,
*context.levels.get(&0).unwrap(),
);
dumper.print_variables( dumper.print_variables(
"Global Variables:", "Global Variables:",
"_global", "_global",
&avm.global_object_cell(), &activation.avm().global_object_cell(),
avm, &mut activation,
context, context,
); );
let levels = context.levels.clone(); let levels = context.levels.clone();
for (level, display_object) in levels { for (level, display_object) in levels {
let object = display_object.object().coerce_to_object(avm, context); let object = display_object
.object()
.coerce_to_object(&mut activation, context);
dumper.print_variables( dumper.print_variables(
&format!("Level #{}:", level), &format!("Level #{}:", level),
&format!("_level{}", level), &format!("_level{}", level),
&object, &object,
avm, &mut activation,
context, context,
); );
} }
@ -521,7 +540,7 @@ impl Player {
/// Update dragged object, if any. /// Update dragged object, if any.
fn update_drag(&mut self) { fn update_drag(&mut self) {
let mouse_pos = self.mouse_pos; let mouse_pos = self.mouse_pos;
self.mutate_with_update_context(|_avm, context| { self.mutate_with_update_context(|_activation, context| {
if let Some(drag_object) = &mut context.drag_object { if let Some(drag_object) = &mut context.drag_object {
if drag_object.display_object.removed() { if drag_object.display_object.removed() {
// Be sure to clear the drag if the object was removed. // Be sure to clear the drag if the object was removed.
@ -608,12 +627,12 @@ impl Player {
/// This should only be called once. Further movie loads should preload the /// This should only be called once. Further movie loads should preload the
/// specific `MovieClip` referenced. /// specific `MovieClip` referenced.
fn preload(&mut self) { fn preload(&mut self) {
self.mutate_with_update_context(|avm, context| { self.mutate_with_update_context(|activation, context| {
let mut morph_shapes = fnv::FnvHashMap::default(); let mut morph_shapes = fnv::FnvHashMap::default();
let root = *context.levels.get(&0).expect("root level"); let root = *context.levels.get(&0).expect("root level");
root.as_movie_clip() root.as_movie_clip()
.unwrap() .unwrap()
.preload(avm, context, &mut morph_shapes); .preload(activation, context, &mut morph_shapes);
// Finalize morph shapes. // Finalize morph shapes.
for (id, static_data) in morph_shapes { for (id, static_data) in morph_shapes {
@ -719,7 +738,7 @@ impl Player {
match actions.action_type { match actions.action_type {
// DoAction/clip event code // DoAction/clip event code
ActionType::Normal { bytecode } => { ActionType::Normal { bytecode } => {
avm.insert_stack_frame_for_action( avm.run_stack_frame_for_action(
actions.clip, actions.clip,
context.swf.header().version, context.swf.header().version,
bytecode, bytecode,
@ -731,41 +750,29 @@ impl Player {
constructor: Some(constructor), constructor: Some(constructor),
events, events,
} => { } => {
avm.insert_stack_frame(GcCell::allocate( let mut activation = Activation::from_nothing(
avm,
context.swf.version(),
avm.global_object_cell(),
context.gc_context, context.gc_context,
Activation::from_nothing( *context.levels.get(&0).unwrap(),
context.swf.header().version, );
avm.global_object_cell(),
context.gc_context,
actions.clip,
),
));
if let Ok(prototype) = constructor if let Ok(prototype) = constructor
.get("prototype", avm, context) .get("prototype", &mut activation, context)
.map(|v| v.coerce_to_object(avm, context)) .map(|v| v.coerce_to_object(&mut activation, context))
{ {
if let Value::Object(object) = actions.clip.object() { if let Value::Object(object) = actions.clip.object() {
object.set_proto(context.gc_context, Some(prototype)); object.set_proto(context.gc_context, Some(prototype));
for event in events { for event in events {
avm.insert_stack_frame_for_action( let _ = activation.run_child_frame_for_action(
actions.clip, actions.clip,
context.swf.header().version, context.swf.header().version,
event, event,
context, context,
); );
} }
let _ = avm.run_stack_till_empty(context);
avm.insert_stack_frame(GcCell::allocate( let _ = constructor.call(&mut activation, context, object, None, &[]);
context.gc_context,
Activation::from_nothing(
context.swf.header().version,
avm.global_object_cell(),
context.gc_context,
actions.clip,
),
));
let _ = constructor.call(avm, context, object, None, &[]);
} }
} }
} }
@ -775,7 +782,7 @@ impl Player {
events, events,
} => { } => {
for event in events { for event in events {
avm.insert_stack_frame_for_action( avm.run_stack_frame_for_action(
actions.clip, actions.clip,
context.swf.header().version, context.swf.header().version,
event, event,
@ -785,7 +792,7 @@ impl Player {
} }
// Event handler method call (e.g. onEnterFrame) // Event handler method call (e.g. onEnterFrame)
ActionType::Method { object, name, args } => { ActionType::Method { object, name, args } => {
avm.insert_stack_frame_for_method( avm.run_stack_frame_for_method(
actions.clip, actions.clip,
object, object,
context.swf.header().version, context.swf.header().version,
@ -813,8 +820,6 @@ impl Player {
); );
} }
} }
// Execute the stack frame (if any).
let _ = avm.run_stack_till_empty(context);
} }
} }
@ -997,10 +1002,18 @@ impl Player {
} }
pub fn flush_shared_objects(&mut self) { pub fn flush_shared_objects(&mut self) {
self.update(|avm, update_context| { self.update(|avm, context| {
let shared_objects = update_context.shared_objects.clone(); let mut activation = Activation::from_nothing(
avm,
context.swf.version(),
avm.global_object_cell(),
context.gc_context,
*context.levels.get(&0).unwrap(),
);
let shared_objects = context.shared_objects.clone();
for so in shared_objects.values() { for so in shared_objects.values() {
let _ = crate::avm1::globals::shared_object::flush(avm, update_context, *so, &[]); let _ =
crate::avm1::globals::shared_object::flush(&mut activation, context, *so, &[]);
} }
}); });
} }