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

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.")]
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.")]
ThrownValue(Value<'gc>),
}
@ -27,9 +18,6 @@ impl Error<'_> {
match self {
Error::PrototypeRecursionLimit => true,
Error::InvalidSwf(_) => true,
Error::NoStackFrame => true,
Error::FrameNotOnStack => true,
Error::AlreadyExecutingFrame => false,
Error::ThrownValue(_) => false,
}
}

View File

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

View File

@ -3,11 +3,10 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::property::{Attribute, Attribute::*};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::scope::Scope;
use crate::avm1::super_object::SuperObject;
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::tag_utils::SwfSlice;
use enumset::EnumSet;
@ -31,11 +30,11 @@ use swf::avm1::types::FunctionParam;
/// your function yields `None`, you must ensure that the top-most activation
/// in the AVM1 runtime will return with the value of this function.
pub type NativeFunction<'gc> = fn(
&mut Avm1<'gc>,
&mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>,
Object<'gc>,
&[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>>;
) -> Result<Value<'gc>, Error<'gc>>;
/// Represents a function defined in the AVM1 runtime, either through
/// `DefineFunction` or `DefineFunction2`.
@ -223,20 +222,21 @@ impl<'gc> Executable<'gc> {
/// create a new stack frame and execute the action data yourself.
pub fn exec(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
match self {
Executable::Native(nf) => nf(avm, ac, this, args),
Executable::Native(nf) => nf(activation, ac, this, args),
Executable::Action(af) => {
let child_scope = GcCell::allocate(
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 {
for i in 0..args.len() {
arguments.define_value(
@ -261,7 +261,7 @@ impl<'gc> Executable<'gc> {
SuperObject::from_this_and_base_proto(
this,
base_proto.unwrap_or(this),
avm,
activation,
ac,
)?
.into(),
@ -270,7 +270,7 @@ impl<'gc> Executable<'gc> {
None
};
let effective_ver = if avm.current_swf_version() > 5 {
let effective_ver = if activation.current_swf_version() > 5 {
af.swf_version()
} else {
this.as_display_object()
@ -278,19 +278,15 @@ impl<'gc> Executable<'gc> {
.unwrap_or(ac.player_version)
};
let frame_cell = GcCell::allocate(
ac.gc_context,
Activation::from_function(
effective_ver,
af.data(),
child_scope,
af.constant_pool,
af.base_clip,
this,
Some(argcell),
),
let mut frame = Activation::from_action(
activation.avm(),
effective_ver,
child_scope,
af.constant_pool,
af.base_clip,
this,
Some(argcell),
);
let mut frame = frame_cell.write(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 {
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
@ -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(
&self,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
self.base.get_local(name, avm, context, this)
self.base.get_local(name, activation, context, this)
}
fn set(
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
self.base.set(name, value, avm, context)
self.base.set(name, value, activation, context)
}
fn call(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(exec) = self.as_executable() {
exec.exec(avm, context, this, base_proto, args)?
.resolve(avm, context)
exec.exec(activation, context, this, base_proto, args)
} else {
Ok(Value::Undefined)
}
@ -493,17 +488,16 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<ReturnValue<'gc>, Error<'gc>> {
self.base.call_setter(name, value, avm, context, this)
) -> Option<Executable<'gc>> {
self.base.call_setter(name, value, activation, context)
}
#[allow(clippy::new_ret_no_self)]
fn new(
&self,
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
prototype: Object<'gc>,
_args: &[Value<'gc>],
@ -525,11 +519,11 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
fn delete(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool {
self.base.delete(avm, gc_context, name)
self.base.delete(activation, gc_context, name)
}
fn proto(&self) -> Option<Object<'gc>> {
@ -575,7 +569,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
fn add_property_with_case(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
@ -583,46 +577,46 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
attributes: EnumSet<Attribute>,
) {
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(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base.has_property(avm, context, name)
self.base.has_property(activation, context, name)
}
fn has_own_property(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base.has_own_property(avm, context, name)
self.base.has_own_property(activation, context, name)
}
fn has_own_virtual(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> 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 {
self.base.is_property_overwritable(avm, name)
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base.is_property_overwritable(activation, name)
}
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
self.base.is_property_enumerable(avm, name)
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base.is_property_enumerable(activation, name)
}
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
self.base.get_keys(avm)
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base.get_keys(activation)
}
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::fscommand;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::listeners::SystemListeners;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::backend::navigator::NavigationMethod;
use enumset::EnumSet;
use gc_arena::MutationContext;
@ -40,21 +40,21 @@ mod xml;
#[allow(non_snake_case, unused_must_use)] //can't use errors yet
pub fn getURL<'a, 'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'a, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
//TODO: Error behavior if no arguments are present
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) {
fscommand::handle(fscommand, avm, context);
return Ok(Value::Undefined.into());
fscommand::handle(fscommand, activation, context);
return Ok(Value::Undefined);
}
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 {
None
};
@ -63,64 +63,67 @@ pub fn getURL<'a, 'gc>(
Some(Value::String(s)) if s == "POST" => Some(NavigationMethod::POST),
_ => 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
.navigator
.navigate_to_url(url.to_string(), window, vars_method);
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn random<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
match args.get(0) {
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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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 {
Ok(true.into())
}
}
pub fn get_infinity<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if avm.current_swf_version() > 4 {
) -> Result<Value<'gc>, Error<'gc>> {
if activation.current_swf_version() > 4 {
Ok(f64::INFINITY.into())
} else {
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
}
pub fn get_nan<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if avm.current_swf_version() > 4 {
) -> Result<Value<'gc>, Error<'gc>> {
if activation.current_swf_version() > 4 {
Ok(f64::NAN.into())
} else {
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
}
@ -473,7 +476,10 @@ pub fn create_globals<'gc>(
mod tests {
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
}

View File

@ -1,10 +1,10 @@
//! Array class
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet;
use gc_arena::MutationContext;
use smallvec::alloc::borrow::Cow;
@ -25,7 +25,12 @@ const DEFAULT_ORDERING: Ordering = Ordering::Equal;
// Compare function used by sort and sortOn.
type CompareFn<'a, 'gc> = Box<
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>(
@ -84,16 +89,16 @@ pub fn create_array_object<'gc>(
/// Implements `Array`
pub fn constructor<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let mut consumed = false;
if args.len() == 1 {
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 {
this.set_length(context.gc_context, length as usize);
consumed = true;
@ -115,15 +120,15 @@ pub fn constructor<'gc>(
this.set_length(context.gc_context, length);
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn push<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let old_length = this.length();
let new_length = old_length + args.len();
this.set_length(context.gc_context, new_length);
@ -140,11 +145,11 @@ pub fn push<'gc>(
}
pub fn unshift<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let old_length = this.length();
let new_length = old_length + args.len();
let offset = new_length - old_length;
@ -165,14 +170,14 @@ pub fn unshift<'gc>(
}
pub fn shift<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let old_length = this.length();
if old_length == 0 {
return Ok(Value::Undefined.into());
return Ok(Value::Undefined);
}
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(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);
Ok(removed.into())
Ok(removed)
}
pub fn pop<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let old_length = this.length();
if old_length == 0 {
return Ok(Value::Undefined.into());
return Ok(Value::Undefined);
}
let new_length = old_length - 1;
let removed = this.array_element(new_length);
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);
Ok(removed.into())
Ok(removed)
}
pub fn reverse<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let length = this.length();
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);
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn join<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let separator = args
.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(","));
let values: Vec<Value<'gc>> = this.array();
Ok(values
.iter()
.map(|v| {
v.coerce_to_string(avm, context)
v.coerce_to_string(activation, context)
.unwrap_or_else(|_| Cow::Borrowed("undefined"))
})
.collect::<Vec<Cow<str>>>()
@ -264,23 +269,23 @@ fn make_index_absolute(mut index: i32, length: usize) -> usize {
}
pub fn slice<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let start = args
.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()))
.unwrap_or(0);
let end = args
.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()))
.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 {
let length = end - start;
@ -295,31 +300,31 @@ pub fn slice<'gc>(
}
pub fn splice<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() {
return Ok(Value::Undefined.into());
return Ok(Value::Undefined);
}
let old_length = this.length();
let start = args
.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))
.unwrap_or(0);
let count = args
.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)
.unwrap_or(old_length as i32);
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_add = if args.len() > 2 { &args[2..] } else { &[] };
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 {
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);
@ -367,17 +372,17 @@ pub fn splice<'gc>(
}
pub fn concat<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
let array = ScriptObject::array(context.gc_context, Some(avm.prototypes.array));
) -> Result<Value<'gc>, Error<'gc>> {
let array = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
let mut length = 0;
for i in 0..this.length() {
let old = this
.get(&i.to_string(), avm, context)
.get(&i.to_string(), activation, context)
.unwrap_or(Value::Undefined);
array.define_value(
context.gc_context,
@ -393,11 +398,11 @@ pub fn concat<'gc>(
if let Value::Object(object) = arg {
let object = *object;
if avm.prototypes.array.is_prototype_of(object) {
if activation.avm().prototypes.array.is_prototype_of(object) {
added = true;
for i in 0..object.length() {
let old = object
.get(&i.to_string(), avm, context)
.get(&i.to_string(), activation, context)
.unwrap_or(Value::Undefined);
array.define_value(
context.gc_context,
@ -427,20 +432,20 @@ pub fn concat<'gc>(
}
pub fn to_string<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
join(avm, context, this, &[])
) -> Result<Value<'gc>, Error<'gc>> {
join(activation, context, this, &[])
}
fn sort<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
// Overloads:
// 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.
@ -453,7 +458,7 @@ fn sort<'gc>(
}
[compare_fn @ Value::Object(_), ..] => (Some(compare_fn), 0),
[] => (None, 0),
_ => return Ok(Value::Undefined.into()),
_ => return Ok(Value::Undefined),
};
let numeric = (flags & NUMERIC) != 0;
@ -466,10 +471,11 @@ fn sort<'gc>(
};
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
Box::new(move |avm, context, a: &Value<'gc>, b: &Value<'gc>| {
sort_compare_custom(avm, context, this, a, b, &f)
Box::new(move |activation, context, a: &Value<'gc>, b: &Value<'gc>| {
sort_compare_custom(activation, context, this, a, b, &f)
})
} else if numeric {
Box::new(sort_compare_numeric(string_compare_fn))
@ -477,15 +483,15 @@ fn sort<'gc>(
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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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_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.
@ -494,15 +500,17 @@ fn sort_on<'gc>(
// Array of field names.
let mut field_names = vec![];
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
}
Some(field_name) => {
// 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.
@ -516,7 +524,7 @@ fn sort_on<'gc>(
if array.length() == fields.len() {
let mut flags = vec![];
for flag in array.array() {
flags.push(flag.coerce_to_i32(avm, context)?);
flags.push(flag.coerce_to_i32(activation, context)?);
}
flags
} else {
@ -526,7 +534,7 @@ fn sort_on<'gc>(
}
Some(flags) => {
// 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()
}
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);
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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
mut compare_fn: impl FnMut(
&mut Avm1<'gc>,
&mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>,
&Value<'gc>,
&Value<'gc>,
) -> Ordering,
flags: i32,
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let length = this.length();
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 unique_sort = (flags & UNIQUE_SORT) != 0;
@ -583,7 +591,7 @@ fn sort_with_function<'gc>(
let mut is_unique = true;
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 {
ret = ret.reverse();
}
@ -710,13 +718,13 @@ pub fn create_proto<'gc>(
}
fn sort_compare_string<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
a: &Value<'gc>,
b: &Value<'gc>,
) -> Ordering {
let a_str = a.coerce_to_string(avm, context);
let b_str = b.coerce_to_string(avm, context);
let a_str = a.coerce_to_string(activation, context);
let b_str = b.coerce_to_string(activation, context);
// TODO: Handle errors.
if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) {
a_str.cmp(&b_str)
@ -726,13 +734,13 @@ fn sort_compare_string<'gc>(
}
fn sort_compare_string_ignore_case<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
a: &Value<'gc>,
b: &Value<'gc>,
) -> Ordering {
let a_str = a.coerce_to_string(avm, context);
let b_str = b.coerce_to_string(avm, context);
let a_str = a.coerce_to_string(activation, context);
let b_str = b.coerce_to_string(activation, context);
// TODO: Handle errors.
if let (Ok(a_str), Ok(b_str)) = (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>(
mut string_compare_fn: impl FnMut(
&mut Avm1<'gc>,
&mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>,
&Value<'gc>,
&Value<'gc>,
) -> Ordering,
) -> impl FnMut(&mut Avm1<'gc>, &mut UpdateContext<'_, 'gc, '_>, &Value<'gc>, &Value<'gc>) -> Ordering
{
move |avm, context, a, b| {
) -> impl FnMut(
&mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>,
&Value<'gc>,
&Value<'gc>,
) -> Ordering {
move |activation, context, a, b| {
if let (Value::Number(a), Value::Number(b)) = (a, b) {
a.partial_cmp(b).unwrap_or(DEFAULT_ORDERING)
} 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>(
field_names: Vec<String>,
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;
move |avm, context, a, b| {
move |activation, context, a, b| {
for (field_name, compare_fn) in field_names.iter().zip(compare_fns.iter_mut()) {
let a_object = ValueObject::boxed(avm, context, a.clone());
let b_object = ValueObject::boxed(avm, context, b.clone());
let a_prop = a_object.get(field_name, avm, context).unwrap();
let b_prop = b_object.get(field_name, avm, context).unwrap();
let a_object = ValueObject::boxed(activation, context, a.clone());
let b_object = ValueObject::boxed(activation, context, b.clone());
let a_prop = a_object.get(field_name, activation, 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 {
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 (?)
fn sort_compare_custom<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
a: &Value<'gc>,
@ -794,7 +811,7 @@ fn sort_compare_custom<'gc>(
// TODO: Handle errors.
let args = [a.clone(), b.clone()];
let ret = compare_fn
.call(avm, context, this, None, &args)
.call(activation, context, this, None, &args)
.unwrap_or(Value::Undefined);
match ret {
Value::Number(n) if n > 0.0 => Ordering::Greater,

View File

@ -1,23 +1,23 @@
//! `Boolean` class impl
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::value_object::ValueObject;
use crate::avm1::{Avm1, Object, TObject, Value};
use crate::avm1::{Object, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::MutationContext;
/// `Boolean` constructor/function
pub fn boolean<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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 b = Value::Bool(val.as_bool(avm.current_swf_version()));
let b = Value::Bool(val.as_bool(activation.current_swf_version()));
(b.clone(), b)
} else {
(Value::Undefined, Value::Bool(false))
@ -30,7 +30,7 @@ pub fn boolean<'gc>(
// If called as a function, return the value.
// Boolean() with no argument returns undefined.
Ok(ret_value.into())
Ok(ret_value)
}
pub fn create_boolean_object<'gc>(
@ -74,11 +74,11 @@ pub fn create_proto<'gc>(
}
pub fn to_string<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(vbox) = this.as_value_object() {
// Must be a bool.
// 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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(vbox) = this.as_value_object() {
// Must be a bool.
// 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
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::globals::display_object;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
use gc_arena::MutationContext;
pub fn create_proto<'gc>(
@ -20,10 +20,10 @@ pub fn create_proto<'gc>(
/// Implements `Button` constructor.
pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
Ok(Value::Undefined.into())
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined)
}

View File

@ -3,24 +3,24 @@
//! TODO: This should change when `ColorTransform` changes to match Flash's representation
//! (See GitHub #193)
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::display_object::{DisplayObject, TDisplayObject};
use enumset::EnumSet;
use gc_arena::MutationContext;
pub fn constructor<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
// The target display object that this color will modify.
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
// Set undocumented `target` property
this.set("target", target, avm, context)?;
this.set("target", target, activation, context)?;
this.set_attributes(
context.gc_context,
Some("target"),
@ -28,7 +28,7 @@ pub fn constructor<'gc>(
EnumSet::empty(),
);
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn create_proto<'gc>(
@ -75,75 +75,116 @@ pub fn create_proto<'gc>(
/// Gets the target display object of this color transform.
fn target<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Option<DisplayObject<'gc>>, Error<'gc>> {
// 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
// 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.
if target != Value::Undefined && !matches!(&target, &Value::String(ref s) if s.is_empty()) {
let start_clip = avm.target_clip_or_root();
avm.resolve_target_display_object(context, start_clip, target)
let start_clip = activation.target_clip_or_root();
activation.resolve_target_display_object(context, start_clip, target)
} else {
Ok(None)
}
}
fn get_rgb<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if let Some(target) = target(avm, context, this)? {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(target) = target(activation, context, this)? {
let color_transform = target.color_transform();
let r = ((color_transform.r_add * 255.0) as i32) << 16;
let g = ((color_transform.g_add * 255.0) as i32) << 8;
let b = (color_transform.b_add * 255.0) as i32;
Ok((r | g | b).into())
} else {
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
}
fn get_transform<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if let Some(target) = target(avm, context, this)? {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(target) = target(activation, context, this)? {
let color_transform = target.color_transform();
let out = ScriptObject::object(context.gc_context, Some(avm.prototypes.object));
out.set("ra", (color_transform.r_mult * 100.0).into(), avm, context)?;
out.set("ga", (color_transform.g_mult * 100.0).into(), avm, context)?;
out.set("ba", (color_transform.b_mult * 100.0).into(), avm, context)?;
out.set("aa", (color_transform.a_mult * 100.0).into(), avm, context)?;
out.set("rb", (color_transform.r_add * 255.0).into(), avm, context)?;
out.set("gb", (color_transform.g_add * 255.0).into(), avm, 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)?;
let out =
ScriptObject::object(context.gc_context, Some(activation.avm().prototypes.object));
out.set(
"ra",
(color_transform.r_mult * 100.0).into(),
activation,
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())
} else {
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
}
fn set_rgb<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if let Some(target) = target(avm, context, this)? {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(target) = target(activation, context, this)? {
let mut color_transform = target.color_transform_mut(context.gc_context);
let rgb = args
.get(0)
.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 g = (((rgb >> 8) & 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.b_add = b;
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
fn set_transform<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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
// 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).
fn set_color_mult<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
transform: Object<'gc>,
property: &str,
out: &mut f32,
) -> Result<(), Error<'gc>> {
// 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
.get(property, avm, context)?
.coerce_to_f64(avm, context)?;
.get(property, activation, context)?
.coerce_to_f64(activation, context)?;
*out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n * 2.56)) / 256.0
}
Ok(())
}
fn set_color_add<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
transform: Object<'gc>,
property: &str,
out: &mut f32,
) -> Result<(), Error<'gc>> {
// 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
.get(property, avm, context)?
.coerce_to_f64(avm, context)?;
.get(property, activation, context)?
.coerce_to_f64(activation, context)?;
*out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n)) / 255.0
}
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 transform = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_object(avm, context);
set_color_mult(avm, context, transform, "ra", &mut color_transform.r_mult)?;
set_color_mult(avm, context, transform, "ga", &mut color_transform.g_mult)?;
set_color_mult(avm, context, transform, "ba", &mut color_transform.b_mult)?;
set_color_mult(avm, context, transform, "aa", &mut color_transform.a_mult)?;
set_color_add(avm, context, transform, "rb", &mut color_transform.r_add)?;
set_color_add(avm, context, transform, "gb", &mut color_transform.g_add)?;
set_color_add(avm, context, transform, "bb", &mut color_transform.b_add)?;
set_color_add(avm, context, transform, "ab", &mut color_transform.a_add)?;
.coerce_to_object(activation, context);
set_color_mult(
activation,
context,
transform,
"ra",
&mut color_transform.r_mult,
)?;
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
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::display_object::{DisplayObject, TDisplayObject};
use enumset::EnumSet;
use gc_arena::MutationContext;
@ -24,11 +24,11 @@ macro_rules! with_display_object {
$(
$object.force_set_function(
$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() {
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>,
$gc_context,
DontDelete | ReadOnly | DontEnum,
@ -54,7 +54,9 @@ pub fn define_display_object_proto<'gc>(
object.add_property(
gc_context,
"_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)),
DontDelete | ReadOnly | DontEnum,
);
@ -62,7 +64,7 @@ pub fn define_display_object_proto<'gc>(
object.add_property(
gc_context,
"_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)),
DontDelete | ReadOnly | DontEnum,
);
@ -77,60 +79,59 @@ pub fn define_display_object_proto<'gc>(
}
pub fn get_parent<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(this
.as_display_object()
.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)
.unwrap_or(Value::Undefined)
.into())
.unwrap_or(Value::Undefined))
}
pub fn get_depth<'gc>(
display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if avm.current_swf_version() >= 6 {
) -> Result<Value<'gc>, Error<'gc>> {
if activation.current_swf_version() >= 6 {
let depth = display_object.depth().wrapping_sub(AVM_DEPTH_BIAS);
Ok(depth.into())
} else {
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
}
pub fn overwrite_root<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let new_val = args
.get(0)
.map(|v| v.to_owned())
.unwrap_or(Value::Undefined);
this.define_value(ac.gc_context, "_root", new_val, EnumSet::new());
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn overwrite_global<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let new_val = args
.get(0)
.map(|v| v.to_owned())
.unwrap_or(Value::Undefined);
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
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet;
use gc_arena::MutationContext;
/// Implements `Function`
pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
Ok(Value::Undefined.into())
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined)
}
/// Implements `Function.prototype.call`
pub fn call<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
func: Object<'gc>,
myargs: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let this = match myargs.get(0) {
Some(Value::Object(this)) => *this,
_ => avm.globals,
_ => activation.avm().globals,
};
let empty = [];
let args = match myargs.len() {
@ -35,52 +35,52 @@ pub fn call<'gc>(
};
match func.as_executable() {
Some(exec) => exec.exec(avm, action_context, this, None, args),
_ => Ok(Value::Undefined.into()),
Some(exec) => exec.exec(activation, action_context, this, None, args),
_ => Ok(Value::Undefined),
}
}
/// Implements `Function.prototype.apply`
pub fn apply<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
func: Object<'gc>,
myargs: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let this = match myargs.get(0) {
Some(Value::Object(this)) => *this,
_ => avm.globals,
_ => activation.avm().globals,
};
let mut child_args = Vec::new();
let args_object = myargs.get(1).cloned().unwrap_or(Value::Undefined);
let length = match args_object {
Value::Object(a) => a
.get("length", avm, action_context)?
.coerce_to_f64(avm, action_context)? as usize,
.get("length", activation, action_context)?
.coerce_to_f64(activation, action_context)? as usize,
_ => 0,
};
while child_args.len() < length {
let args = args_object.coerce_to_object(avm, action_context);
let next_arg = args.get(&format!("{}", child_args.len()), avm, action_context)?;
let args = args_object.coerce_to_object(activation, action_context);
let next_arg = args.get(&format!("{}", child_args.len()), activation, action_context)?;
child_args.push(next_arg);
}
match func.as_executable() {
Some(exec) => exec.exec(avm, action_context, this, None, &child_args),
_ => Ok(Value::Undefined.into()),
Some(exec) => exec.exec(activation, action_context, this, None, &child_args),
_ => Ok(Value::Undefined),
}
}
/// Implements `Function.prototype.toString`
fn to_string<'gc>(
_: &mut Avm1<'gc>,
_: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>,
_: Object<'gc>,
_: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
Ok(ReturnValue::Immediate("[type Function]".into()))
) -> Result<Value<'gc>, Error<'gc>> {
Ok("[type Function]".into())
}
/// Partially construct `Function.prototype`.

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +1,45 @@
//! `MovieClipLoader` impl
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::object::TObject;
use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
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::display_object::{DisplayObject, TDisplayObject};
use enumset::EnumSet;
use gc_arena::MutationContext;
pub fn constructor<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
let listeners = ScriptObject::array(context.gc_context, Some(avm.prototypes().array));
) -> Result<Value<'gc>, Error<'gc>> {
let listeners = ScriptObject::array(
context.gc_context,
Some(activation.avm().prototypes().array),
);
this.define_value(
context.gc_context,
"_listeners",
Value::Object(listeners.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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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 listeners = this.get("_listeners", avm, context)?;
let listeners = this.get("_listeners", activation, context)?;
if let Value::Object(listeners) = listeners {
let length = listeners.length();
@ -48,20 +51,20 @@ pub fn add_listener<'gc>(
}
pub fn remove_listener<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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 listeners = this.get("_listeners", avm, context)?;
let listeners = this.get("_listeners", activation, context)?;
if let Value::Object(listeners) = listeners {
let length = listeners.length();
let mut position = None;
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 {
position = Some(i);
break;
@ -80,7 +83,7 @@ pub fn remove_listener<'gc>(
}
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);
}
@ -91,37 +94,37 @@ pub fn remove_listener<'gc>(
}
pub fn broadcast_message<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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 = 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 listeners = this.get("_listeners", avm, context)?;
let listeners = this.get("_listeners", activation, context)?;
if let Value::Object(listeners) = listeners {
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 {
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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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 = 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);
if let Value::Object(target) = target {
@ -147,11 +150,11 @@ pub fn load_clip<'gc>(
}
pub fn unload_clip<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
if let Value::Object(target) = target {
@ -170,11 +173,11 @@ pub fn unload_clip<'gc>(
}
pub fn get_progress<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
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>(

View File

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

View File

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

View File

@ -1,10 +1,10 @@
//! flash.geom.Point
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value};
use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::MutationContext;
@ -12,103 +12,110 @@ use std::f64::NAN;
pub fn point_to_object<'gc>(
point: (f64, f64),
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Object<'gc>, Error<'gc>> {
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>(
args: &[Value<'gc>],
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Object<'gc>, Error<'gc>> {
let proto = context.system_prototypes.point;
let object = proto.new(avm, context, proto, &args)?;
let _ = constructor(avm, context, object, &args)?;
let object = proto.new(activation, context, proto, &args)?;
let _ = constructor(activation, context, object, &args)?;
Ok(object)
}
pub fn value_to_point<'gc>(
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(f64, f64), Error<'gc>> {
let x = value
.coerce_to_object(avm, context)
.get("x", avm, context)?
.coerce_to_f64(avm, context)?;
.coerce_to_object(activation, context)
.get("x", activation, context)?
.coerce_to_f64(activation, context)?;
let y = value
.coerce_to_object(avm, context)
.get("y", avm, context)?
.coerce_to_f64(avm, context)?;
.coerce_to_object(activation, context)
.get("y", activation, context)?
.coerce_to_f64(activation, context)?;
Ok((x, y))
}
pub fn object_to_point<'gc>(
object: Object<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(f64, f64), Error<'gc>> {
let x = object.get("x", avm, context)?.coerce_to_f64(avm, context)?;
let y = object.get("y", avm, context)?.coerce_to_f64(avm, context)?;
let x = object
.get("x", activation, context)?
.coerce_to_f64(activation, context)?;
let y = object
.get("y", activation, context)?
.coerce_to_f64(activation, context)?;
Ok((x, y))
}
fn constructor<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() {
this.set("x", 0.into(), avm, context)?;
this.set("y", 0.into(), avm, context)?;
this.set("x", 0.into(), activation, context)?;
this.set("y", 0.into(), activation, context)?;
} else {
this.set(
"x",
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
avm,
activation,
context,
)?;
this.set(
"y",
args.get(1).unwrap_or(&Value::Undefined).to_owned(),
avm,
activation,
context,
)?;
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
fn clone<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let proto = context.system_prototypes.point;
let args = [this.get("x", avm, context)?, this.get("y", avm, context)?];
let cloned = proto.new(avm, context, proto, &args)?;
let _ = constructor(avm, context, cloned, &args)?;
let args = [
this.get("x", activation, context)?,
this.get("y", activation, context)?,
];
let cloned = proto.new(activation, context, proto, &args)?;
let _ = constructor(activation, context, cloned, &args)?;
Ok(cloned.into())
}
fn equals<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(other) = args.get(0) {
let this_x = this.get("x", avm, context)?;
let this_y = this.get("y", avm, context)?;
let other = other.coerce_to_object(avm, context);
let other_x = other.get("x", avm, context)?;
let other_y = other.get("y", avm, context)?;
let this_x = this.get("x", activation, context)?;
let this_y = this.get("y", activation, context)?;
let other = other.coerce_to_object(activation, context);
let other_x = other.get("x", activation, context)?;
let other_y = other.get("y", activation, context)?;
return Ok((this_x == other_x && this_y == other_y).into());
}
@ -116,45 +123,53 @@ fn equals<'gc>(
}
fn add<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
let this_x = this.get("x", avm, context)?.coerce_to_f64(avm, context)?;
let this_y = this.get("y", avm, context)?.coerce_to_f64(avm, context)?;
) -> Result<Value<'gc>, Error<'gc>> {
let this_x = this
.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(
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
avm,
activation,
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())
}
fn subtract<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
let this_x = this.get("x", avm, context)?.coerce_to_f64(avm, context)?;
let this_y = this.get("y", avm, context)?.coerce_to_f64(avm, context)?;
) -> Result<Value<'gc>, Error<'gc>> {
let this_x = this
.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(
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
avm,
activation,
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())
}
fn distance<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if args.len() < 2 {
return Ok(NAN.into());
}
@ -162,93 +177,96 @@ fn distance<'gc>(
let a = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_object(avm, context);
.coerce_to_object(activation, context);
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
.coerce_to_object(avm, context)
.get("length", avm, context)?
.into())
.coerce_to_object(activation, context)
.get("length", activation, context)?)
}
fn polar<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let length = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?;
.coerce_to_f64(activation, context)?;
let angle = args
.get(1)
.unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?;
let point = point_to_object((length * angle.cos(), length * angle.sin()), avm, context)?;
.coerce_to_f64(activation, context)?;
let point = point_to_object(
(length * angle.cos(), length * angle.sin()),
activation,
context,
)?;
Ok(point.into())
}
fn interpolate<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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 b = value_to_point(args.get(1).unwrap().to_owned(), avm, context)?;
let f = args.get(2).unwrap().coerce_to_f64(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(), activation, 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);
Ok(point_to_object(result, avm, context)?.into())
Ok(point_to_object(result, activation, context)?.into())
}
fn to_string<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
let x = this.get("x", avm, context)?;
let y = this.get("y", avm, context)?;
) -> Result<Value<'gc>, Error<'gc>> {
let x = this.get("x", activation, context)?;
let y = this.get("y", activation, context)?;
Ok(format!(
"(x={}, y={})",
x.coerce_to_string(avm, context)?,
y.coerce_to_string(avm, context)?
x.coerce_to_string(activation, context)?,
y.coerce_to_string(activation, context)?
)
.into())
}
fn length<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
let point = value_to_point(this.into(), avm, context)?;
) -> Result<Value<'gc>, Error<'gc>> {
let point = value_to_point(this.into(), activation, context)?;
let length = (point.0 * point.0 + point.1 * point.1).sqrt();
Ok(length.into())
}
fn normalize<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let current_length = this
.get("length", avm, context)?
.coerce_to_f64(avm, context)?;
.get("length", activation, context)?
.coerce_to_f64(activation, context)?;
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
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?;
.coerce_to_f64(activation, context)?;
let (x, y) = if current_length == 0.0 {
(point.0 * new_length, point.1 * new_length)
} else {
@ -258,33 +276,33 @@ fn normalize<'gc>(
)
};
this.set("x", x.into(), avm, context)?;
this.set("y", y.into(), avm, context)?;
this.set("x", x.into(), activation, context)?;
this.set("y", y.into(), activation, context)?;
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
fn offset<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
let point = value_to_point(this.into(), avm, context)?;
) -> Result<Value<'gc>, Error<'gc>> {
let point = value_to_point(this.into(), activation, context)?;
let dx = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_f64(avm, context)?;
.coerce_to_f64(activation, context)?;
let dy = args
.get(1)
.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("y", (point.1 + dy).into(), avm, context)?;
this.set("x", (point.0 + dx).into(), activation, context)?;
this.set("y", (point.1 + dy).into(), activation, context)?;
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
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::function::{Executable, FunctionObject};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, TObject, Value};
use crate::avm1::{Object, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::MutationContext;
@ -11,36 +11,36 @@ use crate::avm1::shared_object::SharedObject;
use json::JsonValue;
pub fn delete_all<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.deleteAll() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn get_disk_usage<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.getDiskUsage() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
/// 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
/// Undefined fields aren't serialized
fn recursive_serialize<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
obj: Object<'gc>,
json_obj: &mut JsonValue,
) {
for k in &obj.get_keys(avm) {
if let Ok(elem) = obj.get(k, avm, action_context) {
for k in &obj.get_keys(activation) {
if let Ok(elem) = obj.get(k, activation, action_context) {
match elem {
Value::Undefined => {}
Value::Null => json_obj[k] = JsonValue::Null,
@ -49,12 +49,13 @@ fn recursive_serialize<'gc>(
Value::String(s) => json_obj[k] = s.into(),
Value::Object(o) => {
// Don't attempt to serialize functions
let function = activation.avm().prototypes.function;
if !o
.is_instance_of(avm, action_context, o, avm.prototypes.function)
.is_instance_of(activation, action_context, o, function)
.unwrap_or_default()
{
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;
}
}
@ -68,7 +69,7 @@ fn recursive_serialize<'gc>(
/// Undefined fields aren't deserialized
fn recursive_deserialize<'gc>(
json_obj: JsonValue,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
object: Object<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) {
@ -112,10 +113,11 @@ fn recursive_deserialize<'gc>(
);
}
JsonValue::Object(o) => {
let so = avm.prototypes.object;
let obj = so.new(avm, context, so, &[]).unwrap();
let _ = crate::avm1::globals::object::constructor(avm, context, obj, &[]).unwrap();
recursive_deserialize(JsonValue::Object(o.clone()), avm, obj, context);
let so = activation.avm().prototypes.object;
let obj = so.new(activation, context, so, &[]).unwrap();
let _ = crate::avm1::globals::object::constructor(activation, context, obj, &[])
.unwrap();
recursive_deserialize(JsonValue::Object(o.clone()), activation, obj, context);
object.define_value(
context.gc_context,
@ -130,21 +132,21 @@ fn recursive_deserialize<'gc>(
}
pub fn get_local<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let name = args
.get(0)
.unwrap_or(&Value::Undefined)
.to_owned()
.coerce_to_string(avm, action_context)?
.coerce_to_string(activation, action_context)?
.to_string();
//Check if this is referencing an existing shared object
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 {
@ -152,23 +154,23 @@ pub fn get_local<'gc>(
}
// Data property only should exist when created with getLocal/Remote
let so = avm.prototypes.shared_object;
let this = so.new(avm, action_context, so, &[])?;
let _ = constructor(avm, action_context, this, &[])?;
let so = activation.avm().prototypes.shared_object;
let this = so.new(activation, action_context, so, &[])?;
let _ = constructor(activation, action_context, this, &[])?;
// Set the internal name
let obj_so = this.as_shared_object().unwrap();
obj_so.set_name(action_context.gc_context, name.to_string());
// Create the data object
let data_proto = avm.prototypes.object;
let data = data_proto.new(avm, action_context, so, &[])?;
let _ = crate::avm1::globals::object::constructor(avm, action_context, data, &[])?;
let data_proto = activation.avm().prototypes.object;
let data = data_proto.new(activation, action_context, so, &[])?;
let _ = crate::avm1::globals::object::constructor(activation, action_context, data, &[])?;
// Load the data object from storage if it existed prior
if let Some(saved) = action_context.storage.get_string(&name) {
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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.getRemote() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn get_max_size<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.getMaxSize() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn add_listener<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.addListener() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn remove_listener<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.removeListener() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn create_shared_object_object<'gc>(
@ -297,17 +299,17 @@ pub fn create_shared_object_object<'gc>(
}
pub fn clear<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let data = this
.get("data", avm, action_context)?
.coerce_to_object(avm, action_context);
.get("data", activation, action_context)?
.coerce_to_object(activation, action_context);
for k in &data.get_keys(avm) {
data.delete(avm, action_context.gc_context, k);
for k in &data.get_keys(activation) {
data.delete(activation, action_context.gc_context, k);
}
let so = this.as_shared_object().unwrap();
@ -315,41 +317,41 @@ pub fn clear<'gc>(
action_context.storage.remove_key(&name);
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn close<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.close() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn connect<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.connect() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn flush<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let data = this
.get("data", avm, action_context)?
.coerce_to_object(avm, action_context);
.get("data", activation, action_context)?
.coerce_to_object(activation, action_context);
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 name = this_obj.get_name();
@ -361,53 +363,53 @@ pub fn flush<'gc>(
}
pub fn get_size<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.getSize() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn send<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.send() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn set_fps<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.setFps() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn on_status<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.onStatus() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn on_sync<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("SharedObject.onSync() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn create_proto<'gc>(
@ -470,10 +472,10 @@ pub fn create_proto<'gc>(
}
pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
Ok(Value::Undefined.into())
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined)
}

View File

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

View File

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

View File

@ -1,11 +1,11 @@
//! `String` class impl
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue;
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::string_utils;
use enumset::EnumSet;
@ -13,14 +13,14 @@ use gc_arena::MutationContext;
/// `String` constructor
pub fn string<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let value = match args.get(0).cloned() {
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(),
};
@ -171,19 +171,19 @@ pub fn create_proto<'gc>(
}
fn char_at<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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.
// When we improve our string representation, the unpaired surrogate should be returned.
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
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_i32(avm, context)?;
.coerce_to_i32(activation, context)?;
let ret = if i >= 0 {
string
.encode_utf16()
@ -197,17 +197,17 @@ fn char_at<'gc>(
}
fn char_code_at<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_i32(avm, context)?;
.coerce_to_i32(activation, context)?;
let ret = if i >= 0 {
this.encode_utf16()
.nth(i as usize)
@ -220,31 +220,31 @@ fn char_code_at<'gc>(
}
fn concat<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let mut ret = Value::from(this)
.coerce_to_string(avm, context)?
.coerce_to_string(activation, context)?
.to_string();
for arg in args {
let s = arg.coerce_to_string(avm, context)?;
let s = arg.coerce_to_string(activation, context)?;
ret.push_str(&s)
}
Ok(ret.into())
}
fn from_char_code<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
// TODO: Unpaired surrogates will be replace with Unicode replacement char.
let mut out = String::with_capacity(args.len());
for arg in args {
let i = arg.coerce_to_u16(avm, context)?;
let i = arg.coerce_to_u16(activation, context)?;
if i == 0 {
// Stop at a null-terminator.
break;
@ -255,20 +255,20 @@ fn from_char_code<'gc>(
}
fn index_of<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let this = Value::from(this)
.coerce_to_string(avm, context)?
.coerce_to_string(activation, context)?
.encode_utf16()
.collect::<Vec<u16>>();
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
.clone()
.coerce_to_string(avm, context)?
.coerce_to_string(activation, context)?
.encode_utf16()
.collect::<Vec<_>>(),
};
@ -276,7 +276,7 @@ fn index_of<'gc>(
let n = args
.get(1)
.unwrap_or(&Value::Undefined)
.coerce_to_i32(avm, context)?;
.coerce_to_i32(activation, context)?;
if n >= 0 {
n as usize
} else {
@ -303,27 +303,27 @@ fn index_of<'gc>(
}
fn last_index_of<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let this = Value::from(this)
.coerce_to_string(avm, context)?
.coerce_to_string(activation, context)?
.encode_utf16()
.collect::<Vec<u16>>();
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
.clone()
.coerce_to_string(avm, context)?
.coerce_to_string(activation, context)?
.encode_utf16()
.collect::<Vec<_>>(),
};
let start_index = match args.get(1) {
None | Some(Value::Undefined) => this.len(),
Some(n) => {
let n = n.coerce_to_i32(avm, context)?;
let n = n.coerce_to_i32(activation, context)?;
if n >= 0 {
let n = n as usize;
if n <= this.len() {
@ -355,28 +355,28 @@ fn last_index_of<'gc>(
}
fn slice<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() {
// No args returns undefined immediately.
return Ok(Value::Undefined.into());
return Ok(Value::Undefined);
}
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 start_index = string_wrapping_index(
args.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_i32(avm, context)?,
.coerce_to_i32(activation, context)?,
this_len,
);
let end_index = match args.get(1) {
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 {
let ret = utf16_iter_to_string(
@ -391,20 +391,20 @@ fn slice<'gc>(
}
fn split<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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 = delimiter_val.coerce_to_string(avm, context)?;
let delimiter = delimiter_val.coerce_to_string(activation, context)?;
let limit = match args.get(1) {
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() {
for (i, token) in this.split(delimiter.as_ref()).take(limit).enumerate() {
array.set_array_element(i, token.to_string().into(), context.gc_context);
@ -421,24 +421,26 @@ fn split<'gc>(
}
fn substr<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() {
return Ok(Value::Undefined.into());
return Ok(Value::Undefined);
}
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 start_index =
string_wrapping_index(args.get(0).unwrap().coerce_to_i32(avm, context)?, this_len);
let start_index = string_wrapping_index(
args.get(0).unwrap().coerce_to_i32(activation, context)?,
this_len,
);
let len = match args.get(1) {
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));
@ -446,23 +448,26 @@ fn substr<'gc>(
}
fn substring<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if args.is_empty() {
return Ok(Value::Undefined.into());
return Ok(Value::Undefined);
}
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 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) {
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.
@ -478,13 +483,13 @@ fn substring<'gc>(
}
fn to_lower_case<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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
.chars()
.map(string_utils::swf_char_to_lowercase)
@ -494,11 +499,11 @@ fn to_lower_case<'gc>(
/// `String.toString` / `String.valueOf` impl
pub fn to_string_value_of<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(vbox) = this.as_value_object() {
if let Value::String(s) = vbox.unbox() {
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]`,
//implying that `toString` and `valueOf` are inherent object properties and
//not just methods.
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
fn to_upper_case<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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
.chars()
.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::function::Executable;
use crate::avm1::object::Object;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
use crate::avm1::{ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use core::fmt;
use enumset::{EnumSet, EnumSetType};
@ -273,11 +273,11 @@ pub struct SystemProperties {
}
impl SystemProperties {
pub fn get_version_string(&self, avm: &Avm1) -> String {
pub fn get_version_string(&self, activation: &mut Activation) -> String {
format!(
"{} {},0,0,0",
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()
}
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())
.append_pair("A", self.encode_capability(SystemCapabilities::Audio))
.append_pair(
@ -347,7 +347,7 @@ impl SystemProperties {
"M",
&self.encode_string(
self.manufacturer
.get_manufacturer_string(avm.player_version)
.get_manufacturer_string(activation.avm().player_version)
.as_str(),
),
)
@ -358,7 +358,11 @@ impl SystemProperties {
.append_pair("COL", &self.screen_color.to_string())
.append_pair("AR", &self.aspect_ratio.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("PT", &self.player_type.to_string())
.append_pair(
@ -399,102 +403,102 @@ impl Default for SystemProperties {
}
pub fn set_clipboard<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let new_content = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_string(avm, action_context)?
.coerce_to_string(activation, action_context)?
.to_string();
action_context.input.set_clipboard_content(new_content);
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn show_settings<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
//TODO: should default to the last panel displayed
let last_panel_pos = 0;
let panel_pos = args
.get(0)
.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);
log::warn!("System.showSettings({:?}) not not implemented", panel);
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn set_use_code_page<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let value = args
.get(0)
.unwrap_or(&Value::Undefined)
.to_owned()
.as_bool(avm.current_swf_version());
.as_bool(activation.current_swf_version());
action_context.system.use_codepage = value;
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn get_use_code_page<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(action_context.system.use_codepage.into())
}
pub fn set_exact_settings<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let value = args
.get(0)
.unwrap_or(&Value::Undefined)
.to_owned()
.as_bool(avm.current_swf_version());
.as_bool(activation.current_swf_version());
action_context.system.exact_settings = value;
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn get_exact_settings<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(action_context.system.exact_settings.into())
}
pub fn on_status<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("System.onStatus() not implemented");
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn create<'gc>(

View File

@ -1,9 +1,9 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::globals::system::SystemCapabilities;
use crate::avm1::object::Object;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
use crate::avm1::{ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use gc_arena::MutationContext;
@ -11,11 +11,11 @@ use gc_arena::MutationContext;
macro_rules! capabilities_func {
($func_name: ident, $capability: expr) => {
pub fn $func_name<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.has_capability($capability).into())
}
};
@ -24,11 +24,11 @@ macro_rules! capabilities_func {
macro_rules! inverse_capabilities_func {
($func_name: ident, $capability: expr) => {
pub fn $func_name<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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);
pub fn get_player_type<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.player_type.to_string().into())
}
pub fn get_screen_color<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.screen_color.to_string().into())
}
pub fn get_language<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context
.system
.language
.get_language_code(avm.player_version)
.get_language_code(activation.avm().player_version)
.into())
}
pub fn get_screen_resolution_x<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.screen_resolution.0.into())
}
pub fn get_screen_resolution_y<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.screen_resolution.1.into())
}
pub fn get_pixel_aspect_ratio<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.aspect_ratio.into())
}
pub fn get_screen_dpi<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.dpi.into())
}
pub fn get_manufacturer<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context
.system
.manufacturer
.get_manufacturer_string(avm.player_version)
.get_manufacturer_string(activation.avm().player_version)
.into())
}
pub fn get_os_name<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.os.to_string().into())
}
pub fn get_version<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
Ok(context.system.get_version_string(avm).into())
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.get_version_string(activation).into())
}
pub fn get_server_string<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
Ok(context.system.get_server_string(avm).into())
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.get_server_string(activation).into())
}
pub fn get_cpu_architecture<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(context.system.cpu_architecture.to_string().into())
}
pub fn get_max_idc_level<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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::listeners::Listeners;
use crate::avm1::object::Object;
use crate::avm1::property::Attribute;
use crate::avm1::property::Attribute::{DontDelete, DontEnum, ReadOnly};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
use crate::avm1::{ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use gc_arena::MutationContext;
use std::convert::Into;
fn on_ime_composition<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into())
}
fn do_conversion<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(true.into())
}
fn get_conversion_mode<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok("KOREAN".into())
}
fn get_enabled<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into())
}
fn set_composition_string<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into())
}
fn set_conversion_mode<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into())
}
fn set_enabled<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(false.into())
}

View File

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

View File

@ -1,94 +1,95 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::globals::display_object;
use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject};
use crate::html::TextFormat;
use gc_arena::MutationContext;
/// Implements `TextField`
pub fn constructor<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
Ok(Value::Undefined.into())
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined)
}
pub fn get_text<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
return Ok(text_field.text().into());
}
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn set_text<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
if let Some(value) = args.get(0) {
if let Err(err) =
text_field.set_text(value.coerce_to_string(avm, context)?.to_string(), context)
{
if let Err(err) = text_field.set_text(
value.coerce_to_string(activation, context)?.to_string(),
context,
) {
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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
return Ok(text_field.is_html().into());
}
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn set_html<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
let text = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_string(avm, context)?;
.coerce_to_string(activation, 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).
}
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn get_border<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
return Ok(text_field.has_border().into());
}
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn set_border<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
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);
}
}
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn get_embed_fonts<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
return Ok((!text_field.is_device_font()).into());
}
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn set_embed_fonts<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
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);
}
}
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn get_length<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(text_field) = display_object.as_edit_text() {
return Ok((text_field.text_length() as f64).into());
}
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
macro_rules! with_text_field {
@ -201,13 +202,13 @@ macro_rules! with_text_field {
$(
$object.force_set_function(
$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(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>,
$gc_context,
DontDelete | ReadOnly | DontEnum,
@ -218,11 +219,11 @@ macro_rules! with_text_field {
}
pub fn text_width<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this
.as_display_object()
.and_then(|dobj| dobj.as_edit_text())
@ -232,15 +233,15 @@ pub fn text_width<'gc>(
return Ok(metrics.0.to_pixels().into());
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn text_height<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this
.as_display_object()
.and_then(|dobj| dobj.as_edit_text())
@ -250,15 +251,15 @@ pub fn text_height<'gc>(
return Ok(metrics.1.to_pixels().into());
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn multiline<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this
.as_display_object()
.and_then(|dobj| dobj.as_edit_text())
@ -266,20 +267,20 @@ pub fn multiline<'gc>(
return Ok(etext.is_multiline().into());
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn set_multiline<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let is_multiline = args
.get(0)
.cloned()
.unwrap_or(Value::Undefined)
.as_bool(avm.current_swf_version());
.as_bool(activation.current_swf_version());
if let Some(etext) = this
.as_display_object()
@ -288,15 +289,15 @@ pub fn set_multiline<'gc>(
etext.set_multiline(is_multiline, context);
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
fn variable<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this
.as_display_object()
.and_then(|dobj| dobj.as_edit_text())
@ -307,36 +308,36 @@ fn variable<'gc>(
}
// Unset `variable` retuns null, not undefined
Ok(Value::Null.into())
Ok(Value::Null)
}
fn set_variable<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let variable = match args.get(0) {
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
.as_display_object()
.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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this
.as_display_object()
.and_then(|dobj| dobj.as_edit_text())
@ -344,20 +345,20 @@ pub fn word_wrap<'gc>(
return Ok(etext.is_word_wrap().into());
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn set_word_wrap<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let is_word_wrap = args
.get(0)
.cloned()
.unwrap_or(Value::Undefined)
.as_bool(avm.current_swf_version());
.as_bool(activation.current_swf_version());
if let Some(etext) = this
.as_display_object()
@ -366,15 +367,15 @@ pub fn set_word_wrap<'gc>(
etext.set_word_wrap(is_word_wrap, context);
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn auto_size<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this
.as_display_object()
.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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(etext) = this
.as_display_object()
.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>(
@ -527,44 +528,44 @@ pub fn attach_virtual_properties<'gc>(gc_context: MutationContext<'gc, '_>, obje
fn get_new_text_format<'gc>(
text_field: EditText<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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>(
text_field: EditText<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let tf = args.get(0).cloned().unwrap_or(Value::Undefined);
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);
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
fn get_text_format<'gc>(
text_field: EditText<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let (from, to) = match (args.get(0), args.get(1)) {
(Some(f), Some(t)) => (
f.coerce_to_f64(avm, context)? as usize,
t.coerce_to_f64(avm, context)? as usize,
f.coerce_to_f64(activation, context)? as usize,
t.coerce_to_f64(activation, context)? as usize,
),
(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))
}
_ => (0, text_field.text_length()),
@ -572,28 +573,28 @@ fn get_text_format<'gc>(
Ok(text_field
.text_format(from, to)
.as_avm1_object(avm, context)?
.as_avm1_object(activation, context)?
.into())
}
fn set_text_format<'gc>(
text_field: EditText<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let tf = args.last().cloned().unwrap_or(Value::Undefined);
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)) {
(Some(f), Some(t)) if args.len() > 2 => (
f.coerce_to_f64(avm, context)? as usize,
t.coerce_to_f64(avm, context)? as usize,
f.coerce_to_f64(activation, context)? as usize,
t.coerce_to_f64(activation, context)? as usize,
),
(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))
}
_ => (0, text_field.text_length()),
@ -602,33 +603,33 @@ fn set_text_format<'gc>(
text_field.set_text_format(from, to, tf_parsed, context);
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
fn replace_text<'gc>(
text_field: EditText<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let from = args
.get(0)
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_f64(avm, context)?;
.coerce_to_f64(activation, context)?;
let to = args
.get(1)
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_f64(avm, context)?;
.coerce_to_f64(activation, context)?;
let text = args
.get(2)
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_string(avm, context)?
.coerce_to_string(activation, context)?
.into_owned();
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
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use gc_arena::MutationContext;
fn map_defined_to_string<'gc>(
name: &str,
this: Object<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
val: Option<Value<'gc>>,
) -> Result<(), Error<'gc>> {
@ -16,10 +16,10 @@ fn map_defined_to_string<'gc>(
Some(Value::Undefined) => Value::Null,
Some(Value::Null) => 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(())
}
@ -27,7 +27,7 @@ fn map_defined_to_string<'gc>(
fn map_defined_to_number<'gc>(
name: &str,
this: Object<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
val: Option<Value<'gc>>,
) -> Result<(), Error<'gc>> {
@ -35,10 +35,10 @@ fn map_defined_to_number<'gc>(
Some(Value::Undefined) => Value::Null,
Some(Value::Null) => 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(())
}
@ -46,7 +46,7 @@ fn map_defined_to_number<'gc>(
fn map_defined_to_bool<'gc>(
name: &str,
this: Object<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
val: Option<Value<'gc>>,
) -> Result<(), Error<'gc>> {
@ -54,36 +54,36 @@ fn map_defined_to_bool<'gc>(
Some(Value::Undefined) => Value::Null,
Some(Value::Null) => 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(())
}
/// `TextFormat` constructor
pub fn constructor<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
map_defined_to_string("font", this, avm, ac, args.get(0).cloned())?;
map_defined_to_number("size", this, avm, ac, args.get(1).cloned())?;
map_defined_to_number("color", this, avm, ac, args.get(2).cloned())?;
map_defined_to_bool("bold", this, avm, ac, args.get(3).cloned())?;
map_defined_to_bool("italic", this, avm, ac, args.get(4).cloned())?;
map_defined_to_bool("underline", this, avm, ac, args.get(5).cloned())?;
map_defined_to_string("url", this, avm, ac, args.get(6).cloned())?;
map_defined_to_string("target", this, avm, ac, args.get(7).cloned())?;
map_defined_to_string("align", this, avm, ac, args.get(8).cloned())?;
map_defined_to_number("leftMargin", this, avm, ac, args.get(9).cloned())?;
map_defined_to_number("rightMargin", this, avm, ac, args.get(10).cloned())?;
map_defined_to_number("indent", this, avm, ac, args.get(11).cloned())?;
map_defined_to_number("leading", this, avm, ac, args.get(12).cloned())?;
) -> Result<Value<'gc>, Error<'gc>> {
map_defined_to_string("font", this, activation, ac, args.get(0).cloned())?;
map_defined_to_number("size", this, activation, ac, args.get(1).cloned())?;
map_defined_to_number("color", this, activation, ac, args.get(2).cloned())?;
map_defined_to_bool("bold", this, activation, ac, args.get(3).cloned())?;
map_defined_to_bool("italic", this, activation, ac, args.get(4).cloned())?;
map_defined_to_bool("underline", this, activation, ac, args.get(5).cloned())?;
map_defined_to_string("url", this, activation, ac, args.get(6).cloned())?;
map_defined_to_string("target", this, activation, ac, args.get(7).cloned())?;
map_defined_to_string("align", this, activation, ac, args.get(8).cloned())?;
map_defined_to_number("leftMargin", this, activation, ac, args.get(9).cloned())?;
map_defined_to_number("rightMargin", this, activation, ac, args.get(10).cloned())?;
map_defined_to_number("indent", this, activation, ac, args.get(11).cloned())?;
map_defined_to_number("leading", this, activation, ac, args.get(12).cloned())?;
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
/// `TextFormat.prototype` constructor

View File

@ -1,12 +1,12 @@
//! XML/XMLNode global classes
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::script_object::ScriptObject;
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::xml;
use crate::xml::{XMLDocument, XMLNode};
@ -42,17 +42,17 @@ fn is_as2_compatible(node: XMLNode<'_>) -> bool {
/// XMLNode constructor
pub fn xmlnode_constructor<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let blank_document = XMLDocument::new(ac.gc_context);
match (
args.get(0)
.map(|v| v.coerce_to_f64(avm, ac).map(|v| v as u32)),
args.get(1).map(|v| v.coerce_to_string(avm, ac)),
.map(|v| v.coerce_to_f64(activation, ac).map(|v| v as u32)),
args.get(1).map(|v| v.coerce_to_string(activation, ac)),
this.as_xml_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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(mut xmlnode), Some(child_xmlnode)) = (
this.as_xml_node(),
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() {
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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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)) = (
this.as_xml_node(),
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)
.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 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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(xmlnode), deep) = (
this.as_xml_node(),
args.get(0)
.map(|v| v.as_bool(avm.current_swf_version()))
.map(|v| v.as_bool(activation.current_swf_version()))
.unwrap_or(false),
) {
let mut clone_node = xmlnode.duplicate(ac.gc_context, deep);
return Ok(Value::Object(
clone_node.script_object(ac.gc_context, Some(avm.prototypes.xml_node)),
)
.into());
return Ok(Value::Object(clone_node.script_object(
ac.gc_context,
Some(activation.avm().prototypes.xml_node),
)));
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn xmlnode_get_namespace_for_prefix<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(xmlnode), Some(prefix_string)) = (
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?) {
Ok(uri.into())
} else {
Ok(Value::Null.into())
Ok(Value::Null)
}
} else {
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
}
pub fn xmlnode_get_prefix_for_namespace<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let (Some(xmlnode), Some(uri_string)) = (
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?) {
Ok(prefix.into())
} else {
Ok(Value::Null.into())
Ok(Value::Null)
}
} else {
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
}
pub fn xmlnode_has_child_nodes<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(xmlnode) = this.as_xml_node() {
Ok((xmlnode.children_len() > 0).into())
} else {
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
}
pub fn xmlnode_remove_node<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
if let Ok(Some(mut parent)) = node.parent() {
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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(this
.as_xml_node()
.and_then(|n| n.tag_name())
.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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(this
.as_xml_node()
.and_then(|n| n.tag_name())
.map(|n| n.node_name().into())
.unwrap_or_else(|| Value::Null.into()))
.unwrap_or_else(|| Value::Null))
}
pub fn xmlnode_node_type<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(this
.as_xml_node()
.map(|n| {
@ -278,28 +278,28 @@ pub fn xmlnode_node_type<'gc>(
}
.into()
})
.unwrap_or_else(|| Value::Undefined.into()))
.unwrap_or_else(|| Value::Undefined))
}
pub fn xmlnode_node_value<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(this
.as_xml_node()
.and_then(|n| n.node_value())
.map(|n| n.into())
.unwrap_or_else(|| Value::Null.into()))
.unwrap_or_else(|| Value::Null))
}
pub fn xmlnode_prefix<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(this
.as_xml_node()
.and_then(|n| n.tag_name())
@ -308,17 +308,17 @@ pub fn xmlnode_prefix<'gc>(
.map(|n| n.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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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() {
let mut compatible_nodes = 0;
for mut child in children {
@ -329,7 +329,7 @@ pub fn xmlnode_child_nodes<'gc>(
array.set_array_element(
compatible_nodes as usize,
child
.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.into(),
ac.gc_context,
);
@ -341,80 +341,80 @@ pub fn xmlnode_child_nodes<'gc>(
return Ok(array.into());
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn xmlnode_first_child<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(mut children) = node.children() {
return Ok(children
.next()
.map(|mut child| {
child
.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(mut children) = node.children() {
return Ok(children
.next_back()
.map(|mut child| {
child
.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
return Ok(node
.parent()
.unwrap_or(None)
.map(|mut parent| {
parent
.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
.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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
let mut prev = node.prev_sibling().unwrap_or(None);
while let Some(my_prev) = prev {
@ -427,21 +427,21 @@ pub fn xmlnode_previous_sibling<'gc>(
return Ok(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()
})
.unwrap_or_else(|| Value::Null.into()));
.unwrap_or_else(|| Value::Null));
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn xmlnode_next_sibling<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
let mut next = node.next_sibling().unwrap_or(None);
while let Some(my_next) = next {
@ -454,37 +454,37 @@ pub fn xmlnode_next_sibling<'gc>(
return Ok(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()
})
.unwrap_or_else(|| Value::Null.into()));
.unwrap_or_else(|| Value::Null));
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn xmlnode_attributes<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(mut node) = this.as_xml_node() {
return Ok(node
.attribute_script_object(ac.gc_context)
.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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(name) = node.tag_name() {
return Ok(node
@ -493,10 +493,10 @@ pub fn xmlnode_namespace_uri<'gc>(
.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`.
@ -684,13 +684,13 @@ pub fn create_xmlnode_proto<'gc>(
/// XML (document) constructor
pub fn xml_constructor<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
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(),
) {
(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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let document = if let Some(node) = this.as_xml_node() {
node.document()
} else {
@ -730,10 +730,14 @@ pub fn xml_create_element<'gc>(
let nodename = args
.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();
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);
@ -741,11 +745,11 @@ pub fn xml_create_element<'gc>(
}
pub fn xml_create_text_node<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let document = if let Some(node) = this.as_xml_node() {
node.document()
} else {
@ -754,10 +758,14 @@ pub fn xml_create_text_node<'gc>(
let text_node = args
.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();
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);
@ -765,14 +773,14 @@ pub fn xml_create_text_node<'gc>(
}
pub fn xml_parse_xml<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(mut node) = this.as_xml_node() {
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
} else {
Cow::Borrowed("")
@ -783,7 +791,7 @@ pub fn xml_parse_xml<'gc>(
let result = node.remove_child(ac.gc_context, child);
if let Err(e) = result {
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>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let url = args.get(0).cloned().unwrap_or(Value::Undefined);
if let Value::Null = url {
@ -810,12 +818,12 @@ pub fn xml_load<'gc>(
}
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 target_clip = avm.target_clip_or_root();
let target_clip = activation.target_clip_or_root();
let process = ac.load_manager.load_xml_into_node(
ac.player.clone().unwrap(),
node,
@ -832,33 +840,33 @@ pub fn xml_load<'gc>(
}
pub fn xml_on_data<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
let src = args.get(0).cloned().unwrap_or(Value::Undefined);
if let Value::Undefined = src {
this.call_method("onLoad", &[false.into()], avm, ac)?;
this.call_method("onLoad", &[false.into()], activation, ac)?;
} else {
let src = src.coerce_to_string(avm, ac)?;
this.call_method("parseXML", &[src.into()], avm, ac)?;
let src = src.coerce_to_string(activation, 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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(doctype) = node.document().doctype() {
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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
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>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
return Ok(node.document().idmap_script_object(ac.gc_context).into());
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
pub fn xml_status<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_ac: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(node) = this.as_xml_node() {
return match node.document().last_parse_error() {
None => Ok(XML_NO_ERROR.into()),
Some(err) => match err.ref_error() {
ParseError::UnexpectedEof(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()),
ParseError::EndEventMismatch { .. } => Ok(Value::Number(XML_MISMATCHED_END).into()),
ParseError::XmlDeclWithoutVersion(_) => {
Ok(Value::Number(XML_DECL_NOT_TERMINATED).into())
}
ParseError::NameWithQuote(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()),
ParseError::NoEqAfterName(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()),
ParseError::UnquotedValue(_) => {
Ok(Value::Number(XML_ATTRIBUTE_NOT_TERMINATED).into())
}
ParseError::DuplicatedAttribute(_, _) => {
Ok(Value::Number(XML_ELEMENT_MALFORMED).into())
}
_ => Ok(Value::Number(XML_OUT_OF_MEMORY).into()), //Not accounted for:
//ParseError::UnexpectedToken(_)
//ParseError::UnexpectedBang
//ParseError::TextNotFound
//ParseError::EscapeError(_)
ParseError::UnexpectedEof(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
ParseError::EndEventMismatch { .. } => Ok(Value::Number(XML_MISMATCHED_END)),
ParseError::XmlDeclWithoutVersion(_) => Ok(Value::Number(XML_DECL_NOT_TERMINATED)),
ParseError::NameWithQuote(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
ParseError::NoEqAfterName(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
ParseError::UnquotedValue(_) => Ok(Value::Number(XML_ATTRIBUTE_NOT_TERMINATED)),
ParseError::DuplicatedAttribute(_, _) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
_ => Ok(Value::Number(XML_OUT_OF_MEMORY)), //Not accounted for:
//ParseError::UnexpectedToken(_)
//ParseError::UnexpectedBang
//ParseError::TextNotFound
//ParseError::EscapeError(_)
},
};
}
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
/// Construct the prototype for `XML`.

View File

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

View File

@ -3,15 +3,15 @@
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::shared_object::SharedObject;
use crate::avm1::super_object::SuperObject;
use crate::avm1::value_object::ValueObject;
use crate::avm1::activation::Activation;
use crate::avm1::xml_attributes_object::XMLAttributesObject;
use crate::avm1::xml_idmap_object::XMLIDMapObject;
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::xml::XMLNode;
use enumset::EnumSet;
@ -50,7 +50,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
fn get_local(
&self,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>>;
@ -59,15 +59,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
fn get(
&self,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> {
if self.has_own_property(avm, context, name) {
self.get_local(name, avm, context, (*self).into())
if self.has_own_property(activation, context, name) {
self.get_local(name, activation, context, (*self).into())
} else {
search_prototype(self.proto(), name, avm, context, (*self).into())?
.0
.resolve(avm, context)
Ok(search_prototype(self.proto(), name, activation, context, (*self).into())?.0)
}
}
@ -76,7 +74,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, '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`.
fn call(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
@ -105,24 +103,29 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
&self,
name: &str,
args: &[Value<'gc>],
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> {
let (method, base_proto) =
search_prototype(Some((*self).into()), name, avm, context, (*self).into())?;
let method = method.resolve(avm, context)?;
let (method, base_proto) = search_prototype(
Some((*self).into()),
name,
activation,
context,
(*self).into(),
)?;
let method = method;
if let Value::Object(_) = method {
} else {
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.
///
/// 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
/// or non-existent setter fail silently.
///
@ -133,10 +136,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<ReturnValue<'gc>, Error<'gc>>;
) -> Option<Executable<'gc>>;
/// 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.
fn new(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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.
///
/// Returns false if the property cannot be deleted.
fn delete(&self, avm: &mut Avm1<'gc>, gc_context: MutationContext<'gc, '_>, name: &str)
-> bool;
fn delete(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool;
/// 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__`.
fn add_property_with_case(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
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.
fn has_property(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool;
@ -262,7 +268,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// say, the object's prototype or superclass)
fn has_own_property(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool;
@ -271,19 +277,19 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// virtual.
fn has_own_virtual(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool;
/// 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.
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.
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.
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.
fn is_instance_of(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
constructor: 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);
}
if avm.current_swf_version() >= 7 {
if activation.current_swf_version() >= 7 {
for interface in this_proto.interfaces() {
if Object::ptr_eq(interface, constructor) {
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);
}
}
@ -464,10 +470,10 @@ impl<'gc> Object<'gc> {
pub fn search_prototype<'gc>(
mut proto: Option<Object<'gc>>,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<(ReturnValue<'gc>, Option<Object<'gc>>), Error<'gc>> {
) -> Result<(Value<'gc>, Option<Object<'gc>>), Error<'gc>> {
let mut depth = 0;
while proto.is_some() {
@ -475,9 +481,9 @@ pub fn search_prototype<'gc>(
return Err(Error::PrototypeRecursionLimit);
}
if proto.unwrap().has_own_property(avm, context, name) {
if proto.unwrap().has_own_property(activation, context, name) {
return Ok((
proto.unwrap().get_local(name, avm, context, this)?.into(),
proto.unwrap().get_local(name, activation, context, this)?,
proto,
));
}
@ -486,5 +492,5 @@ pub fn search_prototype<'gc>(
depth += 1;
}
Ok((Value::Undefined.into(), None))
Ok((Value::Undefined, None))
}

View File

@ -1,10 +1,8 @@
//! User-defined properties
use self::Attribute::*;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, UpdateContext, Value};
use crate::avm1::Value;
use core::fmt;
use enumset::{EnumSet, EnumSetType};
@ -32,42 +30,18 @@ pub enum 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.
///
/// 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
/// discarded.
pub fn set(
&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>> {
pub fn set(&mut self, new_value: impl Into<Value<'gc>>) -> Option<Executable<'gc>> {
match self {
Property::Virtual { set, .. } => {
if let Some(function) = set {
function.exec(avm, context, this, base_proto, &[new_value.into()])
Some(function.to_owned())
} else {
Ok(Value::Undefined.into())
None
}
}
Property::Stored {
@ -77,7 +51,7 @@ impl<'gc> Property<'gc> {
*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.
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
use enumset::EnumSet;
use gc_arena::{GcCell, MutationContext};
use std::cell::Ref;
@ -207,6 +207,11 @@ impl<'gc> Scope<'gc> {
&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.
#[allow(dead_code)]
pub fn locals_mut(&mut self) -> &mut Object<'gc> {
@ -234,34 +239,34 @@ impl<'gc> Scope<'gc> {
pub fn resolve(
&self,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if self.locals().has_property(avm, context, name) {
return Ok(self.locals().get(name, avm, context)?.into());
) -> Result<Value<'gc>, Error<'gc>> {
if self.locals().has_property(activation, context, name) {
return Ok(self.locals().get(name, activation, context)?);
}
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?
Ok(Value::Undefined.into())
Ok(Value::Undefined)
}
/// Check if a particular property in the scope chain is defined.
pub fn is_defined(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
if self.locals().has_property(avm, context, name) {
if self.locals().has_property(activation, context, name) {
return true;
}
if let Some(scope) = self.parent() {
return scope.is_defined(avm, context, name);
return scope.is_defined(activation, context, name);
}
false
@ -277,26 +282,26 @@ impl<'gc> Scope<'gc> {
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<(), Error<'gc>> {
if self.class == ScopeClass::Target
|| (self.locals().has_property(avm, context, name)
&& self.locals().is_property_overwritable(avm, name))
|| (self.locals().has_property(activation, context, name)
&& self.locals().is_property_overwritable(activation, name))
{
// Value found on this object, so overwrite it.
// 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() {
// Traverse the scope chain in search of the value.
scope.set(name, value, avm, context, this)
scope.set(name, value, activation, context, this)
} else {
// This probably shouldn't happen -- all AVM1 code runs in reference to some movieclip,
// so we should always have a movieclip scope.
// Define on the top-level 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
pub fn delete(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
mc: MutationContext<'gc, '_>,
) -> bool {
if self.locals().has_property(avm, context, name) {
return self.locals().delete(avm, mc, name);
if self.locals().has_property(activation, context, name) {
return self.locals().delete(activation, mc, name);
}
if let Some(scope) = self.parent() {
return scope.delete(avm, context, name, mc);
return scope.delete(activation, context, name, mc);
}
false

View File

@ -1,8 +1,8 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject, NativeFunction};
use crate::avm1::property::{Attribute, Property};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ObjectPtr, TObject, UpdateContext, Value};
use crate::avm1::{Object, ObjectPtr, TObject, UpdateContext, Value};
use crate::property_map::{Entry, PropertyMap};
use core::fmt;
use enumset::EnumSet;
@ -197,19 +197,20 @@ impl<'gc> ScriptObject<'gc> {
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
) -> Result<(), Error<'gc>> {
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>() {
self.set_array_element(index, value.to_owned(), context.gc_context);
} else if !name.is_empty() {
if name == "length" {
let length = value
.coerce_to_f64(avm, context)
.coerce_to_f64(activation, context)
.map(|v| v.abs() as i32)
.unwrap_or(0);
if length > 0 {
@ -226,14 +227,14 @@ impl<'gc> ScriptObject<'gc> {
.0
.read()
.values
.contains_key(name, avm.is_case_sensitive());
let mut rval = None;
.contains_key(name, activation.is_case_sensitive());
let mut worked = false;
if is_vacant {
let mut proto: Option<Object<'gc>> = Some((*self).into());
while let Some(this_proto) = proto {
if this_proto.has_own_virtual(avm, context, name)
&& this_proto.is_property_overwritable(avm, name)
if this_proto.has_own_virtual(activation, context, name)
&& this_proto.is_property_overwritable(activation, name)
{
break;
}
@ -242,42 +243,45 @@ impl<'gc> ScriptObject<'gc> {
}
if let Some(this_proto) = proto {
rval = Some(this_proto.call_setter(
name,
value.clone(),
avm,
context,
(*self).into(),
)?);
if let Some(rval) =
this_proto.call_setter(name, value.clone(), activation, context)
{
let _ = rval.exec(
activation,
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
//to end before we can do so.
if rval.is_none() {
rval = match self
if !worked {
let rval = match self
.0
.write(context.gc_context)
.values
.entry(name.to_owned(), avm.is_case_sensitive())
.entry(name.to_owned(), activation.is_case_sensitive())
{
Entry::Occupied(mut entry) => {
Some(entry.get_mut().set(avm, context, this, base_proto, value)?)
}
Entry::Occupied(mut entry) => entry.get_mut().set(value.clone()),
Entry::Vacant(entry) => {
entry.insert(Property::Stored {
value,
value: value.clone(),
attributes: Default::default(),
});
None
}
};
}
if let Some(rval) = rval {
rval.resolve(avm, context)?;
if let Some(rval) = rval {
let _ = rval.exec(activation, context, this, base_proto, &[value])?;
}
}
}
@ -297,7 +301,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
fn get_local(
&self,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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));
}
if let Some(value) = self.0.read().values.get(name, avm.is_case_sensitive()) {
return value
.get(avm, context, this, Some((*self).into()))?
.resolve(avm, context);
let mut exec = None;
if let Some(value) = self
.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.
@ -323,13 +339,13 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
self.internal_set(
name,
value,
avm,
activation,
context,
(*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.
fn call(
&self,
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_base_proto: Option<Object<'gc>>,
@ -356,27 +372,24 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Option<Executable<'gc>> {
match self
.0
.write(context.gc_context)
.values
.get_mut(name, avm.is_case_sensitive())
.get_mut(name, activation.is_case_sensitive())
{
Some(propref) if propref.is_virtual() => {
propref.set(avm, context, this, Some((*self).into()), value)
}
_ => Ok(Value::Undefined.into()),
Some(propref) if propref.is_virtual() => propref.set(value),
_ => None,
}
}
#[allow(clippy::new_ret_no_self)]
fn new(
&self,
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
@ -396,14 +409,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
/// Returns false if the property cannot be deleted.
fn delete(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool {
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() {
object.values.remove(name, avm.is_case_sensitive());
object.values.remove(name, activation.is_case_sensitive());
return true;
}
}
@ -432,7 +445,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
fn add_property_with_case(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
@ -446,7 +459,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
set,
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.
fn has_property(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.has_own_property(avm, context, name)
self.has_own_property(activation, context, name)
|| self
.proto()
.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,
/// say, the object's prototype or superclass)
fn has_own_property(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
@ -524,34 +537,44 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
self.0
.read()
.values
.contains_key(name, avm.is_case_sensitive())
.contains_key(name, activation.is_case_sensitive())
}
fn has_own_virtual(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> 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()
} else {
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
.read()
.values
.get(name, avm.is_case_sensitive())
.get(name, activation.is_case_sensitive())
.map(|p| p.is_overwritable())
.unwrap_or(false)
}
/// Checks if a named property appears when enumerating the object.
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
if let Some(prop) = self.0.read().values.get(name, avm.is_case_sensitive()) {
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
if let Some(prop) = self
.0
.read()
.values
.get(name, activation.is_case_sensitive())
{
prop.is_enumerable()
} else {
false
@ -559,17 +582,19 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
}
/// Enumerate the object.
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
let proto_keys = self.proto().map_or_else(Vec::new, |p| p.get_keys(avm));
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
let proto_keys = self
.proto()
.map_or_else(Vec::new, |p| p.get_keys(activation));
let mut out_keys = vec![];
let object = self.0.read();
// Prototype keys come first.
out_keys.extend(
proto_keys
.into_iter()
.filter(|k| !object.values.contains_key(k, avm.is_case_sensitive())),
);
out_keys.extend(proto_keys.into_iter().filter(|k| {
!object
.values
.contains_key(k, activation.is_case_sensitive())
}));
// Then our own keys.
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 {
use super::*;
use crate::avm1::activation::Activation;
use crate::avm1::globals::system::SystemProperties;
use crate::avm1::property::Attribute::*;
use crate::avm1::Avm1;
use crate::backend::audio::NullAudioBackend;
use crate::backend::input::NullInputBackend;
use crate::backend::navigator::NullNavigatorBackend;
@ -730,7 +755,11 @@ mod tests {
fn with_object<F, R>(swf_version: u8, test: F) -> R
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| {
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 globals = avm.global_object_cell();
avm.insert_stack_frame(GcCell::allocate(
gc_context,
Activation::from_nothing(swf_version, globals, gc_context, root),
));
let mut activation = Activation::from_nothing(
&mut avm,
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]
fn test_get_undefined() {
with_object(0, |avm, context, object| {
with_object(0, |activation, context, object| {
assert_eq!(
object.get("not_defined", avm, context).unwrap(),
object.get("not_defined", activation, context).unwrap(),
Value::Undefined
);
})
@ -801,7 +833,7 @@ mod tests {
#[test]
fn test_set_get() {
with_object(0, |avm, context, object| {
with_object(0, |activation, context, object| {
object.as_script_object().unwrap().define_value(
context.gc_context,
"forced",
@ -809,12 +841,15 @@ mod tests {
EnumSet::empty(),
);
object
.set("natural", "natural".into(), avm, context)
.set("natural", "natural".into(), activation, context)
.unwrap();
assert_eq!(object.get("forced", avm, context).unwrap(), "forced".into());
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()
);
})
@ -822,7 +857,7 @@ mod tests {
#[test]
fn test_set_readonly() {
with_object(0, |avm, context, object| {
with_object(0, |activation, context, object| {
object.as_script_object().unwrap().define_value(
context.gc_context,
"normal",
@ -837,18 +872,18 @@ mod tests {
);
object
.set("normal", "replaced".into(), avm, context)
.set("normal", "replaced".into(), activation, context)
.unwrap();
object
.set("readonly", "replaced".into(), avm, context)
.set("readonly", "replaced".into(), activation, context)
.unwrap();
assert_eq!(
object.get("normal", avm, context).unwrap(),
object.get("normal", activation, context).unwrap(),
"replaced".into()
);
assert_eq!(
object.get("readonly", avm, context).unwrap(),
object.get("readonly", activation, context).unwrap(),
"initial".into()
);
})
@ -856,7 +891,7 @@ mod tests {
#[test]
fn test_deletable_not_readonly() {
with_object(0, |avm, context, object| {
with_object(0, |activation, context, object| {
object.as_script_object().unwrap().define_value(
context.gc_context,
"test",
@ -864,26 +899,30 @@ mod tests {
DontDelete.into(),
);
assert_eq!(object.delete(avm, context.gc_context, "test"), false);
assert_eq!(object.get("test", avm, context).unwrap(), "initial".into());
assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!(
object.get("test", activation, context).unwrap(),
"initial".into()
);
object
.as_script_object()
.unwrap()
.set("test", "replaced".into(), avm, context)
.set("test", "replaced".into(), activation, context)
.unwrap();
assert_eq!(object.delete(avm, context.gc_context, "test"), false);
assert_eq!(object.get("test", avm, context).unwrap(), "replaced".into());
assert_eq!(object.delete(activation, context.gc_context, "test"), false);
assert_eq!(
object.get("test", activation, context).unwrap(),
"replaced".into()
);
})
}
#[test]
fn test_virtual_get() {
with_object(0, |avm, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| {
Ok(ReturnValue::Immediate("Virtual!".into()))
});
with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| Ok("Virtual!".into()));
object.as_script_object().unwrap().add_property(
context.gc_context,
@ -893,20 +932,26 @@ mod tests {
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
object.set("test", "Ignored!".into(), avm, context).unwrap();
assert_eq!(object.get("test", avm, context).unwrap(), "Virtual!".into());
object
.set("test", "Ignored!".into(), activation, context)
.unwrap();
assert_eq!(
object.get("test", activation, context).unwrap(),
"Virtual!".into()
);
})
}
#[test]
fn test_delete() {
with_object(0, |avm, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| {
Ok(ReturnValue::Immediate("Virtual!".into()))
});
with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| Ok("Virtual!".into()));
object.as_script_object().unwrap().add_property(
context.gc_context,
@ -935,29 +980,41 @@ mod tests {
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!(
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
);
assert_eq!(
object.get("virtual", avm, context).unwrap(),
object.get("virtual", activation, context).unwrap(),
Value::Undefined
);
assert_eq!(
object.get("virtual_un", avm, context).unwrap(),
object.get("virtual_un", activation, context).unwrap(),
"Virtual!".into()
);
assert_eq!(
object.get("stored", avm, context).unwrap(),
object.get("stored", activation, context).unwrap(),
Value::Undefined
);
assert_eq!(
object.get("stored_un", avm, context).unwrap(),
object.get("stored_un", activation, context).unwrap(),
"Stored!".into()
);
})
@ -965,10 +1022,8 @@ mod tests {
#[test]
fn test_iter_values() {
with_object(0, |avm, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| {
Ok(ReturnValue::Immediate(Value::Null))
});
with_object(0, |activation, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| Ok(Value::Null));
object.as_script_object().unwrap().define_value(
context.gc_context,
@ -997,7 +1052,7 @@ mod tests {
DontEnum.into(),
);
let keys: Vec<_> = object.get_keys(avm);
let keys: Vec<_> = object.get_keys(activation);
assert_eq!(keys.len(), 2);
assert_eq!(keys.contains(&"stored".to_string()), true);
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::function::Executable;
use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
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::display_object::DisplayObject;
use enumset::EnumSet;
@ -73,66 +73,67 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
fn get_local(
&self,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
self.base().get_local(name, avm, context, this)
self.base().get_local(name, activation, context, this)
}
fn set(
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
self.base().set(name, value, avm, context)
self.base().set(name, value, activation, context)
}
fn call(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'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(
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<ReturnValue<'gc>, Error<'gc>> {
self.base().call_setter(name, value, avm, context, this)
) -> Option<Executable<'gc>> {
self.base().call_setter(name, value, activation, context)
}
#[allow(clippy::new_ret_no_self)]
fn new(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Object<'gc>, Error<'gc>> {
Ok(
SharedObject::empty_shared_obj(context.gc_context, Some(avm.prototypes.shared_object))
.into(),
Ok(SharedObject::empty_shared_obj(
context.gc_context,
Some(activation.avm().prototypes.shared_object),
)
.into())
}
fn delete(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool {
self.base().delete(avm, gc_context, name)
self.base().delete(activation, gc_context, name)
}
fn proto(&self) -> Option<Object<'gc>> {
@ -179,7 +180,7 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
fn add_property_with_case(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
@ -187,46 +188,46 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
attributes: EnumSet<Attribute>,
) {
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(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_property(avm, context, name)
self.base().has_property(activation, context, name)
}
fn has_own_property(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_property(avm, context, name)
self.base().has_own_property(activation, context, name)
}
fn has_own_virtual(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> 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 {
self.base().is_property_overwritable(avm, name)
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_overwritable(activation, name)
}
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
self.base().is_property_enumerable(avm, name)
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(activation, name)
}
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
self.base().get_keys(avm)
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(activation)
}
fn as_string(&self) -> Cow<str> {

View File

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

View File

@ -1,11 +1,11 @@
//! AVM1 object type to represent objects on the stage.
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::object::search_prototype;
use crate::avm1::property::Attribute;
use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value};
use crate::avm1::{Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value};
use crate::context::UpdateContext;
use crate::display_object::{DisplayObject, EditText, MovieClip};
use crate::property_map::PropertyMap;
@ -127,19 +127,19 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn get(
&self,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> {
let obj = self.0.read();
let props = avm.display_properties;
let case_sensitive = avm.is_case_sensitive();
let props = activation.avm().display_properties;
let case_sensitive = activation.is_case_sensitive();
// 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
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) {
// 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)
} else if let Some(child) = obj.display_object.get_child_by_name(name, case_sensitive) {
// 3) Child display objects with the given instance name
@ -152,9 +152,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
Ok(level.object())
} else {
// 5) Prototype
search_prototype(self.proto(), name, avm, context, (*self).into())?
.0
.resolve(avm, context)
Ok(search_prototype(self.proto(), name, activation, context, (*self).into())?.0)
}
// 6) TODO: __resolve?
}
@ -162,22 +160,25 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn get_local(
&self,
name: &str,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'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(
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
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.
for binding in obj
@ -185,31 +186,32 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
.iter()
.filter(|binding| binding.variable_name == name)
{
let _ = binding
.text_field
.set_html_text(value.coerce_to_string(avm, context)?.into_owned(), context);
let _ = binding.text_field.set_html_text(
value.coerce_to_string(activation, context)?.into_owned(),
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
obj.base.internal_set(
name,
value,
avm,
activation,
context,
(*self).into(),
Some((*self).into()),
)
} else if let Some(property) = props.read().get_by_name(&name) {
// 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(())
} else {
// 3) TODO: Prototype
obj.base.internal_set(
name,
value,
avm,
activation,
context,
(*self).into(),
Some((*self).into()),
@ -219,7 +221,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn call(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
@ -228,42 +230,41 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
self.0
.read()
.base
.call(avm, context, this, base_proto, args)
.call(activation, context, this, base_proto, args)
}
fn call_setter(
&self,
name: &str,
value: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Option<Executable<'gc>> {
self.0
.read()
.base
.call_setter(name, value, avm, context, this)
.call_setter(name, value, activation, context)
}
#[allow(clippy::new_ret_no_self)]
fn new(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Object<'gc>, Error<'gc>> {
//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(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> 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>> {
@ -318,7 +319,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn add_property_with_case(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
@ -328,21 +329,21 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
self.0
.read()
.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(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
let obj = self.0.read();
if obj.base.has_property(avm, context, name) {
if obj.base.has_property(activation, context, name) {
return true;
}
let case_sensitive = avm.is_case_sensitive();
let case_sensitive = activation.is_case_sensitive();
if obj
.display_object
.get_child_by_name(name, case_sensitive)
@ -364,36 +365,45 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
fn has_own_property(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
// 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(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> 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 {
self.0.read().base.is_property_enumerable(avm, name)
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0.read().base.is_property_enumerable(activation, name)
}
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
self.0.read().base.is_property_overwritable(avm, name)
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
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
// child display objects in order from highest depth to lowest depth.
let obj = self.0.read();
let mut keys = obj.base.get_keys(avm);
let mut keys = obj.base.get_keys(activation);
keys.extend(
obj.display_object
.children()
@ -486,13 +496,13 @@ pub struct DisplayProperty<'gc> {
}
pub type DisplayGetter<'gc> = fn(
&mut Avm1<'gc>,
&mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>,
DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>>;
pub type DisplaySetter<'gc> = fn(
&mut Avm1<'gc>,
&mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>,
DisplayObject<'gc>,
Value<'gc>,
@ -501,22 +511,22 @@ pub type DisplaySetter<'gc> = fn(
impl<'gc> DisplayProperty<'gc> {
pub fn get(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
(self.get)(avm, context, this)
(self.get)(activation, context, this)
}
pub fn set(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
value: Value<'gc>,
) -> Result<(), Error<'gc>> {
self.set
.map(|f| f(avm, context, this, value))
.map(|f| f(activation, context, this, value))
.unwrap_or(Ok(()))
}
}
@ -593,7 +603,7 @@ impl<'gc> DisplayPropertyMap<'gc> {
}
fn x<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -601,19 +611,19 @@ fn x<'gc>(
}
fn set_x<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
val: Value<'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);
}
Ok(())
}
fn y<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -621,19 +631,19 @@ fn y<'gc>(
}
fn set_y<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
val: Value<'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);
}
Ok(())
}
fn x_scale<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -642,19 +652,19 @@ fn x_scale<'gc>(
}
fn set_x_scale<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
val: Value<'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);
}
Ok(())
}
fn y_scale<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -663,19 +673,19 @@ fn y_scale<'gc>(
}
fn set_y_scale<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
val: Value<'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);
}
Ok(())
}
fn current_frame<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -687,7 +697,7 @@ fn current_frame<'gc>(
}
fn total_frames<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -699,7 +709,7 @@ fn total_frames<'gc>(
}
fn alpha<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -708,19 +718,19 @@ fn alpha<'gc>(
}
fn set_alpha<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
val: Value<'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);
}
Ok(())
}
fn visible<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -729,21 +739,21 @@ fn visible<'gc>(
}
fn set_visible<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error<'gc>> {
// 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.
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);
}
Ok(())
}
fn width<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -751,19 +761,19 @@ fn width<'gc>(
}
fn set_width<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
val: Value<'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);
}
Ok(())
}
fn height<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -771,19 +781,19 @@ fn height<'gc>(
}
fn set_height<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
val: Value<'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);
}
Ok(())
}
fn rotation<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -791,12 +801,12 @@ fn rotation<'gc>(
}
fn set_rotation<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
degrees: Value<'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].
degrees %= 360.0;
if degrees < -180.0 {
@ -810,7 +820,7 @@ fn set_rotation<'gc>(
}
fn target<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -818,7 +828,7 @@ fn target<'gc>(
}
fn frames_loaded<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -830,7 +840,7 @@ fn frames_loaded<'gc>(
}
fn name<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -838,18 +848,18 @@ fn name<'gc>(
}
fn set_name<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut this: DisplayObject<'gc>,
val: Value<'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);
Ok(())
}
fn drop_target<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -858,7 +868,7 @@ fn drop_target<'gc>(
}
fn url<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -867,7 +877,7 @@ fn url<'gc>(
}
fn high_quality<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -876,7 +886,7 @@ fn high_quality<'gc>(
}
fn set_high_quality<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
_val: Value<'gc>,
@ -886,7 +896,7 @@ fn set_high_quality<'gc>(
}
fn focus_rect<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -895,7 +905,7 @@ fn focus_rect<'gc>(
}
fn set_focus_rect<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
_val: Value<'gc>,
@ -905,7 +915,7 @@ fn set_focus_rect<'gc>(
}
fn sound_buf_time<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -914,7 +924,7 @@ fn sound_buf_time<'gc>(
}
fn set_sound_buf_time<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
_val: Value<'gc>,
@ -924,7 +934,7 @@ fn set_sound_buf_time<'gc>(
}
fn quality<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -933,7 +943,7 @@ fn quality<'gc>(
}
fn set_quality<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: DisplayObject<'gc>,
_val: Value<'gc>,
@ -943,7 +953,7 @@ fn set_quality<'gc>(
}
fn x_mouse<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -952,7 +962,7 @@ fn x_mouse<'gc>(
}
fn y_mouse<'gc>(
_avm: &mut Avm1<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: DisplayObject<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
@ -961,12 +971,12 @@ fn y_mouse<'gc>(
}
fn property_coerce_to_number<'gc>(
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
value: Value<'gc>,
) -> Result<Option<f64>, Error<'gc>> {
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() {
return Ok(Some(n));
}

View File

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

View File

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

View File

@ -1,28 +1,18 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::test_utils::with_avm;
use crate::avm1::TObject;
use gc_arena::GcCell;
#[test]
fn locals_into_form_values() {
with_avm(19, |avm, context, _this| -> Result<(), Error> {
let my_activation = Activation::from_nothing(
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();
with_avm(19, |activation, context, _this| -> Result<(), Error> {
let my_locals = activation.scope().locals().to_owned();
my_locals
.set("value1", "string".into(), avm, context)
.set("value1", "string".into(), activation, context)
.unwrap();
my_locals.set("value2", 2.0.into(), avm, context).unwrap();
avm.insert_stack_frame(GcCell::allocate(context.gc_context, my_activation));
let my_local_values = avm.locals_into_form_values(context);
my_locals
.set("value2", 2.0.into(), activation, context)
.unwrap();
let my_local_values = activation.locals_into_form_values(context);
assert_eq!(my_local_values.len(), 2);
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::value_object::ValueObject;
use crate::avm1::{Avm1, Object, TObject, UpdateContext};
use crate::avm1::{Object, TObject, UpdateContext};
use std::borrow::Cow;
use std::f64::NAN;
@ -157,19 +158,19 @@ impl<'gc> Value<'gc> {
/// * In SWF5 and lower, hexadecimal is unsupported.
fn primitive_as_number(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
) -> f64 {
match self {
Value::Undefined if avm.current_swf_version() < 7 => 0.0,
Value::Null if avm.current_swf_version() < 7 => 0.0,
Value::Undefined if activation.current_swf_version() < 7 => 0.0,
Value::Null if activation.current_swf_version() < 7 => 0.0,
Value::Undefined => NAN,
Value::Null => NAN,
Value::Bool(false) => 0.0,
Value::Bool(true) => 1.0,
Value::Number(v) => *v,
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;
for c in v[2..].bytes() {
n = n.wrapping_shl(4);
@ -195,7 +196,7 @@ impl<'gc> Value<'gc> {
}
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[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
pub fn coerce_to_f64(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<f64, Error<'gc>> {
Ok(match self {
Value::Object(_) => self
.to_primitive_num(avm, context)?
.primitive_as_number(avm, context),
val => val.primitive_as_number(avm, context),
.to_primitive_num(activation, context)?
.primitive_as_number(activation, context),
val => val.primitive_as_number(activation, context),
})
}
@ -247,11 +248,11 @@ impl<'gc> Value<'gc> {
/// return `undefined` rather than yielding a runtime error.
pub fn to_primitive_num(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> {
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(),
})
}
@ -261,18 +262,18 @@ impl<'gc> Value<'gc> {
pub fn abstract_lt(
&self,
other: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error<'gc>> {
let prim_self = self.to_primitive_num(avm, context)?;
let prim_other = other.to_primitive_num(avm, context)?;
let prim_self = self.to_primitive_num(activation, context)?;
let prim_other = other.to_primitive_num(activation, context)?;
if let (Value::String(a), Value::String(b)) = (&prim_self, &prim_other) {
return Ok(a.to_string().bytes().lt(b.to_string().bytes()).into());
}
let num_self = prim_self.primitive_as_number(avm, context);
let num_other = prim_other.primitive_as_number(avm, context);
let num_self = prim_self.primitive_as_number(activation, context);
let num_other = prim_other.primitive_as_number(activation, context);
if num_self.is_nan() || num_other.is_nan() {
return Ok(Value::Undefined);
@ -301,7 +302,7 @@ impl<'gc> Value<'gc> {
pub fn abstract_eq(
&self,
other: Value<'gc>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
coerced: bool,
) -> Result<Value<'gc>, Error<'gc>> {
@ -327,62 +328,62 @@ impl<'gc> Value<'gc> {
(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::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)) => {
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::Null, Value::Undefined) => Ok(true.into()),
(Value::Number(_), Value::String(_)) => Ok(self.abstract_eq(
Value::Number(other.coerce_to_f64(avm, context)?),
avm,
Value::Number(other.coerce_to_f64(activation, context)?),
activation,
context,
true,
)?),
(Value::String(_), Value::Number(_)) => {
Ok(Value::Number(self.coerce_to_f64(avm, context)?)
.abstract_eq(other, avm, context, true)?)
Ok(Value::Number(self.coerce_to_f64(activation, context)?)
.abstract_eq(other, activation, context, true)?)
}
(Value::Bool(_), _) => Ok(Value::Number(self.coerce_to_f64(avm, context)?)
.abstract_eq(other, avm, context, true)?),
(Value::Bool(_), _) => Ok(Value::Number(self.coerce_to_f64(activation, context)?)
.abstract_eq(other, activation, context, true)?),
(_, Value::Bool(_)) => Ok(self.abstract_eq(
Value::Number(other.coerce_to_f64(avm, context)?),
avm,
Value::Number(other.coerce_to_f64(activation, context)?),
activation,
context,
true,
)?),
(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 {
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(_)) => {
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 {
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(_)) => {
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 {
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(_)) => {
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 {
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()),
}
@ -408,10 +409,11 @@ impl<'gc> Value<'gc> {
#[allow(dead_code)]
pub fn coerce_to_u16(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, '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.
@ -420,10 +422,11 @@ impl<'gc> Value<'gc> {
#[allow(dead_code)]
pub fn coerce_to_i16(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, '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`.
@ -433,10 +436,11 @@ impl<'gc> Value<'gc> {
#[allow(dead_code)]
pub fn coerce_to_i32(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, '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`.
@ -445,25 +449,28 @@ impl<'gc> Value<'gc> {
#[allow(dead_code)]
pub fn coerce_to_u32(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, '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.
pub fn coerce_to_string<'a>(
&'a self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Cow<'a, str>, Error<'gc>> {
Ok(match self {
Value::Object(object) => match object.call_method("toString", &[], avm, context)? {
Value::String(s) => Cow::Owned(s),
_ => Cow::Borrowed("[type Object]"),
},
Value::Object(object) => {
match object.call_method("toString", &[], activation, context)? {
Value::String(s) => Cow::Owned(s),
_ => Cow::Borrowed("[type Object]"),
}
}
Value::Undefined => {
if avm.current_swf_version() >= 7 {
if activation.current_swf_version() >= 7 {
Cow::Borrowed("undefined")
} else {
Cow::Borrowed("")
@ -510,22 +517,22 @@ impl<'gc> Value<'gc> {
pub fn coerce_to_object(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Object<'gc> {
ValueObject::boxed(avm, context, self.to_owned())
ValueObject::boxed(activation, context, self.to_owned())
}
pub fn call(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Value::Object(object) = self {
object.call(avm, context, this, base_proto, args)
object.call(activation, context, this, base_proto, args)
} else {
Ok(Value::Undefined)
}
@ -593,48 +600,54 @@ pub fn f64_to_wrapping_i32(n: f64) -> i32 {
#[cfg(test)]
mod test {
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::globals::create_globals;
use crate::avm1::object::{Object, TObject};
use crate::avm1::return_value::ReturnValue;
use crate::avm1::script_object::ScriptObject;
use crate::avm1::test_utils::with_avm;
use crate::avm1::{Avm1, Value};
use crate::avm1::Value;
use crate::context::UpdateContext;
use enumset::EnumSet;
use std::f64::{INFINITY, NAN, NEG_INFINITY};
#[test]
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 undefined = Value::Undefined;
let false_value = Value::Bool(false);
let null = Value::Null;
assert_eq!(
true_value.to_primitive_num(avm, context).unwrap(),
true_value.to_primitive_num(activation, context).unwrap(),
true_value
);
assert_eq!(undefined.to_primitive_num(avm, context).unwrap(), undefined);
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
);
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 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>(
_: &mut Avm1<'gc>,
_: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>,
_: Object<'gc>,
_: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
) -> Result<Value<'gc>, Error<'gc>> {
Ok(5.0.into())
}
@ -654,7 +667,9 @@ mod test {
);
assert_eq!(
Value::Object(o).to_primitive_num(avm, context).unwrap(),
Value::Object(o)
.to_primitive_num(activation, context)
.unwrap(),
Value::Number(5.0)
);
@ -665,20 +680,20 @@ mod test {
#[test]
#[allow(clippy::float_cmp)]
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 u = Value::Undefined;
let f = Value::Bool(false);
let n = Value::Null;
assert_eq!(t.coerce_to_f64(avm, context).unwrap(), 1.0);
assert!(u.coerce_to_f64(avm, context).unwrap().is_nan());
assert_eq!(f.coerce_to_f64(avm, context).unwrap(), 0.0);
assert!(n.coerce_to_f64(avm, context).unwrap().is_nan());
assert_eq!(t.coerce_to_f64(activation, context).unwrap(), 1.0);
assert!(u.coerce_to_f64(activation, context).unwrap().is_nan());
assert_eq!(f.coerce_to_f64(activation, context).unwrap(), 0.0);
assert!(n.coerce_to_f64(activation, context).unwrap().is_nan());
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(())
});
@ -687,20 +702,20 @@ mod test {
#[test]
#[allow(clippy::float_cmp)]
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 u = Value::Undefined;
let f = Value::Bool(false);
let n = Value::Null;
assert_eq!(t.coerce_to_f64(avm, context).unwrap(), 1.0);
assert_eq!(u.coerce_to_f64(avm, context).unwrap(), 0.0);
assert_eq!(f.coerce_to_f64(avm, context).unwrap(), 0.0);
assert_eq!(n.coerce_to_f64(avm, context).unwrap(), 0.0);
assert_eq!(t.coerce_to_f64(activation, context).unwrap(), 1.0);
assert_eq!(u.coerce_to_f64(activation, context).unwrap(), 0.0);
assert_eq!(f.coerce_to_f64(activation, 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());
assert_eq!(bo.coerce_to_f64(avm, context).unwrap(), 0.0);
assert_eq!(bo.coerce_to_f64(activation, context).unwrap(), 0.0);
Ok(())
});
@ -708,27 +723,36 @@ mod test {
#[test]
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 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);
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);
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);
assert_eq!(
a.abstract_lt(neg_inf, avm, context).unwrap(),
a.abstract_lt(neg_inf, activation, context).unwrap(),
Value::Bool(false)
);
let zero = Value::Number(0.0);
assert_eq!(
a.abstract_lt(zero, avm, context).unwrap(),
a.abstract_lt(zero, activation, context).unwrap(),
Value::Bool(false)
);
@ -738,36 +762,36 @@ mod test {
#[test]
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 b = Value::Number(2.0);
assert_eq!(
b.abstract_lt(a.clone(), avm, context).unwrap(),
b.abstract_lt(a.clone(), activation, context).unwrap(),
Value::Bool(false)
);
let nan = Value::Number(NAN);
assert_eq!(
nan.abstract_lt(a.clone(), avm, context).unwrap(),
nan.abstract_lt(a.clone(), activation, context).unwrap(),
Value::Undefined
);
let inf = Value::Number(INFINITY);
assert_eq!(
inf.abstract_lt(a.clone(), avm, context).unwrap(),
inf.abstract_lt(a.clone(), activation, context).unwrap(),
Value::Bool(false)
);
let neg_inf = Value::Number(NEG_INFINITY);
assert_eq!(
neg_inf.abstract_lt(a.clone(), avm, context).unwrap(),
neg_inf.abstract_lt(a.clone(), activation, context).unwrap(),
Value::Bool(true)
);
let zero = Value::Number(0.0);
assert_eq!(
zero.abstract_lt(a, avm, context).unwrap(),
zero.abstract_lt(a, activation, context).unwrap(),
Value::Bool(true)
);
@ -777,11 +801,14 @@ mod test {
#[test]
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 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(())
})
@ -789,11 +816,14 @@ mod test {
#[test]
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 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(())
})

View File

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

View File

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

View File

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

View File

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

View File

@ -20,6 +20,7 @@ mod morph_shape;
mod movie_clip;
mod text;
use crate::avm1::activation::Activation;
use crate::events::{ClipEvent, ClipEventResult};
pub use bitmap::Bitmap;
pub use button::Button;
@ -913,7 +914,7 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>>
fn bind_text_field_variables(
&self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) {
// 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 len = context.unbound_text_fields.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);
len -= 1;
} else {

View File

@ -1,4 +1,5 @@
//! `EditText` display object and support code.
use crate::avm1::activation::Activation;
use crate::avm1::globals::text_field::attach_virtual_properties;
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
use crate::context::{RenderContext, UpdateContext};
@ -502,7 +503,7 @@ impl<'gc> EditText<'gc> {
pub fn set_variable(
self,
variable: Option<String>,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) {
// Clear previous binding.
@ -526,7 +527,7 @@ impl<'gc> EditText<'gc> {
let _ = self.set_text(text, context);
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
@ -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.
pub fn try_bind_text_field_variable(
self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
set_initial_value: bool,
) -> bool {
@ -714,41 +715,46 @@ impl<'gc> EditText<'gc> {
let parent = self.parent().unwrap();
avm.insert_stack_frame_for_display_object(
activation.run_with_child_frame_for_display_object(
parent,
context.swf.header().version,
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))) =
avm.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(avm, context, property) {
let value = object.get(property, avm, context).unwrap();
let _ = self.set_text(
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;
}
}
}
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
@ -765,7 +771,7 @@ impl<'gc> EditText<'gc> {
///
pub fn propagate_text_binding(
self,
avm: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) {
if !self.0.read().firing_variable_binding {
@ -776,7 +782,7 @@ impl<'gc> EditText<'gc> {
let variable_path = variable.to_string();
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,
self.parent().unwrap(),
&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
// (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(),
context.swf.header().version,
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;
@ -861,12 +868,18 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
drop(text);
// If this text field has a variable set, initialize text field binding.
if !self.try_bind_text_field_variable(avm, 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(avm, context);
avm.run_with_stack_frame_for_display_object(
(*self).into(),
context.swf.version(),
context,
|activation, 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> {

View File

@ -2,6 +2,7 @@
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
use crate::backend::audio::AudioStreamHandle;
use crate::avm1::activation::Activation;
use crate::character::Character;
use crate::context::{ActionType, RenderContext, UpdateContext};
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.swf.header().version,
slice,
context,
);
let frame = avm.current_stack_frame().unwrap();
let _ = avm.run_current_frame(context, frame);
Ok(())
}
@ -976,10 +975,18 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
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
.iter()
.any(|handler| object.has_property(avm, context, handler))
.any(|handler| object.has_property(&mut activation, context, handler))
{
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 not, then this must be queued to be ran first-thing
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
.get("prototype", avm, context)
.map(|v| v.coerce_to_object(avm, context))
.get("prototype", &mut activation, context)
.map(|v| v.coerce_to_object(&mut activation, context))
{
let object: Object<'gc> = StageObject::for_display_object(
context.gc_context,
@ -1046,16 +1060,17 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
)
.into();
if let Some(init_object) = init_object {
for key in init_object.get_keys(avm) {
if let Ok(value) = init_object.get(&key, avm, context) {
let _ = object.set(&key, value, avm, context);
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);
}
}
}
self.0.write(context.gc_context).object = Some(object);
let _ = constructor.call(avm, context, object, None, &[]);
return;
let _ = constructor.call(&mut activation, context, object, None, &[]);
}
return;
}
let object = StageObject::for_display_object(
@ -1064,9 +1079,17 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
Some(context.system_prototypes.movie_clip),
);
if let Some(init_object) = init_object {
for key in init_object.get_keys(avm) {
if let Ok(value) = init_object.get(&key, avm, context) {
let _ = object.set(&key, value, avm, context);
let mut activation = Activation::from_nothing(
avm,
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> {

View File

@ -1,5 +1,6 @@
//! 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::html::iterators::TextSpanIter;
use crate::tag_utils::SwfMovie;
@ -85,57 +86,57 @@ pub struct TextFormat {
fn getstr_from_avm1_object<'gc>(
object: Object<'gc>,
name: &str,
avm1: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, '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::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>(
object: Object<'gc>,
name: &str,
avm1: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, '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::Null => None,
v => Some(v.coerce_to_f64(avm1, uc)?),
v => Some(v.coerce_to_f64(activation, uc)?),
})
}
fn getbool_from_avm1_object<'gc>(
object: Object<'gc>,
name: &str,
avm1: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, '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::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>(
object: Object<'gc>,
name: &str,
avm1: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, '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::Null => None,
v => {
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() {
output.push(v.array_element(i).coerce_to_f64(avm1, uc)?);
output.push(v.array_element(i).coerce_to_f64(activation, uc)?);
}
Some(output)
@ -197,37 +198,38 @@ impl TextFormat {
/// Construct a `TextFormat` from an object that is
pub fn from_avm1_object<'gc>(
object1: Object<'gc>,
avm1: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Self, crate::avm1::error::Error<'gc>> {
Ok(Self {
font: getstr_from_avm1_object(object1, "font", avm1, uc)?,
size: getfloat_from_avm1_object(object1, "size", avm1, uc)?,
color: getfloat_from_avm1_object(object1, "color", avm1, uc)?
font: getstr_from_avm1_object(object1, "font", activation, uc)?,
size: getfloat_from_avm1_object(object1, "size", activation, uc)?,
color: getfloat_from_avm1_object(object1, "color", activation, uc)?
.map(|v| swf::Color::from_rgb(v as u32, 0xFF)),
align: getstr_from_avm1_object(object1, "align", avm1, uc)?.and_then(|v| {
match v.to_lowercase().as_str() {
"left" => Some(swf::TextAlign::Left),
"center" => Some(swf::TextAlign::Center),
"right" => Some(swf::TextAlign::Right),
"justify" => Some(swf::TextAlign::Justify),
_ => None,
}
align: getstr_from_avm1_object(object1, "align", activation, uc)?.and_then(|v| match v
.to_lowercase()
.as_str()
{
"left" => Some(swf::TextAlign::Left),
"center" => Some(swf::TextAlign::Center),
"right" => Some(swf::TextAlign::Right),
"justify" => Some(swf::TextAlign::Justify),
_ => None,
}),
bold: getbool_from_avm1_object(object1, "bold", avm1, uc)?,
italic: getbool_from_avm1_object(object1, "italic", avm1, uc)?,
underline: getbool_from_avm1_object(object1, "underline", avm1, uc)?,
left_margin: getfloat_from_avm1_object(object1, "leftMargin", avm1, uc)?,
right_margin: getfloat_from_avm1_object(object1, "rightMargin", avm1, uc)?,
indent: getfloat_from_avm1_object(object1, "indent", avm1, uc)?,
block_indent: getfloat_from_avm1_object(object1, "blockIndent", avm1, uc)?,
kerning: getbool_from_avm1_object(object1, "kerning", avm1, uc)?,
leading: getfloat_from_avm1_object(object1, "leading", avm1, uc)?,
letter_spacing: getfloat_from_avm1_object(object1, "letterSpacing", avm1, uc)?,
tab_stops: getfloatarray_from_avm1_object(object1, "tabStops", avm1, uc)?,
bullet: getbool_from_avm1_object(object1, "bullet", avm1, uc)?,
url: getstr_from_avm1_object(object1, "url", avm1, uc)?,
target: getstr_from_avm1_object(object1, "target", avm1, uc)?,
bold: getbool_from_avm1_object(object1, "bold", activation, uc)?,
italic: getbool_from_avm1_object(object1, "italic", activation, uc)?,
underline: getbool_from_avm1_object(object1, "underline", activation, uc)?,
left_margin: getfloat_from_avm1_object(object1, "leftMargin", activation, uc)?,
right_margin: getfloat_from_avm1_object(object1, "rightMargin", activation, uc)?,
indent: getfloat_from_avm1_object(object1, "indent", activation, uc)?,
block_indent: getfloat_from_avm1_object(object1, "blockIndent", activation, uc)?,
kerning: getbool_from_avm1_object(object1, "kerning", activation, uc)?,
leading: getfloat_from_avm1_object(object1, "leading", activation, uc)?,
letter_spacing: getfloat_from_avm1_object(object1, "letterSpacing", activation, uc)?,
tab_stops: getfloatarray_from_avm1_object(object1, "tabStops", activation, uc)?,
bullet: getbool_from_avm1_object(object1, "bullet", activation, uc)?,
url: getstr_from_avm1_object(object1, "url", activation, 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.
pub fn as_avm1_object<'gc>(
&self,
avm1: &mut Avm1<'gc>,
activation: &mut Activation<'_, 'gc>,
uc: &mut UpdateContext<'_, '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(
"font",
self.font.clone().map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"size",
self.size.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
@ -373,7 +378,7 @@ impl TextFormat {
.clone()
.map(|v| (((v.r as u32) << 16) + ((v.g as u32) << 8) + v.b as u32).into())
.unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
@ -389,90 +394,91 @@ impl TextFormat {
.into()
})
.unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"bold",
self.bold.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"italic",
self.italic.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"underline",
self.underline.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"leftMargin",
self.left_margin.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"rightMargin",
self.right_margin.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"indent",
self.indent.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"blockIndent",
self.block_indent.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"kerning",
self.kerning.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"leading",
self.leading.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"letterSpacing",
self.letter_spacing.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"bullet",
self.bullet.map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"url",
self.url.clone().map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
object.set(
"target",
self.target.clone().map(|v| v.into()).unwrap_or(Value::Null),
avm1,
activation,
uc,
)?;
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());
@ -480,9 +486,9 @@ impl TextFormat {
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 {
object.set("tabStops", Value::Null, avm1, uc)?;
object.set("tabStops", Value::Null, activation, uc)?;
}
Ok(object.into())

View File

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

View File

@ -1,8 +1,9 @@
use crate::avm1::activation::Activation;
use crate::avm1::debug::VariableDumper;
use crate::avm1::globals::system::SystemProperties;
use crate::avm1::listeners::SystemListener;
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::storage::StorageBackend;
use crate::backend::{
@ -271,11 +272,18 @@ impl Player {
root.set_name(context.gc_context, "");
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(
context.gc_context,
"$version",
context.system.get_version_string(avm).into(),
context.system.get_version_string(&mut activation).into(),
EnumSet::empty(),
);
});
@ -379,21 +387,32 @@ impl Player {
if self.input.is_key_down(KeyCode::Control) && self.input.is_key_down(KeyCode::Alt) {
self.mutate_with_update_context(|avm, context| {
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(
"Global Variables:",
"_global",
&avm.global_object_cell(),
avm,
&activation.avm().global_object_cell(),
&mut activation,
context,
);
let levels = context.levels.clone();
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(
&format!("Level #{}:", level),
&format!("_level{}", level),
&object,
avm,
&mut activation,
context,
);
}
@ -521,7 +540,7 @@ impl Player {
/// Update dragged object, if any.
fn update_drag(&mut self) {
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 drag_object.display_object.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
/// specific `MovieClip` referenced.
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 root = *context.levels.get(&0).expect("root level");
root.as_movie_clip()
.unwrap()
.preload(avm, context, &mut morph_shapes);
.preload(activation, context, &mut morph_shapes);
// Finalize morph shapes.
for (id, static_data) in morph_shapes {
@ -719,7 +738,7 @@ impl Player {
match actions.action_type {
// DoAction/clip event code
ActionType::Normal { bytecode } => {
avm.insert_stack_frame_for_action(
avm.run_stack_frame_for_action(
actions.clip,
context.swf.header().version,
bytecode,
@ -731,41 +750,29 @@ impl Player {
constructor: Some(constructor),
events,
} => {
avm.insert_stack_frame(GcCell::allocate(
let mut activation = Activation::from_nothing(
avm,
context.swf.version(),
avm.global_object_cell(),
context.gc_context,
Activation::from_nothing(
context.swf.header().version,
avm.global_object_cell(),
context.gc_context,
actions.clip,
),
));
*context.levels.get(&0).unwrap(),
);
if let Ok(prototype) = constructor
.get("prototype", avm, context)
.map(|v| v.coerce_to_object(avm, context))
.get("prototype", &mut activation, context)
.map(|v| v.coerce_to_object(&mut activation, context))
{
if let Value::Object(object) = actions.clip.object() {
object.set_proto(context.gc_context, Some(prototype));
for event in events {
avm.insert_stack_frame_for_action(
let _ = activation.run_child_frame_for_action(
actions.clip,
context.swf.header().version,
event,
context,
);
}
let _ = avm.run_stack_till_empty(context);
avm.insert_stack_frame(GcCell::allocate(
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, &[]);
let _ = constructor.call(&mut activation, context, object, None, &[]);
}
}
}
@ -775,7 +782,7 @@ impl Player {
events,
} => {
for event in events {
avm.insert_stack_frame_for_action(
avm.run_stack_frame_for_action(
actions.clip,
context.swf.header().version,
event,
@ -785,7 +792,7 @@ impl Player {
}
// Event handler method call (e.g. onEnterFrame)
ActionType::Method { object, name, args } => {
avm.insert_stack_frame_for_method(
avm.run_stack_frame_for_method(
actions.clip,
object,
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) {
self.update(|avm, update_context| {
let shared_objects = update_context.shared_objects.clone();
self.update(|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 shared_objects = context.shared_objects.clone();
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, &[]);
}
});
}