avm: Refactor Avm1, move execution to StackFrame (merge #767)
AVM1 refactoring: moving execution from Avm1 to StackFrame
This commit is contained in:
commit
4b4370b90d
2921
core/src/avm1.rs
2921
core/src/avm1.rs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
||||||
use crate::avm1::{Avm1, Object, ObjectPtr, TObject, Value};
|
use crate::avm1::activation::Activation;
|
||||||
|
use crate::avm1::{Object, ObjectPtr, TObject, Value};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -23,11 +24,11 @@ impl<'a> VariableDumper<'a> {
|
||||||
pub fn dump<'gc>(
|
pub fn dump<'gc>(
|
||||||
value: &Value<'gc>,
|
value: &Value<'gc>,
|
||||||
indent: &str,
|
indent: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut dumper = VariableDumper::new(indent);
|
let mut dumper = VariableDumper::new(indent);
|
||||||
dumper.print_value(value, avm, context);
|
dumper.print_value(value, activation, context);
|
||||||
dumper.output
|
dumper.output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ impl<'a> VariableDumper<'a> {
|
||||||
pub fn print_object<'gc>(
|
pub fn print_object<'gc>(
|
||||||
&mut self,
|
&mut self,
|
||||||
object: &Object<'gc>,
|
object: &Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
let (id, new) = self.object_id(object);
|
let (id, new) = self.object_id(object);
|
||||||
|
@ -93,7 +94,7 @@ impl<'a> VariableDumper<'a> {
|
||||||
self.output.push_str("]");
|
self.output.push_str("]");
|
||||||
|
|
||||||
if new {
|
if new {
|
||||||
self.print_properties(object, avm, context);
|
self.print_properties(object, activation, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,12 +102,12 @@ impl<'a> VariableDumper<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
object: &Object<'gc>,
|
object: &Object<'gc>,
|
||||||
key: &str,
|
key: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
match object.get(&key, avm, context) {
|
match object.get(&key, activation, context) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
self.print_value(&value, avm, context);
|
self.print_value(&value, activation, context);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.output.push_str("Error: \"");
|
self.output.push_str("Error: \"");
|
||||||
|
@ -119,10 +120,10 @@ impl<'a> VariableDumper<'a> {
|
||||||
pub fn print_properties<'gc>(
|
pub fn print_properties<'gc>(
|
||||||
&mut self,
|
&mut self,
|
||||||
object: &Object<'gc>,
|
object: &Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
let keys = object.get_keys(avm);
|
let keys = object.get_keys(activation);
|
||||||
if keys.is_empty() {
|
if keys.is_empty() {
|
||||||
self.output.push_str(" {}");
|
self.output.push_str(" {}");
|
||||||
} else {
|
} else {
|
||||||
|
@ -133,7 +134,7 @@ impl<'a> VariableDumper<'a> {
|
||||||
self.indent();
|
self.indent();
|
||||||
self.output.push_str(&key);
|
self.output.push_str(&key);
|
||||||
self.output.push_str(": ");
|
self.output.push_str(": ");
|
||||||
self.print_property(object, &key, avm, context);
|
self.print_property(object, &key, activation, context);
|
||||||
self.output.push_str("\n");
|
self.output.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +147,7 @@ impl<'a> VariableDumper<'a> {
|
||||||
pub fn print_value<'gc>(
|
pub fn print_value<'gc>(
|
||||||
&mut self,
|
&mut self,
|
||||||
value: &Value<'gc>,
|
value: &Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
match value {
|
match value {
|
||||||
|
@ -158,7 +159,7 @@ impl<'a> VariableDumper<'a> {
|
||||||
self.print_string(value);
|
self.print_string(value);
|
||||||
}
|
}
|
||||||
Value::Object(object) => {
|
Value::Object(object) => {
|
||||||
self.print_object(object, avm, context);
|
self.print_object(object, activation, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,10 +169,10 @@ impl<'a> VariableDumper<'a> {
|
||||||
header: &str,
|
header: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
object: &Object<'gc>,
|
object: &Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
let keys = object.get_keys(avm);
|
let keys = object.get_keys(activation);
|
||||||
if keys.is_empty() {
|
if keys.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +184,7 @@ impl<'a> VariableDumper<'a> {
|
||||||
for key in keys.into_iter() {
|
for key in keys.into_iter() {
|
||||||
self.output.push_str(&format!("{}.{}", name, key));
|
self.output.push_str(&format!("{}.{}", name, key));
|
||||||
self.output.push_str(" = ");
|
self.output.push_str(" = ");
|
||||||
self.print_property(object, &key, avm, context);
|
self.print_property(object, &key, activation, context);
|
||||||
self.output.push_str("\n");
|
self.output.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,25 +198,24 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::test_utils::with_avm;
|
use crate::avm1::test_utils::with_avm;
|
||||||
use crate::avm1::ScriptObject;
|
use crate::avm1::ScriptObject;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
|
|
||||||
fn throw_error<'gc>(
|
fn throw_error<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Err(Error::PrototypeRecursionLimit)
|
Err(Error::PrototypeRecursionLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_undefined() {
|
fn dump_undefined() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&Value::Undefined, " ", avm, context),
|
VariableDumper::dump(&Value::Undefined, " ", activation, context),
|
||||||
"undefined"
|
"undefined"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -224,9 +224,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_null() {
|
fn dump_null() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&Value::Null, " ", avm, context),
|
VariableDumper::dump(&Value::Null, " ", activation, context),
|
||||||
"null"
|
"null"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -235,13 +235,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_bool() {
|
fn dump_bool() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&Value::Bool(true), " ", avm, context),
|
VariableDumper::dump(&Value::Bool(true), " ", activation, context),
|
||||||
"true"
|
"true"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&Value::Bool(false), " ", avm, context),
|
VariableDumper::dump(&Value::Bool(false), " ", activation, context),
|
||||||
"false"
|
"false"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -250,13 +250,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_number() {
|
fn dump_number() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&Value::Number(1000.0), " ", avm, context),
|
VariableDumper::dump(&Value::Number(1000.0), " ", activation, context),
|
||||||
"1000"
|
"1000"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&Value::Number(-0.05), " ", avm, context),
|
VariableDumper::dump(&Value::Number(-0.05), " ", activation, context),
|
||||||
"-0.05"
|
"-0.05"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -265,13 +265,18 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_string() {
|
fn dump_string() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&Value::String("".to_string()), " ", avm, context),
|
VariableDumper::dump(&Value::String("".to_string()), " ", activation, context),
|
||||||
"\"\""
|
"\"\""
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&Value::String("HELLO WORLD".to_string()), " ", avm, context),
|
VariableDumper::dump(
|
||||||
|
&Value::String("HELLO WORLD".to_string()),
|
||||||
|
" ",
|
||||||
|
activation,
|
||||||
|
context
|
||||||
|
),
|
||||||
"\"HELLO WORLD\""
|
"\"HELLO WORLD\""
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -280,7 +285,7 @@ mod tests {
|
||||||
"Escape \"this\" string\nplease! \u{0008}\u{000C}\n\r\t\"\\".to_string()
|
"Escape \"this\" string\nplease! \u{0008}\u{000C}\n\r\t\"\\".to_string()
|
||||||
),
|
),
|
||||||
" ",
|
" ",
|
||||||
avm,
|
activation,
|
||||||
context
|
context
|
||||||
),
|
),
|
||||||
"\"Escape \\\"this\\\" string\\nplease! \\b\\f\\n\\r\\t\\\"\\\\\""
|
"\"Escape \\\"this\\\" string\\nplease! \\b\\f\\n\\r\\t\\\"\\\\\""
|
||||||
|
@ -291,10 +296,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_empty_object() {
|
fn dump_empty_object() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
let object = ScriptObject::object(context.gc_context, None);
|
let object = ScriptObject::object(context.gc_context, None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&object.into(), " ", avm, context),
|
VariableDumper::dump(&object.into(), " ", activation, context),
|
||||||
"[object #0] {}"
|
"[object #0] {}"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -303,16 +308,21 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_object() {
|
fn dump_object() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
let object = ScriptObject::object(context.gc_context, None);
|
let object = ScriptObject::object(context.gc_context, None);
|
||||||
let child = ScriptObject::object(context.gc_context, None);
|
let child = ScriptObject::object(context.gc_context, None);
|
||||||
object.set("self", object.into(), avm, context)?;
|
object.set("self", object.into(), activation, context)?;
|
||||||
object.set("test", Value::String("value".to_string()), avm, context)?;
|
object.set(
|
||||||
object.set("child", child.into(), avm, context)?;
|
"test",
|
||||||
child.set("parent", object.into(), avm, context)?;
|
Value::String("value".to_string()),
|
||||||
child.set("age", Value::Number(6.0), avm, context)?;
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
|
object.set("child", child.into(), activation, context)?;
|
||||||
|
child.set("parent", object.into(), activation, context)?;
|
||||||
|
child.set("age", Value::Number(6.0), activation, context)?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&object.into(), " ", avm, context),
|
VariableDumper::dump(&object.into(), " ", activation, context),
|
||||||
"[object #0] {\n child: [object #1] {\n age: 6\n parent: [object #0]\n }\n test: \"value\"\n self: [object #0]\n}",
|
"[object #0] {\n child: [object #1] {\n age: 6\n parent: [object #0]\n }\n test: \"value\"\n self: [object #0]\n}",
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -321,7 +331,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_object_with_error() {
|
fn dump_object_with_error() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
let object = ScriptObject::object(context.gc_context, None);
|
let object = ScriptObject::object(context.gc_context, None);
|
||||||
object.add_property(
|
object.add_property(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -331,7 +341,7 @@ mod tests {
|
||||||
EnumSet::empty(),
|
EnumSet::empty(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VariableDumper::dump(&object.into(), " ", avm, context),
|
VariableDumper::dump(&object.into(), " ", activation, context),
|
||||||
"[object #0] {\n broken_value: Error: \"Prototype recursion limit has been exceeded\"\n}"
|
"[object #0] {\n broken_value: Error: \"Prototype recursion limit has been exceeded\"\n}"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -340,16 +350,21 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_variables() {
|
fn dump_variables() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
let object = ScriptObject::object(context.gc_context, None);
|
let object = ScriptObject::object(context.gc_context, None);
|
||||||
let child = ScriptObject::object(context.gc_context, None);
|
let child = ScriptObject::object(context.gc_context, None);
|
||||||
object.set("self", object.into(), avm, context)?;
|
object.set("self", object.into(), activation, context)?;
|
||||||
object.set("test", Value::String("value".to_string()), avm, context)?;
|
object.set(
|
||||||
object.set("child", child.into(), avm, context)?;
|
"test",
|
||||||
child.set("parent", object.into(), avm, context)?;
|
Value::String("value".to_string()),
|
||||||
child.set("age", Value::Number(6.0), avm, context)?;
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
|
object.set("child", child.into(), activation, context)?;
|
||||||
|
child.set("parent", object.into(), activation, context)?;
|
||||||
|
child.set("age", Value::Number(6.0), activation, context)?;
|
||||||
let mut dumper = VariableDumper::new(" ");
|
let mut dumper = VariableDumper::new(" ");
|
||||||
dumper.print_variables("Variables:", "object", &object.into(), avm, context);
|
dumper.print_variables("Variables:", "object", &object.into(), activation, context);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dumper.output,
|
dumper.output,
|
||||||
"Variables:\nobject.child = [object #0] {\n age: 6\n parent: [object #1] {\n child: [object #0]\n test: \"value\"\n self: [object #1]\n }\n }\nobject.test = \"value\"\nobject.self = [object #1]\n\n"
|
"Variables:\nobject.child = [object #0] {\n age: 6\n parent: [object #1] {\n child: [object #0]\n test: \"value\"\n self: [object #1]\n }\n }\nobject.test = \"value\"\nobject.self = [object #1]\n\n"
|
||||||
|
|
|
@ -9,15 +9,6 @@ pub enum Error<'gc> {
|
||||||
#[error("Couldn't parse SWF. This may or may not be a bug in Ruffle, please help us by reporting it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
|
#[error("Couldn't parse SWF. This may or may not be a bug in Ruffle, please help us by reporting it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
|
||||||
InvalidSwf(#[from] swf::error::Error),
|
InvalidSwf(#[from] swf::error::Error),
|
||||||
|
|
||||||
#[error("No stack frame to execute. This is probably a bug in Ruffle, please report it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
|
|
||||||
NoStackFrame,
|
|
||||||
|
|
||||||
#[error("Attempted to run a frame not on the current interpreter stack. This is probably a bug in Ruffle, please report it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
|
|
||||||
FrameNotOnStack,
|
|
||||||
|
|
||||||
#[error("Attempted to execute the same frame twice. This is probably a bug in Ruffle, please report it to https://github.com/ruffle-rs/ruffle/issues and include the swf that triggered it.")]
|
|
||||||
AlreadyExecutingFrame,
|
|
||||||
|
|
||||||
#[error("A script has thrown a custom error.")]
|
#[error("A script has thrown a custom error.")]
|
||||||
ThrownValue(Value<'gc>),
|
ThrownValue(Value<'gc>),
|
||||||
}
|
}
|
||||||
|
@ -27,9 +18,6 @@ impl Error<'_> {
|
||||||
match self {
|
match self {
|
||||||
Error::PrototypeRecursionLimit => true,
|
Error::PrototypeRecursionLimit => true,
|
||||||
Error::InvalidSwf(_) => true,
|
Error::InvalidSwf(_) => true,
|
||||||
Error::NoStackFrame => true,
|
|
||||||
Error::FrameNotOnStack => true,
|
|
||||||
Error::AlreadyExecutingFrame => false,
|
|
||||||
Error::ThrownValue(_) => false,
|
Error::ThrownValue(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
//! FSCommand handling
|
//! FSCommand handling
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::{Avm1, UpdateContext};
|
use crate::avm1::UpdateContext;
|
||||||
|
|
||||||
/// Parse an FSCommand URL.
|
/// Parse an FSCommand URL.
|
||||||
pub fn parse(url: &str) -> Option<&str> {
|
pub fn parse(url: &str) -> Option<&str> {
|
||||||
log::info!("Checking {}", url);
|
log::info!("Checking {}", url);
|
||||||
|
@ -16,7 +16,7 @@ pub fn parse(url: &str) -> Option<&str> {
|
||||||
/// TODO: FSCommand URL handling
|
/// TODO: FSCommand URL handling
|
||||||
pub fn handle<'gc>(
|
pub fn handle<'gc>(
|
||||||
fscommand: &str,
|
fscommand: &str,
|
||||||
_avm: &mut Avm1,
|
_activation: &mut Activation,
|
||||||
_ac: &mut UpdateContext,
|
_ac: &mut UpdateContext,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
log::warn!("Unhandled FSCommand: {}", fscommand);
|
log::warn!("Unhandled FSCommand: {}", fscommand);
|
||||||
|
|
|
@ -3,11 +3,10 @@
|
||||||
use crate::avm1::activation::Activation;
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::property::{Attribute, Attribute::*};
|
use crate::avm1::property::{Attribute, Attribute::*};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::scope::Scope;
|
use crate::avm1::scope::Scope;
|
||||||
use crate::avm1::super_object::SuperObject;
|
use crate::avm1::super_object::SuperObject;
|
||||||
use crate::avm1::value::Value;
|
use crate::avm1::value::Value;
|
||||||
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, UpdateContext};
|
use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, UpdateContext};
|
||||||
use crate::display_object::{DisplayObject, TDisplayObject};
|
use crate::display_object::{DisplayObject, TDisplayObject};
|
||||||
use crate::tag_utils::SwfSlice;
|
use crate::tag_utils::SwfSlice;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
|
@ -31,11 +30,11 @@ use swf::avm1::types::FunctionParam;
|
||||||
/// your function yields `None`, you must ensure that the top-most activation
|
/// your function yields `None`, you must ensure that the top-most activation
|
||||||
/// in the AVM1 runtime will return with the value of this function.
|
/// in the AVM1 runtime will return with the value of this function.
|
||||||
pub type NativeFunction<'gc> = fn(
|
pub type NativeFunction<'gc> = fn(
|
||||||
&mut Avm1<'gc>,
|
&mut Activation<'_, 'gc>,
|
||||||
&mut UpdateContext<'_, 'gc, '_>,
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
Object<'gc>,
|
Object<'gc>,
|
||||||
&[Value<'gc>],
|
&[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>>;
|
) -> Result<Value<'gc>, Error<'gc>>;
|
||||||
|
|
||||||
/// Represents a function defined in the AVM1 runtime, either through
|
/// Represents a function defined in the AVM1 runtime, either through
|
||||||
/// `DefineFunction` or `DefineFunction2`.
|
/// `DefineFunction` or `DefineFunction2`.
|
||||||
|
@ -223,20 +222,21 @@ impl<'gc> Executable<'gc> {
|
||||||
/// create a new stack frame and execute the action data yourself.
|
/// create a new stack frame and execute the action data yourself.
|
||||||
pub fn exec(
|
pub fn exec(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
match self {
|
match self {
|
||||||
Executable::Native(nf) => nf(avm, ac, this, args),
|
Executable::Native(nf) => nf(activation, ac, this, args),
|
||||||
Executable::Action(af) => {
|
Executable::Action(af) => {
|
||||||
let child_scope = GcCell::allocate(
|
let child_scope = GcCell::allocate(
|
||||||
ac.gc_context,
|
ac.gc_context,
|
||||||
Scope::new_local_scope(af.scope(), ac.gc_context),
|
Scope::new_local_scope(af.scope(), ac.gc_context),
|
||||||
);
|
);
|
||||||
let arguments = ScriptObject::object(ac.gc_context, Some(avm.prototypes().object));
|
let arguments =
|
||||||
|
ScriptObject::object(ac.gc_context, Some(activation.avm().prototypes().object));
|
||||||
if !af.suppress_arguments {
|
if !af.suppress_arguments {
|
||||||
for i in 0..args.len() {
|
for i in 0..args.len() {
|
||||||
arguments.define_value(
|
arguments.define_value(
|
||||||
|
@ -261,7 +261,7 @@ impl<'gc> Executable<'gc> {
|
||||||
SuperObject::from_this_and_base_proto(
|
SuperObject::from_this_and_base_proto(
|
||||||
this,
|
this,
|
||||||
base_proto.unwrap_or(this),
|
base_proto.unwrap_or(this),
|
||||||
avm,
|
activation,
|
||||||
ac,
|
ac,
|
||||||
)?
|
)?
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -270,7 +270,7 @@ impl<'gc> Executable<'gc> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let effective_ver = if avm.current_swf_version() > 5 {
|
let effective_ver = if activation.current_swf_version() > 5 {
|
||||||
af.swf_version()
|
af.swf_version()
|
||||||
} else {
|
} else {
|
||||||
this.as_display_object()
|
this.as_display_object()
|
||||||
|
@ -278,19 +278,15 @@ impl<'gc> Executable<'gc> {
|
||||||
.unwrap_or(ac.player_version)
|
.unwrap_or(ac.player_version)
|
||||||
};
|
};
|
||||||
|
|
||||||
let frame_cell = GcCell::allocate(
|
let mut frame = Activation::from_action(
|
||||||
ac.gc_context,
|
activation.avm(),
|
||||||
Activation::from_function(
|
effective_ver,
|
||||||
effective_ver,
|
child_scope,
|
||||||
af.data(),
|
af.constant_pool,
|
||||||
child_scope,
|
af.base_clip,
|
||||||
af.constant_pool,
|
this,
|
||||||
af.base_clip,
|
Some(argcell),
|
||||||
this,
|
|
||||||
Some(argcell),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
let mut frame = frame_cell.write(ac.gc_context);
|
|
||||||
|
|
||||||
frame.allocate_local_registers(af.register_count(), ac.gc_context);
|
frame.allocate_local_registers(af.register_count(), ac.gc_context);
|
||||||
|
|
||||||
|
@ -341,7 +337,8 @@ impl<'gc> Executable<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if af.preload_global {
|
if af.preload_global {
|
||||||
frame.set_local_register(preload_r, avm.global_object(ac), ac.gc_context);
|
let global = frame.avm().global_object(ac);
|
||||||
|
frame.set_local_register(preload_r, global, ac.gc_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: What happens if the argument registers clash with the
|
//TODO: What happens if the argument registers clash with the
|
||||||
|
@ -357,9 +354,8 @@ impl<'gc> Executable<'gc> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
avm.insert_stack_frame(frame_cell);
|
|
||||||
|
|
||||||
Ok(frame_cell.into())
|
Ok(frame.run_actions(ac, af.data.clone())?.value())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,34 +452,33 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base.get_local(name, avm, context, this)
|
self.base.get_local(name, activation, context, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(
|
fn set(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.base.set(name, value, avm, context)
|
self.base.set(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(exec) = self.as_executable() {
|
if let Some(exec) = self.as_executable() {
|
||||||
exec.exec(avm, context, this, base_proto, args)?
|
exec.exec(activation, context, this, base_proto, args)
|
||||||
.resolve(avm, context)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
@ -493,17 +488,16 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
self.base.call_setter(name, value, activation, context)
|
||||||
self.base.call_setter(name, value, avm, context, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
prototype: Object<'gc>,
|
prototype: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
|
@ -525,11 +519,11 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base.delete(avm, gc_context, name)
|
self.base.delete(activation, gc_context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proto(&self) -> Option<Object<'gc>> {
|
fn proto(&self) -> Option<Object<'gc>> {
|
||||||
|
@ -575,7 +569,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -583,46 +577,46 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
attributes: EnumSet<Attribute>,
|
attributes: EnumSet<Attribute>,
|
||||||
) {
|
) {
|
||||||
self.base
|
self.base
|
||||||
.add_property_with_case(avm, gc_context, name, get, set, attributes)
|
.add_property_with_case(activation, gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base.has_property(avm, context, name)
|
self.base.has_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base.has_own_property(avm, context, name)
|
self.base.has_own_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base.has_own_virtual(avm, context, name)
|
self.base.has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base.is_property_overwritable(avm, name)
|
self.base.is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base.is_property_enumerable(avm, name)
|
self.base.is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
self.base.get_keys(avm)
|
self.base.get_keys(activation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string(&self) -> Cow<str> {
|
fn as_string(&self) -> Cow<str> {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::fscommand;
|
use crate::avm1::fscommand;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::listeners::SystemListeners;
|
use crate::avm1::listeners::SystemListeners;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use crate::backend::navigator::NavigationMethod;
|
use crate::backend::navigator::NavigationMethod;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
@ -40,21 +40,21 @@ mod xml;
|
||||||
|
|
||||||
#[allow(non_snake_case, unused_must_use)] //can't use errors yet
|
#[allow(non_snake_case, unused_must_use)] //can't use errors yet
|
||||||
pub fn getURL<'a, 'gc>(
|
pub fn getURL<'a, 'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'a, 'gc, '_>,
|
context: &mut UpdateContext<'a, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
//TODO: Error behavior if no arguments are present
|
//TODO: Error behavior if no arguments are present
|
||||||
if let Some(url_val) = args.get(0) {
|
if let Some(url_val) = args.get(0) {
|
||||||
let url = url_val.coerce_to_string(avm, context)?;
|
let url = url_val.coerce_to_string(activation, context)?;
|
||||||
if let Some(fscommand) = fscommand::parse(&url) {
|
if let Some(fscommand) = fscommand::parse(&url) {
|
||||||
fscommand::handle(fscommand, avm, context);
|
fscommand::handle(fscommand, activation, context);
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
let window = if let Some(window) = args.get(1) {
|
let window = if let Some(window) = args.get(1) {
|
||||||
Some(window.coerce_to_string(avm, context)?.to_string())
|
Some(window.coerce_to_string(activation, context)?.to_string())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -63,64 +63,67 @@ pub fn getURL<'a, 'gc>(
|
||||||
Some(Value::String(s)) if s == "POST" => Some(NavigationMethod::POST),
|
Some(Value::String(s)) if s == "POST" => Some(NavigationMethod::POST),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
let vars_method = method.map(|m| (m, avm.locals_into_form_values(context)));
|
let vars_method = method.map(|m| (m, activation.locals_into_form_values(context)));
|
||||||
|
|
||||||
context
|
context
|
||||||
.navigator
|
.navigator
|
||||||
.navigate_to_url(url.to_string(), window, vars_method);
|
.navigate_to_url(url.to_string(), window, vars_method);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random<'gc>(
|
pub fn random<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
match args.get(0) {
|
match args.get(0) {
|
||||||
Some(Value::Number(max)) => Ok(action_context.rng.gen_range(0.0f64, max).floor().into()),
|
Some(Value::Number(max)) => Ok(action_context.rng.gen_range(0.0f64, max).floor().into()),
|
||||||
_ => Ok(Value::Undefined.into()), //TODO: Shouldn't this be an error condition?
|
_ => Ok(Value::Undefined), //TODO: Shouldn't this be an error condition?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_nan<'gc>(
|
pub fn is_nan<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(val) = args.get(0) {
|
if let Some(val) = args.get(0) {
|
||||||
Ok(val.coerce_to_f64(avm, action_context)?.is_nan().into())
|
Ok(val
|
||||||
|
.coerce_to_f64(activation, action_context)?
|
||||||
|
.is_nan()
|
||||||
|
.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(true.into())
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_infinity<'gc>(
|
pub fn get_infinity<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() > 4 {
|
if activation.current_swf_version() > 4 {
|
||||||
Ok(f64::INFINITY.into())
|
Ok(f64::INFINITY.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_nan<'gc>(
|
pub fn get_nan<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() > 4 {
|
if activation.current_swf_version() > 4 {
|
||||||
Ok(f64::NAN.into())
|
Ok(f64::NAN.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +476,10 @@ pub fn create_globals<'gc>(
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn setup<'gc>(_avm: &mut Avm1<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) -> Object<'gc> {
|
fn setup<'gc>(
|
||||||
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
) -> Object<'gc> {
|
||||||
create_globals(context.gc_context).1
|
create_globals(context.gc_context).1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! Array class
|
//! Array class
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
use smallvec::alloc::borrow::Cow;
|
use smallvec::alloc::borrow::Cow;
|
||||||
|
@ -25,7 +25,12 @@ const DEFAULT_ORDERING: Ordering = Ordering::Equal;
|
||||||
// Compare function used by sort and sortOn.
|
// Compare function used by sort and sortOn.
|
||||||
type CompareFn<'a, 'gc> = Box<
|
type CompareFn<'a, 'gc> = Box<
|
||||||
dyn 'a
|
dyn 'a
|
||||||
+ FnMut(&mut Avm1<'gc>, &mut UpdateContext<'_, 'gc, '_>, &Value<'gc>, &Value<'gc>) -> Ordering,
|
+ FnMut(
|
||||||
|
&mut Activation<'_, 'gc>,
|
||||||
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
&Value<'gc>,
|
||||||
|
&Value<'gc>,
|
||||||
|
) -> Ordering,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub fn create_array_object<'gc>(
|
pub fn create_array_object<'gc>(
|
||||||
|
@ -84,16 +89,16 @@ pub fn create_array_object<'gc>(
|
||||||
|
|
||||||
/// Implements `Array`
|
/// Implements `Array`
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let mut consumed = false;
|
let mut consumed = false;
|
||||||
|
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
let arg = args.get(0).unwrap();
|
let arg = args.get(0).unwrap();
|
||||||
if let Ok(length) = arg.coerce_to_f64(avm, context) {
|
if let Ok(length) = arg.coerce_to_f64(activation, context) {
|
||||||
if length >= 0.0 {
|
if length >= 0.0 {
|
||||||
this.set_length(context.gc_context, length as usize);
|
this.set_length(context.gc_context, length as usize);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
@ -115,15 +120,15 @@ pub fn constructor<'gc>(
|
||||||
this.set_length(context.gc_context, length);
|
this.set_length(context.gc_context, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push<'gc>(
|
pub fn push<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let old_length = this.length();
|
let old_length = this.length();
|
||||||
let new_length = old_length + args.len();
|
let new_length = old_length + args.len();
|
||||||
this.set_length(context.gc_context, new_length);
|
this.set_length(context.gc_context, new_length);
|
||||||
|
@ -140,11 +145,11 @@ pub fn push<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unshift<'gc>(
|
pub fn unshift<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let old_length = this.length();
|
let old_length = this.length();
|
||||||
let new_length = old_length + args.len();
|
let new_length = old_length + args.len();
|
||||||
let offset = new_length - old_length;
|
let offset = new_length - old_length;
|
||||||
|
@ -165,14 +170,14 @@ pub fn unshift<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shift<'gc>(
|
pub fn shift<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let old_length = this.length();
|
let old_length = this.length();
|
||||||
if old_length == 0 {
|
if old_length == 0 {
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_length = old_length - 1;
|
let new_length = old_length - 1;
|
||||||
|
@ -184,41 +189,41 @@ pub fn shift<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
this.delete_array_element(new_length, context.gc_context);
|
this.delete_array_element(new_length, context.gc_context);
|
||||||
this.delete(avm, context.gc_context, &new_length.to_string());
|
this.delete(activation, context.gc_context, &new_length.to_string());
|
||||||
|
|
||||||
this.set_length(context.gc_context, new_length);
|
this.set_length(context.gc_context, new_length);
|
||||||
|
|
||||||
Ok(removed.into())
|
Ok(removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop<'gc>(
|
pub fn pop<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let old_length = this.length();
|
let old_length = this.length();
|
||||||
if old_length == 0 {
|
if old_length == 0 {
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_length = old_length - 1;
|
let new_length = old_length - 1;
|
||||||
|
|
||||||
let removed = this.array_element(new_length);
|
let removed = this.array_element(new_length);
|
||||||
this.delete_array_element(new_length, context.gc_context);
|
this.delete_array_element(new_length, context.gc_context);
|
||||||
this.delete(avm, context.gc_context, &new_length.to_string());
|
this.delete(activation, context.gc_context, &new_length.to_string());
|
||||||
|
|
||||||
this.set_length(context.gc_context, new_length);
|
this.set_length(context.gc_context, new_length);
|
||||||
|
|
||||||
Ok(removed.into())
|
Ok(removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reverse<'gc>(
|
pub fn reverse<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let length = this.length();
|
let length = this.length();
|
||||||
let mut values = this.array().to_vec();
|
let mut values = this.array().to_vec();
|
||||||
|
|
||||||
|
@ -226,25 +231,25 @@ pub fn reverse<'gc>(
|
||||||
this.set_array_element(i, values.pop().unwrap(), context.gc_context);
|
this.set_array_element(i, values.pop().unwrap(), context.gc_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn join<'gc>(
|
pub fn join<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let separator = args
|
let separator = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.and_then(|v| v.coerce_to_string(avm, context).ok())
|
.and_then(|v| v.coerce_to_string(activation, context).ok())
|
||||||
.unwrap_or_else(|| Cow::Borrowed(","));
|
.unwrap_or_else(|| Cow::Borrowed(","));
|
||||||
let values: Vec<Value<'gc>> = this.array();
|
let values: Vec<Value<'gc>> = this.array();
|
||||||
|
|
||||||
Ok(values
|
Ok(values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
v.coerce_to_string(avm, context)
|
v.coerce_to_string(activation, context)
|
||||||
.unwrap_or_else(|_| Cow::Borrowed("undefined"))
|
.unwrap_or_else(|_| Cow::Borrowed("undefined"))
|
||||||
})
|
})
|
||||||
.collect::<Vec<Cow<str>>>()
|
.collect::<Vec<Cow<str>>>()
|
||||||
|
@ -264,23 +269,23 @@ fn make_index_absolute(mut index: i32, length: usize) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slice<'gc>(
|
pub fn slice<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let start = args
|
let start = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.and_then(|v| v.coerce_to_f64(avm, context).ok())
|
.and_then(|v| v.coerce_to_f64(activation, context).ok())
|
||||||
.map(|v| make_index_absolute(v as i32, this.length()))
|
.map(|v| make_index_absolute(v as i32, this.length()))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let end = args
|
let end = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.and_then(|v| v.coerce_to_f64(avm, context).ok())
|
.and_then(|v| v.coerce_to_f64(activation, context).ok())
|
||||||
.map(|v| make_index_absolute(v as i32, this.length()))
|
.map(|v| make_index_absolute(v as i32, this.length()))
|
||||||
.unwrap_or_else(|| this.length());
|
.unwrap_or_else(|| this.length());
|
||||||
|
|
||||||
let array = ScriptObject::array(context.gc_context, Some(avm.prototypes.array));
|
let array = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
|
||||||
|
|
||||||
if start < end {
|
if start < end {
|
||||||
let length = end - start;
|
let length = end - start;
|
||||||
|
@ -295,31 +300,31 @@ pub fn slice<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn splice<'gc>(
|
pub fn splice<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
let old_length = this.length();
|
let old_length = this.length();
|
||||||
let start = args
|
let start = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.and_then(|v| v.coerce_to_f64(avm, context).ok())
|
.and_then(|v| v.coerce_to_f64(activation, context).ok())
|
||||||
.map(|v| make_index_absolute(v as i32, old_length))
|
.map(|v| make_index_absolute(v as i32, old_length))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let count = args
|
let count = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.and_then(|v| v.coerce_to_f64(avm, context).ok())
|
.and_then(|v| v.coerce_to_f64(activation, context).ok())
|
||||||
.map(|v| v as i32)
|
.map(|v| v as i32)
|
||||||
.unwrap_or(old_length as i32);
|
.unwrap_or(old_length as i32);
|
||||||
if count < 0 {
|
if count < 0 {
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
let removed = ScriptObject::array(context.gc_context, Some(avm.prototypes.array));
|
let removed = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
|
||||||
let to_remove = count.min(old_length as i32 - start as i32).max(0) as usize;
|
let to_remove = count.min(old_length as i32 - start as i32).max(0) as usize;
|
||||||
let to_add = if args.len() > 2 { &args[2..] } else { &[] };
|
let to_add = if args.len() > 2 { &args[2..] } else { &[] };
|
||||||
let offset = to_remove as i32 - to_add.len() as i32;
|
let offset = to_remove as i32 - to_add.len() as i32;
|
||||||
|
@ -358,7 +363,7 @@ pub fn splice<'gc>(
|
||||||
|
|
||||||
for i in new_length..old_length {
|
for i in new_length..old_length {
|
||||||
this.delete_array_element(i, context.gc_context);
|
this.delete_array_element(i, context.gc_context);
|
||||||
this.delete(avm, context.gc_context, &i.to_string());
|
this.delete(activation, context.gc_context, &i.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set_length(context.gc_context, new_length);
|
this.set_length(context.gc_context, new_length);
|
||||||
|
@ -367,17 +372,17 @@ pub fn splice<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn concat<'gc>(
|
pub fn concat<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let array = ScriptObject::array(context.gc_context, Some(avm.prototypes.array));
|
let array = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
|
||||||
let mut length = 0;
|
let mut length = 0;
|
||||||
|
|
||||||
for i in 0..this.length() {
|
for i in 0..this.length() {
|
||||||
let old = this
|
let old = this
|
||||||
.get(&i.to_string(), avm, context)
|
.get(&i.to_string(), activation, context)
|
||||||
.unwrap_or(Value::Undefined);
|
.unwrap_or(Value::Undefined);
|
||||||
array.define_value(
|
array.define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -393,11 +398,11 @@ pub fn concat<'gc>(
|
||||||
|
|
||||||
if let Value::Object(object) = arg {
|
if let Value::Object(object) = arg {
|
||||||
let object = *object;
|
let object = *object;
|
||||||
if avm.prototypes.array.is_prototype_of(object) {
|
if activation.avm().prototypes.array.is_prototype_of(object) {
|
||||||
added = true;
|
added = true;
|
||||||
for i in 0..object.length() {
|
for i in 0..object.length() {
|
||||||
let old = object
|
let old = object
|
||||||
.get(&i.to_string(), avm, context)
|
.get(&i.to_string(), activation, context)
|
||||||
.unwrap_or(Value::Undefined);
|
.unwrap_or(Value::Undefined);
|
||||||
array.define_value(
|
array.define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -427,20 +432,20 @@ pub fn concat<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string<'gc>(
|
pub fn to_string<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
join(avm, context, this, &[])
|
join(activation, context, this, &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort<'gc>(
|
fn sort<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
// Overloads:
|
// Overloads:
|
||||||
// 1) a.sort(flags: Number = 0): Sorts with the given flags.
|
// 1) a.sort(flags: Number = 0): Sorts with the given flags.
|
||||||
// 2) a.sort(compare_fn: Object, flags: Number = 0): Sorts using the given compare function and flags.
|
// 2) a.sort(compare_fn: Object, flags: Number = 0): Sorts using the given compare function and flags.
|
||||||
|
@ -453,7 +458,7 @@ fn sort<'gc>(
|
||||||
}
|
}
|
||||||
[compare_fn @ Value::Object(_), ..] => (Some(compare_fn), 0),
|
[compare_fn @ Value::Object(_), ..] => (Some(compare_fn), 0),
|
||||||
[] => (None, 0),
|
[] => (None, 0),
|
||||||
_ => return Ok(Value::Undefined.into()),
|
_ => return Ok(Value::Undefined),
|
||||||
};
|
};
|
||||||
|
|
||||||
let numeric = (flags & NUMERIC) != 0;
|
let numeric = (flags & NUMERIC) != 0;
|
||||||
|
@ -466,10 +471,11 @@ fn sort<'gc>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let compare_fn: CompareFn<'_, 'gc> = if let Some(f) = compare_fn {
|
let compare_fn: CompareFn<'_, 'gc> = if let Some(f) = compare_fn {
|
||||||
let this = crate::avm1::value_object::ValueObject::boxed(avm, context, Value::Undefined);
|
let this =
|
||||||
|
crate::avm1::value_object::ValueObject::boxed(activation, context, Value::Undefined);
|
||||||
// this is undefined in the compare function
|
// this is undefined in the compare function
|
||||||
Box::new(move |avm, context, a: &Value<'gc>, b: &Value<'gc>| {
|
Box::new(move |activation, context, a: &Value<'gc>, b: &Value<'gc>| {
|
||||||
sort_compare_custom(avm, context, this, a, b, &f)
|
sort_compare_custom(activation, context, this, a, b, &f)
|
||||||
})
|
})
|
||||||
} else if numeric {
|
} else if numeric {
|
||||||
Box::new(sort_compare_numeric(string_compare_fn))
|
Box::new(sort_compare_numeric(string_compare_fn))
|
||||||
|
@ -477,15 +483,15 @@ fn sort<'gc>(
|
||||||
Box::new(string_compare_fn)
|
Box::new(string_compare_fn)
|
||||||
};
|
};
|
||||||
|
|
||||||
sort_with_function(avm, context, this, compare_fn, flags)
|
sort_with_function(activation, context, this, compare_fn, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_on<'gc>(
|
fn sort_on<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
// a.sortOn(field_name, flags: Number = 0): Sorts with the given flags.
|
// a.sortOn(field_name, flags: Number = 0): Sorts with the given flags.
|
||||||
// a.sortOn(field_names: Array, flags: Number = 0): Sorts with fields in order of precedence with the given flags.
|
// a.sortOn(field_names: Array, flags: Number = 0): Sorts with fields in order of precedence with the given flags.
|
||||||
// a.sortOn(field_names: Array, flags: Array: Sorts with fields in order of precedence with the given flags respectively.
|
// a.sortOn(field_names: Array, flags: Array: Sorts with fields in order of precedence with the given flags respectively.
|
||||||
|
@ -494,15 +500,17 @@ fn sort_on<'gc>(
|
||||||
// Array of field names.
|
// Array of field names.
|
||||||
let mut field_names = vec![];
|
let mut field_names = vec![];
|
||||||
for name in array.array() {
|
for name in array.array() {
|
||||||
field_names.push(name.coerce_to_string(avm, context)?.to_string());
|
field_names.push(name.coerce_to_string(activation, context)?.to_string());
|
||||||
}
|
}
|
||||||
field_names
|
field_names
|
||||||
}
|
}
|
||||||
Some(field_name) => {
|
Some(field_name) => {
|
||||||
// Single field.
|
// Single field.
|
||||||
vec![field_name.coerce_to_string(avm, context)?.to_string()]
|
vec![field_name
|
||||||
|
.coerce_to_string(activation, context)?
|
||||||
|
.to_string()]
|
||||||
}
|
}
|
||||||
None => return Ok(Value::Undefined.into()),
|
None => return Ok(Value::Undefined),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bail out if we don't have any fields.
|
// Bail out if we don't have any fields.
|
||||||
|
@ -516,7 +524,7 @@ fn sort_on<'gc>(
|
||||||
if array.length() == fields.len() {
|
if array.length() == fields.len() {
|
||||||
let mut flags = vec![];
|
let mut flags = vec![];
|
||||||
for flag in array.array() {
|
for flag in array.array() {
|
||||||
flags.push(flag.coerce_to_i32(avm, context)?);
|
flags.push(flag.coerce_to_i32(activation, context)?);
|
||||||
}
|
}
|
||||||
flags
|
flags
|
||||||
} else {
|
} else {
|
||||||
|
@ -526,7 +534,7 @@ fn sort_on<'gc>(
|
||||||
}
|
}
|
||||||
Some(flags) => {
|
Some(flags) => {
|
||||||
// Single field.
|
// Single field.
|
||||||
let flags = flags.coerce_to_i32(avm, context)?;
|
let flags = flags.coerce_to_i32(activation, context)?;
|
||||||
std::iter::repeat(flags).take(fields.len()).collect()
|
std::iter::repeat(flags).take(fields.len()).collect()
|
||||||
}
|
}
|
||||||
None => std::iter::repeat(0).take(fields.len()).collect(),
|
None => std::iter::repeat(0).take(fields.len()).collect(),
|
||||||
|
@ -558,24 +566,24 @@ fn sort_on<'gc>(
|
||||||
|
|
||||||
let compare_fn = sort_compare_fields(fields, field_compare_fns);
|
let compare_fn = sort_compare_fields(fields, field_compare_fns);
|
||||||
|
|
||||||
sort_with_function(avm, context, this, compare_fn, main_flags)
|
sort_with_function(activation, context, this, compare_fn, main_flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_with_function<'gc>(
|
fn sort_with_function<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
mut compare_fn: impl FnMut(
|
mut compare_fn: impl FnMut(
|
||||||
&mut Avm1<'gc>,
|
&mut Activation<'_, 'gc>,
|
||||||
&mut UpdateContext<'_, 'gc, '_>,
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
&Value<'gc>,
|
&Value<'gc>,
|
||||||
&Value<'gc>,
|
&Value<'gc>,
|
||||||
) -> Ordering,
|
) -> Ordering,
|
||||||
flags: i32,
|
flags: i32,
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let length = this.length();
|
let length = this.length();
|
||||||
let mut values: Vec<(usize, Value<'gc>)> = this.array().into_iter().enumerate().collect();
|
let mut values: Vec<(usize, Value<'gc>)> = this.array().into_iter().enumerate().collect();
|
||||||
let array_proto = avm.prototypes.array;
|
let array_proto = activation.avm().prototypes.array;
|
||||||
|
|
||||||
let descending = (flags & DESCENDING) != 0;
|
let descending = (flags & DESCENDING) != 0;
|
||||||
let unique_sort = (flags & UNIQUE_SORT) != 0;
|
let unique_sort = (flags & UNIQUE_SORT) != 0;
|
||||||
|
@ -583,7 +591,7 @@ fn sort_with_function<'gc>(
|
||||||
|
|
||||||
let mut is_unique = true;
|
let mut is_unique = true;
|
||||||
values.sort_unstable_by(|a, b| {
|
values.sort_unstable_by(|a, b| {
|
||||||
let mut ret = compare_fn(avm, context, &a.1, &b.1);
|
let mut ret = compare_fn(activation, context, &a.1, &b.1);
|
||||||
if descending {
|
if descending {
|
||||||
ret = ret.reverse();
|
ret = ret.reverse();
|
||||||
}
|
}
|
||||||
|
@ -710,13 +718,13 @@ pub fn create_proto<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_compare_string<'gc>(
|
fn sort_compare_string<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
a: &Value<'gc>,
|
a: &Value<'gc>,
|
||||||
b: &Value<'gc>,
|
b: &Value<'gc>,
|
||||||
) -> Ordering {
|
) -> Ordering {
|
||||||
let a_str = a.coerce_to_string(avm, context);
|
let a_str = a.coerce_to_string(activation, context);
|
||||||
let b_str = b.coerce_to_string(avm, context);
|
let b_str = b.coerce_to_string(activation, context);
|
||||||
// TODO: Handle errors.
|
// TODO: Handle errors.
|
||||||
if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) {
|
if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) {
|
||||||
a_str.cmp(&b_str)
|
a_str.cmp(&b_str)
|
||||||
|
@ -726,13 +734,13 @@ fn sort_compare_string<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_compare_string_ignore_case<'gc>(
|
fn sort_compare_string_ignore_case<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
a: &Value<'gc>,
|
a: &Value<'gc>,
|
||||||
b: &Value<'gc>,
|
b: &Value<'gc>,
|
||||||
) -> Ordering {
|
) -> Ordering {
|
||||||
let a_str = a.coerce_to_string(avm, context);
|
let a_str = a.coerce_to_string(activation, context);
|
||||||
let b_str = b.coerce_to_string(avm, context);
|
let b_str = b.coerce_to_string(activation, context);
|
||||||
// TODO: Handle errors.
|
// TODO: Handle errors.
|
||||||
if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) {
|
if let (Ok(a_str), Ok(b_str)) = (a_str, b_str) {
|
||||||
crate::string_utils::swf_string_cmp_ignore_case(&a_str, &b_str)
|
crate::string_utils::swf_string_cmp_ignore_case(&a_str, &b_str)
|
||||||
|
@ -743,18 +751,22 @@ fn sort_compare_string_ignore_case<'gc>(
|
||||||
|
|
||||||
fn sort_compare_numeric<'gc>(
|
fn sort_compare_numeric<'gc>(
|
||||||
mut string_compare_fn: impl FnMut(
|
mut string_compare_fn: impl FnMut(
|
||||||
&mut Avm1<'gc>,
|
&mut Activation<'_, 'gc>,
|
||||||
&mut UpdateContext<'_, 'gc, '_>,
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
&Value<'gc>,
|
&Value<'gc>,
|
||||||
&Value<'gc>,
|
&Value<'gc>,
|
||||||
) -> Ordering,
|
) -> Ordering,
|
||||||
) -> impl FnMut(&mut Avm1<'gc>, &mut UpdateContext<'_, 'gc, '_>, &Value<'gc>, &Value<'gc>) -> Ordering
|
) -> impl FnMut(
|
||||||
{
|
&mut Activation<'_, 'gc>,
|
||||||
move |avm, context, a, b| {
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
&Value<'gc>,
|
||||||
|
&Value<'gc>,
|
||||||
|
) -> Ordering {
|
||||||
|
move |activation, context, a, b| {
|
||||||
if let (Value::Number(a), Value::Number(b)) = (a, b) {
|
if let (Value::Number(a), Value::Number(b)) = (a, b) {
|
||||||
a.partial_cmp(b).unwrap_or(DEFAULT_ORDERING)
|
a.partial_cmp(b).unwrap_or(DEFAULT_ORDERING)
|
||||||
} else {
|
} else {
|
||||||
string_compare_fn(avm, context, a, b)
|
string_compare_fn(activation, context, a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -762,17 +774,22 @@ fn sort_compare_numeric<'gc>(
|
||||||
fn sort_compare_fields<'a, 'gc: 'a>(
|
fn sort_compare_fields<'a, 'gc: 'a>(
|
||||||
field_names: Vec<String>,
|
field_names: Vec<String>,
|
||||||
mut compare_fns: Vec<CompareFn<'a, 'gc>>,
|
mut compare_fns: Vec<CompareFn<'a, 'gc>>,
|
||||||
) -> impl 'a + FnMut(&mut Avm1<'gc>, &mut UpdateContext<'_, 'gc, '_>, &Value<'gc>, &Value<'gc>) -> Ordering
|
) -> impl 'a
|
||||||
{
|
+ FnMut(
|
||||||
|
&mut Activation<'_, 'gc>,
|
||||||
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
&Value<'gc>,
|
||||||
|
&Value<'gc>,
|
||||||
|
) -> Ordering {
|
||||||
use crate::avm1::value_object::ValueObject;
|
use crate::avm1::value_object::ValueObject;
|
||||||
move |avm, context, a, b| {
|
move |activation, context, a, b| {
|
||||||
for (field_name, compare_fn) in field_names.iter().zip(compare_fns.iter_mut()) {
|
for (field_name, compare_fn) in field_names.iter().zip(compare_fns.iter_mut()) {
|
||||||
let a_object = ValueObject::boxed(avm, context, a.clone());
|
let a_object = ValueObject::boxed(activation, context, a.clone());
|
||||||
let b_object = ValueObject::boxed(avm, context, b.clone());
|
let b_object = ValueObject::boxed(activation, context, b.clone());
|
||||||
let a_prop = a_object.get(field_name, avm, context).unwrap();
|
let a_prop = a_object.get(field_name, activation, context).unwrap();
|
||||||
let b_prop = b_object.get(field_name, avm, context).unwrap();
|
let b_prop = b_object.get(field_name, activation, context).unwrap();
|
||||||
|
|
||||||
let result = compare_fn(avm, context, &a_prop, &b_prop);
|
let result = compare_fn(activation, context, &a_prop, &b_prop);
|
||||||
if result != Ordering::Equal {
|
if result != Ordering::Equal {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -784,7 +801,7 @@ fn sort_compare_fields<'a, 'gc: 'a>(
|
||||||
|
|
||||||
// Returning an impl Trait here doesn't work yet because of https://github.com/rust-lang/rust/issues/65805 (?)
|
// Returning an impl Trait here doesn't work yet because of https://github.com/rust-lang/rust/issues/65805 (?)
|
||||||
fn sort_compare_custom<'gc>(
|
fn sort_compare_custom<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
a: &Value<'gc>,
|
a: &Value<'gc>,
|
||||||
|
@ -794,7 +811,7 @@ fn sort_compare_custom<'gc>(
|
||||||
// TODO: Handle errors.
|
// TODO: Handle errors.
|
||||||
let args = [a.clone(), b.clone()];
|
let args = [a.clone(), b.clone()];
|
||||||
let ret = compare_fn
|
let ret = compare_fn
|
||||||
.call(avm, context, this, None, &args)
|
.call(activation, context, this, None, &args)
|
||||||
.unwrap_or(Value::Undefined);
|
.unwrap_or(Value::Undefined);
|
||||||
match ret {
|
match ret {
|
||||||
Value::Number(n) if n > 0.0 => Ordering::Greater,
|
Value::Number(n) if n > 0.0 => Ordering::Greater,
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
//! `Boolean` class impl
|
//! `Boolean` class impl
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::value_object::ValueObject;
|
use crate::avm1::value_object::ValueObject;
|
||||||
use crate::avm1::{Avm1, Object, TObject, Value};
|
use crate::avm1::{Object, TObject, Value};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
/// `Boolean` constructor/function
|
/// `Boolean` constructor/function
|
||||||
pub fn boolean<'gc>(
|
pub fn boolean<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let (ret_value, cons_value) = if let Some(val) = args.get(0) {
|
let (ret_value, cons_value) = if let Some(val) = args.get(0) {
|
||||||
let b = Value::Bool(val.as_bool(avm.current_swf_version()));
|
let b = Value::Bool(val.as_bool(activation.current_swf_version()));
|
||||||
(b.clone(), b)
|
(b.clone(), b)
|
||||||
} else {
|
} else {
|
||||||
(Value::Undefined, Value::Bool(false))
|
(Value::Undefined, Value::Bool(false))
|
||||||
|
@ -30,7 +30,7 @@ pub fn boolean<'gc>(
|
||||||
|
|
||||||
// If called as a function, return the value.
|
// If called as a function, return the value.
|
||||||
// Boolean() with no argument returns undefined.
|
// Boolean() with no argument returns undefined.
|
||||||
Ok(ret_value.into())
|
Ok(ret_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_boolean_object<'gc>(
|
pub fn create_boolean_object<'gc>(
|
||||||
|
@ -74,11 +74,11 @@ pub fn create_proto<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string<'gc>(
|
pub fn to_string<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(vbox) = this.as_value_object() {
|
if let Some(vbox) = this.as_value_object() {
|
||||||
// Must be a bool.
|
// Must be a bool.
|
||||||
// Boolean.prototype.toString.call(x) returns undefined for non-bools.
|
// Boolean.prototype.toString.call(x) returns undefined for non-bools.
|
||||||
|
@ -87,15 +87,15 @@ pub fn to_string<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_of<'gc>(
|
pub fn value_of<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(vbox) = this.as_value_object() {
|
if let Some(vbox) = this.as_value_object() {
|
||||||
// Must be a bool.
|
// Must be a bool.
|
||||||
// Boolean.prototype.valueOf.call(x) returns undefined for non-bools.
|
// Boolean.prototype.valueOf.call(x) returns undefined for non-bools.
|
||||||
|
@ -104,5 +104,5 @@ pub fn value_of<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! Button/SimpleButton prototype
|
//! Button/SimpleButton prototype
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::globals::display_object;
|
use crate::avm1::globals::display_object;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
|
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
pub fn create_proto<'gc>(
|
pub fn create_proto<'gc>(
|
||||||
|
@ -20,10 +20,10 @@ pub fn create_proto<'gc>(
|
||||||
|
|
||||||
/// Implements `Button` constructor.
|
/// Implements `Button` constructor.
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,24 @@
|
||||||
//! TODO: This should change when `ColorTransform` changes to match Flash's representation
|
//! TODO: This should change when `ColorTransform` changes to match Flash's representation
|
||||||
//! (See GitHub #193)
|
//! (See GitHub #193)
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use crate::display_object::{DisplayObject, TDisplayObject};
|
use crate::display_object::{DisplayObject, TDisplayObject};
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: Object<'gc>,
|
mut this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
// The target display object that this color will modify.
|
// The target display object that this color will modify.
|
||||||
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
// Set undocumented `target` property
|
// Set undocumented `target` property
|
||||||
this.set("target", target, avm, context)?;
|
this.set("target", target, activation, context)?;
|
||||||
this.set_attributes(
|
this.set_attributes(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
Some("target"),
|
Some("target"),
|
||||||
|
@ -28,7 +28,7 @@ pub fn constructor<'gc>(
|
||||||
EnumSet::empty(),
|
EnumSet::empty(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_proto<'gc>(
|
pub fn create_proto<'gc>(
|
||||||
|
@ -75,75 +75,116 @@ pub fn create_proto<'gc>(
|
||||||
|
|
||||||
/// Gets the target display object of this color transform.
|
/// Gets the target display object of this color transform.
|
||||||
fn target<'gc>(
|
fn target<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Option<DisplayObject<'gc>>, Error<'gc>> {
|
) -> Result<Option<DisplayObject<'gc>>, Error<'gc>> {
|
||||||
// The target path resolves based on the active tellTarget clip of the stack frame.
|
// The target path resolves based on the active tellTarget clip of the stack frame.
|
||||||
// This means calls on the same `Color` object could set the color of different clips
|
// This means calls on the same `Color` object could set the color of different clips
|
||||||
// depending on which timeline its called from!
|
// depending on which timeline its called from!
|
||||||
let target = this.get("target", avm, context)?;
|
let target = this.get("target", activation, context)?;
|
||||||
// Undefined or empty target is no-op.
|
// Undefined or empty target is no-op.
|
||||||
if target != Value::Undefined && !matches!(&target, &Value::String(ref s) if s.is_empty()) {
|
if target != Value::Undefined && !matches!(&target, &Value::String(ref s) if s.is_empty()) {
|
||||||
let start_clip = avm.target_clip_or_root();
|
let start_clip = activation.target_clip_or_root();
|
||||||
avm.resolve_target_display_object(context, start_clip, target)
|
activation.resolve_target_display_object(context, start_clip, target)
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rgb<'gc>(
|
fn get_rgb<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(target) = target(avm, context, this)? {
|
if let Some(target) = target(activation, context, this)? {
|
||||||
let color_transform = target.color_transform();
|
let color_transform = target.color_transform();
|
||||||
let r = ((color_transform.r_add * 255.0) as i32) << 16;
|
let r = ((color_transform.r_add * 255.0) as i32) << 16;
|
||||||
let g = ((color_transform.g_add * 255.0) as i32) << 8;
|
let g = ((color_transform.g_add * 255.0) as i32) << 8;
|
||||||
let b = (color_transform.b_add * 255.0) as i32;
|
let b = (color_transform.b_add * 255.0) as i32;
|
||||||
Ok((r | g | b).into())
|
Ok((r | g | b).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_transform<'gc>(
|
fn get_transform<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(target) = target(avm, context, this)? {
|
if let Some(target) = target(activation, context, this)? {
|
||||||
let color_transform = target.color_transform();
|
let color_transform = target.color_transform();
|
||||||
let out = ScriptObject::object(context.gc_context, Some(avm.prototypes.object));
|
let out =
|
||||||
out.set("ra", (color_transform.r_mult * 100.0).into(), avm, context)?;
|
ScriptObject::object(context.gc_context, Some(activation.avm().prototypes.object));
|
||||||
out.set("ga", (color_transform.g_mult * 100.0).into(), avm, context)?;
|
out.set(
|
||||||
out.set("ba", (color_transform.b_mult * 100.0).into(), avm, context)?;
|
"ra",
|
||||||
out.set("aa", (color_transform.a_mult * 100.0).into(), avm, context)?;
|
(color_transform.r_mult * 100.0).into(),
|
||||||
out.set("rb", (color_transform.r_add * 255.0).into(), avm, context)?;
|
activation,
|
||||||
out.set("gb", (color_transform.g_add * 255.0).into(), avm, context)?;
|
context,
|
||||||
out.set("bb", (color_transform.b_add * 255.0).into(), avm, context)?;
|
)?;
|
||||||
out.set("ab", (color_transform.a_add * 255.0).into(), avm, context)?;
|
out.set(
|
||||||
|
"ga",
|
||||||
|
(color_transform.g_mult * 100.0).into(),
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
|
out.set(
|
||||||
|
"ba",
|
||||||
|
(color_transform.b_mult * 100.0).into(),
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
|
out.set(
|
||||||
|
"aa",
|
||||||
|
(color_transform.a_mult * 100.0).into(),
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
|
out.set(
|
||||||
|
"rb",
|
||||||
|
(color_transform.r_add * 255.0).into(),
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
|
out.set(
|
||||||
|
"gb",
|
||||||
|
(color_transform.g_add * 255.0).into(),
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
|
out.set(
|
||||||
|
"bb",
|
||||||
|
(color_transform.b_add * 255.0).into(),
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
|
out.set(
|
||||||
|
"ab",
|
||||||
|
(color_transform.a_add * 255.0).into(),
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
Ok(out.into())
|
Ok(out.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_rgb<'gc>(
|
fn set_rgb<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(target) = target(avm, context, this)? {
|
if let Some(target) = target(activation, context, this)? {
|
||||||
let mut color_transform = target.color_transform_mut(context.gc_context);
|
let mut color_transform = target.color_transform_mut(context.gc_context);
|
||||||
let rgb = args
|
let rgb = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)? as i32;
|
.coerce_to_f64(activation, context)? as i32;
|
||||||
let r = (((rgb >> 16) & 0xff) as f32) / 255.0;
|
let r = (((rgb >> 16) & 0xff) as f32) / 255.0;
|
||||||
let g = (((rgb >> 8) & 0xff) as f32) / 255.0;
|
let g = (((rgb >> 8) & 0xff) as f32) / 255.0;
|
||||||
let b = ((rgb & 0xff) as f32) / 255.0;
|
let b = ((rgb & 0xff) as f32) / 255.0;
|
||||||
|
@ -155,67 +196,115 @@ fn set_rgb<'gc>(
|
||||||
color_transform.g_add = g;
|
color_transform.g_add = g;
|
||||||
color_transform.b_add = b;
|
color_transform.b_add = b;
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_transform<'gc>(
|
fn set_transform<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
// TODO: These map from the 0-100% range for mult and the -255-255 range for addition used by ActionScript
|
// TODO: These map from the 0-100% range for mult and the -255-255 range for addition used by ActionScript
|
||||||
// to the 16-bit range used by the internal representations of the Flash Player.
|
// to the 16-bit range used by the internal representations of the Flash Player.
|
||||||
// This will get slightly simpler when we change ColorTransform to the proper representation (see #193).
|
// This will get slightly simpler when we change ColorTransform to the proper representation (see #193).
|
||||||
fn set_color_mult<'gc>(
|
fn set_color_mult<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
transform: Object<'gc>,
|
transform: Object<'gc>,
|
||||||
property: &str,
|
property: &str,
|
||||||
out: &mut f32,
|
out: &mut f32,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
// The parameters are set only if the property exists on the object itself (prototype excluded).
|
// The parameters are set only if the property exists on the object itself (prototype excluded).
|
||||||
if transform.has_own_property(avm, context, property) {
|
if transform.has_own_property(activation, context, property) {
|
||||||
let n = transform
|
let n = transform
|
||||||
.get(property, avm, context)?
|
.get(property, activation, context)?
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
*out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n * 2.56)) / 256.0
|
*out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n * 2.56)) / 256.0
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_color_add<'gc>(
|
fn set_color_add<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
transform: Object<'gc>,
|
transform: Object<'gc>,
|
||||||
property: &str,
|
property: &str,
|
||||||
out: &mut f32,
|
out: &mut f32,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
// The parameters are set only if the property exists on the object itself (prototype excluded).
|
// The parameters are set only if the property exists on the object itself (prototype excluded).
|
||||||
if transform.has_own_property(avm, context, property) {
|
if transform.has_own_property(activation, context, property) {
|
||||||
let n = transform
|
let n = transform
|
||||||
.get(property, avm, context)?
|
.get(property, activation, context)?
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
*out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n)) / 255.0
|
*out = f32::from(crate::avm1::value::f64_to_wrapping_i16(n)) / 255.0
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(target) = target(avm, context, this)? {
|
if let Some(target) = target(activation, context, this)? {
|
||||||
let mut color_transform = target.color_transform_mut(context.gc_context);
|
let mut color_transform = target.color_transform_mut(context.gc_context);
|
||||||
let transform = args
|
let transform = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_object(avm, context);
|
.coerce_to_object(activation, context);
|
||||||
set_color_mult(avm, context, transform, "ra", &mut color_transform.r_mult)?;
|
set_color_mult(
|
||||||
set_color_mult(avm, context, transform, "ga", &mut color_transform.g_mult)?;
|
activation,
|
||||||
set_color_mult(avm, context, transform, "ba", &mut color_transform.b_mult)?;
|
context,
|
||||||
set_color_mult(avm, context, transform, "aa", &mut color_transform.a_mult)?;
|
transform,
|
||||||
set_color_add(avm, context, transform, "rb", &mut color_transform.r_add)?;
|
"ra",
|
||||||
set_color_add(avm, context, transform, "gb", &mut color_transform.g_add)?;
|
&mut color_transform.r_mult,
|
||||||
set_color_add(avm, context, transform, "bb", &mut color_transform.b_add)?;
|
)?;
|
||||||
set_color_add(avm, context, transform, "ab", &mut color_transform.a_add)?;
|
set_color_mult(
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
transform,
|
||||||
|
"ga",
|
||||||
|
&mut color_transform.g_mult,
|
||||||
|
)?;
|
||||||
|
set_color_mult(
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
transform,
|
||||||
|
"ba",
|
||||||
|
&mut color_transform.b_mult,
|
||||||
|
)?;
|
||||||
|
set_color_mult(
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
transform,
|
||||||
|
"aa",
|
||||||
|
&mut color_transform.a_mult,
|
||||||
|
)?;
|
||||||
|
set_color_add(
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
transform,
|
||||||
|
"rb",
|
||||||
|
&mut color_transform.r_add,
|
||||||
|
)?;
|
||||||
|
set_color_add(
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
transform,
|
||||||
|
"gb",
|
||||||
|
&mut color_transform.g_add,
|
||||||
|
)?;
|
||||||
|
set_color_add(
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
transform,
|
||||||
|
"bb",
|
||||||
|
&mut color_transform.b_add,
|
||||||
|
)?;
|
||||||
|
set_color_add(
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
transform,
|
||||||
|
"ab",
|
||||||
|
&mut color_transform.a_add,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! DisplayObject common methods
|
//! DisplayObject common methods
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use crate::display_object::{DisplayObject, TDisplayObject};
|
use crate::display_object::{DisplayObject, TDisplayObject};
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
@ -24,11 +24,11 @@ macro_rules! with_display_object {
|
||||||
$(
|
$(
|
||||||
$object.force_set_function(
|
$object.force_set_function(
|
||||||
$name,
|
$name,
|
||||||
|avm, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|activation, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
return $fn(display_object, avm, context, args);
|
return $fn(display_object, activation, context, args);
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
} as crate::avm1::function::NativeFunction<'gc>,
|
} as crate::avm1::function::NativeFunction<'gc>,
|
||||||
$gc_context,
|
$gc_context,
|
||||||
DontDelete | ReadOnly | DontEnum,
|
DontDelete | ReadOnly | DontEnum,
|
||||||
|
@ -54,7 +54,9 @@ pub fn define_display_object_proto<'gc>(
|
||||||
object.add_property(
|
object.add_property(
|
||||||
gc_context,
|
gc_context,
|
||||||
"_global",
|
"_global",
|
||||||
Executable::Native(|avm, context, _this, _args| Ok(avm.global_object(context).into())),
|
Executable::Native(|activation, context, _this, _args| {
|
||||||
|
Ok(activation.avm().global_object(context))
|
||||||
|
}),
|
||||||
Some(Executable::Native(overwrite_global)),
|
Some(Executable::Native(overwrite_global)),
|
||||||
DontDelete | ReadOnly | DontEnum,
|
DontDelete | ReadOnly | DontEnum,
|
||||||
);
|
);
|
||||||
|
@ -62,7 +64,7 @@ pub fn define_display_object_proto<'gc>(
|
||||||
object.add_property(
|
object.add_property(
|
||||||
gc_context,
|
gc_context,
|
||||||
"_root",
|
"_root",
|
||||||
Executable::Native(|avm, context, _this, _args| Ok(avm.root_object(context).into())),
|
Executable::Native(|activation, context, _this, _args| Ok(activation.root_object(context))),
|
||||||
Some(Executable::Native(overwrite_root)),
|
Some(Executable::Native(overwrite_root)),
|
||||||
DontDelete | ReadOnly | DontEnum,
|
DontDelete | ReadOnly | DontEnum,
|
||||||
);
|
);
|
||||||
|
@ -77,60 +79,59 @@ pub fn define_display_object_proto<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_parent<'gc>(
|
pub fn get_parent<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(this
|
Ok(this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|mc| mc.parent())
|
.and_then(|mc| mc.parent())
|
||||||
.map(|dn| dn.object().coerce_to_object(avm, context))
|
.map(|dn| dn.object().coerce_to_object(activation, context))
|
||||||
.map(Value::Object)
|
.map(Value::Object)
|
||||||
.unwrap_or(Value::Undefined)
|
.unwrap_or(Value::Undefined))
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_depth<'gc>(
|
pub fn get_depth<'gc>(
|
||||||
display_object: DisplayObject<'gc>,
|
display_object: DisplayObject<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() >= 6 {
|
if activation.current_swf_version() >= 6 {
|
||||||
let depth = display_object.depth().wrapping_sub(AVM_DEPTH_BIAS);
|
let depth = display_object.depth().wrapping_sub(AVM_DEPTH_BIAS);
|
||||||
Ok(depth.into())
|
Ok(depth.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overwrite_root<'gc>(
|
pub fn overwrite_root<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let new_val = args
|
let new_val = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.map(|v| v.to_owned())
|
.map(|v| v.to_owned())
|
||||||
.unwrap_or(Value::Undefined);
|
.unwrap_or(Value::Undefined);
|
||||||
this.define_value(ac.gc_context, "_root", new_val, EnumSet::new());
|
this.define_value(ac.gc_context, "_root", new_val, EnumSet::new());
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overwrite_global<'gc>(
|
pub fn overwrite_global<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let new_val = args
|
let new_val = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.map(|v| v.to_owned())
|
.map(|v| v.to_owned())
|
||||||
.unwrap_or(Value::Undefined);
|
.unwrap_or(Value::Undefined);
|
||||||
this.define_value(ac.gc_context, "_global", new_val, EnumSet::new());
|
this.define_value(ac.gc_context, "_global", new_val, EnumSet::new());
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
//! Function prototype
|
//! Function prototype
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
/// Implements `Function`
|
/// Implements `Function`
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `Function.prototype.call`
|
/// Implements `Function.prototype.call`
|
||||||
pub fn call<'gc>(
|
pub fn call<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
func: Object<'gc>,
|
func: Object<'gc>,
|
||||||
myargs: &[Value<'gc>],
|
myargs: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this = match myargs.get(0) {
|
let this = match myargs.get(0) {
|
||||||
Some(Value::Object(this)) => *this,
|
Some(Value::Object(this)) => *this,
|
||||||
_ => avm.globals,
|
_ => activation.avm().globals,
|
||||||
};
|
};
|
||||||
let empty = [];
|
let empty = [];
|
||||||
let args = match myargs.len() {
|
let args = match myargs.len() {
|
||||||
|
@ -35,52 +35,52 @@ pub fn call<'gc>(
|
||||||
};
|
};
|
||||||
|
|
||||||
match func.as_executable() {
|
match func.as_executable() {
|
||||||
Some(exec) => exec.exec(avm, action_context, this, None, args),
|
Some(exec) => exec.exec(activation, action_context, this, None, args),
|
||||||
_ => Ok(Value::Undefined.into()),
|
_ => Ok(Value::Undefined),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `Function.prototype.apply`
|
/// Implements `Function.prototype.apply`
|
||||||
pub fn apply<'gc>(
|
pub fn apply<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
func: Object<'gc>,
|
func: Object<'gc>,
|
||||||
myargs: &[Value<'gc>],
|
myargs: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this = match myargs.get(0) {
|
let this = match myargs.get(0) {
|
||||||
Some(Value::Object(this)) => *this,
|
Some(Value::Object(this)) => *this,
|
||||||
_ => avm.globals,
|
_ => activation.avm().globals,
|
||||||
};
|
};
|
||||||
let mut child_args = Vec::new();
|
let mut child_args = Vec::new();
|
||||||
let args_object = myargs.get(1).cloned().unwrap_or(Value::Undefined);
|
let args_object = myargs.get(1).cloned().unwrap_or(Value::Undefined);
|
||||||
let length = match args_object {
|
let length = match args_object {
|
||||||
Value::Object(a) => a
|
Value::Object(a) => a
|
||||||
.get("length", avm, action_context)?
|
.get("length", activation, action_context)?
|
||||||
.coerce_to_f64(avm, action_context)? as usize,
|
.coerce_to_f64(activation, action_context)? as usize,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
while child_args.len() < length {
|
while child_args.len() < length {
|
||||||
let args = args_object.coerce_to_object(avm, action_context);
|
let args = args_object.coerce_to_object(activation, action_context);
|
||||||
let next_arg = args.get(&format!("{}", child_args.len()), avm, action_context)?;
|
let next_arg = args.get(&format!("{}", child_args.len()), activation, action_context)?;
|
||||||
|
|
||||||
child_args.push(next_arg);
|
child_args.push(next_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
match func.as_executable() {
|
match func.as_executable() {
|
||||||
Some(exec) => exec.exec(avm, action_context, this, None, &child_args),
|
Some(exec) => exec.exec(activation, action_context, this, None, &child_args),
|
||||||
_ => Ok(Value::Undefined.into()),
|
_ => Ok(Value::Undefined),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `Function.prototype.toString`
|
/// Implements `Function.prototype.toString`
|
||||||
fn to_string<'gc>(
|
fn to_string<'gc>(
|
||||||
_: &mut Avm1<'gc>,
|
_: &mut Activation<'_, 'gc>,
|
||||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_: Object<'gc>,
|
_: Object<'gc>,
|
||||||
_: &[Value<'gc>],
|
_: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(ReturnValue::Immediate("[type Function]".into()))
|
Ok("[type Function]".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Partially construct `Function.prototype`.
|
/// Partially construct `Function.prototype`.
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
|
|
||||||
use crate::events::KeyCode;
|
use crate::events::KeyCode;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
pub fn is_down<'gc>(
|
pub fn is_down<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(key) = args
|
if let Some(key) = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.and_then(|v| v.coerce_to_f64(avm, context).ok())
|
.and_then(|v| v.coerce_to_f64(activation, context).ok())
|
||||||
.and_then(|k| KeyCode::try_from(k as u8).ok())
|
.and_then(|k| KeyCode::try_from(k as u8).ok())
|
||||||
{
|
{
|
||||||
Ok(context.input.is_key_down(key).into())
|
Ok(context.input.is_key_down(key).into())
|
||||||
|
@ -25,11 +24,11 @@ pub fn is_down<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_code<'gc>(
|
pub fn get_code<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let code: u8 = context.input.get_last_key_code().into();
|
let code: u8 = context.input.get_last_key_code().into();
|
||||||
Ok(code.into())
|
Ok(code.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::object::Object;
|
use crate::avm1::object::Object;
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::f64::{INFINITY, NAN, NEG_INFINITY};
|
use std::f64::{INFINITY, NAN, NEG_INFINITY};
|
||||||
|
@ -12,9 +12,9 @@ macro_rules! wrap_std {
|
||||||
$(
|
$(
|
||||||
$object.force_set_function(
|
$object.force_set_function(
|
||||||
$name,
|
$name,
|
||||||
|avm, context, _this, args| -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|activation, context, _this, args| -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(input) = args.get(0) {
|
if let Some(input) = args.get(0) {
|
||||||
Ok($std(input.coerce_to_f64(avm, context)?).into())
|
Ok($std(input.coerce_to_f64(activation, context)?).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(NAN.into())
|
Ok(NAN.into())
|
||||||
}
|
}
|
||||||
|
@ -28,50 +28,50 @@ macro_rules! wrap_std {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn atan2<'gc>(
|
fn atan2<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(y) = args.get(0) {
|
if let Some(y) = args.get(0) {
|
||||||
if let Some(x) = args.get(1) {
|
if let Some(x) = args.get(1) {
|
||||||
return Ok(y
|
return Ok(y
|
||||||
.coerce_to_f64(avm, context)?
|
.coerce_to_f64(activation, context)?
|
||||||
.atan2(x.coerce_to_f64(avm, context)?)
|
.atan2(x.coerce_to_f64(activation, context)?)
|
||||||
.into());
|
.into());
|
||||||
} else {
|
} else {
|
||||||
return Ok(y.coerce_to_f64(avm, context)?.atan2(0.0).into());
|
return Ok(y.coerce_to_f64(activation, context)?.atan2(0.0).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(NAN.into())
|
Ok(NAN.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pow<'gc>(
|
fn pow<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(y) = args.get(0) {
|
if let Some(y) = args.get(0) {
|
||||||
if let Some(x) = args.get(1) {
|
if let Some(x) = args.get(1) {
|
||||||
let x = x.coerce_to_f64(avm, context)?;
|
let x = x.coerce_to_f64(activation, context)?;
|
||||||
if x.is_nan() {
|
if x.is_nan() {
|
||||||
return Ok(NAN.into());
|
return Ok(NAN.into());
|
||||||
}
|
}
|
||||||
return Ok(y.coerce_to_f64(avm, context)?.powf(x).into());
|
return Ok(y.coerce_to_f64(activation, context)?.powf(x).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(NAN.into())
|
Ok(NAN.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn round<'gc>(
|
fn round<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(x) = args.get(0) {
|
if let Some(x) = args.get(0) {
|
||||||
let x = x.coerce_to_f64(avm, context)?;
|
let x = x.coerce_to_f64(activation, context)?;
|
||||||
// Note that Flash Math.round always rounds toward infinity,
|
// Note that Flash Math.round always rounds toward infinity,
|
||||||
// unlike Rust f32::round which rounds away from zero.
|
// unlike Rust f32::round which rounds away from zero.
|
||||||
let ret = (x + 0.5).floor();
|
let ret = (x + 0.5).floor();
|
||||||
|
@ -81,19 +81,19 @@ fn round<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max<'gc>(
|
fn max<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(a) = args.get(0) {
|
if let Some(a) = args.get(0) {
|
||||||
return if let Some(b) = args.get(1) {
|
return if let Some(b) = args.get(1) {
|
||||||
match a.abstract_lt(b.to_owned(), avm, context)? {
|
match a.abstract_lt(b.to_owned(), activation, context)? {
|
||||||
Value::Bool(value) => {
|
Value::Bool(value) => {
|
||||||
if value {
|
if value {
|
||||||
Ok(b.coerce_to_f64(avm, context)?.into())
|
Ok(b.coerce_to_f64(activation, context)?.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(a.coerce_to_f64(avm, context)?.into())
|
Ok(a.coerce_to_f64(activation, context)?.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Ok(NAN.into()),
|
_ => Ok(NAN.into()),
|
||||||
|
@ -106,19 +106,19 @@ fn max<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn min<'gc>(
|
fn min<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(a) = args.get(0) {
|
if let Some(a) = args.get(0) {
|
||||||
return if let Some(b) = args.get(1) {
|
return if let Some(b) = args.get(1) {
|
||||||
match a.abstract_lt(b.to_owned(), avm, context)? {
|
match a.abstract_lt(b.to_owned(), activation, context)? {
|
||||||
Value::Bool(value) => {
|
Value::Bool(value) => {
|
||||||
if value {
|
if value {
|
||||||
Ok(a.coerce_to_f64(avm, context)?.into())
|
Ok(a.coerce_to_f64(activation, context)?.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(b.coerce_to_f64(avm, context)?.into())
|
Ok(b.coerce_to_f64(activation, context)?.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Ok(NAN.into()),
|
_ => Ok(NAN.into()),
|
||||||
|
@ -131,11 +131,11 @@ fn min<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random<'gc>(
|
pub fn random<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(action_context.rng.gen_range(0.0f64, 1.0f64).into())
|
Ok(action_context.rng.gen_range(0.0f64, 1.0f64).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,11 +261,14 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::avm1::test_utils::with_avm;
|
use crate::avm1::test_utils::with_avm;
|
||||||
|
|
||||||
fn setup<'gc>(avm: &mut Avm1<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) -> Object<'gc> {
|
fn setup<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
) -> Object<'gc> {
|
||||||
create(
|
create(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
Some(avm.prototypes().object),
|
Some(activation.avm().prototypes().object),
|
||||||
Some(avm.prototypes().function),
|
Some(activation.avm().prototypes().function),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,24 +482,24 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_atan2_nan() {
|
fn test_atan2_nan() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
let math = create(
|
let math = create(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
Some(avm.prototypes().object),
|
Some(activation.avm().prototypes().object),
|
||||||
Some(avm.prototypes().function),
|
Some(activation.avm().prototypes().function),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(atan2(avm, context, math, &[]).unwrap(), NAN.into());
|
assert_eq!(atan2(activation, context, math, &[]).unwrap(), NAN.into());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
atan2(avm, context, math, &[1.0.into(), Value::Null]).unwrap(),
|
atan2(activation, context, math, &[1.0.into(), Value::Null]).unwrap(),
|
||||||
NAN.into()
|
NAN.into()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
atan2(avm, context, math, &[1.0.into(), Value::Undefined]).unwrap(),
|
atan2(activation, context, math, &[1.0.into(), Value::Undefined]).unwrap(),
|
||||||
NAN.into()
|
NAN.into()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
atan2(avm, context, math, &[Value::Undefined, 1.0.into()]).unwrap(),
|
atan2(activation, context, math, &[Value::Undefined, 1.0.into()]).unwrap(),
|
||||||
NAN.into()
|
NAN.into()
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -505,19 +508,19 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_atan2_valid() {
|
fn test_atan2_valid() {
|
||||||
with_avm(19, |avm, context, _root| -> Result<(), Error> {
|
with_avm(19, |activation, context, _root| -> Result<(), Error> {
|
||||||
let math = create(
|
let math = create(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
Some(avm.prototypes().object),
|
Some(activation.avm().prototypes().object),
|
||||||
Some(avm.prototypes().function),
|
Some(activation.avm().prototypes().function),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
atan2(avm, context, math, &[10.0.into()]).unwrap(),
|
atan2(activation, context, math, &[10.0.into()]).unwrap(),
|
||||||
std::f64::consts::FRAC_PI_2.into()
|
std::f64::consts::FRAC_PI_2.into()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
atan2(avm, context, math, &[1.0.into(), 2.0.into()]).unwrap(),
|
atan2(activation, context, math, &[1.0.into(), 2.0.into()]).unwrap(),
|
||||||
f64::atan2(1.0, 2.0).into()
|
f64::atan2(1.0, 2.0).into()
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! flash.geom.Matrix
|
//! flash.geom.Matrix
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value};
|
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
@ -11,36 +11,36 @@ use swf::{Matrix, Twips};
|
||||||
|
|
||||||
pub fn value_to_matrix<'gc>(
|
pub fn value_to_matrix<'gc>(
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Matrix, Error<'gc>> {
|
) -> Result<Matrix, Error<'gc>> {
|
||||||
let a = value
|
let a = value
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("a", avm, context)?
|
.get("a", activation, context)?
|
||||||
.coerce_to_f64(avm, context)? as f32;
|
.coerce_to_f64(activation, context)? as f32;
|
||||||
let b = value
|
let b = value
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("b", avm, context)?
|
.get("b", activation, context)?
|
||||||
.coerce_to_f64(avm, context)? as f32;
|
.coerce_to_f64(activation, context)? as f32;
|
||||||
let c = value
|
let c = value
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("c", avm, context)?
|
.get("c", activation, context)?
|
||||||
.coerce_to_f64(avm, context)? as f32;
|
.coerce_to_f64(activation, context)? as f32;
|
||||||
let d = value
|
let d = value
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("d", avm, context)?
|
.get("d", activation, context)?
|
||||||
.coerce_to_f64(avm, context)? as f32;
|
.coerce_to_f64(activation, context)? as f32;
|
||||||
let tx = Twips::from_pixels(
|
let tx = Twips::from_pixels(
|
||||||
value
|
value
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("tx", avm, context)?
|
.get("tx", activation, context)?
|
||||||
.coerce_to_f64(avm, context)?,
|
.coerce_to_f64(activation, context)?,
|
||||||
);
|
);
|
||||||
let ty = Twips::from_pixels(
|
let ty = Twips::from_pixels(
|
||||||
value
|
value
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("ty", avm, context)?
|
.get("ty", activation, context)?
|
||||||
.coerce_to_f64(avm, context)?,
|
.coerce_to_f64(activation, context)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Matrix { a, b, c, d, tx, ty })
|
Ok(Matrix { a, b, c, d, tx, ty })
|
||||||
|
@ -48,19 +48,29 @@ pub fn value_to_matrix<'gc>(
|
||||||
|
|
||||||
pub fn gradient_object_to_matrix<'gc>(
|
pub fn gradient_object_to_matrix<'gc>(
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Matrix, Error<'gc>> {
|
) -> Result<Matrix, Error<'gc>> {
|
||||||
if object
|
if object
|
||||||
.get("matrixType", avm, context)?
|
.get("matrixType", activation, context)?
|
||||||
.coerce_to_string(avm, context)?
|
.coerce_to_string(activation, context)?
|
||||||
== "box"
|
== "box"
|
||||||
{
|
{
|
||||||
let width = object.get("w", avm, context)?.coerce_to_f64(avm, context)?;
|
let width = object
|
||||||
let height = object.get("h", avm, context)?.coerce_to_f64(avm, context)?;
|
.get("w", activation, context)?
|
||||||
let rotation = object.get("r", avm, context)?.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let tx = object.get("x", avm, context)?.coerce_to_f64(avm, context)?;
|
let height = object
|
||||||
let ty = object.get("y", avm, context)?.coerce_to_f64(avm, context)?;
|
.get("h", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
|
let rotation = object
|
||||||
|
.get("r", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
|
let tx = object
|
||||||
|
.get("x", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
|
let ty = object
|
||||||
|
.get("y", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
Ok(Matrix::create_gradient_box(
|
Ok(Matrix::create_gradient_box(
|
||||||
width as f32,
|
width as f32,
|
||||||
height as f32,
|
height as f32,
|
||||||
|
@ -70,28 +80,36 @@ pub fn gradient_object_to_matrix<'gc>(
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// TODO: You can apparently pass a 3x3 matrix here. Did anybody actually? How does it work?
|
// TODO: You can apparently pass a 3x3 matrix here. Did anybody actually? How does it work?
|
||||||
object_to_matrix(object, avm, context)
|
object_to_matrix(object, activation, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_to_matrix<'gc>(
|
pub fn object_to_matrix<'gc>(
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Matrix, Error<'gc>> {
|
) -> Result<Matrix, Error<'gc>> {
|
||||||
let a = object.get("a", avm, context)?.coerce_to_f64(avm, context)? as f32;
|
let a = object
|
||||||
let b = object.get("b", avm, context)?.coerce_to_f64(avm, context)? as f32;
|
.get("a", activation, context)?
|
||||||
let c = object.get("c", avm, context)?.coerce_to_f64(avm, context)? as f32;
|
.coerce_to_f64(activation, context)? as f32;
|
||||||
let d = object.get("d", avm, context)?.coerce_to_f64(avm, context)? as f32;
|
let b = object
|
||||||
|
.get("b", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)? as f32;
|
||||||
|
let c = object
|
||||||
|
.get("c", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)? as f32;
|
||||||
|
let d = object
|
||||||
|
.get("d", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)? as f32;
|
||||||
let tx = Twips::from_pixels(
|
let tx = Twips::from_pixels(
|
||||||
object
|
object
|
||||||
.get("tx", avm, context)?
|
.get("tx", activation, context)?
|
||||||
.coerce_to_f64(avm, context)?,
|
.coerce_to_f64(activation, context)?,
|
||||||
);
|
);
|
||||||
let ty = Twips::from_pixels(
|
let ty = Twips::from_pixels(
|
||||||
object
|
object
|
||||||
.get("ty", avm, context)?
|
.get("ty", activation, context)?
|
||||||
.coerce_to_f64(avm, context)?,
|
.coerce_to_f64(activation, context)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Matrix { a, b, c, d, tx, ty })
|
Ok(Matrix { a, b, c, d, tx, ty })
|
||||||
|
@ -101,7 +119,7 @@ pub fn object_to_matrix<'gc>(
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn matrix_to_object<'gc>(
|
pub fn matrix_to_object<'gc>(
|
||||||
matrix: Matrix,
|
matrix: Matrix,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
let proto = context.system_prototypes.matrix;
|
let proto = context.system_prototypes.matrix;
|
||||||
|
@ -113,207 +131,207 @@ pub fn matrix_to_object<'gc>(
|
||||||
matrix.tx.to_pixels().into(),
|
matrix.tx.to_pixels().into(),
|
||||||
matrix.ty.to_pixels().into(),
|
matrix.ty.to_pixels().into(),
|
||||||
];
|
];
|
||||||
let object = proto.new(avm, context, proto, &args)?;
|
let object = proto.new(activation, context, proto, &args)?;
|
||||||
let _ = constructor(avm, context, object, &args)?;
|
let _ = constructor(activation, context, object, &args)?;
|
||||||
Ok(object)
|
Ok(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_matrix_to_object<'gc>(
|
pub fn apply_matrix_to_object<'gc>(
|
||||||
matrix: Matrix,
|
matrix: Matrix,
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
object.set("a", matrix.a.into(), avm, context)?;
|
object.set("a", matrix.a.into(), activation, context)?;
|
||||||
object.set("b", matrix.b.into(), avm, context)?;
|
object.set("b", matrix.b.into(), activation, context)?;
|
||||||
object.set("c", matrix.c.into(), avm, context)?;
|
object.set("c", matrix.c.into(), activation, context)?;
|
||||||
object.set("d", matrix.d.into(), avm, context)?;
|
object.set("d", matrix.d.into(), activation, context)?;
|
||||||
object.set("tx", matrix.tx.to_pixels().into(), avm, context)?;
|
object.set("tx", matrix.tx.to_pixels().into(), activation, context)?;
|
||||||
object.set("ty", matrix.ty.to_pixels().into(), avm, context)?;
|
object.set("ty", matrix.ty.to_pixels().into(), activation, context)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constructor<'gc>(
|
fn constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
apply_matrix_to_object(Matrix::identity(), this, avm, context)?;
|
apply_matrix_to_object(Matrix::identity(), this, activation, context)?;
|
||||||
} else {
|
} else {
|
||||||
if let Some(a) = args.get(0) {
|
if let Some(a) = args.get(0) {
|
||||||
this.set("a", a.clone(), avm, context)?;
|
this.set("a", a.clone(), activation, context)?;
|
||||||
}
|
}
|
||||||
if let Some(b) = args.get(1) {
|
if let Some(b) = args.get(1) {
|
||||||
this.set("b", b.clone(), avm, context)?;
|
this.set("b", b.clone(), activation, context)?;
|
||||||
}
|
}
|
||||||
if let Some(c) = args.get(2) {
|
if let Some(c) = args.get(2) {
|
||||||
this.set("c", c.clone(), avm, context)?;
|
this.set("c", c.clone(), activation, context)?;
|
||||||
}
|
}
|
||||||
if let Some(d) = args.get(3) {
|
if let Some(d) = args.get(3) {
|
||||||
this.set("d", d.clone(), avm, context)?;
|
this.set("d", d.clone(), activation, context)?;
|
||||||
}
|
}
|
||||||
if let Some(tx) = args.get(4) {
|
if let Some(tx) = args.get(4) {
|
||||||
this.set("tx", tx.clone(), avm, context)?;
|
this.set("tx", tx.clone(), activation, context)?;
|
||||||
}
|
}
|
||||||
if let Some(ty) = args.get(5) {
|
if let Some(ty) = args.get(5) {
|
||||||
this.set("ty", ty.clone(), avm, context)?;
|
this.set("ty", ty.clone(), activation, context)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identity<'gc>(
|
fn identity<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
apply_matrix_to_object(Matrix::identity(), this, avm, context)?;
|
apply_matrix_to_object(Matrix::identity(), this, activation, context)?;
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone<'gc>(
|
fn clone<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let proto = context.system_prototypes.matrix;
|
let proto = context.system_prototypes.matrix;
|
||||||
let args = [
|
let args = [
|
||||||
this.get("a", avm, context)?,
|
this.get("a", activation, context)?,
|
||||||
this.get("b", avm, context)?,
|
this.get("b", activation, context)?,
|
||||||
this.get("c", avm, context)?,
|
this.get("c", activation, context)?,
|
||||||
this.get("d", avm, context)?,
|
this.get("d", activation, context)?,
|
||||||
this.get("tx", avm, context)?,
|
this.get("tx", activation, context)?,
|
||||||
this.get("ty", avm, context)?,
|
this.get("ty", activation, context)?,
|
||||||
];
|
];
|
||||||
let cloned = proto.new(avm, context, proto, &args)?;
|
let cloned = proto.new(activation, context, proto, &args)?;
|
||||||
let _ = constructor(avm, context, cloned, &args)?;
|
let _ = constructor(activation, context, cloned, &args)?;
|
||||||
Ok(cloned.into())
|
Ok(cloned.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale<'gc>(
|
fn scale<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let scale_x = args
|
let scale_x = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let scale_y = args
|
let scale_y = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let mut matrix = Matrix::scale(scale_x as f32, scale_y as f32);
|
let mut matrix = Matrix::scale(scale_x as f32, scale_y as f32);
|
||||||
matrix *= object_to_matrix(this, avm, context)?;
|
matrix *= object_to_matrix(this, activation, context)?;
|
||||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
apply_matrix_to_object(matrix, this, activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate<'gc>(
|
fn rotate<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let angle = args
|
let angle = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let mut matrix = Matrix::rotate(angle as f32);
|
let mut matrix = Matrix::rotate(angle as f32);
|
||||||
matrix *= object_to_matrix(this, avm, context)?;
|
matrix *= object_to_matrix(this, activation, context)?;
|
||||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
apply_matrix_to_object(matrix, this, activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate<'gc>(
|
fn translate<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let translate_x = args
|
let translate_x = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let translate_y = args
|
let translate_y = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let mut matrix = Matrix::translate(
|
let mut matrix = Matrix::translate(
|
||||||
Twips::from_pixels(translate_x),
|
Twips::from_pixels(translate_x),
|
||||||
Twips::from_pixels(translate_y),
|
Twips::from_pixels(translate_y),
|
||||||
);
|
);
|
||||||
matrix *= object_to_matrix(this, avm, context)?;
|
matrix *= object_to_matrix(this, activation, context)?;
|
||||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
apply_matrix_to_object(matrix, this, activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn concat<'gc>(
|
fn concat<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let mut matrix = object_to_matrix(this, avm, context)?;
|
let mut matrix = object_to_matrix(this, activation, context)?;
|
||||||
let other = value_to_matrix(
|
let other = value_to_matrix(
|
||||||
args.get(0).unwrap_or(&Value::Undefined).clone(),
|
args.get(0).unwrap_or(&Value::Undefined).clone(),
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
)?;
|
)?;
|
||||||
matrix = other * matrix;
|
matrix = other * matrix;
|
||||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
apply_matrix_to_object(matrix, this, activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert<'gc>(
|
fn invert<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let mut matrix = object_to_matrix(this, avm, context)?;
|
let mut matrix = object_to_matrix(this, activation, context)?;
|
||||||
matrix.invert();
|
matrix.invert();
|
||||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
apply_matrix_to_object(matrix, this, activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_box<'gc>(
|
fn create_box<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let scale_x = args
|
let scale_x = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let scale_y = args
|
let scale_y = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
// [NA] Docs say rotation is optional and defaults to 0, but that's wrong?
|
// [NA] Docs say rotation is optional and defaults to 0, but that's wrong?
|
||||||
let rotation = args
|
let rotation = args
|
||||||
.get(2)
|
.get(2)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let translate_x = if let Some(value) = args.get(3) {
|
let translate_x = if let Some(value) = args.get(3) {
|
||||||
value.coerce_to_f64(avm, context)?
|
value.coerce_to_f64(activation, context)?
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
let translate_y = if let Some(value) = args.get(4) {
|
let translate_y = if let Some(value) = args.get(4) {
|
||||||
value.coerce_to_f64(avm, context)?
|
value.coerce_to_f64(activation, context)?
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
@ -325,37 +343,37 @@ fn create_box<'gc>(
|
||||||
Twips::from_pixels(translate_x),
|
Twips::from_pixels(translate_x),
|
||||||
Twips::from_pixels(translate_y),
|
Twips::from_pixels(translate_y),
|
||||||
);
|
);
|
||||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
apply_matrix_to_object(matrix, this, activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_gradient_box<'gc>(
|
fn create_gradient_box<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let width = args
|
let width = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let height = args
|
let height = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let rotation = if let Some(value) = args.get(2) {
|
let rotation = if let Some(value) = args.get(2) {
|
||||||
value.coerce_to_f64(avm, context)?
|
value.coerce_to_f64(activation, context)?
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
let translate_x = if let Some(value) = args.get(3) {
|
let translate_x = if let Some(value) = args.get(3) {
|
||||||
value.coerce_to_f64(avm, context)?
|
value.coerce_to_f64(activation, context)?
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
let translate_y = if let Some(value) = args.get(4) {
|
let translate_y = if let Some(value) = args.get(4) {
|
||||||
value.coerce_to_f64(avm, context)?
|
value.coerce_to_f64(activation, context)?
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
@ -367,32 +385,32 @@ fn create_gradient_box<'gc>(
|
||||||
Twips::from_pixels(translate_x),
|
Twips::from_pixels(translate_x),
|
||||||
Twips::from_pixels(translate_y),
|
Twips::from_pixels(translate_y),
|
||||||
);
|
);
|
||||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
apply_matrix_to_object(matrix, this, activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string<'gc>(
|
fn to_string<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let a = this.get("a", avm, context)?;
|
let a = this.get("a", activation, context)?;
|
||||||
let b = this.get("b", avm, context)?;
|
let b = this.get("b", activation, context)?;
|
||||||
let c = this.get("c", avm, context)?;
|
let c = this.get("c", activation, context)?;
|
||||||
let d = this.get("d", avm, context)?;
|
let d = this.get("d", activation, context)?;
|
||||||
let tx = this.get("tx", avm, context)?;
|
let tx = this.get("tx", activation, context)?;
|
||||||
let ty = this.get("ty", avm, context)?;
|
let ty = this.get("ty", activation, context)?;
|
||||||
|
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"(a={}, b={}, c={}, d={}, tx={}, ty={})",
|
"(a={}, b={}, c={}, d={}, tx={}, ty={})",
|
||||||
a.coerce_to_string(avm, context)?,
|
a.coerce_to_string(activation, context)?,
|
||||||
b.coerce_to_string(avm, context)?,
|
b.coerce_to_string(activation, context)?,
|
||||||
c.coerce_to_string(avm, context)?,
|
c.coerce_to_string(activation, context)?,
|
||||||
d.coerce_to_string(avm, context)?,
|
d.coerce_to_string(activation, context)?,
|
||||||
tx.coerce_to_string(avm, context)?,
|
tx.coerce_to_string(activation, context)?,
|
||||||
ty.coerce_to_string(avm, context)?
|
ty.coerce_to_string(activation, context)?
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::listeners::Listeners;
|
use crate::avm1::listeners::Listeners;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
|
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
pub fn show_mouse<'gc>(
|
pub fn show_mouse<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let was_visible = context.input.mouse_visible();
|
let was_visible = context.input.mouse_visible();
|
||||||
context.input.show_mouse();
|
context.input.show_mouse();
|
||||||
if was_visible {
|
if was_visible {
|
||||||
|
@ -22,11 +21,11 @@ pub fn show_mouse<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide_mouse<'gc>(
|
pub fn hide_mouse<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let was_visible = context.input.mouse_visible();
|
let was_visible = context.input.mouse_visible();
|
||||||
context.input.hide_mouse();
|
context.input.hide_mouse();
|
||||||
if was_visible {
|
if was_visible {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,42 +1,45 @@
|
||||||
//! `MovieClipLoader` impl
|
//! `MovieClipLoader` impl
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::object::TObject;
|
use crate::avm1::object::TObject;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::script_object::ScriptObject;
|
use crate::avm1::script_object::ScriptObject;
|
||||||
use crate::avm1::{Avm1, Object, UpdateContext, Value};
|
use crate::avm1::{Object, UpdateContext, Value};
|
||||||
use crate::backend::navigator::RequestOptions;
|
use crate::backend::navigator::RequestOptions;
|
||||||
use crate::display_object::{DisplayObject, TDisplayObject};
|
use crate::display_object::{DisplayObject, TDisplayObject};
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let listeners = ScriptObject::array(context.gc_context, Some(avm.prototypes().array));
|
let listeners = ScriptObject::array(
|
||||||
|
context.gc_context,
|
||||||
|
Some(activation.avm().prototypes().array),
|
||||||
|
);
|
||||||
this.define_value(
|
this.define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
"_listeners",
|
"_listeners",
|
||||||
Value::Object(listeners.into()),
|
Value::Object(listeners.into()),
|
||||||
Attribute::DontEnum.into(),
|
Attribute::DontEnum.into(),
|
||||||
);
|
);
|
||||||
listeners.set("0", Value::Object(this), avm, context)?;
|
listeners.set("0", Value::Object(this), activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_listener<'gc>(
|
pub fn add_listener<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let new_listener = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let new_listener = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
let listeners = this.get("_listeners", avm, context)?;
|
let listeners = this.get("_listeners", activation, context)?;
|
||||||
|
|
||||||
if let Value::Object(listeners) = listeners {
|
if let Value::Object(listeners) = listeners {
|
||||||
let length = listeners.length();
|
let length = listeners.length();
|
||||||
|
@ -48,20 +51,20 @@ pub fn add_listener<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_listener<'gc>(
|
pub fn remove_listener<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let old_listener = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let old_listener = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
let listeners = this.get("_listeners", avm, context)?;
|
let listeners = this.get("_listeners", activation, context)?;
|
||||||
|
|
||||||
if let Value::Object(listeners) = listeners {
|
if let Value::Object(listeners) = listeners {
|
||||||
let length = listeners.length();
|
let length = listeners.length();
|
||||||
let mut position = None;
|
let mut position = None;
|
||||||
|
|
||||||
for i in 0..length {
|
for i in 0..length {
|
||||||
let other_listener = listeners.get(&format!("{}", i), avm, context)?;
|
let other_listener = listeners.get(&format!("{}", i), activation, context)?;
|
||||||
if old_listener == other_listener {
|
if old_listener == other_listener {
|
||||||
position = Some(i);
|
position = Some(i);
|
||||||
break;
|
break;
|
||||||
|
@ -80,7 +83,7 @@ pub fn remove_listener<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.delete_array_element(new_length, context.gc_context);
|
listeners.delete_array_element(new_length, context.gc_context);
|
||||||
listeners.delete(avm, context.gc_context, &new_length.to_string());
|
listeners.delete(activation, context.gc_context, &new_length.to_string());
|
||||||
|
|
||||||
listeners.set_length(context.gc_context, new_length);
|
listeners.set_length(context.gc_context, new_length);
|
||||||
}
|
}
|
||||||
|
@ -91,37 +94,37 @@ pub fn remove_listener<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn broadcast_message<'gc>(
|
pub fn broadcast_message<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let event_name_val = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let event_name_val = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
let event_name = event_name_val.coerce_to_string(avm, context)?;
|
let event_name = event_name_val.coerce_to_string(activation, context)?;
|
||||||
let call_args = &args[0..];
|
let call_args = &args[0..];
|
||||||
|
|
||||||
let listeners = this.get("_listeners", avm, context)?;
|
let listeners = this.get("_listeners", activation, context)?;
|
||||||
if let Value::Object(listeners) = listeners {
|
if let Value::Object(listeners) = listeners {
|
||||||
for i in 0..listeners.length() {
|
for i in 0..listeners.length() {
|
||||||
let listener = listeners.get(&format!("{}", i), avm, context)?;
|
let listener = listeners.get(&format!("{}", i), activation, context)?;
|
||||||
|
|
||||||
if let Value::Object(listener) = listener {
|
if let Value::Object(listener) = listener {
|
||||||
listener.call_method(&event_name, call_args, avm, context)?;
|
listener.call_method(&event_name, call_args, activation, context)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_clip<'gc>(
|
pub fn load_clip<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let url_val = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let url_val = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
let url = url_val.coerce_to_string(avm, context)?;
|
let url = url_val.coerce_to_string(activation, context)?;
|
||||||
let target = args.get(1).cloned().unwrap_or(Value::Undefined);
|
let target = args.get(1).cloned().unwrap_or(Value::Undefined);
|
||||||
|
|
||||||
if let Value::Object(target) = target {
|
if let Value::Object(target) = target {
|
||||||
|
@ -147,11 +150,11 @@ pub fn load_clip<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unload_clip<'gc>(
|
pub fn unload_clip<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
|
|
||||||
if let Value::Object(target) = target {
|
if let Value::Object(target) = target {
|
||||||
|
@ -170,11 +173,11 @@ pub fn unload_clip<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_progress<'gc>(
|
pub fn get_progress<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let target = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
|
|
||||||
if let Value::Object(target) = target {
|
if let Value::Object(target) = target {
|
||||||
|
@ -206,7 +209,7 @@ pub fn get_progress<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_proto<'gc>(
|
pub fn create_proto<'gc>(
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
//! `Number` class impl
|
//! `Number` class impl
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::value_object::ValueObject;
|
use crate::avm1::value_object::ValueObject;
|
||||||
use crate::avm1::{Avm1, Object, TObject, Value};
|
use crate::avm1::{Object, TObject, Value};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
/// `Number` constructor/function
|
/// `Number` constructor/function
|
||||||
pub fn number<'gc>(
|
pub fn number<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let value = if let Some(val) = args.get(0) {
|
let value = if let Some(val) = args.get(0) {
|
||||||
val.coerce_to_f64(avm, context)?
|
val.coerce_to_f64(activation, context)?
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
@ -113,27 +113,27 @@ pub fn create_proto<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string<'gc>(
|
fn to_string<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
// Boxed value must be a number. No coercion.
|
// Boxed value must be a number. No coercion.
|
||||||
let this = if let Some(vbox) = this.as_value_object() {
|
let this = if let Some(vbox) = this.as_value_object() {
|
||||||
if let Value::Number(n) = vbox.unbox() {
|
if let Value::Number(n) = vbox.unbox() {
|
||||||
n
|
n
|
||||||
} else {
|
} else {
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
let radix = {
|
let radix = {
|
||||||
let radix = args
|
let radix = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
if radix >= 2.0 && radix <= 36.0 {
|
if radix >= 2.0 && radix <= 36.0 {
|
||||||
radix as u32
|
radix as u32
|
||||||
} else {
|
} else {
|
||||||
|
@ -143,7 +143,9 @@ fn to_string<'gc>(
|
||||||
|
|
||||||
if radix == 10 {
|
if radix == 10 {
|
||||||
// Output number as floating-point decimal.
|
// Output number as floating-point decimal.
|
||||||
Ok(Value::from(this).coerce_to_string(avm, context)?.into())
|
Ok(Value::from(this)
|
||||||
|
.coerce_to_string(activation, context)?
|
||||||
|
.into())
|
||||||
} else if this > -2_147_483_648.0 && this < 2_147_483_648.0 {
|
} else if this > -2_147_483_648.0 && this < 2_147_483_648.0 {
|
||||||
// Output truncated integer in specified base.
|
// Output truncated integer in specified base.
|
||||||
let n = this as i32;
|
let n = this as i32;
|
||||||
|
@ -184,18 +186,18 @@ fn to_string<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_of<'gc>(
|
fn value_of<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(vbox) = this.as_value_object() {
|
if let Some(vbox) = this.as_value_object() {
|
||||||
if let Value::Number(n) = vbox.unbox() {
|
if let Value::Number(n) = vbox.unbox() {
|
||||||
return Ok(n.into());
|
return Ok(n.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The values returned by `NaN.toString(radix)` in Flash Player v7+
|
// The values returned by `NaN.toString(radix)` in Flash Player v7+
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! Object prototype
|
//! Object prototype
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::property::Attribute::{self, *};
|
use crate::avm1::property::Attribute::{self, *};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, TObject, UpdateContext, Value};
|
|
||||||
use crate::character::Character;
|
use crate::character::Character;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
@ -11,24 +11,24 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
/// Implements `Object`
|
/// Implements `Object`
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `Object.prototype.addProperty`
|
/// Implements `Object.prototype.addProperty`
|
||||||
pub fn add_property<'gc>(
|
pub fn add_property<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let name = args
|
let name = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.and_then(|v| v.coerce_to_string(avm, context).ok())
|
.and_then(|v| v.coerce_to_string(activation, context).ok())
|
||||||
.unwrap_or_else(|| Cow::Borrowed("undefined"));
|
.unwrap_or_else(|| Cow::Borrowed("undefined"));
|
||||||
let getter = args.get(1).unwrap_or(&Value::Undefined);
|
let getter = args.get(1).unwrap_or(&Value::Undefined);
|
||||||
let setter = args.get(2).unwrap_or(&Value::Undefined);
|
let setter = args.get(2).unwrap_or(&Value::Undefined);
|
||||||
|
@ -39,7 +39,7 @@ pub fn add_property<'gc>(
|
||||||
if let Value::Object(set) = setter {
|
if let Value::Object(set) = setter {
|
||||||
if let Some(set_func) = set.as_executable() {
|
if let Some(set_func) = set.as_executable() {
|
||||||
this.add_property_with_case(
|
this.add_property_with_case(
|
||||||
avm,
|
activation,
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
&name,
|
&name,
|
||||||
get_func.clone(),
|
get_func.clone(),
|
||||||
|
@ -51,7 +51,7 @@ pub fn add_property<'gc>(
|
||||||
}
|
}
|
||||||
} else if let Value::Null = setter {
|
} else if let Value::Null = setter {
|
||||||
this.add_property_with_case(
|
this.add_property_with_case(
|
||||||
avm,
|
activation,
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
&name,
|
&name,
|
||||||
get_func.clone(),
|
get_func.clone(),
|
||||||
|
@ -71,14 +71,16 @@ pub fn add_property<'gc>(
|
||||||
|
|
||||||
/// Implements `Object.prototype.hasOwnProperty`
|
/// Implements `Object.prototype.hasOwnProperty`
|
||||||
pub fn has_own_property<'gc>(
|
pub fn has_own_property<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(value) = args.get(0) {
|
if let Some(value) = args.get(0) {
|
||||||
let name = value.coerce_to_string(avm, context)?;
|
let name = value.coerce_to_string(activation, context)?;
|
||||||
Ok(Value::Bool(this.has_own_property(avm, context, &name)).into())
|
Ok(Value::Bool(
|
||||||
|
this.has_own_property(activation, context, &name),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
|
@ -86,62 +88,62 @@ pub fn has_own_property<'gc>(
|
||||||
|
|
||||||
/// Implements `Object.prototype.toString`
|
/// Implements `Object.prototype.toString`
|
||||||
fn to_string<'gc>(
|
fn to_string<'gc>(
|
||||||
_: &mut Avm1<'gc>,
|
_: &mut Activation<'_, 'gc>,
|
||||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_: Object<'gc>,
|
_: Object<'gc>,
|
||||||
_: &[Value<'gc>],
|
_: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(ReturnValue::Immediate("[object Object]".into()))
|
Ok("[object Object]".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `Object.prototype.isPropertyEnumerable`
|
/// Implements `Object.prototype.isPropertyEnumerable`
|
||||||
fn is_property_enumerable<'gc>(
|
fn is_property_enumerable<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
match args.get(0) {
|
match args.get(0) {
|
||||||
Some(Value::String(name)) => Ok(Value::Bool(this.is_property_enumerable(avm, name)).into()),
|
Some(Value::String(name)) => Ok(Value::Bool(this.is_property_enumerable(activation, name))),
|
||||||
_ => Ok(Value::Bool(false).into()),
|
_ => Ok(Value::Bool(false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `Object.prototype.isPrototypeOf`
|
/// Implements `Object.prototype.isPrototypeOf`
|
||||||
fn is_prototype_of<'gc>(
|
fn is_prototype_of<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
match args.get(0) {
|
match args.get(0) {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
let ob = val.coerce_to_object(avm, context);
|
let ob = val.coerce_to_object(activation, context);
|
||||||
Ok(Value::Bool(this.is_prototype_of(ob)).into())
|
Ok(Value::Bool(this.is_prototype_of(ob)))
|
||||||
}
|
}
|
||||||
_ => Ok(Value::Bool(false).into()),
|
_ => Ok(Value::Bool(false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `Object.prototype.valueOf`
|
/// Implements `Object.prototype.valueOf`
|
||||||
fn value_of<'gc>(
|
fn value_of<'gc>(
|
||||||
_: &mut Avm1<'gc>,
|
_: &mut Activation<'_, 'gc>,
|
||||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_: &[Value<'gc>],
|
_: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(ReturnValue::Immediate(this.into()))
|
Ok(this.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `Object.registerClass`
|
/// Implements `Object.registerClass`
|
||||||
pub fn register_class<'gc>(
|
pub fn register_class<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(class_name) = args.get(0).cloned() {
|
if let Some(class_name) = args.get(0).cloned() {
|
||||||
let class_name = class_name.coerce_to_string(avm, context)?;
|
let class_name = class_name.coerce_to_string(activation, context)?;
|
||||||
if let Some(Character::MovieClip(movie_clip)) = context
|
if let Some(Character::MovieClip(movie_clip)) = context
|
||||||
.library
|
.library
|
||||||
.library_for_movie_mut(context.swf.clone())
|
.library_for_movie_mut(context.swf.clone())
|
||||||
|
@ -150,14 +152,14 @@ pub fn register_class<'gc>(
|
||||||
if let Some(constructor) = args.get(1) {
|
if let Some(constructor) = args.get(1) {
|
||||||
movie_clip.set_avm1_constructor(
|
movie_clip.set_avm1_constructor(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
Some(constructor.coerce_to_object(avm, context)),
|
Some(constructor.coerce_to_object(activation, context)),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
movie_clip.set_avm1_constructor(context.gc_context, None);
|
movie_clip.set_avm1_constructor(context.gc_context, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Partially construct `Object.prototype`.
|
/// Partially construct `Object.prototype`.
|
||||||
|
@ -224,16 +226,16 @@ pub fn fill_proto<'gc>(
|
||||||
/// declare the property flags of a given property. It's not part of
|
/// declare the property flags of a given property. It's not part of
|
||||||
/// `Object.prototype`, and I suspect that's a deliberate omission.
|
/// `Object.prototype`, and I suspect that's a deliberate omission.
|
||||||
pub fn as_set_prop_flags<'gc>(
|
pub fn as_set_prop_flags<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_: Object<'gc>,
|
_: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let mut object = if let Some(object) = args.get(0).map(|v| v.coerce_to_object(avm, ac)) {
|
let mut object = if let Some(object) = args.get(0).map(|v| v.coerce_to_object(activation, ac)) {
|
||||||
object
|
object
|
||||||
} else {
|
} else {
|
||||||
log::warn!("ASSetPropFlags called without object to apply to!");
|
log::warn!("ASSetPropFlags called without object to apply to!");
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
let properties = match args.get(1) {
|
let properties = match args.get(1) {
|
||||||
|
@ -241,11 +243,13 @@ pub fn as_set_prop_flags<'gc>(
|
||||||
//Convert to native array.
|
//Convert to native array.
|
||||||
//TODO: Can we make this an iterator?
|
//TODO: Can we make this an iterator?
|
||||||
let mut array = vec![];
|
let mut array = vec![];
|
||||||
let length = ob.get("length", avm, ac)?.coerce_to_f64(avm, ac)? as usize;
|
let length = ob
|
||||||
|
.get("length", activation, ac)?
|
||||||
|
.coerce_to_f64(activation, ac)? as usize;
|
||||||
for i in 0..length {
|
for i in 0..length {
|
||||||
array.push(
|
array.push(
|
||||||
ob.get(&format!("{}", i), avm, ac)?
|
ob.get(&format!("{}", i), activation, ac)?
|
||||||
.coerce_to_string(avm, ac)?
|
.coerce_to_string(activation, ac)?
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -256,20 +260,20 @@ pub fn as_set_prop_flags<'gc>(
|
||||||
Some(_) => None,
|
Some(_) => None,
|
||||||
None => {
|
None => {
|
||||||
log::warn!("ASSetPropFlags called without object list!");
|
log::warn!("ASSetPropFlags called without object list!");
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let set_attributes = EnumSet::<Attribute>::from_u128(
|
let set_attributes = EnumSet::<Attribute>::from_u128(
|
||||||
args.get(2)
|
args.get(2)
|
||||||
.unwrap_or(&Value::Number(0.0))
|
.unwrap_or(&Value::Number(0.0))
|
||||||
.coerce_to_f64(avm, ac)? as u128,
|
.coerce_to_f64(activation, ac)? as u128,
|
||||||
);
|
);
|
||||||
|
|
||||||
let clear_attributes = EnumSet::<Attribute>::from_u128(
|
let clear_attributes = EnumSet::<Attribute>::from_u128(
|
||||||
args.get(3)
|
args.get(3)
|
||||||
.unwrap_or(&Value::Number(0.0))
|
.unwrap_or(&Value::Number(0.0))
|
||||||
.coerce_to_f64(avm, ac)? as u128,
|
.coerce_to_f64(activation, ac)? as u128,
|
||||||
);
|
);
|
||||||
|
|
||||||
match properties {
|
match properties {
|
||||||
|
@ -286,7 +290,7 @@ pub fn as_set_prop_flags<'gc>(
|
||||||
None => object.set_attributes(ac.gc_context, None, set_attributes, clear_attributes),
|
None => object.set_attributes(ac.gc_context, None, set_attributes, clear_attributes),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_object_object<'gc>(
|
pub fn create_object_object<'gc>(
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! flash.geom.Point
|
//! flash.geom.Point
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value};
|
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
@ -12,103 +12,110 @@ use std::f64::NAN;
|
||||||
|
|
||||||
pub fn point_to_object<'gc>(
|
pub fn point_to_object<'gc>(
|
||||||
point: (f64, f64),
|
point: (f64, f64),
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
let args = [point.0.into(), point.1.into()];
|
let args = [point.0.into(), point.1.into()];
|
||||||
construct_new_point(&args, avm, context)
|
construct_new_point(&args, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn construct_new_point<'gc>(
|
pub fn construct_new_point<'gc>(
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
let proto = context.system_prototypes.point;
|
let proto = context.system_prototypes.point;
|
||||||
let object = proto.new(avm, context, proto, &args)?;
|
let object = proto.new(activation, context, proto, &args)?;
|
||||||
let _ = constructor(avm, context, object, &args)?;
|
let _ = constructor(activation, context, object, &args)?;
|
||||||
Ok(object)
|
Ok(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_to_point<'gc>(
|
pub fn value_to_point<'gc>(
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(f64, f64), Error<'gc>> {
|
) -> Result<(f64, f64), Error<'gc>> {
|
||||||
let x = value
|
let x = value
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("x", avm, context)?
|
.get("x", activation, context)?
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let y = value
|
let y = value
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("y", avm, context)?
|
.get("y", activation, context)?
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
Ok((x, y))
|
Ok((x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_to_point<'gc>(
|
pub fn object_to_point<'gc>(
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(f64, f64), Error<'gc>> {
|
) -> Result<(f64, f64), Error<'gc>> {
|
||||||
let x = object.get("x", avm, context)?.coerce_to_f64(avm, context)?;
|
let x = object
|
||||||
let y = object.get("y", avm, context)?.coerce_to_f64(avm, context)?;
|
.get("x", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
|
let y = object
|
||||||
|
.get("y", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
Ok((x, y))
|
Ok((x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constructor<'gc>(
|
fn constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
this.set("x", 0.into(), avm, context)?;
|
this.set("x", 0.into(), activation, context)?;
|
||||||
this.set("y", 0.into(), avm, context)?;
|
this.set("y", 0.into(), activation, context)?;
|
||||||
} else {
|
} else {
|
||||||
this.set(
|
this.set(
|
||||||
"x",
|
"x",
|
||||||
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
|
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
)?;
|
)?;
|
||||||
this.set(
|
this.set(
|
||||||
"y",
|
"y",
|
||||||
args.get(1).unwrap_or(&Value::Undefined).to_owned(),
|
args.get(1).unwrap_or(&Value::Undefined).to_owned(),
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone<'gc>(
|
fn clone<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let proto = context.system_prototypes.point;
|
let proto = context.system_prototypes.point;
|
||||||
let args = [this.get("x", avm, context)?, this.get("y", avm, context)?];
|
let args = [
|
||||||
let cloned = proto.new(avm, context, proto, &args)?;
|
this.get("x", activation, context)?,
|
||||||
let _ = constructor(avm, context, cloned, &args)?;
|
this.get("y", activation, context)?,
|
||||||
|
];
|
||||||
|
let cloned = proto.new(activation, context, proto, &args)?;
|
||||||
|
let _ = constructor(activation, context, cloned, &args)?;
|
||||||
|
|
||||||
Ok(cloned.into())
|
Ok(cloned.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn equals<'gc>(
|
fn equals<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(other) = args.get(0) {
|
if let Some(other) = args.get(0) {
|
||||||
let this_x = this.get("x", avm, context)?;
|
let this_x = this.get("x", activation, context)?;
|
||||||
let this_y = this.get("y", avm, context)?;
|
let this_y = this.get("y", activation, context)?;
|
||||||
let other = other.coerce_to_object(avm, context);
|
let other = other.coerce_to_object(activation, context);
|
||||||
let other_x = other.get("x", avm, context)?;
|
let other_x = other.get("x", activation, context)?;
|
||||||
let other_y = other.get("y", avm, context)?;
|
let other_y = other.get("y", activation, context)?;
|
||||||
return Ok((this_x == other_x && this_y == other_y).into());
|
return Ok((this_x == other_x && this_y == other_y).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,45 +123,53 @@ fn equals<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add<'gc>(
|
fn add<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this_x = this.get("x", avm, context)?.coerce_to_f64(avm, context)?;
|
let this_x = this
|
||||||
let this_y = this.get("y", avm, context)?.coerce_to_f64(avm, context)?;
|
.get("x", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
|
let this_y = this
|
||||||
|
.get("y", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
let other = value_to_point(
|
let other = value_to_point(
|
||||||
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
|
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
)?;
|
)?;
|
||||||
let object = point_to_object((this_x + other.0, this_y + other.1), avm, context)?;
|
let object = point_to_object((this_x + other.0, this_y + other.1), activation, context)?;
|
||||||
Ok(object.into())
|
Ok(object.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtract<'gc>(
|
fn subtract<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this_x = this.get("x", avm, context)?.coerce_to_f64(avm, context)?;
|
let this_x = this
|
||||||
let this_y = this.get("y", avm, context)?.coerce_to_f64(avm, context)?;
|
.get("x", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
|
let this_y = this
|
||||||
|
.get("y", activation, context)?
|
||||||
|
.coerce_to_f64(activation, context)?;
|
||||||
let other = value_to_point(
|
let other = value_to_point(
|
||||||
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
|
args.get(0).unwrap_or(&Value::Undefined).to_owned(),
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
)?;
|
)?;
|
||||||
let object = point_to_object((this_x - other.0, this_y - other.1), avm, context)?;
|
let object = point_to_object((this_x - other.0, this_y - other.1), activation, context)?;
|
||||||
Ok(object.into())
|
Ok(object.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn distance<'gc>(
|
fn distance<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
return Ok(NAN.into());
|
return Ok(NAN.into());
|
||||||
}
|
}
|
||||||
|
@ -162,93 +177,96 @@ fn distance<'gc>(
|
||||||
let a = args
|
let a = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_object(avm, context);
|
.coerce_to_object(activation, context);
|
||||||
let b = args.get(1).unwrap_or(&Value::Undefined);
|
let b = args.get(1).unwrap_or(&Value::Undefined);
|
||||||
let delta = a.call_method("subtract", &[b.to_owned()], avm, context)?;
|
let delta = a.call_method("subtract", &[b.to_owned()], activation, context)?;
|
||||||
Ok(delta
|
Ok(delta
|
||||||
.coerce_to_object(avm, context)
|
.coerce_to_object(activation, context)
|
||||||
.get("length", avm, context)?
|
.get("length", activation, context)?)
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn polar<'gc>(
|
fn polar<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let length = args
|
let length = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let angle = args
|
let angle = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let point = point_to_object((length * angle.cos(), length * angle.sin()), avm, context)?;
|
let point = point_to_object(
|
||||||
|
(length * angle.cos(), length * angle.sin()),
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
)?;
|
||||||
Ok(point.into())
|
Ok(point.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interpolate<'gc>(
|
fn interpolate<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if args.len() < 3 {
|
if args.len() < 3 {
|
||||||
return Ok(point_to_object((NAN, NAN), avm, context)?.into());
|
return Ok(point_to_object((NAN, NAN), activation, context)?.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let a = value_to_point(args.get(0).unwrap().to_owned(), avm, context)?;
|
let a = value_to_point(args.get(0).unwrap().to_owned(), activation, context)?;
|
||||||
let b = value_to_point(args.get(1).unwrap().to_owned(), avm, context)?;
|
let b = value_to_point(args.get(1).unwrap().to_owned(), activation, context)?;
|
||||||
let f = args.get(2).unwrap().coerce_to_f64(avm, context)?;
|
let f = args.get(2).unwrap().coerce_to_f64(activation, context)?;
|
||||||
let result = (b.0 - (b.0 - a.0) * f, b.1 - (b.1 - a.1) * f);
|
let result = (b.0 - (b.0 - a.0) * f, b.1 - (b.1 - a.1) * f);
|
||||||
Ok(point_to_object(result, avm, context)?.into())
|
Ok(point_to_object(result, activation, context)?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string<'gc>(
|
fn to_string<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let x = this.get("x", avm, context)?;
|
let x = this.get("x", activation, context)?;
|
||||||
let y = this.get("y", avm, context)?;
|
let y = this.get("y", activation, context)?;
|
||||||
|
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"(x={}, y={})",
|
"(x={}, y={})",
|
||||||
x.coerce_to_string(avm, context)?,
|
x.coerce_to_string(activation, context)?,
|
||||||
y.coerce_to_string(avm, context)?
|
y.coerce_to_string(activation, context)?
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn length<'gc>(
|
fn length<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let point = value_to_point(this.into(), avm, context)?;
|
let point = value_to_point(this.into(), activation, context)?;
|
||||||
let length = (point.0 * point.0 + point.1 * point.1).sqrt();
|
let length = (point.0 * point.0 + point.1 * point.1).sqrt();
|
||||||
Ok(length.into())
|
Ok(length.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize<'gc>(
|
fn normalize<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let current_length = this
|
let current_length = this
|
||||||
.get("length", avm, context)?
|
.get("length", activation, context)?
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
if current_length.is_finite() {
|
if current_length.is_finite() {
|
||||||
let point = object_to_point(this, avm, context)?;
|
let point = object_to_point(this, activation, context)?;
|
||||||
let new_length = args
|
let new_length = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let (x, y) = if current_length == 0.0 {
|
let (x, y) = if current_length == 0.0 {
|
||||||
(point.0 * new_length, point.1 * new_length)
|
(point.0 * new_length, point.1 * new_length)
|
||||||
} else {
|
} else {
|
||||||
|
@ -258,33 +276,33 @@ fn normalize<'gc>(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.set("x", x.into(), avm, context)?;
|
this.set("x", x.into(), activation, context)?;
|
||||||
this.set("y", y.into(), avm, context)?;
|
this.set("y", y.into(), activation, context)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset<'gc>(
|
fn offset<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let point = value_to_point(this.into(), avm, context)?;
|
let point = value_to_point(this.into(), activation, context)?;
|
||||||
let dx = args
|
let dx = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let dy = args
|
let dy = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
|
|
||||||
this.set("x", (point.0 + dx).into(), avm, context)?;
|
this.set("x", (point.0 + dx).into(), activation, context)?;
|
||||||
this.set("y", (point.1 + dy).into(), avm, context)?;
|
this.set("y", (point.1 + dy).into(), activation, context)?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_point_object<'gc>(
|
pub fn create_point_object<'gc>(
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, TObject, Value};
|
||||||
use crate::avm1::{Avm1, Object, TObject, Value};
|
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
@ -11,36 +11,36 @@ use crate::avm1::shared_object::SharedObject;
|
||||||
use json::JsonValue;
|
use json::JsonValue;
|
||||||
|
|
||||||
pub fn delete_all<'gc>(
|
pub fn delete_all<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.deleteAll() not implemented");
|
log::warn!("SharedObject.deleteAll() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_disk_usage<'gc>(
|
pub fn get_disk_usage<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.getDiskUsage() not implemented");
|
log::warn!("SharedObject.getDiskUsage() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize an Object and any children to a JSON object
|
/// Serialize an Object and any children to a JSON object
|
||||||
/// It would be best if this was implemented via serde but due to avm and context it can't
|
/// It would be best if this was implemented via serde but due to avm and context it can't
|
||||||
/// Undefined fields aren't serialized
|
/// Undefined fields aren't serialized
|
||||||
fn recursive_serialize<'gc>(
|
fn recursive_serialize<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
obj: Object<'gc>,
|
obj: Object<'gc>,
|
||||||
json_obj: &mut JsonValue,
|
json_obj: &mut JsonValue,
|
||||||
) {
|
) {
|
||||||
for k in &obj.get_keys(avm) {
|
for k in &obj.get_keys(activation) {
|
||||||
if let Ok(elem) = obj.get(k, avm, action_context) {
|
if let Ok(elem) = obj.get(k, activation, action_context) {
|
||||||
match elem {
|
match elem {
|
||||||
Value::Undefined => {}
|
Value::Undefined => {}
|
||||||
Value::Null => json_obj[k] = JsonValue::Null,
|
Value::Null => json_obj[k] = JsonValue::Null,
|
||||||
|
@ -49,12 +49,13 @@ fn recursive_serialize<'gc>(
|
||||||
Value::String(s) => json_obj[k] = s.into(),
|
Value::String(s) => json_obj[k] = s.into(),
|
||||||
Value::Object(o) => {
|
Value::Object(o) => {
|
||||||
// Don't attempt to serialize functions
|
// Don't attempt to serialize functions
|
||||||
|
let function = activation.avm().prototypes.function;
|
||||||
if !o
|
if !o
|
||||||
.is_instance_of(avm, action_context, o, avm.prototypes.function)
|
.is_instance_of(activation, action_context, o, function)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
{
|
{
|
||||||
let mut sub_data_json = JsonValue::new_object();
|
let mut sub_data_json = JsonValue::new_object();
|
||||||
recursive_serialize(avm, action_context, o, &mut sub_data_json);
|
recursive_serialize(activation, action_context, o, &mut sub_data_json);
|
||||||
json_obj[k] = sub_data_json;
|
json_obj[k] = sub_data_json;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +69,7 @@ fn recursive_serialize<'gc>(
|
||||||
/// Undefined fields aren't deserialized
|
/// Undefined fields aren't deserialized
|
||||||
fn recursive_deserialize<'gc>(
|
fn recursive_deserialize<'gc>(
|
||||||
json_obj: JsonValue,
|
json_obj: JsonValue,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
|
@ -112,10 +113,11 @@ fn recursive_deserialize<'gc>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
JsonValue::Object(o) => {
|
JsonValue::Object(o) => {
|
||||||
let so = avm.prototypes.object;
|
let so = activation.avm().prototypes.object;
|
||||||
let obj = so.new(avm, context, so, &[]).unwrap();
|
let obj = so.new(activation, context, so, &[]).unwrap();
|
||||||
let _ = crate::avm1::globals::object::constructor(avm, context, obj, &[]).unwrap();
|
let _ = crate::avm1::globals::object::constructor(activation, context, obj, &[])
|
||||||
recursive_deserialize(JsonValue::Object(o.clone()), avm, obj, context);
|
.unwrap();
|
||||||
|
recursive_deserialize(JsonValue::Object(o.clone()), activation, obj, context);
|
||||||
|
|
||||||
object.define_value(
|
object.define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -130,21 +132,21 @@ fn recursive_deserialize<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local<'gc>(
|
pub fn get_local<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let name = args
|
let name = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.coerce_to_string(avm, action_context)?
|
.coerce_to_string(activation, action_context)?
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
//Check if this is referencing an existing shared object
|
//Check if this is referencing an existing shared object
|
||||||
if let Some(so) = action_context.shared_objects.get(&name) {
|
if let Some(so) = action_context.shared_objects.get(&name) {
|
||||||
return Ok(Value::Object(*so).into());
|
return Ok(Value::Object(*so));
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
|
@ -152,23 +154,23 @@ pub fn get_local<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data property only should exist when created with getLocal/Remote
|
// Data property only should exist when created with getLocal/Remote
|
||||||
let so = avm.prototypes.shared_object;
|
let so = activation.avm().prototypes.shared_object;
|
||||||
let this = so.new(avm, action_context, so, &[])?;
|
let this = so.new(activation, action_context, so, &[])?;
|
||||||
let _ = constructor(avm, action_context, this, &[])?;
|
let _ = constructor(activation, action_context, this, &[])?;
|
||||||
|
|
||||||
// Set the internal name
|
// Set the internal name
|
||||||
let obj_so = this.as_shared_object().unwrap();
|
let obj_so = this.as_shared_object().unwrap();
|
||||||
obj_so.set_name(action_context.gc_context, name.to_string());
|
obj_so.set_name(action_context.gc_context, name.to_string());
|
||||||
|
|
||||||
// Create the data object
|
// Create the data object
|
||||||
let data_proto = avm.prototypes.object;
|
let data_proto = activation.avm().prototypes.object;
|
||||||
let data = data_proto.new(avm, action_context, so, &[])?;
|
let data = data_proto.new(activation, action_context, so, &[])?;
|
||||||
let _ = crate::avm1::globals::object::constructor(avm, action_context, data, &[])?;
|
let _ = crate::avm1::globals::object::constructor(activation, action_context, data, &[])?;
|
||||||
|
|
||||||
// Load the data object from storage if it existed prior
|
// Load the data object from storage if it existed prior
|
||||||
if let Some(saved) = action_context.storage.get_string(&name) {
|
if let Some(saved) = action_context.storage.get_string(&name) {
|
||||||
if let Ok(json_data) = json::parse(&saved) {
|
if let Ok(json_data) = json::parse(&saved) {
|
||||||
recursive_deserialize(json_data, avm, data, action_context);
|
recursive_deserialize(json_data, activation, data, action_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,43 +187,43 @@ pub fn get_local<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_remote<'gc>(
|
pub fn get_remote<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.getRemote() not implemented");
|
log::warn!("SharedObject.getRemote() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_size<'gc>(
|
pub fn get_max_size<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.getMaxSize() not implemented");
|
log::warn!("SharedObject.getMaxSize() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_listener<'gc>(
|
pub fn add_listener<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.addListener() not implemented");
|
log::warn!("SharedObject.addListener() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_listener<'gc>(
|
pub fn remove_listener<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.removeListener() not implemented");
|
log::warn!("SharedObject.removeListener() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_shared_object_object<'gc>(
|
pub fn create_shared_object_object<'gc>(
|
||||||
|
@ -297,17 +299,17 @@ pub fn create_shared_object_object<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear<'gc>(
|
pub fn clear<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let data = this
|
let data = this
|
||||||
.get("data", avm, action_context)?
|
.get("data", activation, action_context)?
|
||||||
.coerce_to_object(avm, action_context);
|
.coerce_to_object(activation, action_context);
|
||||||
|
|
||||||
for k in &data.get_keys(avm) {
|
for k in &data.get_keys(activation) {
|
||||||
data.delete(avm, action_context.gc_context, k);
|
data.delete(activation, action_context.gc_context, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
let so = this.as_shared_object().unwrap();
|
let so = this.as_shared_object().unwrap();
|
||||||
|
@ -315,41 +317,41 @@ pub fn clear<'gc>(
|
||||||
|
|
||||||
action_context.storage.remove_key(&name);
|
action_context.storage.remove_key(&name);
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close<'gc>(
|
pub fn close<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.close() not implemented");
|
log::warn!("SharedObject.close() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect<'gc>(
|
pub fn connect<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.connect() not implemented");
|
log::warn!("SharedObject.connect() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush<'gc>(
|
pub fn flush<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let data = this
|
let data = this
|
||||||
.get("data", avm, action_context)?
|
.get("data", activation, action_context)?
|
||||||
.coerce_to_object(avm, action_context);
|
.coerce_to_object(activation, action_context);
|
||||||
|
|
||||||
let mut data_json = JsonValue::new_object();
|
let mut data_json = JsonValue::new_object();
|
||||||
recursive_serialize(avm, action_context, data, &mut data_json);
|
recursive_serialize(activation, action_context, data, &mut data_json);
|
||||||
|
|
||||||
let this_obj = this.as_shared_object().unwrap();
|
let this_obj = this.as_shared_object().unwrap();
|
||||||
let name = this_obj.get_name();
|
let name = this_obj.get_name();
|
||||||
|
@ -361,53 +363,53 @@ pub fn flush<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_size<'gc>(
|
pub fn get_size<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.getSize() not implemented");
|
log::warn!("SharedObject.getSize() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send<'gc>(
|
pub fn send<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.send() not implemented");
|
log::warn!("SharedObject.send() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fps<'gc>(
|
pub fn set_fps<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.setFps() not implemented");
|
log::warn!("SharedObject.setFps() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_status<'gc>(
|
pub fn on_status<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.onStatus() not implemented");
|
log::warn!("SharedObject.onStatus() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_sync<'gc>(
|
pub fn on_sync<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("SharedObject.onSync() not implemented");
|
log::warn!("SharedObject.onSync() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_proto<'gc>(
|
pub fn create_proto<'gc>(
|
||||||
|
@ -470,10 +472,10 @@ pub fn create_proto<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
//! AVM1 Sound object
|
//! AVM1 Sound object
|
||||||
//! TODO: Sound position, transform, loadSound
|
//! TODO: Sound position, transform, loadSound
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, SoundObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, SoundObject, TObject, UpdateContext, Value};
|
|
||||||
use crate::character::Character;
|
use crate::character::Character;
|
||||||
use crate::display_object::TDisplayObject;
|
use crate::display_object::TDisplayObject;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
/// Implements `Sound`
|
/// Implements `Sound`
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
// 1st parameter is the movie clip that "owns" all sounds started by this object.
|
// 1st parameter is the movie clip that "owns" all sounds started by this object.
|
||||||
// `Sound.setTransform`, `Sound.stop`, etc. will affect all sounds owned by this clip.
|
// `Sound.setTransform`, `Sound.stop`, etc. will affect all sounds owned by this clip.
|
||||||
let owner = args
|
let owner = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.map(|o| o.coerce_to_object(avm, context))
|
.map(|o| o.coerce_to_object(activation, context))
|
||||||
.and_then(|o| o.as_display_object());
|
.and_then(|o| o.as_display_object());
|
||||||
|
|
||||||
let sound = this.as_sound_object().unwrap();
|
let sound = this.as_sound_object().unwrap();
|
||||||
|
@ -161,14 +161,14 @@ pub fn create_proto<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attach_sound<'gc>(
|
fn attach_sound<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let name = args.get(0).unwrap_or(&Value::Undefined);
|
let name = args.get(0).unwrap_or(&Value::Undefined);
|
||||||
if let Some(sound_object) = this.as_sound_object() {
|
if let Some(sound_object) = this.as_sound_object() {
|
||||||
let name = name.coerce_to_string(avm, context)?;
|
let name = name.coerce_to_string(activation, context)?;
|
||||||
let movie = sound_object
|
let movie = sound_object
|
||||||
.owner()
|
.owner()
|
||||||
.or_else(|| context.levels.get(&0).copied())
|
.or_else(|| context.levels.get(&0).copied())
|
||||||
|
@ -197,16 +197,16 @@ fn attach_sound<'gc>(
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Sound.attachSound: this is not a Sound");
|
log::warn!("Sound.attachSound: this is not a Sound");
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duration<'gc>(
|
fn duration<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() >= 6 {
|
if activation.current_swf_version() >= 6 {
|
||||||
if let Some(sound_object) = this.as_sound_object() {
|
if let Some(sound_object) = this.as_sound_object() {
|
||||||
return Ok(sound_object.duration().into());
|
return Ok(sound_object.duration().into());
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,98 +214,98 @@ fn duration<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bytes_loaded<'gc>(
|
fn get_bytes_loaded<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() >= 6 {
|
if activation.current_swf_version() >= 6 {
|
||||||
log::warn!("Sound.getBytesLoaded: Unimplemented");
|
log::warn!("Sound.getBytesLoaded: Unimplemented");
|
||||||
Ok(1.into())
|
Ok(1.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bytes_total<'gc>(
|
fn get_bytes_total<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() >= 6 {
|
if activation.current_swf_version() >= 6 {
|
||||||
log::warn!("Sound.getBytesTotal: Unimplemented");
|
log::warn!("Sound.getBytesTotal: Unimplemented");
|
||||||
Ok(1.into())
|
Ok(1.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pan<'gc>(
|
fn get_pan<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Sound.getPan: Unimplemented");
|
log::warn!("Sound.getPan: Unimplemented");
|
||||||
Ok(0.into())
|
Ok(0.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_transform<'gc>(
|
fn get_transform<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Sound.getTransform: Unimplemented");
|
log::warn!("Sound.getTransform: Unimplemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_volume<'gc>(
|
fn get_volume<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Sound.getVolume: Unimplemented");
|
log::warn!("Sound.getVolume: Unimplemented");
|
||||||
Ok(100.into())
|
Ok(100.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id3<'gc>(
|
fn id3<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() >= 6 {
|
if activation.current_swf_version() >= 6 {
|
||||||
log::warn!("Sound.id3: Unimplemented");
|
log::warn!("Sound.id3: Unimplemented");
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_sound<'gc>(
|
fn load_sound<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() >= 6 {
|
if activation.current_swf_version() >= 6 {
|
||||||
log::warn!("Sound.loadSound: Unimplemented");
|
log::warn!("Sound.loadSound: Unimplemented");
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn position<'gc>(
|
fn position<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if avm.current_swf_version() >= 6 {
|
if activation.current_swf_version() >= 6 {
|
||||||
if let Some(sound_object) = this.as_sound_object() {
|
if let Some(sound_object) = this.as_sound_object() {
|
||||||
// TODO: The position is "sticky"; even if the sound is no longer playing, it should return
|
// TODO: The position is "sticky"; even if the sound is no longer playing, it should return
|
||||||
// the previous valid position.
|
// the previous valid position.
|
||||||
|
@ -320,53 +320,53 @@ fn position<'gc>(
|
||||||
log::warn!("Sound.position: this is not a Sound");
|
log::warn!("Sound.position: this is not a Sound");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_pan<'gc>(
|
fn set_pan<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Sound.setPan: Unimplemented");
|
log::warn!("Sound.setPan: Unimplemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_transform<'gc>(
|
fn set_transform<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Sound.setTransform: Unimplemented");
|
log::warn!("Sound.setTransform: Unimplemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_volume<'gc>(
|
fn set_volume<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Sound.setVolume: Unimplemented");
|
log::warn!("Sound.setVolume: Unimplemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start<'gc>(
|
fn start<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let start_offset = args
|
let start_offset = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Number(0.0))
|
.unwrap_or(&Value::Number(0.0))
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let loops = args
|
let loops = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap_or(&Value::Number(1.0))
|
.unwrap_or(&Value::Number(1.0))
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
|
|
||||||
let loops = if loops >= 1.0 && loops <= f64::from(std::i16::MAX) {
|
let loops = if loops >= 1.0 && loops <= f64::from(std::i16::MAX) {
|
||||||
loops as u16
|
loops as u16
|
||||||
|
@ -401,19 +401,19 @@ fn start<'gc>(
|
||||||
log::warn!("Sound.start: Invalid sound");
|
log::warn!("Sound.start: Invalid sound");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop<'gc>(
|
fn stop<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(sound) = this.as_sound_object() {
|
if let Some(sound) = this.as_sound_object() {
|
||||||
if let Some(name) = args.get(0) {
|
if let Some(name) = args.get(0) {
|
||||||
// Usage 1: Stop all instances of a particular sound, using the name parameter.
|
// Usage 1: Stop all instances of a particular sound, using the name parameter.
|
||||||
let name = name.coerce_to_string(avm, context)?;
|
let name = name.coerce_to_string(activation, context)?;
|
||||||
let movie = sound
|
let movie = sound
|
||||||
.owner()
|
.owner()
|
||||||
.or_else(|| context.levels.get(&0).copied())
|
.or_else(|| context.levels.get(&0).copied())
|
||||||
|
@ -449,5 +449,5 @@ fn stop<'gc>(
|
||||||
log::warn!("Sound.stop: this is not a Sound");
|
log::warn!("Sound.stop: this is not a Sound");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
//! Stage object
|
//! Stage object
|
||||||
//!
|
//!
|
||||||
//! TODO: This is a very rough stub with not much implementation.
|
//! TODO: This is a very rough stub with not much implementation.
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
|
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
pub fn create_stage_object<'gc>(
|
pub fn create_stage_object<'gc>(
|
||||||
|
@ -77,99 +76,99 @@ pub fn create_stage_object<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_listener<'gc>(
|
fn add_listener<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Stage.addListener: unimplemented");
|
log::warn!("Stage.addListener: unimplemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align<'gc>(
|
fn align<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Stage.align: unimplemented");
|
log::warn!("Stage.align: unimplemented");
|
||||||
Ok("".into())
|
Ok("".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_align<'gc>(
|
fn set_align<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Stage.align: unimplemented");
|
log::warn!("Stage.align: unimplemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn height<'gc>(
|
fn height<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.stage_size.1.to_pixels().into())
|
Ok(context.stage_size.1.to_pixels().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_listener<'gc>(
|
fn remove_listener<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Stage.removeListener: unimplemented");
|
log::warn!("Stage.removeListener: unimplemented");
|
||||||
Ok("".into())
|
Ok("".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale_mode<'gc>(
|
fn scale_mode<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Stage.scaleMode: unimplemented");
|
log::warn!("Stage.scaleMode: unimplemented");
|
||||||
Ok("noScale".into())
|
Ok("noScale".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_scale_mode<'gc>(
|
fn set_scale_mode<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Stage.scaleMode: unimplemented");
|
log::warn!("Stage.scaleMode: unimplemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_menu<'gc>(
|
fn show_menu<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Stage.showMenu: unimplemented");
|
log::warn!("Stage.showMenu: unimplemented");
|
||||||
Ok(true.into())
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_show_menu<'gc>(
|
fn set_show_menu<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("Stage.showMenu: unimplemented");
|
log::warn!("Stage.showMenu: unimplemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn width<'gc>(
|
fn width<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.stage_size.0.to_pixels().into())
|
Ok(context.stage_size.0.to_pixels().into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! `String` class impl
|
//! `String` class impl
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::value_object::ValueObject;
|
use crate::avm1::value_object::ValueObject;
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value};
|
use crate::avm1::{Object, ScriptObject, TObject, Value};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::string_utils;
|
use crate::string_utils;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
|
@ -13,14 +13,14 @@ use gc_arena::MutationContext;
|
||||||
|
|
||||||
/// `String` constructor
|
/// `String` constructor
|
||||||
pub fn string<'gc>(
|
pub fn string<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let value = match args.get(0).cloned() {
|
let value = match args.get(0).cloned() {
|
||||||
Some(Value::String(s)) => s,
|
Some(Value::String(s)) => s,
|
||||||
Some(v) => v.coerce_to_string(avm, ac)?.to_string(),
|
Some(v) => v.coerce_to_string(activation, ac)?.to_string(),
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,19 +171,19 @@ pub fn create_proto<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn char_at<'gc>(
|
fn char_at<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
// TODO: Will return REPLACEMENT_CHAR if this indexes a character outside the BMP, losing info about the surrogate.
|
// TODO: Will return REPLACEMENT_CHAR if this indexes a character outside the BMP, losing info about the surrogate.
|
||||||
// When we improve our string representation, the unpaired surrogate should be returned.
|
// When we improve our string representation, the unpaired surrogate should be returned.
|
||||||
let this_val = Value::from(this);
|
let this_val = Value::from(this);
|
||||||
let string = this_val.coerce_to_string(avm, context)?;
|
let string = this_val.coerce_to_string(activation, context)?;
|
||||||
let i = args
|
let i = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_i32(avm, context)?;
|
.coerce_to_i32(activation, context)?;
|
||||||
let ret = if i >= 0 {
|
let ret = if i >= 0 {
|
||||||
string
|
string
|
||||||
.encode_utf16()
|
.encode_utf16()
|
||||||
|
@ -197,17 +197,17 @@ fn char_at<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn char_code_at<'gc>(
|
fn char_code_at<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this_val = Value::from(this);
|
let this_val = Value::from(this);
|
||||||
let this = this_val.coerce_to_string(avm, context)?;
|
let this = this_val.coerce_to_string(activation, context)?;
|
||||||
let i = args
|
let i = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_i32(avm, context)?;
|
.coerce_to_i32(activation, context)?;
|
||||||
let ret = if i >= 0 {
|
let ret = if i >= 0 {
|
||||||
this.encode_utf16()
|
this.encode_utf16()
|
||||||
.nth(i as usize)
|
.nth(i as usize)
|
||||||
|
@ -220,31 +220,31 @@ fn char_code_at<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn concat<'gc>(
|
fn concat<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let mut ret = Value::from(this)
|
let mut ret = Value::from(this)
|
||||||
.coerce_to_string(avm, context)?
|
.coerce_to_string(activation, context)?
|
||||||
.to_string();
|
.to_string();
|
||||||
for arg in args {
|
for arg in args {
|
||||||
let s = arg.coerce_to_string(avm, context)?;
|
let s = arg.coerce_to_string(activation, context)?;
|
||||||
ret.push_str(&s)
|
ret.push_str(&s)
|
||||||
}
|
}
|
||||||
Ok(ret.into())
|
Ok(ret.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_char_code<'gc>(
|
fn from_char_code<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
// TODO: Unpaired surrogates will be replace with Unicode replacement char.
|
// TODO: Unpaired surrogates will be replace with Unicode replacement char.
|
||||||
let mut out = String::with_capacity(args.len());
|
let mut out = String::with_capacity(args.len());
|
||||||
for arg in args {
|
for arg in args {
|
||||||
let i = arg.coerce_to_u16(avm, context)?;
|
let i = arg.coerce_to_u16(activation, context)?;
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
// Stop at a null-terminator.
|
// Stop at a null-terminator.
|
||||||
break;
|
break;
|
||||||
|
@ -255,20 +255,20 @@ fn from_char_code<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index_of<'gc>(
|
fn index_of<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this = Value::from(this)
|
let this = Value::from(this)
|
||||||
.coerce_to_string(avm, context)?
|
.coerce_to_string(activation, context)?
|
||||||
.encode_utf16()
|
.encode_utf16()
|
||||||
.collect::<Vec<u16>>();
|
.collect::<Vec<u16>>();
|
||||||
let pattern = match args.get(0) {
|
let pattern = match args.get(0) {
|
||||||
None | Some(Value::Undefined) => return Ok(Value::Undefined.into()),
|
None | Some(Value::Undefined) => return Ok(Value::Undefined),
|
||||||
Some(s) => s
|
Some(s) => s
|
||||||
.clone()
|
.clone()
|
||||||
.coerce_to_string(avm, context)?
|
.coerce_to_string(activation, context)?
|
||||||
.encode_utf16()
|
.encode_utf16()
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
};
|
};
|
||||||
|
@ -276,7 +276,7 @@ fn index_of<'gc>(
|
||||||
let n = args
|
let n = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_i32(avm, context)?;
|
.coerce_to_i32(activation, context)?;
|
||||||
if n >= 0 {
|
if n >= 0 {
|
||||||
n as usize
|
n as usize
|
||||||
} else {
|
} else {
|
||||||
|
@ -303,27 +303,27 @@ fn index_of<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_index_of<'gc>(
|
fn last_index_of<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this = Value::from(this)
|
let this = Value::from(this)
|
||||||
.coerce_to_string(avm, context)?
|
.coerce_to_string(activation, context)?
|
||||||
.encode_utf16()
|
.encode_utf16()
|
||||||
.collect::<Vec<u16>>();
|
.collect::<Vec<u16>>();
|
||||||
let pattern = match args.get(0) {
|
let pattern = match args.get(0) {
|
||||||
None | Some(Value::Undefined) => return Ok(Value::Undefined.into()),
|
None | Some(Value::Undefined) => return Ok(Value::Undefined),
|
||||||
Some(s) => s
|
Some(s) => s
|
||||||
.clone()
|
.clone()
|
||||||
.coerce_to_string(avm, context)?
|
.coerce_to_string(activation, context)?
|
||||||
.encode_utf16()
|
.encode_utf16()
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
};
|
};
|
||||||
let start_index = match args.get(1) {
|
let start_index = match args.get(1) {
|
||||||
None | Some(Value::Undefined) => this.len(),
|
None | Some(Value::Undefined) => this.len(),
|
||||||
Some(n) => {
|
Some(n) => {
|
||||||
let n = n.coerce_to_i32(avm, context)?;
|
let n = n.coerce_to_i32(activation, context)?;
|
||||||
if n >= 0 {
|
if n >= 0 {
|
||||||
let n = n as usize;
|
let n = n as usize;
|
||||||
if n <= this.len() {
|
if n <= this.len() {
|
||||||
|
@ -355,28 +355,28 @@ fn last_index_of<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slice<'gc>(
|
fn slice<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
// No args returns undefined immediately.
|
// No args returns undefined immediately.
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_val = Value::from(this);
|
let this_val = Value::from(this);
|
||||||
let this = this_val.coerce_to_string(avm, context)?;
|
let this = this_val.coerce_to_string(activation, context)?;
|
||||||
let this_len = this.encode_utf16().count();
|
let this_len = this.encode_utf16().count();
|
||||||
let start_index = string_wrapping_index(
|
let start_index = string_wrapping_index(
|
||||||
args.get(0)
|
args.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_i32(avm, context)?,
|
.coerce_to_i32(activation, context)?,
|
||||||
this_len,
|
this_len,
|
||||||
);
|
);
|
||||||
let end_index = match args.get(1) {
|
let end_index = match args.get(1) {
|
||||||
None | Some(Value::Undefined) => this_len,
|
None | Some(Value::Undefined) => this_len,
|
||||||
Some(n) => string_wrapping_index(n.coerce_to_i32(avm, context)?, this_len),
|
Some(n) => string_wrapping_index(n.coerce_to_i32(activation, context)?, this_len),
|
||||||
};
|
};
|
||||||
if start_index < end_index {
|
if start_index < end_index {
|
||||||
let ret = utf16_iter_to_string(
|
let ret = utf16_iter_to_string(
|
||||||
|
@ -391,20 +391,20 @@ fn slice<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split<'gc>(
|
fn split<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this_val = Value::from(this);
|
let this_val = Value::from(this);
|
||||||
let this = this_val.coerce_to_string(avm, context)?;
|
let this = this_val.coerce_to_string(activation, context)?;
|
||||||
let delimiter_val = args.get(0).unwrap_or(&Value::Undefined);
|
let delimiter_val = args.get(0).unwrap_or(&Value::Undefined);
|
||||||
let delimiter = delimiter_val.coerce_to_string(avm, context)?;
|
let delimiter = delimiter_val.coerce_to_string(activation, context)?;
|
||||||
let limit = match args.get(1) {
|
let limit = match args.get(1) {
|
||||||
None | Some(Value::Undefined) => std::usize::MAX,
|
None | Some(Value::Undefined) => std::usize::MAX,
|
||||||
Some(n) => std::cmp::max(0, n.coerce_to_i32(avm, context)?) as usize,
|
Some(n) => std::cmp::max(0, n.coerce_to_i32(activation, context)?) as usize,
|
||||||
};
|
};
|
||||||
let array = ScriptObject::array(context.gc_context, Some(avm.prototypes.array));
|
let array = ScriptObject::array(context.gc_context, Some(activation.avm().prototypes.array));
|
||||||
if !delimiter.is_empty() {
|
if !delimiter.is_empty() {
|
||||||
for (i, token) in this.split(delimiter.as_ref()).take(limit).enumerate() {
|
for (i, token) in this.split(delimiter.as_ref()).take(limit).enumerate() {
|
||||||
array.set_array_element(i, token.to_string().into(), context.gc_context);
|
array.set_array_element(i, token.to_string().into(), context.gc_context);
|
||||||
|
@ -421,24 +421,26 @@ fn split<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn substr<'gc>(
|
fn substr<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_val = Value::from(this);
|
let this_val = Value::from(this);
|
||||||
let this = this_val.coerce_to_string(avm, context)?;
|
let this = this_val.coerce_to_string(activation, context)?;
|
||||||
let this_len = this.encode_utf16().count();
|
let this_len = this.encode_utf16().count();
|
||||||
let start_index =
|
let start_index = string_wrapping_index(
|
||||||
string_wrapping_index(args.get(0).unwrap().coerce_to_i32(avm, context)?, this_len);
|
args.get(0).unwrap().coerce_to_i32(activation, context)?,
|
||||||
|
this_len,
|
||||||
|
);
|
||||||
|
|
||||||
let len = match args.get(1) {
|
let len = match args.get(1) {
|
||||||
None | Some(Value::Undefined) => this_len,
|
None | Some(Value::Undefined) => this_len,
|
||||||
Some(n) => string_index(n.coerce_to_i32(avm, context)?, this_len),
|
Some(n) => string_index(n.coerce_to_i32(activation, context)?, this_len),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ret = utf16_iter_to_string(this.encode_utf16().skip(start_index).take(len));
|
let ret = utf16_iter_to_string(this.encode_utf16().skip(start_index).take(len));
|
||||||
|
@ -446,23 +448,26 @@ fn substr<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn substring<'gc>(
|
fn substring<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_val = Value::from(this);
|
let this_val = Value::from(this);
|
||||||
let this = this_val.coerce_to_string(avm, context)?;
|
let this = this_val.coerce_to_string(activation, context)?;
|
||||||
let this_len = this.encode_utf16().count();
|
let this_len = this.encode_utf16().count();
|
||||||
let mut start_index = string_index(args.get(0).unwrap().coerce_to_i32(avm, context)?, this_len);
|
let mut start_index = string_index(
|
||||||
|
args.get(0).unwrap().coerce_to_i32(activation, context)?,
|
||||||
|
this_len,
|
||||||
|
);
|
||||||
|
|
||||||
let mut end_index = match args.get(1) {
|
let mut end_index = match args.get(1) {
|
||||||
None | Some(Value::Undefined) => this_len,
|
None | Some(Value::Undefined) => this_len,
|
||||||
Some(n) => string_index(n.coerce_to_i32(avm, context)?, this_len),
|
Some(n) => string_index(n.coerce_to_i32(activation, context)?, this_len),
|
||||||
};
|
};
|
||||||
|
|
||||||
// substring automatically swaps the start/end if they are flipped.
|
// substring automatically swaps the start/end if they are flipped.
|
||||||
|
@ -478,13 +483,13 @@ fn substring<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_lower_case<'gc>(
|
fn to_lower_case<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this_val = Value::from(this);
|
let this_val = Value::from(this);
|
||||||
let this = this_val.coerce_to_string(avm, context)?;
|
let this = this_val.coerce_to_string(activation, context)?;
|
||||||
Ok(this
|
Ok(this
|
||||||
.chars()
|
.chars()
|
||||||
.map(string_utils::swf_char_to_lowercase)
|
.map(string_utils::swf_char_to_lowercase)
|
||||||
|
@ -494,11 +499,11 @@ fn to_lower_case<'gc>(
|
||||||
|
|
||||||
/// `String.toString` / `String.valueOf` impl
|
/// `String.toString` / `String.valueOf` impl
|
||||||
pub fn to_string_value_of<'gc>(
|
pub fn to_string_value_of<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(vbox) = this.as_value_object() {
|
if let Some(vbox) = this.as_value_object() {
|
||||||
if let Value::String(s) = vbox.unbox() {
|
if let Value::String(s) = vbox.unbox() {
|
||||||
return Ok(s.into());
|
return Ok(s.into());
|
||||||
|
@ -508,17 +513,17 @@ pub fn to_string_value_of<'gc>(
|
||||||
//TODO: This normally falls back to `[object Object]` or `[type Function]`,
|
//TODO: This normally falls back to `[object Object]` or `[type Function]`,
|
||||||
//implying that `toString` and `valueOf` are inherent object properties and
|
//implying that `toString` and `valueOf` are inherent object properties and
|
||||||
//not just methods.
|
//not just methods.
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_upper_case<'gc>(
|
fn to_upper_case<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this_val = Value::from(this);
|
let this_val = Value::from(this);
|
||||||
let this = this_val.coerce_to_string(avm, context)?;
|
let this = this_val.coerce_to_string(activation, context)?;
|
||||||
Ok(this
|
Ok(this
|
||||||
.chars()
|
.chars()
|
||||||
.map(string_utils::swf_char_to_uppercase)
|
.map(string_utils::swf_char_to_uppercase)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::object::Object;
|
use crate::avm1::object::Object;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{ScriptObject, TObject, Value};
|
||||||
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
|
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use enumset::{EnumSet, EnumSetType};
|
use enumset::{EnumSet, EnumSetType};
|
||||||
|
@ -273,11 +273,11 @@ pub struct SystemProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemProperties {
|
impl SystemProperties {
|
||||||
pub fn get_version_string(&self, avm: &Avm1) -> String {
|
pub fn get_version_string(&self, activation: &mut Activation) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{} {},0,0,0",
|
"{} {},0,0,0",
|
||||||
self.manufacturer.get_platform_name(),
|
self.manufacturer.get_platform_name(),
|
||||||
avm.player_version
|
activation.avm().player_version
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,7 +305,7 @@ impl SystemProperties {
|
||||||
percent_encoding::utf8_percent_encode(s, percent_encoding::NON_ALPHANUMERIC).to_string()
|
percent_encoding::utf8_percent_encode(s, percent_encoding::NON_ALPHANUMERIC).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_server_string(&self, avm: &Avm1) -> String {
|
pub fn get_server_string(&self, activation: &mut Activation) -> String {
|
||||||
url::form_urlencoded::Serializer::new(String::new())
|
url::form_urlencoded::Serializer::new(String::new())
|
||||||
.append_pair("A", self.encode_capability(SystemCapabilities::Audio))
|
.append_pair("A", self.encode_capability(SystemCapabilities::Audio))
|
||||||
.append_pair(
|
.append_pair(
|
||||||
|
@ -347,7 +347,7 @@ impl SystemProperties {
|
||||||
"M",
|
"M",
|
||||||
&self.encode_string(
|
&self.encode_string(
|
||||||
self.manufacturer
|
self.manufacturer
|
||||||
.get_manufacturer_string(avm.player_version)
|
.get_manufacturer_string(activation.avm().player_version)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -358,7 +358,11 @@ impl SystemProperties {
|
||||||
.append_pair("COL", &self.screen_color.to_string())
|
.append_pair("COL", &self.screen_color.to_string())
|
||||||
.append_pair("AR", &self.aspect_ratio.to_string())
|
.append_pair("AR", &self.aspect_ratio.to_string())
|
||||||
.append_pair("OS", &self.encode_string(&self.os.to_string()))
|
.append_pair("OS", &self.encode_string(&self.os.to_string()))
|
||||||
.append_pair("L", self.language.get_language_code(avm.player_version))
|
.append_pair(
|
||||||
|
"L",
|
||||||
|
self.language
|
||||||
|
.get_language_code(activation.avm().player_version),
|
||||||
|
)
|
||||||
.append_pair("IME", self.encode_capability(SystemCapabilities::IME))
|
.append_pair("IME", self.encode_capability(SystemCapabilities::IME))
|
||||||
.append_pair("PT", &self.player_type.to_string())
|
.append_pair("PT", &self.player_type.to_string())
|
||||||
.append_pair(
|
.append_pair(
|
||||||
|
@ -399,102 +403,102 @@ impl Default for SystemProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_clipboard<'gc>(
|
pub fn set_clipboard<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let new_content = args
|
let new_content = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(avm, action_context)?
|
.coerce_to_string(activation, action_context)?
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
action_context.input.set_clipboard_content(new_content);
|
action_context.input.set_clipboard_content(new_content);
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_settings<'gc>(
|
pub fn show_settings<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
//TODO: should default to the last panel displayed
|
//TODO: should default to the last panel displayed
|
||||||
let last_panel_pos = 0;
|
let last_panel_pos = 0;
|
||||||
|
|
||||||
let panel_pos = args
|
let panel_pos = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Number(last_panel_pos as f64))
|
.unwrap_or(&Value::Number(last_panel_pos as f64))
|
||||||
.coerce_to_i32(avm, action_context)?;
|
.coerce_to_i32(activation, action_context)?;
|
||||||
|
|
||||||
let panel = SettingsPanel::try_from(panel_pos as u8).unwrap_or(SettingsPanel::Privacy);
|
let panel = SettingsPanel::try_from(panel_pos as u8).unwrap_or(SettingsPanel::Privacy);
|
||||||
|
|
||||||
log::warn!("System.showSettings({:?}) not not implemented", panel);
|
log::warn!("System.showSettings({:?}) not not implemented", panel);
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_use_code_page<'gc>(
|
pub fn set_use_code_page<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let value = args
|
let value = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.as_bool(avm.current_swf_version());
|
.as_bool(activation.current_swf_version());
|
||||||
|
|
||||||
action_context.system.use_codepage = value;
|
action_context.system.use_codepage = value;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_use_code_page<'gc>(
|
pub fn get_use_code_page<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(action_context.system.use_codepage.into())
|
Ok(action_context.system.use_codepage.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_exact_settings<'gc>(
|
pub fn set_exact_settings<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let value = args
|
let value = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.as_bool(avm.current_swf_version());
|
.as_bool(activation.current_swf_version());
|
||||||
|
|
||||||
action_context.system.exact_settings = value;
|
action_context.system.exact_settings = value;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_exact_settings<'gc>(
|
pub fn get_exact_settings<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(action_context.system.exact_settings.into())
|
Ok(action_context.system.exact_settings.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_status<'gc>(
|
pub fn on_status<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("System.onStatus() not implemented");
|
log::warn!("System.onStatus() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create<'gc>(
|
pub fn create<'gc>(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::globals::system::SystemCapabilities;
|
use crate::avm1::globals::system::SystemCapabilities;
|
||||||
use crate::avm1::object::Object;
|
use crate::avm1::object::Object;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{ScriptObject, TObject, Value};
|
||||||
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
|
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
@ -11,11 +11,11 @@ use gc_arena::MutationContext;
|
||||||
macro_rules! capabilities_func {
|
macro_rules! capabilities_func {
|
||||||
($func_name: ident, $capability: expr) => {
|
($func_name: ident, $capability: expr) => {
|
||||||
pub fn $func_name<'gc>(
|
pub fn $func_name<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.has_capability($capability).into())
|
Ok(context.system.has_capability($capability).into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -24,11 +24,11 @@ macro_rules! capabilities_func {
|
||||||
macro_rules! inverse_capabilities_func {
|
macro_rules! inverse_capabilities_func {
|
||||||
($func_name: ident, $capability: expr) => {
|
($func_name: ident, $capability: expr) => {
|
||||||
pub fn $func_name<'gc>(
|
pub fn $func_name<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok((!context.system.has_capability($capability)).into())
|
Ok((!context.system.has_capability($capability)).into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -77,127 +77,127 @@ inverse_capabilities_func!(get_is_av_hardware_disabled, SystemCapabilities::AvHa
|
||||||
inverse_capabilities_func!(get_is_windowless_disabled, SystemCapabilities::WindowLess);
|
inverse_capabilities_func!(get_is_windowless_disabled, SystemCapabilities::WindowLess);
|
||||||
|
|
||||||
pub fn get_player_type<'gc>(
|
pub fn get_player_type<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.player_type.to_string().into())
|
Ok(context.system.player_type.to_string().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_screen_color<'gc>(
|
pub fn get_screen_color<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.screen_color.to_string().into())
|
Ok(context.system.screen_color.to_string().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_language<'gc>(
|
pub fn get_language<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context
|
Ok(context
|
||||||
.system
|
.system
|
||||||
.language
|
.language
|
||||||
.get_language_code(avm.player_version)
|
.get_language_code(activation.avm().player_version)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_screen_resolution_x<'gc>(
|
pub fn get_screen_resolution_x<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.screen_resolution.0.into())
|
Ok(context.system.screen_resolution.0.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_screen_resolution_y<'gc>(
|
pub fn get_screen_resolution_y<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.screen_resolution.1.into())
|
Ok(context.system.screen_resolution.1.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pixel_aspect_ratio<'gc>(
|
pub fn get_pixel_aspect_ratio<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.aspect_ratio.into())
|
Ok(context.system.aspect_ratio.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_screen_dpi<'gc>(
|
pub fn get_screen_dpi<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.dpi.into())
|
Ok(context.system.dpi.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_manufacturer<'gc>(
|
pub fn get_manufacturer<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context
|
Ok(context
|
||||||
.system
|
.system
|
||||||
.manufacturer
|
.manufacturer
|
||||||
.get_manufacturer_string(avm.player_version)
|
.get_manufacturer_string(activation.avm().player_version)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_os_name<'gc>(
|
pub fn get_os_name<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.os.to_string().into())
|
Ok(context.system.os.to_string().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_version<'gc>(
|
pub fn get_version<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.get_version_string(avm).into())
|
Ok(context.system.get_version_string(activation).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_server_string<'gc>(
|
pub fn get_server_string<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.get_server_string(avm).into())
|
Ok(context.system.get_server_string(activation).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cpu_architecture<'gc>(
|
pub fn get_cpu_architecture<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.cpu_architecture.to_string().into())
|
Ok(context.system.cpu_architecture.to_string().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_idc_level<'gc>(
|
pub fn get_max_idc_level<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.idc_level.clone().into())
|
Ok(context.system.idc_level.clone().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,74 +1,74 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::listeners::Listeners;
|
use crate::avm1::listeners::Listeners;
|
||||||
use crate::avm1::object::Object;
|
use crate::avm1::object::Object;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::property::Attribute::{DontDelete, DontEnum, ReadOnly};
|
use crate::avm1::property::Attribute::{DontDelete, DontEnum, ReadOnly};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{ScriptObject, TObject, Value};
|
||||||
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
|
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
|
|
||||||
fn on_ime_composition<'gc>(
|
fn on_ime_composition<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_conversion<'gc>(
|
fn do_conversion<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(true.into())
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_conversion_mode<'gc>(
|
fn get_conversion_mode<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok("KOREAN".into())
|
Ok("KOREAN".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_enabled<'gc>(
|
fn get_enabled<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_composition_string<'gc>(
|
fn set_composition_string<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_conversion_mode<'gc>(
|
fn set_conversion_mode<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_enabled<'gc>(
|
fn set_enabled<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,80 +1,80 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::object::Object;
|
use crate::avm1::object::Object;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{ScriptObject, TObject, Value};
|
||||||
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
|
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
|
|
||||||
fn allow_domain<'gc>(
|
fn allow_domain<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("System.security.allowDomain() not implemented");
|
log::warn!("System.security.allowDomain() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allow_insecure_domain<'gc>(
|
fn allow_insecure_domain<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("System.security.allowInsecureDomain() not implemented");
|
log::warn!("System.security.allowInsecureDomain() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_policy_file<'gc>(
|
fn load_policy_file<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("System.security.allowInsecureDomain() not implemented");
|
log::warn!("System.security.allowInsecureDomain() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn escape_domain<'gc>(
|
fn escape_domain<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("System.security.escapeDomain() not implemented");
|
log::warn!("System.security.escapeDomain() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sandbox_type<'gc>(
|
fn get_sandbox_type<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(context.system.sandbox_type.to_string().into())
|
Ok(context.system.sandbox_type.to_string().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_choose_local_swf_path<'gc>(
|
fn get_choose_local_swf_path<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("System.security.chooseLocalSwfPath() not implemented");
|
log::warn!("System.security.chooseLocalSwfPath() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn policy_file_resolver<'gc>(
|
fn policy_file_resolver<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
log::warn!("System.security.chooseLocalSwfPath() not implemented");
|
log::warn!("System.security.chooseLocalSwfPath() not implemented");
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create<'gc>(
|
pub fn create<'gc>(
|
||||||
|
|
|
@ -1,94 +1,95 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::globals::display_object;
|
use crate::avm1::globals::display_object;
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject};
|
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject};
|
||||||
use crate::html::TextFormat;
|
use crate::html::TextFormat;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
/// Implements `TextField`
|
/// Implements `TextField`
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_text<'gc>(
|
pub fn get_text<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
return Ok(text_field.text().into());
|
return Ok(text_field.text().into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text<'gc>(
|
pub fn set_text<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
if let Some(value) = args.get(0) {
|
if let Some(value) = args.get(0) {
|
||||||
if let Err(err) =
|
if let Err(err) = text_field.set_text(
|
||||||
text_field.set_text(value.coerce_to_string(avm, context)?.to_string(), context)
|
value.coerce_to_string(activation, context)?.to_string(),
|
||||||
{
|
context,
|
||||||
|
) {
|
||||||
log::error!("Error when setting TextField.text: {}", err);
|
log::error!("Error when setting TextField.text: {}", err);
|
||||||
}
|
}
|
||||||
text_field.propagate_text_binding(avm, context);
|
text_field.propagate_text_binding(activation, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_html<'gc>(
|
pub fn get_html<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
return Ok(text_field.is_html().into());
|
return Ok(text_field.is_html().into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_html<'gc>(
|
pub fn set_html<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
if let Some(value) = args.get(0) {
|
if let Some(value) = args.get(0) {
|
||||||
text_field.set_is_html(context, value.as_bool(avm.current_swf_version()));
|
text_field.set_is_html(context, value.as_bool(activation.current_swf_version()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_html_text<'gc>(
|
pub fn get_html_text<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
if let Ok(text) = text_field.html_text(context) {
|
if let Ok(text) = text_field.html_text(context) {
|
||||||
|
@ -96,104 +97,104 @@ pub fn get_html_text<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_html_text<'gc>(
|
pub fn set_html_text<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
let text = args
|
let text = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(avm, context)?;
|
.coerce_to_string(activation, context)?;
|
||||||
let _ = text_field.set_html_text(text.into_owned(), context);
|
let _ = text_field.set_html_text(text.into_owned(), context);
|
||||||
// Changing the htmlText does NOT update variable bindings (does not call EditText::propagate_text_binding).
|
// Changing the htmlText does NOT update variable bindings (does not call EditText::propagate_text_binding).
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_border<'gc>(
|
pub fn get_border<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
return Ok(text_field.has_border().into());
|
return Ok(text_field.has_border().into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_border<'gc>(
|
pub fn set_border<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
if let Some(value) = args.get(0) {
|
if let Some(value) = args.get(0) {
|
||||||
let has_border = value.as_bool(avm.current_swf_version());
|
let has_border = value.as_bool(activation.current_swf_version());
|
||||||
text_field.set_has_border(context.gc_context, has_border);
|
text_field.set_has_border(context.gc_context, has_border);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_embed_fonts<'gc>(
|
pub fn get_embed_fonts<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
return Ok((!text_field.is_device_font()).into());
|
return Ok((!text_field.is_device_font()).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_embed_fonts<'gc>(
|
pub fn set_embed_fonts<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
if let Some(value) = args.get(0) {
|
if let Some(value) = args.get(0) {
|
||||||
let embed_fonts = value.as_bool(avm.current_swf_version());
|
let embed_fonts = value.as_bool(activation.current_swf_version());
|
||||||
text_field.set_is_device_font(context, !embed_fonts);
|
text_field.set_is_device_font(context, !embed_fonts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_length<'gc>(
|
pub fn get_length<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
return Ok((text_field.text_length() as f64).into());
|
return Ok((text_field.text_length() as f64).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! with_text_field {
|
macro_rules! with_text_field {
|
||||||
|
@ -201,13 +202,13 @@ macro_rules! with_text_field {
|
||||||
$(
|
$(
|
||||||
$object.force_set_function(
|
$object.force_set_function(
|
||||||
$name,
|
$name,
|
||||||
|avm, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|activation, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
return $fn(text_field, avm, context, args);
|
return $fn(text_field, activation, context, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
} as crate::avm1::function::NativeFunction<'gc>,
|
} as crate::avm1::function::NativeFunction<'gc>,
|
||||||
$gc_context,
|
$gc_context,
|
||||||
DontDelete | ReadOnly | DontEnum,
|
DontDelete | ReadOnly | DontEnum,
|
||||||
|
@ -218,11 +219,11 @@ macro_rules! with_text_field {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_width<'gc>(
|
pub fn text_width<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|dobj| dobj.as_edit_text())
|
.and_then(|dobj| dobj.as_edit_text())
|
||||||
|
@ -232,15 +233,15 @@ pub fn text_width<'gc>(
|
||||||
return Ok(metrics.0.to_pixels().into());
|
return Ok(metrics.0.to_pixels().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_height<'gc>(
|
pub fn text_height<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|dobj| dobj.as_edit_text())
|
.and_then(|dobj| dobj.as_edit_text())
|
||||||
|
@ -250,15 +251,15 @@ pub fn text_height<'gc>(
|
||||||
return Ok(metrics.1.to_pixels().into());
|
return Ok(metrics.1.to_pixels().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiline<'gc>(
|
pub fn multiline<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|dobj| dobj.as_edit_text())
|
.and_then(|dobj| dobj.as_edit_text())
|
||||||
|
@ -266,20 +267,20 @@ pub fn multiline<'gc>(
|
||||||
return Ok(etext.is_multiline().into());
|
return Ok(etext.is_multiline().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_multiline<'gc>(
|
pub fn set_multiline<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let is_multiline = args
|
let is_multiline = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(Value::Undefined)
|
.unwrap_or(Value::Undefined)
|
||||||
.as_bool(avm.current_swf_version());
|
.as_bool(activation.current_swf_version());
|
||||||
|
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
|
@ -288,15 +289,15 @@ pub fn set_multiline<'gc>(
|
||||||
etext.set_multiline(is_multiline, context);
|
etext.set_multiline(is_multiline, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variable<'gc>(
|
fn variable<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|dobj| dobj.as_edit_text())
|
.and_then(|dobj| dobj.as_edit_text())
|
||||||
|
@ -307,36 +308,36 @@ fn variable<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unset `variable` retuns null, not undefined
|
// Unset `variable` retuns null, not undefined
|
||||||
Ok(Value::Null.into())
|
Ok(Value::Null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_variable<'gc>(
|
fn set_variable<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let variable = match args.get(0) {
|
let variable = match args.get(0) {
|
||||||
None | Some(Value::Undefined) | Some(Value::Null) => None,
|
None | Some(Value::Undefined) | Some(Value::Null) => None,
|
||||||
Some(v) => Some(v.coerce_to_string(avm, context)?),
|
Some(v) => Some(v.coerce_to_string(activation, context)?),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|dobj| dobj.as_edit_text())
|
.and_then(|dobj| dobj.as_edit_text())
|
||||||
{
|
{
|
||||||
etext.set_variable(variable.map(|v| v.into_owned()), avm, context);
|
etext.set_variable(variable.map(|v| v.into_owned()), activation, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn word_wrap<'gc>(
|
pub fn word_wrap<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|dobj| dobj.as_edit_text())
|
.and_then(|dobj| dobj.as_edit_text())
|
||||||
|
@ -344,20 +345,20 @@ pub fn word_wrap<'gc>(
|
||||||
return Ok(etext.is_word_wrap().into());
|
return Ok(etext.is_word_wrap().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_word_wrap<'gc>(
|
pub fn set_word_wrap<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let is_word_wrap = args
|
let is_word_wrap = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(Value::Undefined)
|
.unwrap_or(Value::Undefined)
|
||||||
.as_bool(avm.current_swf_version());
|
.as_bool(activation.current_swf_version());
|
||||||
|
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
|
@ -366,15 +367,15 @@ pub fn set_word_wrap<'gc>(
|
||||||
etext.set_word_wrap(is_word_wrap, context);
|
etext.set_word_wrap(is_word_wrap, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auto_size<'gc>(
|
pub fn auto_size<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|dobj| dobj.as_edit_text())
|
.and_then(|dobj| dobj.as_edit_text())
|
||||||
|
@ -387,15 +388,15 @@ pub fn auto_size<'gc>(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_auto_size<'gc>(
|
pub fn set_auto_size<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(etext) = this
|
if let Some(etext) = this
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|dobj| dobj.as_edit_text())
|
.and_then(|dobj| dobj.as_edit_text())
|
||||||
|
@ -412,7 +413,7 @@ pub fn set_auto_size<'gc>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_proto<'gc>(
|
pub fn create_proto<'gc>(
|
||||||
|
@ -527,44 +528,44 @@ pub fn attach_virtual_properties<'gc>(gc_context: MutationContext<'gc, '_>, obje
|
||||||
|
|
||||||
fn get_new_text_format<'gc>(
|
fn get_new_text_format<'gc>(
|
||||||
text_field: EditText<'gc>,
|
text_field: EditText<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let tf = text_field.new_text_format();
|
let tf = text_field.new_text_format();
|
||||||
|
|
||||||
Ok(tf.as_avm1_object(avm, context)?.into())
|
Ok(tf.as_avm1_object(activation, context)?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_new_text_format<'gc>(
|
fn set_new_text_format<'gc>(
|
||||||
text_field: EditText<'gc>,
|
text_field: EditText<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let tf = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let tf = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
|
|
||||||
if let Value::Object(tf) = tf {
|
if let Value::Object(tf) = tf {
|
||||||
let tf_parsed = TextFormat::from_avm1_object(tf, avm, context)?;
|
let tf_parsed = TextFormat::from_avm1_object(tf, activation, context)?;
|
||||||
text_field.set_new_text_format(tf_parsed, context);
|
text_field.set_new_text_format(tf_parsed, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_text_format<'gc>(
|
fn get_text_format<'gc>(
|
||||||
text_field: EditText<'gc>,
|
text_field: EditText<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let (from, to) = match (args.get(0), args.get(1)) {
|
let (from, to) = match (args.get(0), args.get(1)) {
|
||||||
(Some(f), Some(t)) => (
|
(Some(f), Some(t)) => (
|
||||||
f.coerce_to_f64(avm, context)? as usize,
|
f.coerce_to_f64(activation, context)? as usize,
|
||||||
t.coerce_to_f64(avm, context)? as usize,
|
t.coerce_to_f64(activation, context)? as usize,
|
||||||
),
|
),
|
||||||
(Some(f), None) => {
|
(Some(f), None) => {
|
||||||
let v = f.coerce_to_f64(avm, context)? as usize;
|
let v = f.coerce_to_f64(activation, context)? as usize;
|
||||||
(v, v.saturating_add(1))
|
(v, v.saturating_add(1))
|
||||||
}
|
}
|
||||||
_ => (0, text_field.text_length()),
|
_ => (0, text_field.text_length()),
|
||||||
|
@ -572,28 +573,28 @@ fn get_text_format<'gc>(
|
||||||
|
|
||||||
Ok(text_field
|
Ok(text_field
|
||||||
.text_format(from, to)
|
.text_format(from, to)
|
||||||
.as_avm1_object(avm, context)?
|
.as_avm1_object(activation, context)?
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_text_format<'gc>(
|
fn set_text_format<'gc>(
|
||||||
text_field: EditText<'gc>,
|
text_field: EditText<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let tf = args.last().cloned().unwrap_or(Value::Undefined);
|
let tf = args.last().cloned().unwrap_or(Value::Undefined);
|
||||||
|
|
||||||
if let Value::Object(tf) = tf {
|
if let Value::Object(tf) = tf {
|
||||||
let tf_parsed = TextFormat::from_avm1_object(tf, avm, context)?;
|
let tf_parsed = TextFormat::from_avm1_object(tf, activation, context)?;
|
||||||
|
|
||||||
let (from, to) = match (args.get(0), args.get(1)) {
|
let (from, to) = match (args.get(0), args.get(1)) {
|
||||||
(Some(f), Some(t)) if args.len() > 2 => (
|
(Some(f), Some(t)) if args.len() > 2 => (
|
||||||
f.coerce_to_f64(avm, context)? as usize,
|
f.coerce_to_f64(activation, context)? as usize,
|
||||||
t.coerce_to_f64(avm, context)? as usize,
|
t.coerce_to_f64(activation, context)? as usize,
|
||||||
),
|
),
|
||||||
(Some(f), _) if args.len() > 1 => {
|
(Some(f), _) if args.len() > 1 => {
|
||||||
let v = f.coerce_to_f64(avm, context)? as usize;
|
let v = f.coerce_to_f64(activation, context)? as usize;
|
||||||
(v, v.saturating_add(1))
|
(v, v.saturating_add(1))
|
||||||
}
|
}
|
||||||
_ => (0, text_field.text_length()),
|
_ => (0, text_field.text_length()),
|
||||||
|
@ -602,33 +603,33 @@ fn set_text_format<'gc>(
|
||||||
text_field.set_text_format(from, to, tf_parsed, context);
|
text_field.set_text_format(from, to, tf_parsed, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_text<'gc>(
|
fn replace_text<'gc>(
|
||||||
text_field: EditText<'gc>,
|
text_field: EditText<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let from = args
|
let from = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(Value::Undefined)
|
.unwrap_or(Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let to = args
|
let to = args
|
||||||
.get(1)
|
.get(1)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(Value::Undefined)
|
.unwrap_or(Value::Undefined)
|
||||||
.coerce_to_f64(avm, context)?;
|
.coerce_to_f64(activation, context)?;
|
||||||
let text = args
|
let text = args
|
||||||
.get(2)
|
.get(2)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(Value::Undefined)
|
.unwrap_or(Value::Undefined)
|
||||||
.coerce_to_string(avm, context)?
|
.coerce_to_string(activation, context)?
|
||||||
.into_owned();
|
.into_owned();
|
||||||
|
|
||||||
text_field.replace_text(from as usize, to as usize, &text, context);
|
text_field.replace_text(from as usize, to as usize, &text, context);
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
//! `TextFormat` impl
|
//! `TextFormat` impl
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
fn map_defined_to_string<'gc>(
|
fn map_defined_to_string<'gc>(
|
||||||
name: &str,
|
name: &str,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
val: Option<Value<'gc>>,
|
val: Option<Value<'gc>>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
|
@ -16,10 +16,10 @@ fn map_defined_to_string<'gc>(
|
||||||
Some(Value::Undefined) => Value::Null,
|
Some(Value::Undefined) => Value::Null,
|
||||||
Some(Value::Null) => Value::Null,
|
Some(Value::Null) => Value::Null,
|
||||||
None => Value::Null,
|
None => Value::Null,
|
||||||
Some(v) => v.coerce_to_string(avm, ac)?.into(),
|
Some(v) => v.coerce_to_string(activation, ac)?.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.set(name, val, avm, ac)?;
|
this.set(name, val, activation, ac)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ fn map_defined_to_string<'gc>(
|
||||||
fn map_defined_to_number<'gc>(
|
fn map_defined_to_number<'gc>(
|
||||||
name: &str,
|
name: &str,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
val: Option<Value<'gc>>,
|
val: Option<Value<'gc>>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
|
@ -35,10 +35,10 @@ fn map_defined_to_number<'gc>(
|
||||||
Some(Value::Undefined) => Value::Null,
|
Some(Value::Undefined) => Value::Null,
|
||||||
Some(Value::Null) => Value::Null,
|
Some(Value::Null) => Value::Null,
|
||||||
None => Value::Null,
|
None => Value::Null,
|
||||||
Some(v) => v.coerce_to_f64(avm, ac)?.into(),
|
Some(v) => v.coerce_to_f64(activation, ac)?.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.set(name, val, avm, ac)?;
|
this.set(name, val, activation, ac)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ fn map_defined_to_number<'gc>(
|
||||||
fn map_defined_to_bool<'gc>(
|
fn map_defined_to_bool<'gc>(
|
||||||
name: &str,
|
name: &str,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
val: Option<Value<'gc>>,
|
val: Option<Value<'gc>>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
|
@ -54,36 +54,36 @@ fn map_defined_to_bool<'gc>(
|
||||||
Some(Value::Undefined) => Value::Null,
|
Some(Value::Undefined) => Value::Null,
|
||||||
Some(Value::Null) => Value::Null,
|
Some(Value::Null) => Value::Null,
|
||||||
None => Value::Null,
|
None => Value::Null,
|
||||||
Some(v) => v.as_bool(avm.current_swf_version()).into(),
|
Some(v) => v.as_bool(activation.current_swf_version()).into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.set(name, val, avm, ac)?;
|
this.set(name, val, activation, ac)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `TextFormat` constructor
|
/// `TextFormat` constructor
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
map_defined_to_string("font", this, avm, ac, args.get(0).cloned())?;
|
map_defined_to_string("font", this, activation, ac, args.get(0).cloned())?;
|
||||||
map_defined_to_number("size", this, avm, ac, args.get(1).cloned())?;
|
map_defined_to_number("size", this, activation, ac, args.get(1).cloned())?;
|
||||||
map_defined_to_number("color", this, avm, ac, args.get(2).cloned())?;
|
map_defined_to_number("color", this, activation, ac, args.get(2).cloned())?;
|
||||||
map_defined_to_bool("bold", this, avm, ac, args.get(3).cloned())?;
|
map_defined_to_bool("bold", this, activation, ac, args.get(3).cloned())?;
|
||||||
map_defined_to_bool("italic", this, avm, ac, args.get(4).cloned())?;
|
map_defined_to_bool("italic", this, activation, ac, args.get(4).cloned())?;
|
||||||
map_defined_to_bool("underline", this, avm, ac, args.get(5).cloned())?;
|
map_defined_to_bool("underline", this, activation, ac, args.get(5).cloned())?;
|
||||||
map_defined_to_string("url", this, avm, ac, args.get(6).cloned())?;
|
map_defined_to_string("url", this, activation, ac, args.get(6).cloned())?;
|
||||||
map_defined_to_string("target", this, avm, ac, args.get(7).cloned())?;
|
map_defined_to_string("target", this, activation, ac, args.get(7).cloned())?;
|
||||||
map_defined_to_string("align", this, avm, ac, args.get(8).cloned())?;
|
map_defined_to_string("align", this, activation, ac, args.get(8).cloned())?;
|
||||||
map_defined_to_number("leftMargin", this, avm, ac, args.get(9).cloned())?;
|
map_defined_to_number("leftMargin", this, activation, ac, args.get(9).cloned())?;
|
||||||
map_defined_to_number("rightMargin", this, avm, ac, args.get(10).cloned())?;
|
map_defined_to_number("rightMargin", this, activation, ac, args.get(10).cloned())?;
|
||||||
map_defined_to_number("indent", this, avm, ac, args.get(11).cloned())?;
|
map_defined_to_number("indent", this, activation, ac, args.get(11).cloned())?;
|
||||||
map_defined_to_number("leading", this, avm, ac, args.get(12).cloned())?;
|
map_defined_to_number("leading", this, activation, ac, args.get(12).cloned())?;
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `TextFormat.prototype` constructor
|
/// `TextFormat.prototype` constructor
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
//! XML/XMLNode global classes
|
//! XML/XMLNode global classes
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::script_object::ScriptObject;
|
use crate::avm1::script_object::ScriptObject;
|
||||||
use crate::avm1::xml_object::XMLObject;
|
use crate::avm1::xml_object::XMLObject;
|
||||||
use crate::avm1::{Avm1, Object, TObject, UpdateContext, Value};
|
use crate::avm1::{Object, TObject, UpdateContext, Value};
|
||||||
use crate::backend::navigator::RequestOptions;
|
use crate::backend::navigator::RequestOptions;
|
||||||
use crate::xml;
|
use crate::xml;
|
||||||
use crate::xml::{XMLDocument, XMLNode};
|
use crate::xml::{XMLDocument, XMLNode};
|
||||||
|
@ -42,17 +42,17 @@ fn is_as2_compatible(node: XMLNode<'_>) -> bool {
|
||||||
|
|
||||||
/// XMLNode constructor
|
/// XMLNode constructor
|
||||||
pub fn xmlnode_constructor<'gc>(
|
pub fn xmlnode_constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let blank_document = XMLDocument::new(ac.gc_context);
|
let blank_document = XMLDocument::new(ac.gc_context);
|
||||||
|
|
||||||
match (
|
match (
|
||||||
args.get(0)
|
args.get(0)
|
||||||
.map(|v| v.coerce_to_f64(avm, ac).map(|v| v as u32)),
|
.map(|v| v.coerce_to_f64(activation, ac).map(|v| v as u32)),
|
||||||
args.get(1).map(|v| v.coerce_to_string(avm, ac)),
|
args.get(1).map(|v| v.coerce_to_string(activation, ac)),
|
||||||
this.as_xml_node(),
|
this.as_xml_node(),
|
||||||
) {
|
) {
|
||||||
(Some(Ok(1)), Some(Ok(ref strval)), Some(ref mut this_node)) => {
|
(Some(Ok(1)), Some(Ok(ref strval)), Some(ref mut this_node)) => {
|
||||||
|
@ -69,19 +69,19 @@ pub fn xmlnode_constructor<'gc>(
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_append_child<'gc>(
|
pub fn xmlnode_append_child<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let (Some(mut xmlnode), Some(child_xmlnode)) = (
|
if let (Some(mut xmlnode), Some(child_xmlnode)) = (
|
||||||
this.as_xml_node(),
|
this.as_xml_node(),
|
||||||
args.get(0)
|
args.get(0)
|
||||||
.and_then(|n| n.coerce_to_object(avm, ac).as_xml_node()),
|
.and_then(|n| n.coerce_to_object(activation, ac).as_xml_node()),
|
||||||
) {
|
) {
|
||||||
if let Ok(None) = child_xmlnode.parent() {
|
if let Ok(None) = child_xmlnode.parent() {
|
||||||
let position = xmlnode.children_len();
|
let position = xmlnode.children_len();
|
||||||
|
@ -91,21 +91,21 @@ pub fn xmlnode_append_child<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_insert_before<'gc>(
|
pub fn xmlnode_insert_before<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let (Some(mut xmlnode), Some(child_xmlnode), Some(insertpoint_xmlnode)) = (
|
if let (Some(mut xmlnode), Some(child_xmlnode), Some(insertpoint_xmlnode)) = (
|
||||||
this.as_xml_node(),
|
this.as_xml_node(),
|
||||||
args.get(0)
|
args.get(0)
|
||||||
.and_then(|n| n.coerce_to_object(avm, ac).as_xml_node()),
|
.and_then(|n| n.coerce_to_object(activation, ac).as_xml_node()),
|
||||||
args.get(1)
|
args.get(1)
|
||||||
.and_then(|n| n.coerce_to_object(avm, ac).as_xml_node()),
|
.and_then(|n| n.coerce_to_object(activation, ac).as_xml_node()),
|
||||||
) {
|
) {
|
||||||
if let Ok(None) = child_xmlnode.parent() {
|
if let Ok(None) = child_xmlnode.parent() {
|
||||||
if let Some(position) = xmlnode.child_position(insertpoint_xmlnode) {
|
if let Some(position) = xmlnode.child_position(insertpoint_xmlnode) {
|
||||||
|
@ -119,91 +119,91 @@ pub fn xmlnode_insert_before<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_clone_node<'gc>(
|
pub fn xmlnode_clone_node<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let (Some(xmlnode), deep) = (
|
if let (Some(xmlnode), deep) = (
|
||||||
this.as_xml_node(),
|
this.as_xml_node(),
|
||||||
args.get(0)
|
args.get(0)
|
||||||
.map(|v| v.as_bool(avm.current_swf_version()))
|
.map(|v| v.as_bool(activation.current_swf_version()))
|
||||||
.unwrap_or(false),
|
.unwrap_or(false),
|
||||||
) {
|
) {
|
||||||
let mut clone_node = xmlnode.duplicate(ac.gc_context, deep);
|
let mut clone_node = xmlnode.duplicate(ac.gc_context, deep);
|
||||||
|
|
||||||
return Ok(Value::Object(
|
return Ok(Value::Object(clone_node.script_object(
|
||||||
clone_node.script_object(ac.gc_context, Some(avm.prototypes.xml_node)),
|
ac.gc_context,
|
||||||
)
|
Some(activation.avm().prototypes.xml_node),
|
||||||
.into());
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_get_namespace_for_prefix<'gc>(
|
pub fn xmlnode_get_namespace_for_prefix<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let (Some(xmlnode), Some(prefix_string)) = (
|
if let (Some(xmlnode), Some(prefix_string)) = (
|
||||||
this.as_xml_node(),
|
this.as_xml_node(),
|
||||||
args.get(0).map(|v| v.coerce_to_string(avm, ac)),
|
args.get(0).map(|v| v.coerce_to_string(activation, ac)),
|
||||||
) {
|
) {
|
||||||
if let Some(uri) = xmlnode.lookup_uri_for_namespace(&prefix_string?) {
|
if let Some(uri) = xmlnode.lookup_uri_for_namespace(&prefix_string?) {
|
||||||
Ok(uri.into())
|
Ok(uri.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Null.into())
|
Ok(Value::Null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_get_prefix_for_namespace<'gc>(
|
pub fn xmlnode_get_prefix_for_namespace<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let (Some(xmlnode), Some(uri_string)) = (
|
if let (Some(xmlnode), Some(uri_string)) = (
|
||||||
this.as_xml_node(),
|
this.as_xml_node(),
|
||||||
args.get(0).map(|v| v.coerce_to_string(avm, ac)),
|
args.get(0).map(|v| v.coerce_to_string(activation, ac)),
|
||||||
) {
|
) {
|
||||||
if let Some(prefix) = xmlnode.lookup_namespace_for_uri(&uri_string?) {
|
if let Some(prefix) = xmlnode.lookup_namespace_for_uri(&uri_string?) {
|
||||||
Ok(prefix.into())
|
Ok(prefix.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Null.into())
|
Ok(Value::Null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_has_child_nodes<'gc>(
|
pub fn xmlnode_has_child_nodes<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(xmlnode) = this.as_xml_node() {
|
if let Some(xmlnode) = this.as_xml_node() {
|
||||||
Ok((xmlnode.children_len() > 0).into())
|
Ok((xmlnode.children_len() > 0).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_remove_node<'gc>(
|
pub fn xmlnode_remove_node<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
if let Ok(Some(mut parent)) = node.parent() {
|
if let Ok(Some(mut parent)) = node.parent() {
|
||||||
if let Err(e) = parent.remove_child(ac.gc_context, node) {
|
if let Err(e) = parent.remove_child(ac.gc_context, node) {
|
||||||
|
@ -212,15 +212,15 @@ pub fn xmlnode_remove_node<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_to_string<'gc>(
|
pub fn xmlnode_to_string<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
let result = node.into_string(&mut is_as2_compatible);
|
let result = node.into_string(&mut is_as2_compatible);
|
||||||
|
|
||||||
|
@ -236,37 +236,37 @@ pub fn xmlnode_to_string<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_local_name<'gc>(
|
pub fn xmlnode_local_name<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(this
|
Ok(this
|
||||||
.as_xml_node()
|
.as_xml_node()
|
||||||
.and_then(|n| n.tag_name())
|
.and_then(|n| n.tag_name())
|
||||||
.map(|n| n.local_name().to_string().into())
|
.map(|n| n.local_name().to_string().into())
|
||||||
.unwrap_or_else(|| Value::Null.into()))
|
.unwrap_or_else(|| Value::Null))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_node_name<'gc>(
|
pub fn xmlnode_node_name<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(this
|
Ok(this
|
||||||
.as_xml_node()
|
.as_xml_node()
|
||||||
.and_then(|n| n.tag_name())
|
.and_then(|n| n.tag_name())
|
||||||
.map(|n| n.node_name().into())
|
.map(|n| n.node_name().into())
|
||||||
.unwrap_or_else(|| Value::Null.into()))
|
.unwrap_or_else(|| Value::Null))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_node_type<'gc>(
|
pub fn xmlnode_node_type<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(this
|
Ok(this
|
||||||
.as_xml_node()
|
.as_xml_node()
|
||||||
.map(|n| {
|
.map(|n| {
|
||||||
|
@ -278,28 +278,28 @@ pub fn xmlnode_node_type<'gc>(
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| Value::Undefined.into()))
|
.unwrap_or_else(|| Value::Undefined))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_node_value<'gc>(
|
pub fn xmlnode_node_value<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(this
|
Ok(this
|
||||||
.as_xml_node()
|
.as_xml_node()
|
||||||
.and_then(|n| n.node_value())
|
.and_then(|n| n.node_value())
|
||||||
.map(|n| n.into())
|
.map(|n| n.into())
|
||||||
.unwrap_or_else(|| Value::Null.into()))
|
.unwrap_or_else(|| Value::Null))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_prefix<'gc>(
|
pub fn xmlnode_prefix<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(this
|
Ok(this
|
||||||
.as_xml_node()
|
.as_xml_node()
|
||||||
.and_then(|n| n.tag_name())
|
.and_then(|n| n.tag_name())
|
||||||
|
@ -308,17 +308,17 @@ pub fn xmlnode_prefix<'gc>(
|
||||||
.map(|n| n.to_string().into())
|
.map(|n| n.to_string().into())
|
||||||
.unwrap_or_else(|| "".to_string().into())
|
.unwrap_or_else(|| "".to_string().into())
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| Value::Null.into()))
|
.unwrap_or_else(|| Value::Null))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_child_nodes<'gc>(
|
pub fn xmlnode_child_nodes<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
let array = ScriptObject::array(ac.gc_context, Some(avm.prototypes.array));
|
let array = ScriptObject::array(ac.gc_context, Some(activation.avm().prototypes.array));
|
||||||
if let Some(children) = node.children() {
|
if let Some(children) = node.children() {
|
||||||
let mut compatible_nodes = 0;
|
let mut compatible_nodes = 0;
|
||||||
for mut child in children {
|
for mut child in children {
|
||||||
|
@ -329,7 +329,7 @@ pub fn xmlnode_child_nodes<'gc>(
|
||||||
array.set_array_element(
|
array.set_array_element(
|
||||||
compatible_nodes as usize,
|
compatible_nodes as usize,
|
||||||
child
|
child
|
||||||
.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
|
.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
|
||||||
.into(),
|
.into(),
|
||||||
ac.gc_context,
|
ac.gc_context,
|
||||||
);
|
);
|
||||||
|
@ -341,80 +341,80 @@ pub fn xmlnode_child_nodes<'gc>(
|
||||||
return Ok(array.into());
|
return Ok(array.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_first_child<'gc>(
|
pub fn xmlnode_first_child<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
if let Some(mut children) = node.children() {
|
if let Some(mut children) = node.children() {
|
||||||
return Ok(children
|
return Ok(children
|
||||||
.next()
|
.next()
|
||||||
.map(|mut child| {
|
.map(|mut child| {
|
||||||
child
|
child
|
||||||
.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
|
.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| Value::Null.into()));
|
.unwrap_or_else(|| Value::Null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_last_child<'gc>(
|
pub fn xmlnode_last_child<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
if let Some(mut children) = node.children() {
|
if let Some(mut children) = node.children() {
|
||||||
return Ok(children
|
return Ok(children
|
||||||
.next_back()
|
.next_back()
|
||||||
.map(|mut child| {
|
.map(|mut child| {
|
||||||
child
|
child
|
||||||
.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
|
.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| Value::Null.into()));
|
.unwrap_or_else(|| Value::Null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_parent_node<'gc>(
|
pub fn xmlnode_parent_node<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
return Ok(node
|
return Ok(node
|
||||||
.parent()
|
.parent()
|
||||||
.unwrap_or(None)
|
.unwrap_or(None)
|
||||||
.map(|mut parent| {
|
.map(|mut parent| {
|
||||||
parent
|
parent
|
||||||
.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
|
.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| Value::Null.into()));
|
.unwrap_or_else(|| Value::Null));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_previous_sibling<'gc>(
|
pub fn xmlnode_previous_sibling<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
let mut prev = node.prev_sibling().unwrap_or(None);
|
let mut prev = node.prev_sibling().unwrap_or(None);
|
||||||
while let Some(my_prev) = prev {
|
while let Some(my_prev) = prev {
|
||||||
|
@ -427,21 +427,21 @@ pub fn xmlnode_previous_sibling<'gc>(
|
||||||
|
|
||||||
return Ok(prev
|
return Ok(prev
|
||||||
.map(|mut prev| {
|
.map(|mut prev| {
|
||||||
prev.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
|
prev.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| Value::Null.into()));
|
.unwrap_or_else(|| Value::Null));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_next_sibling<'gc>(
|
pub fn xmlnode_next_sibling<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
let mut next = node.next_sibling().unwrap_or(None);
|
let mut next = node.next_sibling().unwrap_or(None);
|
||||||
while let Some(my_next) = next {
|
while let Some(my_next) = next {
|
||||||
|
@ -454,37 +454,37 @@ pub fn xmlnode_next_sibling<'gc>(
|
||||||
|
|
||||||
return Ok(next
|
return Ok(next
|
||||||
.map(|mut next| {
|
.map(|mut next| {
|
||||||
next.script_object(ac.gc_context, Some(avm.prototypes.xml_node))
|
next.script_object(ac.gc_context, Some(activation.avm().prototypes.xml_node))
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| Value::Null.into()));
|
.unwrap_or_else(|| Value::Null));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_attributes<'gc>(
|
pub fn xmlnode_attributes<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(mut node) = this.as_xml_node() {
|
if let Some(mut node) = this.as_xml_node() {
|
||||||
return Ok(node
|
return Ok(node
|
||||||
.attribute_script_object(ac.gc_context)
|
.attribute_script_object(ac.gc_context)
|
||||||
.map(|o| o.into())
|
.map(|o| o.into())
|
||||||
.unwrap_or_else(|| Value::Undefined.into()));
|
.unwrap_or_else(|| Value::Undefined));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xmlnode_namespace_uri<'gc>(
|
pub fn xmlnode_namespace_uri<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
if let Some(name) = node.tag_name() {
|
if let Some(name) = node.tag_name() {
|
||||||
return Ok(node
|
return Ok(node
|
||||||
|
@ -493,10 +493,10 @@ pub fn xmlnode_namespace_uri<'gc>(
|
||||||
.unwrap_or_else(|| "".into()));
|
.unwrap_or_else(|| "".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Value::Null.into());
|
return Ok(Value::Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct the prototype for `XMLNode`.
|
/// Construct the prototype for `XMLNode`.
|
||||||
|
@ -684,13 +684,13 @@ pub fn create_xmlnode_proto<'gc>(
|
||||||
|
|
||||||
/// XML (document) constructor
|
/// XML (document) constructor
|
||||||
pub fn xml_constructor<'gc>(
|
pub fn xml_constructor<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
match (
|
match (
|
||||||
args.get(0).map(|v| v.coerce_to_string(avm, ac)),
|
args.get(0).map(|v| v.coerce_to_string(activation, ac)),
|
||||||
this.as_xml_node(),
|
this.as_xml_node(),
|
||||||
) {
|
) {
|
||||||
(Some(Ok(ref string)), Some(ref mut this_node)) => {
|
(Some(Ok(ref string)), Some(ref mut this_node)) => {
|
||||||
|
@ -713,15 +713,15 @@ pub fn xml_constructor<'gc>(
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_create_element<'gc>(
|
pub fn xml_create_element<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let document = if let Some(node) = this.as_xml_node() {
|
let document = if let Some(node) = this.as_xml_node() {
|
||||||
node.document()
|
node.document()
|
||||||
} else {
|
} else {
|
||||||
|
@ -730,10 +730,14 @@ pub fn xml_create_element<'gc>(
|
||||||
|
|
||||||
let nodename = args
|
let nodename = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.map(|v| v.coerce_to_string(avm, ac).unwrap_or_default())
|
.map(|v| v.coerce_to_string(activation, ac).unwrap_or_default())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let mut xml_node = XMLNode::new_element(ac.gc_context, &nodename, document);
|
let mut xml_node = XMLNode::new_element(ac.gc_context, &nodename, document);
|
||||||
let object = XMLObject::from_xml_node(ac.gc_context, xml_node, Some(avm.prototypes().xml_node));
|
let object = XMLObject::from_xml_node(
|
||||||
|
ac.gc_context,
|
||||||
|
xml_node,
|
||||||
|
Some(activation.avm().prototypes().xml_node),
|
||||||
|
);
|
||||||
|
|
||||||
xml_node.introduce_script_object(ac.gc_context, object);
|
xml_node.introduce_script_object(ac.gc_context, object);
|
||||||
|
|
||||||
|
@ -741,11 +745,11 @@ pub fn xml_create_element<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_create_text_node<'gc>(
|
pub fn xml_create_text_node<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let document = if let Some(node) = this.as_xml_node() {
|
let document = if let Some(node) = this.as_xml_node() {
|
||||||
node.document()
|
node.document()
|
||||||
} else {
|
} else {
|
||||||
|
@ -754,10 +758,14 @@ pub fn xml_create_text_node<'gc>(
|
||||||
|
|
||||||
let text_node = args
|
let text_node = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.map(|v| v.coerce_to_string(avm, ac).unwrap_or_default())
|
.map(|v| v.coerce_to_string(activation, ac).unwrap_or_default())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let mut xml_node = XMLNode::new_text(ac.gc_context, &text_node, document);
|
let mut xml_node = XMLNode::new_text(ac.gc_context, &text_node, document);
|
||||||
let object = XMLObject::from_xml_node(ac.gc_context, xml_node, Some(avm.prototypes().xml_node));
|
let object = XMLObject::from_xml_node(
|
||||||
|
ac.gc_context,
|
||||||
|
xml_node,
|
||||||
|
Some(activation.avm().prototypes().xml_node),
|
||||||
|
);
|
||||||
|
|
||||||
xml_node.introduce_script_object(ac.gc_context, object);
|
xml_node.introduce_script_object(ac.gc_context, object);
|
||||||
|
|
||||||
|
@ -765,14 +773,14 @@ pub fn xml_create_text_node<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_parse_xml<'gc>(
|
pub fn xml_parse_xml<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(mut node) = this.as_xml_node() {
|
if let Some(mut node) = this.as_xml_node() {
|
||||||
let xmlstring =
|
let xmlstring =
|
||||||
if let Some(Ok(xmlstring)) = args.get(0).map(|s| s.coerce_to_string(avm, ac)) {
|
if let Some(Ok(xmlstring)) = args.get(0).map(|s| s.coerce_to_string(activation, ac)) {
|
||||||
xmlstring
|
xmlstring
|
||||||
} else {
|
} else {
|
||||||
Cow::Borrowed("")
|
Cow::Borrowed("")
|
||||||
|
@ -783,7 +791,7 @@ pub fn xml_parse_xml<'gc>(
|
||||||
let result = node.remove_child(ac.gc_context, child);
|
let result = node.remove_child(ac.gc_context, child);
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
log::warn!("XML.parseXML: Error removing node contents: {}", e);
|
log::warn!("XML.parseXML: Error removing node contents: {}", e);
|
||||||
return Ok(Value::Undefined.into());
|
return Ok(Value::Undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -794,15 +802,15 @@ pub fn xml_parse_xml<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_load<'gc>(
|
pub fn xml_load<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let url = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let url = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
|
|
||||||
if let Value::Null = url {
|
if let Value::Null = url {
|
||||||
|
@ -810,12 +818,12 @@ pub fn xml_load<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
let url = url.coerce_to_string(avm, ac)?;
|
let url = url.coerce_to_string(activation, ac)?;
|
||||||
|
|
||||||
this.set("loaded", false.into(), avm, ac)?;
|
this.set("loaded", false.into(), activation, ac)?;
|
||||||
|
|
||||||
let fetch = ac.navigator.fetch(&url, RequestOptions::get());
|
let fetch = ac.navigator.fetch(&url, RequestOptions::get());
|
||||||
let target_clip = avm.target_clip_or_root();
|
let target_clip = activation.target_clip_or_root();
|
||||||
let process = ac.load_manager.load_xml_into_node(
|
let process = ac.load_manager.load_xml_into_node(
|
||||||
ac.player.clone().unwrap(),
|
ac.player.clone().unwrap(),
|
||||||
node,
|
node,
|
||||||
|
@ -832,33 +840,33 @@ pub fn xml_load<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_on_data<'gc>(
|
pub fn xml_on_data<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let src = args.get(0).cloned().unwrap_or(Value::Undefined);
|
let src = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
|
|
||||||
if let Value::Undefined = src {
|
if let Value::Undefined = src {
|
||||||
this.call_method("onLoad", &[false.into()], avm, ac)?;
|
this.call_method("onLoad", &[false.into()], activation, ac)?;
|
||||||
} else {
|
} else {
|
||||||
let src = src.coerce_to_string(avm, ac)?;
|
let src = src.coerce_to_string(activation, ac)?;
|
||||||
this.call_method("parseXML", &[src.into()], avm, ac)?;
|
this.call_method("parseXML", &[src.into()], activation, ac)?;
|
||||||
|
|
||||||
this.set("loaded", true.into(), avm, ac)?;
|
this.set("loaded", true.into(), activation, ac)?;
|
||||||
|
|
||||||
this.call_method("onLoad", &[true.into()], avm, ac)?;
|
this.call_method("onLoad", &[true.into()], activation, ac)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_doc_type_decl<'gc>(
|
pub fn xml_doc_type_decl<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
if let Some(doctype) = node.document().doctype() {
|
if let Some(doctype) = node.document().doctype() {
|
||||||
let result = doctype.into_string(&mut |_| true);
|
let result = doctype.into_string(&mut |_| true);
|
||||||
|
@ -872,15 +880,15 @@ pub fn xml_doc_type_decl<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_xml_decl<'gc>(
|
pub fn xml_xml_decl<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
let result = node.document().xmldecl_string();
|
let result = node.document().xmldecl_string();
|
||||||
|
|
||||||
|
@ -891,55 +899,49 @@ pub fn xml_xml_decl<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_id_map<'gc>(
|
pub fn xml_id_map<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
return Ok(node.document().idmap_script_object(ac.gc_context).into());
|
return Ok(node.document().idmap_script_object(ac.gc_context).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xml_status<'gc>(
|
pub fn xml_status<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
_ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
return match node.document().last_parse_error() {
|
return match node.document().last_parse_error() {
|
||||||
None => Ok(XML_NO_ERROR.into()),
|
None => Ok(XML_NO_ERROR.into()),
|
||||||
Some(err) => match err.ref_error() {
|
Some(err) => match err.ref_error() {
|
||||||
ParseError::UnexpectedEof(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()),
|
ParseError::UnexpectedEof(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
|
||||||
ParseError::EndEventMismatch { .. } => Ok(Value::Number(XML_MISMATCHED_END).into()),
|
ParseError::EndEventMismatch { .. } => Ok(Value::Number(XML_MISMATCHED_END)),
|
||||||
ParseError::XmlDeclWithoutVersion(_) => {
|
ParseError::XmlDeclWithoutVersion(_) => Ok(Value::Number(XML_DECL_NOT_TERMINATED)),
|
||||||
Ok(Value::Number(XML_DECL_NOT_TERMINATED).into())
|
ParseError::NameWithQuote(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
|
||||||
}
|
ParseError::NoEqAfterName(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
|
||||||
ParseError::NameWithQuote(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()),
|
ParseError::UnquotedValue(_) => Ok(Value::Number(XML_ATTRIBUTE_NOT_TERMINATED)),
|
||||||
ParseError::NoEqAfterName(_) => Ok(Value::Number(XML_ELEMENT_MALFORMED).into()),
|
ParseError::DuplicatedAttribute(_, _) => Ok(Value::Number(XML_ELEMENT_MALFORMED)),
|
||||||
ParseError::UnquotedValue(_) => {
|
_ => Ok(Value::Number(XML_OUT_OF_MEMORY)), //Not accounted for:
|
||||||
Ok(Value::Number(XML_ATTRIBUTE_NOT_TERMINATED).into())
|
//ParseError::UnexpectedToken(_)
|
||||||
}
|
//ParseError::UnexpectedBang
|
||||||
ParseError::DuplicatedAttribute(_, _) => {
|
//ParseError::TextNotFound
|
||||||
Ok(Value::Number(XML_ELEMENT_MALFORMED).into())
|
//ParseError::EscapeError(_)
|
||||||
}
|
|
||||||
_ => Ok(Value::Number(XML_OUT_OF_MEMORY).into()), //Not accounted for:
|
|
||||||
//ParseError::UnexpectedToken(_)
|
|
||||||
//ParseError::UnexpectedBang
|
|
||||||
//ParseError::TextNotFound
|
|
||||||
//ParseError::EscapeError(_)
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct the prototype for `XML`.
|
/// Construct the prototype for `XML`.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
|
|
||||||
use gc_arena::{Collect, MutationContext};
|
use gc_arena::{Collect, MutationContext};
|
||||||
|
|
||||||
#[derive(Clone, Collect, Debug, Copy)]
|
#[derive(Clone, Collect, Debug, Copy)]
|
||||||
|
@ -11,24 +10,26 @@ pub struct Listeners<'gc>(Object<'gc>);
|
||||||
macro_rules! register_listener {
|
macro_rules! register_listener {
|
||||||
( $gc_context: ident, $object:ident, $listener: ident, $fn_proto: ident, $system_listeners_key: ident ) => {{
|
( $gc_context: ident, $object:ident, $listener: ident, $fn_proto: ident, $system_listeners_key: ident ) => {{
|
||||||
pub fn add_listener<'gc>(
|
pub fn add_listener<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
avm.system_listeners
|
activation
|
||||||
|
.avm()
|
||||||
|
.system_listeners
|
||||||
.$system_listeners_key
|
.$system_listeners_key
|
||||||
.add_listener(context, args)
|
.add_listener(context, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_listener<'gc>(
|
pub fn remove_listener<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let listener = avm.system_listeners.$system_listeners_key;
|
let listener = activation.avm().system_listeners.$system_listeners_key;
|
||||||
listener.remove_listener(avm, context, args)
|
listener.remove_listener(activation, context, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
$object.define_value(
|
$object.define_value(
|
||||||
|
@ -65,7 +66,7 @@ impl<'gc> Listeners<'gc> {
|
||||||
&self,
|
&self,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let listeners = self.0;
|
let listeners = self.0;
|
||||||
let listener = args.get(0).unwrap_or(&Value::Undefined).to_owned();
|
let listener = args.get(0).unwrap_or(&Value::Undefined).to_owned();
|
||||||
for i in 0..listeners.length() {
|
for i in 0..listeners.length() {
|
||||||
|
@ -80,10 +81,10 @@ impl<'gc> Listeners<'gc> {
|
||||||
|
|
||||||
pub fn remove_listener(
|
pub fn remove_listener(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let listeners = self.0;
|
let listeners = self.0;
|
||||||
let listener = args.get(0).unwrap_or(&Value::Undefined).to_owned();
|
let listener = args.get(0).unwrap_or(&Value::Undefined).to_owned();
|
||||||
for index in 0..listeners.length() {
|
for index in 0..listeners.length() {
|
||||||
|
@ -99,7 +100,7 @@ impl<'gc> Listeners<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.delete_array_element(new_length, context.gc_context);
|
listeners.delete_array_element(new_length, context.gc_context);
|
||||||
listeners.delete(avm, context.gc_context, &new_length.to_string());
|
listeners.delete(activation, context.gc_context, &new_length.to_string());
|
||||||
listeners.set_length(context.gc_context, new_length);
|
listeners.set_length(context.gc_context, new_length);
|
||||||
|
|
||||||
return Ok(true.into());
|
return Ok(true.into());
|
||||||
|
@ -111,7 +112,7 @@ impl<'gc> Listeners<'gc> {
|
||||||
|
|
||||||
pub fn prepare_handlers(
|
pub fn prepare_handlers(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
method: &str,
|
method: &str,
|
||||||
) -> Vec<(Object<'gc>, Value<'gc>)> {
|
) -> Vec<(Object<'gc>, Value<'gc>)> {
|
||||||
|
@ -119,8 +120,10 @@ impl<'gc> Listeners<'gc> {
|
||||||
let mut handlers = Vec::with_capacity(listeners.length());
|
let mut handlers = Vec::with_capacity(listeners.length());
|
||||||
|
|
||||||
for i in 0..listeners.length() {
|
for i in 0..listeners.length() {
|
||||||
let listener = listeners.array_element(i).coerce_to_object(avm, context);
|
let listener = listeners
|
||||||
if let Ok(handler) = listener.get(method, avm, context) {
|
.array_element(i)
|
||||||
|
.coerce_to_object(activation, context);
|
||||||
|
if let Ok(handler) = listener.get(method, activation, context) {
|
||||||
handlers.push((listener, handler));
|
handlers.push((listener, handler));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::shared_object::SharedObject;
|
use crate::avm1::shared_object::SharedObject;
|
||||||
use crate::avm1::super_object::SuperObject;
|
use crate::avm1::super_object::SuperObject;
|
||||||
use crate::avm1::value_object::ValueObject;
|
use crate::avm1::value_object::ValueObject;
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::xml_attributes_object::XMLAttributesObject;
|
use crate::avm1::xml_attributes_object::XMLAttributesObject;
|
||||||
use crate::avm1::xml_idmap_object::XMLIDMapObject;
|
use crate::avm1::xml_idmap_object::XMLIDMapObject;
|
||||||
use crate::avm1::xml_object::XMLObject;
|
use crate::avm1::xml_object::XMLObject;
|
||||||
use crate::avm1::{Avm1, ScriptObject, SoundObject, StageObject, UpdateContext, Value};
|
use crate::avm1::{ScriptObject, SoundObject, StageObject, UpdateContext, Value};
|
||||||
use crate::display_object::DisplayObject;
|
use crate::display_object::DisplayObject;
|
||||||
use crate::xml::XMLNode;
|
use crate::xml::XMLNode;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
|
@ -50,7 +50,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>>;
|
) -> Result<Value<'gc>, Error<'gc>>;
|
||||||
|
@ -59,15 +59,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
fn get(
|
fn get(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if self.has_own_property(avm, context, name) {
|
if self.has_own_property(activation, context, name) {
|
||||||
self.get_local(name, avm, context, (*self).into())
|
self.get_local(name, activation, context, (*self).into())
|
||||||
} else {
|
} else {
|
||||||
search_prototype(self.proto(), name, avm, context, (*self).into())?
|
Ok(search_prototype(self.proto(), name, activation, context, (*self).into())?.0)
|
||||||
.0
|
|
||||||
.resolve(avm, context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +74,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>>;
|
) -> Result<(), Error<'gc>>;
|
||||||
|
|
||||||
|
@ -87,7 +85,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// it can be changed by `Function.apply`/`Function.call`.
|
/// it can be changed by `Function.apply`/`Function.call`.
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
|
@ -105,24 +103,29 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let (method, base_proto) =
|
let (method, base_proto) = search_prototype(
|
||||||
search_prototype(Some((*self).into()), name, avm, context, (*self).into())?;
|
Some((*self).into()),
|
||||||
let method = method.resolve(avm, context)?;
|
name,
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
(*self).into(),
|
||||||
|
)?;
|
||||||
|
let method = method;
|
||||||
|
|
||||||
if let Value::Object(_) = method {
|
if let Value::Object(_) = method {
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Object method {} is not callable", name);
|
log::warn!("Object method {} is not callable", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.call(avm, context, (*self).into(), base_proto, args)
|
method.call(activation, context, (*self).into(), base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a setter defined in this object.
|
/// Call a setter defined in this object.
|
||||||
///
|
///
|
||||||
/// This function returns the `ReturnValue` of the called function; it
|
/// This function may return a `Executable` of the function to call; it
|
||||||
/// should be resolved and discarded. Attempts to call a non-virtual setter
|
/// should be resolved and discarded. Attempts to call a non-virtual setter
|
||||||
/// or non-existent setter fail silently.
|
/// or non-existent setter fail silently.
|
||||||
///
|
///
|
||||||
|
@ -133,10 +136,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>>;
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>>;
|
|
||||||
|
|
||||||
/// Construct a host object of some kind and return it's cell.
|
/// Construct a host object of some kind and return it's cell.
|
||||||
///
|
///
|
||||||
|
@ -151,7 +153,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// purely so that host objects can be constructed by the VM.
|
/// purely so that host objects can be constructed by the VM.
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
|
@ -160,8 +162,12 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// Delete a named property from the object.
|
/// Delete a named property from the object.
|
||||||
///
|
///
|
||||||
/// Returns false if the property cannot be deleted.
|
/// Returns false if the property cannot be deleted.
|
||||||
fn delete(&self, avm: &mut Avm1<'gc>, gc_context: MutationContext<'gc, '_>, name: &str)
|
fn delete(
|
||||||
-> bool;
|
&self,
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
gc_context: MutationContext<'gc, '_>,
|
||||||
|
name: &str,
|
||||||
|
) -> bool;
|
||||||
|
|
||||||
/// Retrieve the `__proto__` of a given object.
|
/// Retrieve the `__proto__` of a given object.
|
||||||
///
|
///
|
||||||
|
@ -242,7 +248,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// as `__proto__`.
|
/// as `__proto__`.
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -253,7 +259,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// Checks if the object has a given named property.
|
/// Checks if the object has a given named property.
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
|
@ -262,7 +268,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// say, the object's prototype or superclass)
|
/// say, the object's prototype or superclass)
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
|
@ -271,19 +277,19 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// virtual.
|
/// virtual.
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
|
|
||||||
/// Checks if a named property can be overwritten.
|
/// Checks if a named property can be overwritten.
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool;
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool;
|
||||||
|
|
||||||
/// Checks if a named property appears when enumerating the object.
|
/// Checks if a named property appears when enumerating the object.
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool;
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool;
|
||||||
|
|
||||||
/// Enumerate the object.
|
/// Enumerate the object.
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String>;
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String>;
|
||||||
|
|
||||||
/// Coerce the object into a string.
|
/// Coerce the object into a string.
|
||||||
fn as_string(&self) -> Cow<str>;
|
fn as_string(&self) -> Cow<str>;
|
||||||
|
@ -314,7 +320,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// somehow could this would support that, too.
|
/// somehow could this would support that, too.
|
||||||
fn is_instance_of(
|
fn is_instance_of(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
constructor: Object<'gc>,
|
constructor: Object<'gc>,
|
||||||
prototype: Object<'gc>,
|
prototype: Object<'gc>,
|
||||||
|
@ -333,13 +339,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
proto_stack.push(p);
|
proto_stack.push(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if avm.current_swf_version() >= 7 {
|
if activation.current_swf_version() >= 7 {
|
||||||
for interface in this_proto.interfaces() {
|
for interface in this_proto.interfaces() {
|
||||||
if Object::ptr_eq(interface, constructor) {
|
if Object::ptr_eq(interface, constructor) {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Value::Object(o) = interface.get("prototype", avm, context)? {
|
if let Value::Object(o) = interface.get("prototype", activation, context)? {
|
||||||
proto_stack.push(o);
|
proto_stack.push(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -464,10 +470,10 @@ impl<'gc> Object<'gc> {
|
||||||
pub fn search_prototype<'gc>(
|
pub fn search_prototype<'gc>(
|
||||||
mut proto: Option<Object<'gc>>,
|
mut proto: Option<Object<'gc>>,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<(ReturnValue<'gc>, Option<Object<'gc>>), Error<'gc>> {
|
) -> Result<(Value<'gc>, Option<Object<'gc>>), Error<'gc>> {
|
||||||
let mut depth = 0;
|
let mut depth = 0;
|
||||||
|
|
||||||
while proto.is_some() {
|
while proto.is_some() {
|
||||||
|
@ -475,9 +481,9 @@ pub fn search_prototype<'gc>(
|
||||||
return Err(Error::PrototypeRecursionLimit);
|
return Err(Error::PrototypeRecursionLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if proto.unwrap().has_own_property(avm, context, name) {
|
if proto.unwrap().has_own_property(activation, context, name) {
|
||||||
return Ok((
|
return Ok((
|
||||||
proto.unwrap().get_local(name, avm, context, this)?.into(),
|
proto.unwrap().get_local(name, activation, context, this)?,
|
||||||
proto,
|
proto,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -486,5 +492,5 @@ pub fn search_prototype<'gc>(
|
||||||
depth += 1;
|
depth += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((Value::Undefined.into(), None))
|
Ok((Value::Undefined, None))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
//! User-defined properties
|
//! User-defined properties
|
||||||
|
|
||||||
use self::Attribute::*;
|
use self::Attribute::*;
|
||||||
use crate::avm1::error::Error;
|
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::Value;
|
||||||
use crate::avm1::{Avm1, Object, UpdateContext, Value};
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use enumset::{EnumSet, EnumSetType};
|
use enumset::{EnumSet, EnumSetType};
|
||||||
|
|
||||||
|
@ -32,42 +30,18 @@ pub enum Property<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Property<'gc> {
|
impl<'gc> Property<'gc> {
|
||||||
/// Get the value of a property slot.
|
|
||||||
///
|
|
||||||
/// This function yields `ReturnValue` because some properties may be
|
|
||||||
/// user-defined.
|
|
||||||
pub fn get(
|
|
||||||
&self,
|
|
||||||
avm: &mut Avm1<'gc>,
|
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
||||||
this: Object<'gc>,
|
|
||||||
base_proto: Option<Object<'gc>>,
|
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|
||||||
match self {
|
|
||||||
Property::Virtual { get, .. } => get.exec(avm, context, this, base_proto, &[]),
|
|
||||||
Property::Stored { value, .. } => Ok(value.to_owned().into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a property slot.
|
/// Set a property slot.
|
||||||
///
|
///
|
||||||
/// This function returns the `ReturnValue` of the property's virtual
|
/// This function may return an `Executable` of the property's virtual
|
||||||
/// function, if any happen to exist. It should be resolved, and it's value
|
/// function, if any happen to exist. It should be resolved, and it's value
|
||||||
/// discarded.
|
/// discarded.
|
||||||
pub fn set(
|
pub fn set(&mut self, new_value: impl Into<Value<'gc>>) -> Option<Executable<'gc>> {
|
||||||
&mut self,
|
|
||||||
avm: &mut Avm1<'gc>,
|
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
||||||
this: Object<'gc>,
|
|
||||||
base_proto: Option<Object<'gc>>,
|
|
||||||
new_value: impl Into<Value<'gc>>,
|
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|
||||||
match self {
|
match self {
|
||||||
Property::Virtual { set, .. } => {
|
Property::Virtual { set, .. } => {
|
||||||
if let Some(function) = set {
|
if let Some(function) = set {
|
||||||
function.exec(avm, context, this, base_proto, &[new_value.into()])
|
Some(function.to_owned())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Property::Stored {
|
Property::Stored {
|
||||||
|
@ -77,7 +51,7 @@ impl<'gc> Property<'gc> {
|
||||||
*value = new_value.into();
|
*value = new_value.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined.into())
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
//! Represents AVM1 scope chain resolution.
|
//! Represents AVM1 scope chain resolution.
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
use std::cell::Ref;
|
use std::cell::Ref;
|
||||||
|
@ -207,6 +207,11 @@ impl<'gc> Scope<'gc> {
|
||||||
&self.values
|
&self.values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the current local scope object.
|
||||||
|
pub fn locals_cell(&self) -> Object<'gc> {
|
||||||
|
self.values
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a reference to the current local scope object for mutation.
|
/// Returns a reference to the current local scope object for mutation.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn locals_mut(&mut self) -> &mut Object<'gc> {
|
pub fn locals_mut(&mut self) -> &mut Object<'gc> {
|
||||||
|
@ -234,34 +239,34 @@ impl<'gc> Scope<'gc> {
|
||||||
pub fn resolve(
|
pub fn resolve(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if self.locals().has_property(avm, context, name) {
|
if self.locals().has_property(activation, context, name) {
|
||||||
return Ok(self.locals().get(name, avm, context)?.into());
|
return Ok(self.locals().get(name, activation, context)?);
|
||||||
}
|
}
|
||||||
if let Some(scope) = self.parent() {
|
if let Some(scope) = self.parent() {
|
||||||
return scope.resolve(name, avm, context, this);
|
return scope.resolve(name, activation, context, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Should undefined variables halt execution?
|
//TODO: Should undefined variables halt execution?
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a particular property in the scope chain is defined.
|
/// Check if a particular property in the scope chain is defined.
|
||||||
pub fn is_defined(
|
pub fn is_defined(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if self.locals().has_property(avm, context, name) {
|
if self.locals().has_property(activation, context, name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scope) = self.parent() {
|
if let Some(scope) = self.parent() {
|
||||||
return scope.is_defined(avm, context, name);
|
return scope.is_defined(activation, context, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
@ -277,26 +282,26 @@ impl<'gc> Scope<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if self.class == ScopeClass::Target
|
if self.class == ScopeClass::Target
|
||||||
|| (self.locals().has_property(avm, context, name)
|
|| (self.locals().has_property(activation, context, name)
|
||||||
&& self.locals().is_property_overwritable(avm, name))
|
&& self.locals().is_property_overwritable(activation, name))
|
||||||
{
|
{
|
||||||
// Value found on this object, so overwrite it.
|
// Value found on this object, so overwrite it.
|
||||||
// Or we've hit the executing movie clip, so create it here.
|
// Or we've hit the executing movie clip, so create it here.
|
||||||
self.locals().set(name, value, avm, context)
|
self.locals().set(name, value, activation, context)
|
||||||
} else if let Some(scope) = self.parent() {
|
} else if let Some(scope) = self.parent() {
|
||||||
// Traverse the scope chain in search of the value.
|
// Traverse the scope chain in search of the value.
|
||||||
scope.set(name, value, avm, context, this)
|
scope.set(name, value, activation, context, this)
|
||||||
} else {
|
} else {
|
||||||
// This probably shouldn't happen -- all AVM1 code runs in reference to some movieclip,
|
// This probably shouldn't happen -- all AVM1 code runs in reference to some movieclip,
|
||||||
// so we should always have a movieclip scope.
|
// so we should always have a movieclip scope.
|
||||||
// Define on the top-level scope.
|
// Define on the top-level scope.
|
||||||
debug_assert!(false, "Scope::set: No top-level movie clip scope");
|
debug_assert!(false, "Scope::set: No top-level movie clip scope");
|
||||||
self.locals().set(name, value, avm, context)
|
self.locals().set(name, value, activation, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,17 +319,17 @@ impl<'gc> Scope<'gc> {
|
||||||
/// Delete a value from scope
|
/// Delete a value from scope
|
||||||
pub fn delete(
|
pub fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if self.locals().has_property(avm, context, name) {
|
if self.locals().has_property(activation, context, name) {
|
||||||
return self.locals().delete(avm, mc, name);
|
return self.locals().delete(activation, mc, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scope) = self.parent() {
|
if let Some(scope) = self.parent() {
|
||||||
return scope.delete(avm, context, name, mc);
|
return scope.delete(activation, context, name, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject, NativeFunction};
|
use crate::avm1::function::{Executable, FunctionObject, NativeFunction};
|
||||||
use crate::avm1::property::{Attribute, Property};
|
use crate::avm1::property::{Attribute, Property};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ObjectPtr, TObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ObjectPtr, TObject, UpdateContext, Value};
|
|
||||||
use crate::property_map::{Entry, PropertyMap};
|
use crate::property_map::{Entry, PropertyMap};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
|
@ -197,19 +197,20 @@ impl<'gc> ScriptObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if name == "__proto__" {
|
if name == "__proto__" {
|
||||||
self.0.write(context.gc_context).prototype = Some(value.coerce_to_object(avm, context));
|
self.0.write(context.gc_context).prototype =
|
||||||
|
Some(value.coerce_to_object(activation, context));
|
||||||
} else if let Ok(index) = name.parse::<usize>() {
|
} else if let Ok(index) = name.parse::<usize>() {
|
||||||
self.set_array_element(index, value.to_owned(), context.gc_context);
|
self.set_array_element(index, value.to_owned(), context.gc_context);
|
||||||
} else if !name.is_empty() {
|
} else if !name.is_empty() {
|
||||||
if name == "length" {
|
if name == "length" {
|
||||||
let length = value
|
let length = value
|
||||||
.coerce_to_f64(avm, context)
|
.coerce_to_f64(activation, context)
|
||||||
.map(|v| v.abs() as i32)
|
.map(|v| v.abs() as i32)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
if length > 0 {
|
if length > 0 {
|
||||||
|
@ -226,14 +227,14 @@ impl<'gc> ScriptObject<'gc> {
|
||||||
.0
|
.0
|
||||||
.read()
|
.read()
|
||||||
.values
|
.values
|
||||||
.contains_key(name, avm.is_case_sensitive());
|
.contains_key(name, activation.is_case_sensitive());
|
||||||
let mut rval = None;
|
let mut worked = false;
|
||||||
|
|
||||||
if is_vacant {
|
if is_vacant {
|
||||||
let mut proto: Option<Object<'gc>> = Some((*self).into());
|
let mut proto: Option<Object<'gc>> = Some((*self).into());
|
||||||
while let Some(this_proto) = proto {
|
while let Some(this_proto) = proto {
|
||||||
if this_proto.has_own_virtual(avm, context, name)
|
if this_proto.has_own_virtual(activation, context, name)
|
||||||
&& this_proto.is_property_overwritable(avm, name)
|
&& this_proto.is_property_overwritable(activation, name)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -242,42 +243,45 @@ impl<'gc> ScriptObject<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(this_proto) = proto {
|
if let Some(this_proto) = proto {
|
||||||
rval = Some(this_proto.call_setter(
|
if let Some(rval) =
|
||||||
name,
|
this_proto.call_setter(name, value.clone(), activation, context)
|
||||||
value.clone(),
|
{
|
||||||
avm,
|
let _ = rval.exec(
|
||||||
context,
|
activation,
|
||||||
(*self).into(),
|
context,
|
||||||
)?);
|
this,
|
||||||
|
Some(this_proto),
|
||||||
|
&[value.clone()],
|
||||||
|
)?;
|
||||||
|
worked = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//No `rval` signals we didn't call a virtual setter above. Normally,
|
//This signals we didn't call a virtual setter above. Normally,
|
||||||
//we'd resolve and return up there, but we have borrows that need
|
//we'd resolve and return up there, but we have borrows that need
|
||||||
//to end before we can do so.
|
//to end before we can do so.
|
||||||
if rval.is_none() {
|
if !worked {
|
||||||
rval = match self
|
let rval = match self
|
||||||
.0
|
.0
|
||||||
.write(context.gc_context)
|
.write(context.gc_context)
|
||||||
.values
|
.values
|
||||||
.entry(name.to_owned(), avm.is_case_sensitive())
|
.entry(name.to_owned(), activation.is_case_sensitive())
|
||||||
{
|
{
|
||||||
Entry::Occupied(mut entry) => {
|
Entry::Occupied(mut entry) => entry.get_mut().set(value.clone()),
|
||||||
Some(entry.get_mut().set(avm, context, this, base_proto, value)?)
|
|
||||||
}
|
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
entry.insert(Property::Stored {
|
entry.insert(Property::Stored {
|
||||||
value,
|
value: value.clone(),
|
||||||
attributes: Default::default(),
|
attributes: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(rval) = rval {
|
if let Some(rval) = rval {
|
||||||
rval.resolve(avm, context)?;
|
let _ = rval.exec(activation, context, this, base_proto, &[value])?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +301,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -305,13 +309,25 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
return Ok(self.proto().map_or(Value::Undefined, Value::Object));
|
return Ok(self.proto().map_or(Value::Undefined, Value::Object));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(value) = self.0.read().values.get(name, avm.is_case_sensitive()) {
|
let mut exec = None;
|
||||||
return value
|
|
||||||
.get(avm, context, this, Some((*self).into()))?
|
if let Some(value) = self
|
||||||
.resolve(avm, context);
|
.0
|
||||||
|
.read()
|
||||||
|
.values
|
||||||
|
.get(name, activation.is_case_sensitive())
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
Property::Virtual { get, .. } => exec = Some(get.to_owned()),
|
||||||
|
Property::Stored { value, .. } => return Ok(value.to_owned()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
if let Some(get) = exec {
|
||||||
|
get.exec(activation, context, this, Some((*self).into()), &[])
|
||||||
|
} else {
|
||||||
|
Ok(Value::Undefined)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a named property on the object.
|
/// Set a named property on the object.
|
||||||
|
@ -323,13 +339,13 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.internal_set(
|
self.internal_set(
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
(*self).into(),
|
(*self).into(),
|
||||||
Some((*self).into()),
|
Some((*self).into()),
|
||||||
|
@ -343,7 +359,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
/// overrides that may need to interact with the underlying object.
|
/// overrides that may need to interact with the underlying object.
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_base_proto: Option<Object<'gc>>,
|
_base_proto: Option<Object<'gc>>,
|
||||||
|
@ -356,27 +372,24 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|
||||||
match self
|
match self
|
||||||
.0
|
.0
|
||||||
.write(context.gc_context)
|
.write(context.gc_context)
|
||||||
.values
|
.values
|
||||||
.get_mut(name, avm.is_case_sensitive())
|
.get_mut(name, activation.is_case_sensitive())
|
||||||
{
|
{
|
||||||
Some(propref) if propref.is_virtual() => {
|
Some(propref) if propref.is_virtual() => propref.set(value),
|
||||||
propref.set(avm, context, this, Some((*self).into()), value)
|
_ => None,
|
||||||
}
|
|
||||||
_ => Ok(Value::Undefined.into()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
|
@ -396,14 +409,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
/// Returns false if the property cannot be deleted.
|
/// Returns false if the property cannot be deleted.
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut object = self.0.write(gc_context);
|
let mut object = self.0.write(gc_context);
|
||||||
if let Some(prop) = object.values.get(name, avm.is_case_sensitive()) {
|
if let Some(prop) = object.values.get(name, activation.is_case_sensitive()) {
|
||||||
if prop.can_delete() {
|
if prop.can_delete() {
|
||||||
object.values.remove(name, avm.is_case_sensitive());
|
object.values.remove(name, activation.is_case_sensitive());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,7 +445,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -446,7 +459,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
set,
|
set,
|
||||||
attributes,
|
attributes,
|
||||||
},
|
},
|
||||||
avm.is_case_sensitive(),
|
activation.is_case_sensitive(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,22 +512,22 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
/// Checks if the object has a given named property.
|
/// Checks if the object has a given named property.
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.has_own_property(avm, context, name)
|
self.has_own_property(activation, context, name)
|
||||||
|| self
|
|| self
|
||||||
.proto()
|
.proto()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |p| p.has_property(avm, context, name))
|
.map_or(false, |p| p.has_property(activation, context, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the object has a given named property on itself (and not,
|
/// Checks if the object has a given named property on itself (and not,
|
||||||
/// say, the object's prototype or superclass)
|
/// say, the object's prototype or superclass)
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -524,34 +537,44 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.values
|
.values
|
||||||
.contains_key(name, avm.is_case_sensitive())
|
.contains_key(name, activation.is_case_sensitive())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let Some(slot) = self.0.read().values.get(name, avm.is_case_sensitive()) {
|
if let Some(slot) = self
|
||||||
|
.0
|
||||||
|
.read()
|
||||||
|
.values
|
||||||
|
.get(name, activation.is_case_sensitive())
|
||||||
|
{
|
||||||
slot.is_virtual()
|
slot.is_virtual()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.values
|
.values
|
||||||
.get(name, avm.is_case_sensitive())
|
.get(name, activation.is_case_sensitive())
|
||||||
.map(|p| p.is_overwritable())
|
.map(|p| p.is_overwritable())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a named property appears when enumerating the object.
|
/// Checks if a named property appears when enumerating the object.
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
if let Some(prop) = self.0.read().values.get(name, avm.is_case_sensitive()) {
|
if let Some(prop) = self
|
||||||
|
.0
|
||||||
|
.read()
|
||||||
|
.values
|
||||||
|
.get(name, activation.is_case_sensitive())
|
||||||
|
{
|
||||||
prop.is_enumerable()
|
prop.is_enumerable()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -559,17 +582,19 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumerate the object.
|
/// Enumerate the object.
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
let proto_keys = self.proto().map_or_else(Vec::new, |p| p.get_keys(avm));
|
let proto_keys = self
|
||||||
|
.proto()
|
||||||
|
.map_or_else(Vec::new, |p| p.get_keys(activation));
|
||||||
let mut out_keys = vec![];
|
let mut out_keys = vec![];
|
||||||
let object = self.0.read();
|
let object = self.0.read();
|
||||||
|
|
||||||
// Prototype keys come first.
|
// Prototype keys come first.
|
||||||
out_keys.extend(
|
out_keys.extend(proto_keys.into_iter().filter(|k| {
|
||||||
proto_keys
|
!object
|
||||||
.into_iter()
|
.values
|
||||||
.filter(|k| !object.values.contains_key(k, avm.is_case_sensitive())),
|
.contains_key(k, activation.is_case_sensitive())
|
||||||
);
|
}));
|
||||||
|
|
||||||
// Then our own keys.
|
// Then our own keys.
|
||||||
out_keys.extend(self.0.read().values.iter().filter_map(move |(k, p)| {
|
out_keys.extend(self.0.read().values.iter().filter_map(move |(k, p)| {
|
||||||
|
@ -710,9 +735,9 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::avm1::activation::Activation;
|
|
||||||
use crate::avm1::globals::system::SystemProperties;
|
use crate::avm1::globals::system::SystemProperties;
|
||||||
use crate::avm1::property::Attribute::*;
|
use crate::avm1::property::Attribute::*;
|
||||||
|
use crate::avm1::Avm1;
|
||||||
use crate::backend::audio::NullAudioBackend;
|
use crate::backend::audio::NullAudioBackend;
|
||||||
use crate::backend::input::NullInputBackend;
|
use crate::backend::input::NullInputBackend;
|
||||||
use crate::backend::navigator::NullNavigatorBackend;
|
use crate::backend::navigator::NullNavigatorBackend;
|
||||||
|
@ -730,7 +755,11 @@ mod tests {
|
||||||
|
|
||||||
fn with_object<F, R>(swf_version: u8, test: F) -> R
|
fn with_object<F, R>(swf_version: u8, test: F) -> R
|
||||||
where
|
where
|
||||||
F: for<'a, 'gc> FnOnce(&mut Avm1<'gc>, &mut UpdateContext<'a, 'gc, '_>, Object<'gc>) -> R,
|
F: for<'a, 'gc> FnOnce(
|
||||||
|
&mut Activation<'_, 'gc>,
|
||||||
|
&mut UpdateContext<'a, 'gc, '_>,
|
||||||
|
Object<'gc>,
|
||||||
|
) -> R,
|
||||||
{
|
{
|
||||||
rootless_arena(|gc_context| {
|
rootless_arena(|gc_context| {
|
||||||
let mut avm = Avm1::new(gc_context, swf_version);
|
let mut avm = Avm1::new(gc_context, swf_version);
|
||||||
|
@ -780,20 +809,23 @@ mod tests {
|
||||||
let object = ScriptObject::object(gc_context, Some(avm.prototypes().object)).into();
|
let object = ScriptObject::object(gc_context, Some(avm.prototypes().object)).into();
|
||||||
|
|
||||||
let globals = avm.global_object_cell();
|
let globals = avm.global_object_cell();
|
||||||
avm.insert_stack_frame(GcCell::allocate(
|
let mut activation = Activation::from_nothing(
|
||||||
gc_context,
|
&mut avm,
|
||||||
Activation::from_nothing(swf_version, globals, gc_context, root),
|
context.swf.version(),
|
||||||
));
|
globals,
|
||||||
|
context.gc_context,
|
||||||
|
*context.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
test(&mut avm, &mut context, object)
|
test(&mut activation, &mut context, object)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_undefined() {
|
fn test_get_undefined() {
|
||||||
with_object(0, |avm, context, object| {
|
with_object(0, |activation, context, object| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.get("not_defined", avm, context).unwrap(),
|
object.get("not_defined", activation, context).unwrap(),
|
||||||
Value::Undefined
|
Value::Undefined
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -801,7 +833,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_get() {
|
fn test_set_get() {
|
||||||
with_object(0, |avm, context, object| {
|
with_object(0, |activation, context, object| {
|
||||||
object.as_script_object().unwrap().define_value(
|
object.as_script_object().unwrap().define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
"forced",
|
"forced",
|
||||||
|
@ -809,12 +841,15 @@ mod tests {
|
||||||
EnumSet::empty(),
|
EnumSet::empty(),
|
||||||
);
|
);
|
||||||
object
|
object
|
||||||
.set("natural", "natural".into(), avm, context)
|
.set("natural", "natural".into(), activation, context)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(object.get("forced", avm, context).unwrap(), "forced".into());
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.get("natural", avm, context).unwrap(),
|
object.get("forced", activation, context).unwrap(),
|
||||||
|
"forced".into()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
object.get("natural", activation, context).unwrap(),
|
||||||
"natural".into()
|
"natural".into()
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -822,7 +857,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_readonly() {
|
fn test_set_readonly() {
|
||||||
with_object(0, |avm, context, object| {
|
with_object(0, |activation, context, object| {
|
||||||
object.as_script_object().unwrap().define_value(
|
object.as_script_object().unwrap().define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
"normal",
|
"normal",
|
||||||
|
@ -837,18 +872,18 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
object
|
object
|
||||||
.set("normal", "replaced".into(), avm, context)
|
.set("normal", "replaced".into(), activation, context)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
object
|
object
|
||||||
.set("readonly", "replaced".into(), avm, context)
|
.set("readonly", "replaced".into(), activation, context)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.get("normal", avm, context).unwrap(),
|
object.get("normal", activation, context).unwrap(),
|
||||||
"replaced".into()
|
"replaced".into()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.get("readonly", avm, context).unwrap(),
|
object.get("readonly", activation, context).unwrap(),
|
||||||
"initial".into()
|
"initial".into()
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -856,7 +891,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_deletable_not_readonly() {
|
fn test_deletable_not_readonly() {
|
||||||
with_object(0, |avm, context, object| {
|
with_object(0, |activation, context, object| {
|
||||||
object.as_script_object().unwrap().define_value(
|
object.as_script_object().unwrap().define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
"test",
|
"test",
|
||||||
|
@ -864,26 +899,30 @@ mod tests {
|
||||||
DontDelete.into(),
|
DontDelete.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(object.delete(avm, context.gc_context, "test"), false);
|
assert_eq!(object.delete(activation, context.gc_context, "test"), false);
|
||||||
assert_eq!(object.get("test", avm, context).unwrap(), "initial".into());
|
assert_eq!(
|
||||||
|
object.get("test", activation, context).unwrap(),
|
||||||
|
"initial".into()
|
||||||
|
);
|
||||||
|
|
||||||
object
|
object
|
||||||
.as_script_object()
|
.as_script_object()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set("test", "replaced".into(), avm, context)
|
.set("test", "replaced".into(), activation, context)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(object.delete(avm, context.gc_context, "test"), false);
|
assert_eq!(object.delete(activation, context.gc_context, "test"), false);
|
||||||
assert_eq!(object.get("test", avm, context).unwrap(), "replaced".into());
|
assert_eq!(
|
||||||
|
object.get("test", activation, context).unwrap(),
|
||||||
|
"replaced".into()
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_virtual_get() {
|
fn test_virtual_get() {
|
||||||
with_object(0, |avm, context, object| {
|
with_object(0, |activation, context, object| {
|
||||||
let getter = Executable::Native(|_avm, _context, _this, _args| {
|
let getter = Executable::Native(|_avm, _context, _this, _args| Ok("Virtual!".into()));
|
||||||
Ok(ReturnValue::Immediate("Virtual!".into()))
|
|
||||||
});
|
|
||||||
|
|
||||||
object.as_script_object().unwrap().add_property(
|
object.as_script_object().unwrap().add_property(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -893,20 +932,26 @@ mod tests {
|
||||||
EnumSet::empty(),
|
EnumSet::empty(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(object.get("test", avm, context).unwrap(), "Virtual!".into());
|
assert_eq!(
|
||||||
|
object.get("test", activation, context).unwrap(),
|
||||||
|
"Virtual!".into()
|
||||||
|
);
|
||||||
|
|
||||||
// This set should do nothing
|
// This set should do nothing
|
||||||
object.set("test", "Ignored!".into(), avm, context).unwrap();
|
object
|
||||||
assert_eq!(object.get("test", avm, context).unwrap(), "Virtual!".into());
|
.set("test", "Ignored!".into(), activation, context)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
object.get("test", activation, context).unwrap(),
|
||||||
|
"Virtual!".into()
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete() {
|
fn test_delete() {
|
||||||
with_object(0, |avm, context, object| {
|
with_object(0, |activation, context, object| {
|
||||||
let getter = Executable::Native(|_avm, _context, _this, _args| {
|
let getter = Executable::Native(|_avm, _context, _this, _args| Ok("Virtual!".into()));
|
||||||
Ok(ReturnValue::Immediate("Virtual!".into()))
|
|
||||||
});
|
|
||||||
|
|
||||||
object.as_script_object().unwrap().add_property(
|
object.as_script_object().unwrap().add_property(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -935,29 +980,41 @@ mod tests {
|
||||||
DontDelete.into(),
|
DontDelete.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(object.delete(avm, context.gc_context, "virtual"), true);
|
|
||||||
assert_eq!(object.delete(avm, context.gc_context, "virtual_un"), false);
|
|
||||||
assert_eq!(object.delete(avm, context.gc_context, "stored"), true);
|
|
||||||
assert_eq!(object.delete(avm, context.gc_context, "stored_un"), false);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.delete(avm, context.gc_context, "non_existent"),
|
object.delete(activation, context.gc_context, "virtual"),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
object.delete(activation, context.gc_context, "virtual_un"),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
object.delete(activation, context.gc_context, "stored"),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
object.delete(activation, context.gc_context, "stored_un"),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
object.delete(activation, context.gc_context, "non_existent"),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.get("virtual", avm, context).unwrap(),
|
object.get("virtual", activation, context).unwrap(),
|
||||||
Value::Undefined
|
Value::Undefined
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.get("virtual_un", avm, context).unwrap(),
|
object.get("virtual_un", activation, context).unwrap(),
|
||||||
"Virtual!".into()
|
"Virtual!".into()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.get("stored", avm, context).unwrap(),
|
object.get("stored", activation, context).unwrap(),
|
||||||
Value::Undefined
|
Value::Undefined
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.get("stored_un", avm, context).unwrap(),
|
object.get("stored_un", activation, context).unwrap(),
|
||||||
"Stored!".into()
|
"Stored!".into()
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -965,10 +1022,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iter_values() {
|
fn test_iter_values() {
|
||||||
with_object(0, |avm, context, object| {
|
with_object(0, |activation, context, object| {
|
||||||
let getter = Executable::Native(|_avm, _context, _this, _args| {
|
let getter = Executable::Native(|_avm, _context, _this, _args| Ok(Value::Null));
|
||||||
Ok(ReturnValue::Immediate(Value::Null))
|
|
||||||
});
|
|
||||||
|
|
||||||
object.as_script_object().unwrap().define_value(
|
object.as_script_object().unwrap().define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -997,7 +1052,7 @@ mod tests {
|
||||||
DontEnum.into(),
|
DontEnum.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let keys: Vec<_> = object.get_keys(avm);
|
let keys: Vec<_> = object.get_keys(activation);
|
||||||
assert_eq!(keys.len(), 2);
|
assert_eq!(keys.len(), 2);
|
||||||
assert_eq!(keys.contains(&"stored".to_string()), true);
|
assert_eq!(keys.contains(&"stored".to_string()), true);
|
||||||
assert_eq!(keys.contains(&"stored_hidden".to_string()), false);
|
assert_eq!(keys.contains(&"stored_hidden".to_string()), false);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::sound_object::SoundObject;
|
use crate::avm1::sound_object::SoundObject;
|
||||||
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, Value};
|
use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::display_object::DisplayObject;
|
use crate::display_object::DisplayObject;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
|
@ -73,66 +73,67 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base().get_local(name, avm, context, this)
|
self.base().get_local(name, activation, context, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(
|
fn set(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.base().set(name, value, avm, context)
|
self.base().set(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base().call(avm, context, this, base_proto, args)
|
self.base()
|
||||||
|
.call(activation, context, this, base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_setter(
|
fn call_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
self.base().call_setter(name, value, activation, context)
|
||||||
self.base().call_setter(name, value, avm, context, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
Ok(
|
Ok(SharedObject::empty_shared_obj(
|
||||||
SharedObject::empty_shared_obj(context.gc_context, Some(avm.prototypes.shared_object))
|
context.gc_context,
|
||||||
.into(),
|
Some(activation.avm().prototypes.shared_object),
|
||||||
)
|
)
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().delete(avm, gc_context, name)
|
self.base().delete(activation, gc_context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proto(&self) -> Option<Object<'gc>> {
|
fn proto(&self) -> Option<Object<'gc>> {
|
||||||
|
@ -179,7 +180,7 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -187,46 +188,46 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
|
||||||
attributes: EnumSet<Attribute>,
|
attributes: EnumSet<Attribute>,
|
||||||
) {
|
) {
|
||||||
self.base()
|
self.base()
|
||||||
.add_property_with_case(avm, gc_context, name, get, set, attributes)
|
.add_property_with_case(activation, gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_property(avm, context, name)
|
self.base().has_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_own_property(avm, context, name)
|
self.base().has_own_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_own_virtual(avm, context, name)
|
self.base().has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_overwritable(avm, name)
|
self.base().is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_enumerable(avm, name)
|
self.base().is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
self.base().get_keys(avm)
|
self.base().get_keys(activation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string(&self) -> Cow<str> {
|
fn as_string(&self) -> Cow<str> {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! AVM1 object type to represent Sound objects.
|
//! AVM1 object type to represent Sound objects.
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
|
||||||
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, Value};
|
|
||||||
use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
|
use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::display_object::DisplayObject;
|
use crate::display_object::DisplayObject;
|
||||||
|
@ -134,63 +134,66 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base().get_local(name, avm, context, this)
|
self.base().get_local(name, activation, context, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(
|
fn set(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.base().set(name, value, avm, context)
|
self.base().set(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base().call(avm, context, this, base_proto, args)
|
self.base()
|
||||||
|
.call(activation, context, this, base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_setter(
|
fn call_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
self.base().call_setter(name, value, activation, context)
|
||||||
self.base().call_setter(name, value, avm, context, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
Ok(SoundObject::empty_sound(context.gc_context, Some(avm.prototypes.sound)).into())
|
Ok(
|
||||||
|
SoundObject::empty_sound(context.gc_context, Some(activation.avm().prototypes.sound))
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().delete(avm, gc_context, name)
|
self.base().delete(activation, gc_context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proto(&self) -> Option<Object<'gc>> {
|
fn proto(&self) -> Option<Object<'gc>> {
|
||||||
|
@ -237,7 +240,7 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -245,46 +248,46 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
|
||||||
attributes: EnumSet<Attribute>,
|
attributes: EnumSet<Attribute>,
|
||||||
) {
|
) {
|
||||||
self.base()
|
self.base()
|
||||||
.add_property_with_case(avm, gc_context, name, get, set, attributes)
|
.add_property_with_case(activation, gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_property(avm, context, name)
|
self.base().has_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_own_property(avm, context, name)
|
self.base().has_own_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_own_virtual(avm, context, name)
|
self.base().has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_overwritable(avm, name)
|
self.base().is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_enumerable(avm, name)
|
self.base().is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
self.base().get_keys(avm)
|
self.base().get_keys(activation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string(&self) -> Cow<str> {
|
fn as_string(&self) -> Cow<str> {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! AVM1 object type to represent objects on the stage.
|
//! AVM1 object type to represent objects on the stage.
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::object::search_prototype;
|
use crate::avm1::object::search_prototype;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value};
|
||||||
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value};
|
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::display_object::{DisplayObject, EditText, MovieClip};
|
use crate::display_object::{DisplayObject, EditText, MovieClip};
|
||||||
use crate::property_map::PropertyMap;
|
use crate::property_map::PropertyMap;
|
||||||
|
@ -127,19 +127,19 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
fn get(
|
fn get(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let obj = self.0.read();
|
let obj = self.0.read();
|
||||||
let props = avm.display_properties;
|
let props = activation.avm().display_properties;
|
||||||
let case_sensitive = avm.is_case_sensitive();
|
let case_sensitive = activation.is_case_sensitive();
|
||||||
// Property search order for DisplayObjects:
|
// Property search order for DisplayObjects:
|
||||||
if self.has_own_property(avm, context, name) {
|
if self.has_own_property(activation, context, name) {
|
||||||
// 1) Actual properties on the underlying object
|
// 1) Actual properties on the underlying object
|
||||||
self.get_local(name, avm, context, (*self).into())
|
self.get_local(name, activation, context, (*self).into())
|
||||||
} else if let Some(property) = props.read().get_by_name(&name) {
|
} else if let Some(property) = props.read().get_by_name(&name) {
|
||||||
// 2) Display object properties such as _x, _y
|
// 2) Display object properties such as _x, _y
|
||||||
let val = property.get(avm, context, obj.display_object)?;
|
let val = property.get(activation, context, obj.display_object)?;
|
||||||
Ok(val)
|
Ok(val)
|
||||||
} else if let Some(child) = obj.display_object.get_child_by_name(name, case_sensitive) {
|
} else if let Some(child) = obj.display_object.get_child_by_name(name, case_sensitive) {
|
||||||
// 3) Child display objects with the given instance name
|
// 3) Child display objects with the given instance name
|
||||||
|
@ -152,9 +152,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
Ok(level.object())
|
Ok(level.object())
|
||||||
} else {
|
} else {
|
||||||
// 5) Prototype
|
// 5) Prototype
|
||||||
search_prototype(self.proto(), name, avm, context, (*self).into())?
|
Ok(search_prototype(self.proto(), name, activation, context, (*self).into())?.0)
|
||||||
.0
|
|
||||||
.resolve(avm, context)
|
|
||||||
}
|
}
|
||||||
// 6) TODO: __resolve?
|
// 6) TODO: __resolve?
|
||||||
}
|
}
|
||||||
|
@ -162,22 +160,25 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.0.read().base.get_local(name, avm, context, this)
|
self.0
|
||||||
|
.read()
|
||||||
|
.base
|
||||||
|
.get_local(name, activation, context, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(
|
fn set(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
let obj = self.0.read();
|
let obj = self.0.read();
|
||||||
let props = avm.display_properties;
|
let props = activation.avm().display_properties;
|
||||||
|
|
||||||
// Check if a text field is bound to this property and update the text if so.
|
// Check if a text field is bound to this property and update the text if so.
|
||||||
for binding in obj
|
for binding in obj
|
||||||
|
@ -185,31 +186,32 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|binding| binding.variable_name == name)
|
.filter(|binding| binding.variable_name == name)
|
||||||
{
|
{
|
||||||
let _ = binding
|
let _ = binding.text_field.set_html_text(
|
||||||
.text_field
|
value.coerce_to_string(activation, context)?.into_owned(),
|
||||||
.set_html_text(value.coerce_to_string(avm, context)?.into_owned(), context);
|
context,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj.base.has_own_property(avm, context, name) {
|
if obj.base.has_own_property(activation, context, name) {
|
||||||
// 1) Actual proeprties on the underlying object
|
// 1) Actual proeprties on the underlying object
|
||||||
obj.base.internal_set(
|
obj.base.internal_set(
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
(*self).into(),
|
(*self).into(),
|
||||||
Some((*self).into()),
|
Some((*self).into()),
|
||||||
)
|
)
|
||||||
} else if let Some(property) = props.read().get_by_name(&name) {
|
} else if let Some(property) = props.read().get_by_name(&name) {
|
||||||
// 2) Display object properties such as _x, _y
|
// 2) Display object properties such as _x, _y
|
||||||
property.set(avm, context, obj.display_object, value)?;
|
property.set(activation, context, obj.display_object, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
// 3) TODO: Prototype
|
// 3) TODO: Prototype
|
||||||
obj.base.internal_set(
|
obj.base.internal_set(
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
(*self).into(),
|
(*self).into(),
|
||||||
Some((*self).into()),
|
Some((*self).into()),
|
||||||
|
@ -219,7 +221,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
|
@ -228,42 +230,41 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.base
|
.base
|
||||||
.call(avm, context, this, base_proto, args)
|
.call(activation, context, this, base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_setter(
|
fn call_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.base
|
.base
|
||||||
.call_setter(name, value, avm, context, this)
|
.call_setter(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
//TODO: Create a StageObject of some kind
|
//TODO: Create a StageObject of some kind
|
||||||
self.0.read().base.new(avm, context, this, args)
|
self.0.read().base.new(activation, context, this, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().base.delete(avm, gc_context, name)
|
self.0.read().base.delete(activation, gc_context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proto(&self) -> Option<Object<'gc>> {
|
fn proto(&self) -> Option<Object<'gc>> {
|
||||||
|
@ -318,7 +319,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -328,21 +329,21 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.base
|
.base
|
||||||
.add_property_with_case(avm, gc_context, name, get, set, attributes)
|
.add_property_with_case(activation, gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let obj = self.0.read();
|
let obj = self.0.read();
|
||||||
if obj.base.has_property(avm, context, name) {
|
if obj.base.has_property(activation, context, name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let case_sensitive = avm.is_case_sensitive();
|
let case_sensitive = activation.is_case_sensitive();
|
||||||
if obj
|
if obj
|
||||||
.display_object
|
.display_object
|
||||||
.get_child_by_name(name, case_sensitive)
|
.get_child_by_name(name, case_sensitive)
|
||||||
|
@ -364,36 +365,45 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Note that `hasOwnProperty` does NOT return true for child display objects.
|
// Note that `hasOwnProperty` does NOT return true for child display objects.
|
||||||
self.0.read().base.has_own_property(avm, context, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.base
|
||||||
|
.has_own_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().base.has_own_virtual(avm, context, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.base
|
||||||
|
.has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.0.read().base.is_property_enumerable(avm, name)
|
self.0.read().base.is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.0.read().base.is_property_overwritable(avm, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.base
|
||||||
|
.is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
// Keys from the underlying object are listed first, followed by
|
// Keys from the underlying object are listed first, followed by
|
||||||
// child display objects in order from highest depth to lowest depth.
|
// child display objects in order from highest depth to lowest depth.
|
||||||
let obj = self.0.read();
|
let obj = self.0.read();
|
||||||
let mut keys = obj.base.get_keys(avm);
|
let mut keys = obj.base.get_keys(activation);
|
||||||
keys.extend(
|
keys.extend(
|
||||||
obj.display_object
|
obj.display_object
|
||||||
.children()
|
.children()
|
||||||
|
@ -486,13 +496,13 @@ pub struct DisplayProperty<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type DisplayGetter<'gc> = fn(
|
pub type DisplayGetter<'gc> = fn(
|
||||||
&mut Avm1<'gc>,
|
&mut Activation<'_, 'gc>,
|
||||||
&mut UpdateContext<'_, 'gc, '_>,
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
DisplayObject<'gc>,
|
DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>>;
|
) -> Result<Value<'gc>, Error<'gc>>;
|
||||||
|
|
||||||
pub type DisplaySetter<'gc> = fn(
|
pub type DisplaySetter<'gc> = fn(
|
||||||
&mut Avm1<'gc>,
|
&mut Activation<'_, 'gc>,
|
||||||
&mut UpdateContext<'_, 'gc, '_>,
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
DisplayObject<'gc>,
|
DisplayObject<'gc>,
|
||||||
Value<'gc>,
|
Value<'gc>,
|
||||||
|
@ -501,22 +511,22 @@ pub type DisplaySetter<'gc> = fn(
|
||||||
impl<'gc> DisplayProperty<'gc> {
|
impl<'gc> DisplayProperty<'gc> {
|
||||||
pub fn get(
|
pub fn get(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
(self.get)(avm, context, this)
|
(self.get)(activation, context, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(
|
pub fn set(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.set
|
self.set
|
||||||
.map(|f| f(avm, context, this, value))
|
.map(|f| f(activation, context, this, value))
|
||||||
.unwrap_or(Ok(()))
|
.unwrap_or(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,7 +603,7 @@ impl<'gc> DisplayPropertyMap<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn x<'gc>(
|
fn x<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -601,19 +611,19 @@ fn x<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_x<'gc>(
|
fn set_x<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(val) = property_coerce_to_number(avm, context, val)? {
|
if let Some(val) = property_coerce_to_number(activation, context, val)? {
|
||||||
this.set_x(context.gc_context, val);
|
this.set_x(context.gc_context, val);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y<'gc>(
|
fn y<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -621,19 +631,19 @@ fn y<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_y<'gc>(
|
fn set_y<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(val) = property_coerce_to_number(avm, context, val)? {
|
if let Some(val) = property_coerce_to_number(activation, context, val)? {
|
||||||
this.set_y(context.gc_context, val);
|
this.set_y(context.gc_context, val);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn x_scale<'gc>(
|
fn x_scale<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -642,19 +652,19 @@ fn x_scale<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_x_scale<'gc>(
|
fn set_x_scale<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(val) = property_coerce_to_number(avm, context, val)? {
|
if let Some(val) = property_coerce_to_number(activation, context, val)? {
|
||||||
this.set_scale_x(context.gc_context, val / 100.0);
|
this.set_scale_x(context.gc_context, val / 100.0);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y_scale<'gc>(
|
fn y_scale<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -663,19 +673,19 @@ fn y_scale<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_y_scale<'gc>(
|
fn set_y_scale<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(val) = property_coerce_to_number(avm, context, val)? {
|
if let Some(val) = property_coerce_to_number(activation, context, val)? {
|
||||||
this.set_scale_y(context.gc_context, val / 100.0);
|
this.set_scale_y(context.gc_context, val / 100.0);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_frame<'gc>(
|
fn current_frame<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -687,7 +697,7 @@ fn current_frame<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn total_frames<'gc>(
|
fn total_frames<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -699,7 +709,7 @@ fn total_frames<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alpha<'gc>(
|
fn alpha<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -708,19 +718,19 @@ fn alpha<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_alpha<'gc>(
|
fn set_alpha<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(val) = property_coerce_to_number(avm, context, val)? {
|
if let Some(val) = property_coerce_to_number(activation, context, val)? {
|
||||||
this.set_alpha(context.gc_context, val / 100.0);
|
this.set_alpha(context.gc_context, val / 100.0);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visible<'gc>(
|
fn visible<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -729,21 +739,21 @@ fn visible<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_visible<'gc>(
|
fn set_visible<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
// Because this property dates to the era of Flash 4, this is actually coerced to an integer.
|
// Because this property dates to the era of Flash 4, this is actually coerced to an integer.
|
||||||
// `_visible = "false";` coerces to NaN and has no effect.
|
// `_visible = "false";` coerces to NaN and has no effect.
|
||||||
if let Some(n) = property_coerce_to_number(avm, context, val)? {
|
if let Some(n) = property_coerce_to_number(activation, context, val)? {
|
||||||
this.set_visible(context.gc_context, n != 0.0);
|
this.set_visible(context.gc_context, n != 0.0);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn width<'gc>(
|
fn width<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -751,19 +761,19 @@ fn width<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_width<'gc>(
|
fn set_width<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(val) = property_coerce_to_number(avm, context, val)? {
|
if let Some(val) = property_coerce_to_number(activation, context, val)? {
|
||||||
this.set_width(context.gc_context, val);
|
this.set_width(context.gc_context, val);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn height<'gc>(
|
fn height<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -771,19 +781,19 @@ fn height<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_height<'gc>(
|
fn set_height<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(val) = property_coerce_to_number(avm, context, val)? {
|
if let Some(val) = property_coerce_to_number(activation, context, val)? {
|
||||||
this.set_height(context.gc_context, val);
|
this.set_height(context.gc_context, val);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotation<'gc>(
|
fn rotation<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -791,12 +801,12 @@ fn rotation<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_rotation<'gc>(
|
fn set_rotation<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
degrees: Value<'gc>,
|
degrees: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(mut degrees) = property_coerce_to_number(avm, context, degrees)? {
|
if let Some(mut degrees) = property_coerce_to_number(activation, context, degrees)? {
|
||||||
// Normalize into the range of [-180, 180].
|
// Normalize into the range of [-180, 180].
|
||||||
degrees %= 360.0;
|
degrees %= 360.0;
|
||||||
if degrees < -180.0 {
|
if degrees < -180.0 {
|
||||||
|
@ -810,7 +820,7 @@ fn set_rotation<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target<'gc>(
|
fn target<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -818,7 +828,7 @@ fn target<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frames_loaded<'gc>(
|
fn frames_loaded<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -830,7 +840,7 @@ fn frames_loaded<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name<'gc>(
|
fn name<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -838,18 +848,18 @@ fn name<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_name<'gc>(
|
fn set_name<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
mut this: DisplayObject<'gc>,
|
mut this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
let name = val.coerce_to_string(avm, context)?;
|
let name = val.coerce_to_string(activation, context)?;
|
||||||
this.set_name(context.gc_context, &name);
|
this.set_name(context.gc_context, &name);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_target<'gc>(
|
fn drop_target<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -858,7 +868,7 @@ fn drop_target<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn url<'gc>(
|
fn url<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -867,7 +877,7 @@ fn url<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn high_quality<'gc>(
|
fn high_quality<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -876,7 +886,7 @@ fn high_quality<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_high_quality<'gc>(
|
fn set_high_quality<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
_val: Value<'gc>,
|
_val: Value<'gc>,
|
||||||
|
@ -886,7 +896,7 @@ fn set_high_quality<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_rect<'gc>(
|
fn focus_rect<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -895,7 +905,7 @@ fn focus_rect<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_focus_rect<'gc>(
|
fn set_focus_rect<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
_val: Value<'gc>,
|
_val: Value<'gc>,
|
||||||
|
@ -905,7 +915,7 @@ fn set_focus_rect<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sound_buf_time<'gc>(
|
fn sound_buf_time<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -914,7 +924,7 @@ fn sound_buf_time<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_sound_buf_time<'gc>(
|
fn set_sound_buf_time<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
_val: Value<'gc>,
|
_val: Value<'gc>,
|
||||||
|
@ -924,7 +934,7 @@ fn set_sound_buf_time<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quality<'gc>(
|
fn quality<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -933,7 +943,7 @@ fn quality<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_quality<'gc>(
|
fn set_quality<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
_val: Value<'gc>,
|
_val: Value<'gc>,
|
||||||
|
@ -943,7 +953,7 @@ fn set_quality<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn x_mouse<'gc>(
|
fn x_mouse<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -952,7 +962,7 @@ fn x_mouse<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y_mouse<'gc>(
|
fn y_mouse<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: DisplayObject<'gc>,
|
this: DisplayObject<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -961,12 +971,12 @@ fn y_mouse<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn property_coerce_to_number<'gc>(
|
fn property_coerce_to_number<'gc>(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
) -> Result<Option<f64>, Error<'gc>> {
|
) -> Result<Option<f64>, Error<'gc>> {
|
||||||
if value != Value::Undefined && value != Value::Null {
|
if value != Value::Undefined && value != Value::Null {
|
||||||
let n = value.coerce_to_f64(avm, context)?;
|
let n = value.coerce_to_f64(activation, context)?;
|
||||||
if n.is_finite() {
|
if n.is_finite() {
|
||||||
return Ok(Some(n));
|
return Ok(Some(n));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
//! Special object that implements `super`
|
//! Special object that implements `super`
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::object::search_prototype;
|
use crate::avm1::object::search_prototype;
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::script_object::TYPE_OF_OBJECT;
|
use crate::avm1::script_object::TYPE_OF_OBJECT;
|
||||||
use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, Value};
|
use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::display_object::DisplayObject;
|
use crate::display_object::DisplayObject;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
|
@ -45,7 +45,7 @@ impl<'gc> SuperObject<'gc> {
|
||||||
pub fn from_this_and_base_proto(
|
pub fn from_this_and_base_proto(
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Object<'gc>,
|
base_proto: Object<'gc>,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Self, Error<'gc>> {
|
) -> Result<Self, Error<'gc>> {
|
||||||
Ok(Self(GcCell::allocate(
|
Ok(Self(GcCell::allocate(
|
||||||
|
@ -65,14 +65,14 @@ impl<'gc> SuperObject<'gc> {
|
||||||
/// Retrieve the constructor associated with the super proto.
|
/// Retrieve the constructor associated with the super proto.
|
||||||
fn super_constr(
|
fn super_constr(
|
||||||
self,
|
self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Option<Object<'gc>>, Error<'gc>> {
|
) -> Result<Option<Object<'gc>>, Error<'gc>> {
|
||||||
if let Some(super_proto) = self.super_proto() {
|
if let Some(super_proto) = self.super_proto() {
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
super_proto
|
super_proto
|
||||||
.get("__constructor__", avm, context)?
|
.get("__constructor__", activation, context)?
|
||||||
.coerce_to_object(avm, context),
|
.coerce_to_object(activation, context),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -84,7 +84,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -95,7 +95,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
_value: Value<'gc>,
|
_value: Value<'gc>,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
//TODO: What happens if you set `super.__proto__`?
|
//TODO: What happens if you set `super.__proto__`?
|
||||||
|
@ -104,14 +104,20 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_base_proto: Option<Object<'gc>>,
|
_base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(constr) = self.super_constr(avm, context)? {
|
if let Some(constr) = self.super_constr(activation, context)? {
|
||||||
constr.call(avm, context, self.0.read().child, self.super_proto(), args)
|
constr.call(
|
||||||
|
activation,
|
||||||
|
context,
|
||||||
|
self.0.read().child,
|
||||||
|
self.super_proto(),
|
||||||
|
args,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
@ -121,56 +127,55 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let child = self.0.read().child;
|
let child = self.0.read().child;
|
||||||
let super_proto = self.super_proto();
|
let super_proto = self.super_proto();
|
||||||
let (method, base_proto) = search_prototype(super_proto, name, avm, context, child)?;
|
let (method, base_proto) = search_prototype(super_proto, name, activation, context, child)?;
|
||||||
let method = method.resolve(avm, context)?;
|
let method = method;
|
||||||
|
|
||||||
if let Value::Object(_) = method {
|
if let Value::Object(_) = method {
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Super method {} is not callable", name);
|
log::warn!("Super method {} is not callable", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.call(avm, context, child, base_proto, args)
|
method.call(activation, context, child, base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_setter(
|
fn call_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.child
|
.child
|
||||||
.call_setter(name, value, avm, context, this)
|
.call_setter(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
if let Some(proto) = self.proto() {
|
if let Some(proto) = self.proto() {
|
||||||
proto.new(avm, context, this, args)
|
proto.new(activation, context, this, args)
|
||||||
} else {
|
} else {
|
||||||
// TODO: What happens when you `new super` but there's no
|
// TODO: What happens when you `new super` but there's no
|
||||||
// super? Is this code even reachable?!
|
// super? Is this code even reachable?!
|
||||||
self.0.read().child.new(avm, context, this, args)
|
self.0.read().child.new(activation, context, this, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_gc_context: MutationContext<'gc, '_>,
|
_gc_context: MutationContext<'gc, '_>,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -221,7 +226,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_gc_context: MutationContext<'gc, '_>,
|
_gc_context: MutationContext<'gc, '_>,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
_get: Executable<'gc>,
|
_get: Executable<'gc>,
|
||||||
|
@ -233,40 +238,49 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().child.has_property(avm, context, name)
|
self.0.read().child.has_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().child.has_own_property(avm, context, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.child
|
||||||
|
.has_own_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().child.has_own_virtual(avm, context, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.child
|
||||||
|
.has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.0.read().child.is_property_enumerable(avm, name)
|
self.0.read().child.is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.0.read().child.is_property_overwritable(avm, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.child
|
||||||
|
.is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, _avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, _activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::library::Library;
|
||||||
use crate::loader::LoadManager;
|
use crate::loader::LoadManager;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::tag_utils::{SwfMovie, SwfSlice};
|
use crate::tag_utils::{SwfMovie, SwfSlice};
|
||||||
use gc_arena::{rootless_arena, GcCell, MutationContext};
|
use gc_arena::{rootless_arena, MutationContext};
|
||||||
use rand::{rngs::SmallRng, SeedableRng};
|
use rand::{rngs::SmallRng, SeedableRng};
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -21,22 +21,22 @@ use std::sync::Arc;
|
||||||
pub fn with_avm<F>(swf_version: u8, test: F)
|
pub fn with_avm<F>(swf_version: u8, test: F)
|
||||||
where
|
where
|
||||||
F: for<'a, 'gc> FnOnce(
|
F: for<'a, 'gc> FnOnce(
|
||||||
&mut Avm1<'gc>,
|
&mut Activation<'_, 'gc>,
|
||||||
&mut UpdateContext<'a, 'gc, '_>,
|
&mut UpdateContext<'a, 'gc, '_>,
|
||||||
Object<'gc>,
|
Object<'gc>,
|
||||||
) -> Result<(), Error<'gc>>,
|
) -> Result<(), Error<'gc>>,
|
||||||
{
|
{
|
||||||
fn in_the_arena<'gc, F>(swf_version: u8, test: F, gc_context: MutationContext<'gc, '_>)
|
fn in_the_arena<'a, 'gc: 'a, F>(swf_version: u8, test: F, gc_context: MutationContext<'gc, '_>)
|
||||||
where
|
where
|
||||||
F: for<'a> FnOnce(
|
F: FnOnce(
|
||||||
&mut Avm1<'gc>,
|
&mut Activation<'_, 'gc>,
|
||||||
&mut UpdateContext<'a, 'gc, '_>,
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
Object<'gc>,
|
Object<'gc>,
|
||||||
) -> Result<(), Error<'gc>>,
|
) -> Result<(), Error<'gc>>,
|
||||||
{
|
{
|
||||||
let mut avm = Avm1::new(gc_context, swf_version);
|
let mut avm = Avm1::new(gc_context, swf_version);
|
||||||
let swf = Arc::new(SwfMovie::empty(swf_version));
|
let swf = Arc::new(SwfMovie::empty(swf_version));
|
||||||
let mut root: DisplayObject<'_> =
|
let mut root: DisplayObject<'gc> =
|
||||||
MovieClip::new(SwfSlice::empty(swf.clone()), gc_context).into();
|
MovieClip::new(SwfSlice::empty(swf.clone()), gc_context).into();
|
||||||
root.set_depth(gc_context, 0);
|
root.set_depth(gc_context, 0);
|
||||||
let mut levels = BTreeMap::new();
|
let mut levels = BTreeMap::new();
|
||||||
|
@ -77,17 +77,35 @@ where
|
||||||
root.post_instantiation(&mut avm, &mut context, root, None, false);
|
root.post_instantiation(&mut avm, &mut context, root, None, false);
|
||||||
root.set_name(context.gc_context, "");
|
root.set_name(context.gc_context, "");
|
||||||
|
|
||||||
let globals = avm.global_object_cell();
|
fn run_test<'a, 'gc: 'a, F>(
|
||||||
avm.insert_stack_frame(GcCell::allocate(
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
Activation::from_nothing(swf_version, globals, gc_context, root),
|
root: DisplayObject<'gc>,
|
||||||
));
|
test: F,
|
||||||
|
) where
|
||||||
let this = root.object().coerce_to_object(&mut avm, &mut context);
|
F: FnOnce(
|
||||||
|
&mut Activation<'_, 'gc>,
|
||||||
if let Err(e) = test(&mut avm, &mut context, this) {
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
panic!("Encountered exception during test: {}", e);
|
Object<'gc>,
|
||||||
|
) -> Result<(), Error<'gc>>,
|
||||||
|
{
|
||||||
|
let this = root.object().coerce_to_object(activation, context);
|
||||||
|
let result = test(activation, context, this);
|
||||||
|
if let Err(e) = result {
|
||||||
|
panic!("Encountered exception during test: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let globals = avm.global_object_cell();
|
||||||
|
let mut activation = Activation::from_nothing(
|
||||||
|
&mut avm,
|
||||||
|
context.swf.version(),
|
||||||
|
globals,
|
||||||
|
context.gc_context,
|
||||||
|
*context.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
run_test(&mut activation, &mut context, root, test)
|
||||||
}
|
}
|
||||||
|
|
||||||
rootless_arena(|gc_context| in_the_arena(swf_version, test, gc_context))
|
rootless_arena(|gc_context| in_the_arena(swf_version, test, gc_context))
|
||||||
|
@ -100,9 +118,9 @@ macro_rules! test_method {
|
||||||
use $crate::avm1::test_utils::*;
|
use $crate::avm1::test_utils::*;
|
||||||
$(
|
$(
|
||||||
for version in &$versions {
|
for version in &$versions {
|
||||||
with_avm(*version, |avm, context, _root| -> Result<(), Error> {
|
with_avm(*version, |activation, context, _root| -> Result<(), Error> {
|
||||||
let object = $object(avm, context);
|
let object = $object(activation, context);
|
||||||
let function = object.get($name, avm, context)?;
|
let function = object.get($name, activation, context)?;
|
||||||
|
|
||||||
$(
|
$(
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
|
@ -110,7 +128,7 @@ macro_rules! test_method {
|
||||||
$(
|
$(
|
||||||
args.push($arg.into());
|
args.push($arg.into());
|
||||||
)*
|
)*
|
||||||
assert_eq!(function.call(avm, context, object, None, &args)?, $out.into(), "{:?} => {:?} in swf {}", args, $out, version);
|
assert_eq!(function.call(activation, context, object, None, &args)?, $out.into(), "{:?} => {:?} in swf {}", args, $out, version);
|
||||||
)*
|
)*
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,28 +1,18 @@
|
||||||
use crate::avm1::activation::Activation;
|
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::test_utils::with_avm;
|
use crate::avm1::test_utils::with_avm;
|
||||||
use crate::avm1::TObject;
|
use crate::avm1::TObject;
|
||||||
use gc_arena::GcCell;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn locals_into_form_values() {
|
fn locals_into_form_values() {
|
||||||
with_avm(19, |avm, context, _this| -> Result<(), Error> {
|
with_avm(19, |activation, context, _this| -> Result<(), Error> {
|
||||||
let my_activation = Activation::from_nothing(
|
let my_locals = activation.scope().locals().to_owned();
|
||||||
19,
|
|
||||||
avm.global_object_cell(),
|
|
||||||
context.gc_context,
|
|
||||||
*context.levels.get(&0).expect("_level0 in test"),
|
|
||||||
);
|
|
||||||
let my_locals = my_activation.scope().locals().to_owned();
|
|
||||||
|
|
||||||
my_locals
|
my_locals
|
||||||
.set("value1", "string".into(), avm, context)
|
.set("value1", "string".into(), activation, context)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
my_locals.set("value2", 2.0.into(), avm, context).unwrap();
|
my_locals
|
||||||
|
.set("value2", 2.0.into(), activation, context)
|
||||||
avm.insert_stack_frame(GcCell::allocate(context.gc_context, my_activation));
|
.unwrap();
|
||||||
|
let my_local_values = activation.locals_into_form_values(context);
|
||||||
let my_local_values = avm.locals_into_form_values(context);
|
|
||||||
|
|
||||||
assert_eq!(my_local_values.len(), 2);
|
assert_eq!(my_local_values.len(), 2);
|
||||||
assert_eq!(my_local_values.get("value1"), Some(&"string".to_string()));
|
assert_eq!(my_local_values.get("value1"), Some(&"string".to_string()));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::value_object::ValueObject;
|
use crate::avm1::value_object::ValueObject;
|
||||||
use crate::avm1::{Avm1, Object, TObject, UpdateContext};
|
use crate::avm1::{Object, TObject, UpdateContext};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::f64::NAN;
|
use std::f64::NAN;
|
||||||
|
|
||||||
|
@ -157,19 +158,19 @@ impl<'gc> Value<'gc> {
|
||||||
/// * In SWF5 and lower, hexadecimal is unsupported.
|
/// * In SWF5 and lower, hexadecimal is unsupported.
|
||||||
fn primitive_as_number(
|
fn primitive_as_number(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
Value::Undefined if avm.current_swf_version() < 7 => 0.0,
|
Value::Undefined if activation.current_swf_version() < 7 => 0.0,
|
||||||
Value::Null if avm.current_swf_version() < 7 => 0.0,
|
Value::Null if activation.current_swf_version() < 7 => 0.0,
|
||||||
Value::Undefined => NAN,
|
Value::Undefined => NAN,
|
||||||
Value::Null => NAN,
|
Value::Null => NAN,
|
||||||
Value::Bool(false) => 0.0,
|
Value::Bool(false) => 0.0,
|
||||||
Value::Bool(true) => 1.0,
|
Value::Bool(true) => 1.0,
|
||||||
Value::Number(v) => *v,
|
Value::Number(v) => *v,
|
||||||
Value::String(v) => match v.as_str() {
|
Value::String(v) => match v.as_str() {
|
||||||
v if avm.current_swf_version() >= 6 && v.starts_with("0x") => {
|
v if activation.current_swf_version() >= 6 && v.starts_with("0x") => {
|
||||||
let mut n: u32 = 0;
|
let mut n: u32 = 0;
|
||||||
for c in v[2..].bytes() {
|
for c in v[2..].bytes() {
|
||||||
n = n.wrapping_shl(4);
|
n = n.wrapping_shl(4);
|
||||||
|
@ -195,7 +196,7 @@ impl<'gc> Value<'gc> {
|
||||||
}
|
}
|
||||||
f64::from(n as i32)
|
f64::from(n as i32)
|
||||||
}
|
}
|
||||||
v if avm.current_swf_version() >= 6
|
v if activation.current_swf_version() >= 6
|
||||||
&& (v.starts_with('0') || v.starts_with("+0") || v.starts_with("-0"))
|
&& (v.starts_with('0') || v.starts_with("+0") || v.starts_with("-0"))
|
||||||
&& v[1..].bytes().all(|c| c >= b'0' && c <= b'7') =>
|
&& v[1..].bytes().all(|c| c >= b'0' && c <= b'7') =>
|
||||||
{
|
{
|
||||||
|
@ -223,14 +224,14 @@ impl<'gc> Value<'gc> {
|
||||||
/// ECMA-262 2nd edition s. 9.3 ToNumber
|
/// ECMA-262 2nd edition s. 9.3 ToNumber
|
||||||
pub fn coerce_to_f64(
|
pub fn coerce_to_f64(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<f64, Error<'gc>> {
|
) -> Result<f64, Error<'gc>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Value::Object(_) => self
|
Value::Object(_) => self
|
||||||
.to_primitive_num(avm, context)?
|
.to_primitive_num(activation, context)?
|
||||||
.primitive_as_number(avm, context),
|
.primitive_as_number(activation, context),
|
||||||
val => val.primitive_as_number(avm, context),
|
val => val.primitive_as_number(activation, context),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,11 +248,11 @@ impl<'gc> Value<'gc> {
|
||||||
/// return `undefined` rather than yielding a runtime error.
|
/// return `undefined` rather than yielding a runtime error.
|
||||||
pub fn to_primitive_num(
|
pub fn to_primitive_num(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Value::Object(object) => object.call_method("valueOf", &[], avm, context)?,
|
Value::Object(object) => object.call_method("valueOf", &[], activation, context)?,
|
||||||
val => val.to_owned(),
|
val => val.to_owned(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -261,18 +262,18 @@ impl<'gc> Value<'gc> {
|
||||||
pub fn abstract_lt(
|
pub fn abstract_lt(
|
||||||
&self,
|
&self,
|
||||||
other: Value<'gc>,
|
other: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let prim_self = self.to_primitive_num(avm, context)?;
|
let prim_self = self.to_primitive_num(activation, context)?;
|
||||||
let prim_other = other.to_primitive_num(avm, context)?;
|
let prim_other = other.to_primitive_num(activation, context)?;
|
||||||
|
|
||||||
if let (Value::String(a), Value::String(b)) = (&prim_self, &prim_other) {
|
if let (Value::String(a), Value::String(b)) = (&prim_self, &prim_other) {
|
||||||
return Ok(a.to_string().bytes().lt(b.to_string().bytes()).into());
|
return Ok(a.to_string().bytes().lt(b.to_string().bytes()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let num_self = prim_self.primitive_as_number(avm, context);
|
let num_self = prim_self.primitive_as_number(activation, context);
|
||||||
let num_other = prim_other.primitive_as_number(avm, context);
|
let num_other = prim_other.primitive_as_number(activation, context);
|
||||||
|
|
||||||
if num_self.is_nan() || num_other.is_nan() {
|
if num_self.is_nan() || num_other.is_nan() {
|
||||||
return Ok(Value::Undefined);
|
return Ok(Value::Undefined);
|
||||||
|
@ -301,7 +302,7 @@ impl<'gc> Value<'gc> {
|
||||||
pub fn abstract_eq(
|
pub fn abstract_eq(
|
||||||
&self,
|
&self,
|
||||||
other: Value<'gc>,
|
other: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
coerced: bool,
|
coerced: bool,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -327,62 +328,62 @@ impl<'gc> Value<'gc> {
|
||||||
(Value::Bool(a), Value::Bool(b)) => Ok((a == b).into()),
|
(Value::Bool(a), Value::Bool(b)) => Ok((a == b).into()),
|
||||||
(Value::Object(a), Value::Object(b)) => Ok(Object::ptr_eq(*a, *b).into()),
|
(Value::Object(a), Value::Object(b)) => Ok(Object::ptr_eq(*a, *b).into()),
|
||||||
(Value::Object(a), Value::Null) | (Value::Object(a), Value::Undefined) => {
|
(Value::Object(a), Value::Null) | (Value::Object(a), Value::Undefined) => {
|
||||||
Ok(Object::ptr_eq(*a, avm.global_object_cell()).into())
|
Ok(Object::ptr_eq(*a, activation.avm().global_object_cell()).into())
|
||||||
}
|
}
|
||||||
(Value::Null, Value::Object(b)) | (Value::Undefined, Value::Object(b)) => {
|
(Value::Null, Value::Object(b)) | (Value::Undefined, Value::Object(b)) => {
|
||||||
Ok(Object::ptr_eq(*b, avm.global_object_cell()).into())
|
Ok(Object::ptr_eq(*b, activation.avm().global_object_cell()).into())
|
||||||
}
|
}
|
||||||
(Value::Undefined, Value::Null) => Ok(true.into()),
|
(Value::Undefined, Value::Null) => Ok(true.into()),
|
||||||
(Value::Null, Value::Undefined) => Ok(true.into()),
|
(Value::Null, Value::Undefined) => Ok(true.into()),
|
||||||
(Value::Number(_), Value::String(_)) => Ok(self.abstract_eq(
|
(Value::Number(_), Value::String(_)) => Ok(self.abstract_eq(
|
||||||
Value::Number(other.coerce_to_f64(avm, context)?),
|
Value::Number(other.coerce_to_f64(activation, context)?),
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
true,
|
true,
|
||||||
)?),
|
)?),
|
||||||
(Value::String(_), Value::Number(_)) => {
|
(Value::String(_), Value::Number(_)) => {
|
||||||
Ok(Value::Number(self.coerce_to_f64(avm, context)?)
|
Ok(Value::Number(self.coerce_to_f64(activation, context)?)
|
||||||
.abstract_eq(other, avm, context, true)?)
|
.abstract_eq(other, activation, context, true)?)
|
||||||
}
|
}
|
||||||
(Value::Bool(_), _) => Ok(Value::Number(self.coerce_to_f64(avm, context)?)
|
(Value::Bool(_), _) => Ok(Value::Number(self.coerce_to_f64(activation, context)?)
|
||||||
.abstract_eq(other, avm, context, true)?),
|
.abstract_eq(other, activation, context, true)?),
|
||||||
(_, Value::Bool(_)) => Ok(self.abstract_eq(
|
(_, Value::Bool(_)) => Ok(self.abstract_eq(
|
||||||
Value::Number(other.coerce_to_f64(avm, context)?),
|
Value::Number(other.coerce_to_f64(activation, context)?),
|
||||||
avm,
|
activation,
|
||||||
context,
|
context,
|
||||||
true,
|
true,
|
||||||
)?),
|
)?),
|
||||||
(Value::String(_), Value::Object(_)) => {
|
(Value::String(_), Value::Object(_)) => {
|
||||||
let non_obj_other = other.to_primitive_num(avm, context)?;
|
let non_obj_other = other.to_primitive_num(activation, context)?;
|
||||||
if let Value::Object(_) = non_obj_other {
|
if let Value::Object(_) = non_obj_other {
|
||||||
return Ok(false.into());
|
return Ok(false.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self.abstract_eq(non_obj_other, avm, context, true)?)
|
Ok(self.abstract_eq(non_obj_other, activation, context, true)?)
|
||||||
}
|
}
|
||||||
(Value::Number(_), Value::Object(_)) => {
|
(Value::Number(_), Value::Object(_)) => {
|
||||||
let non_obj_other = other.to_primitive_num(avm, context)?;
|
let non_obj_other = other.to_primitive_num(activation, context)?;
|
||||||
if let Value::Object(_) = non_obj_other {
|
if let Value::Object(_) = non_obj_other {
|
||||||
return Ok(false.into());
|
return Ok(false.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self.abstract_eq(non_obj_other, avm, context, true)?)
|
Ok(self.abstract_eq(non_obj_other, activation, context, true)?)
|
||||||
}
|
}
|
||||||
(Value::Object(_), Value::String(_)) => {
|
(Value::Object(_), Value::String(_)) => {
|
||||||
let non_obj_self = self.to_primitive_num(avm, context)?;
|
let non_obj_self = self.to_primitive_num(activation, context)?;
|
||||||
if let Value::Object(_) = non_obj_self {
|
if let Value::Object(_) = non_obj_self {
|
||||||
return Ok(false.into());
|
return Ok(false.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(non_obj_self.abstract_eq(other, avm, context, true)?)
|
Ok(non_obj_self.abstract_eq(other, activation, context, true)?)
|
||||||
}
|
}
|
||||||
(Value::Object(_), Value::Number(_)) => {
|
(Value::Object(_), Value::Number(_)) => {
|
||||||
let non_obj_self = self.to_primitive_num(avm, context)?;
|
let non_obj_self = self.to_primitive_num(activation, context)?;
|
||||||
if let Value::Object(_) = non_obj_self {
|
if let Value::Object(_) = non_obj_self {
|
||||||
return Ok(false.into());
|
return Ok(false.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(non_obj_self.abstract_eq(other, avm, context, true)?)
|
Ok(non_obj_self.abstract_eq(other, activation, context, true)?)
|
||||||
}
|
}
|
||||||
_ => Ok(false.into()),
|
_ => Ok(false.into()),
|
||||||
}
|
}
|
||||||
|
@ -408,10 +409,11 @@ impl<'gc> Value<'gc> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn coerce_to_u16(
|
pub fn coerce_to_u16(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<u16, Error<'gc>> {
|
) -> Result<u16, Error<'gc>> {
|
||||||
self.coerce_to_f64(avm, context).map(f64_to_wrapping_u16)
|
self.coerce_to_f64(activation, context)
|
||||||
|
.map(f64_to_wrapping_u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce a number to an `i16` following the wrapping behavior ECMAScript specifications.
|
/// Coerce a number to an `i16` following the wrapping behavior ECMAScript specifications.
|
||||||
|
@ -420,10 +422,11 @@ impl<'gc> Value<'gc> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn coerce_to_i16(
|
pub fn coerce_to_i16(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<i16, Error<'gc>> {
|
) -> Result<i16, Error<'gc>> {
|
||||||
self.coerce_to_f64(avm, context).map(f64_to_wrapping_i16)
|
self.coerce_to_f64(activation, context)
|
||||||
|
.map(f64_to_wrapping_i16)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce a number to an `i32` following the ECMAScript specifications for `ToInt32`.
|
/// Coerce a number to an `i32` following the ECMAScript specifications for `ToInt32`.
|
||||||
|
@ -433,10 +436,11 @@ impl<'gc> Value<'gc> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn coerce_to_i32(
|
pub fn coerce_to_i32(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<i32, Error<'gc>> {
|
) -> Result<i32, Error<'gc>> {
|
||||||
self.coerce_to_f64(avm, context).map(f64_to_wrapping_i32)
|
self.coerce_to_f64(activation, context)
|
||||||
|
.map(f64_to_wrapping_i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce a number to an `u32` following the ECMAScript specifications for `ToUInt32`.
|
/// Coerce a number to an `u32` following the ECMAScript specifications for `ToUInt32`.
|
||||||
|
@ -445,25 +449,28 @@ impl<'gc> Value<'gc> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn coerce_to_u32(
|
pub fn coerce_to_u32(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<u32, Error<'gc>> {
|
) -> Result<u32, Error<'gc>> {
|
||||||
self.coerce_to_f64(avm, context).map(f64_to_wrapping_u32)
|
self.coerce_to_f64(activation, context)
|
||||||
|
.map(f64_to_wrapping_u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce a value to a string.
|
/// Coerce a value to a string.
|
||||||
pub fn coerce_to_string<'a>(
|
pub fn coerce_to_string<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Cow<'a, str>, Error<'gc>> {
|
) -> Result<Cow<'a, str>, Error<'gc>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Value::Object(object) => match object.call_method("toString", &[], avm, context)? {
|
Value::Object(object) => {
|
||||||
Value::String(s) => Cow::Owned(s),
|
match object.call_method("toString", &[], activation, context)? {
|
||||||
_ => Cow::Borrowed("[type Object]"),
|
Value::String(s) => Cow::Owned(s),
|
||||||
},
|
_ => Cow::Borrowed("[type Object]"),
|
||||||
|
}
|
||||||
|
}
|
||||||
Value::Undefined => {
|
Value::Undefined => {
|
||||||
if avm.current_swf_version() >= 7 {
|
if activation.current_swf_version() >= 7 {
|
||||||
Cow::Borrowed("undefined")
|
Cow::Borrowed("undefined")
|
||||||
} else {
|
} else {
|
||||||
Cow::Borrowed("")
|
Cow::Borrowed("")
|
||||||
|
@ -510,22 +517,22 @@ impl<'gc> Value<'gc> {
|
||||||
|
|
||||||
pub fn coerce_to_object(
|
pub fn coerce_to_object(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Object<'gc> {
|
) -> Object<'gc> {
|
||||||
ValueObject::boxed(avm, context, self.to_owned())
|
ValueObject::boxed(activation, context, self.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(
|
pub fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Value::Object(object) = self {
|
if let Value::Object(object) = self {
|
||||||
object.call(avm, context, this, base_proto, args)
|
object.call(activation, context, this, base_proto, args)
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
@ -593,48 +600,54 @@ pub fn f64_to_wrapping_i32(n: f64) -> i32 {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, FunctionObject};
|
use crate::avm1::function::{Executable, FunctionObject};
|
||||||
use crate::avm1::globals::create_globals;
|
use crate::avm1::globals::create_globals;
|
||||||
use crate::avm1::object::{Object, TObject};
|
use crate::avm1::object::{Object, TObject};
|
||||||
use crate::avm1::return_value::ReturnValue;
|
|
||||||
use crate::avm1::script_object::ScriptObject;
|
use crate::avm1::script_object::ScriptObject;
|
||||||
use crate::avm1::test_utils::with_avm;
|
use crate::avm1::test_utils::with_avm;
|
||||||
use crate::avm1::{Avm1, Value};
|
use crate::avm1::Value;
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use std::f64::{INFINITY, NAN, NEG_INFINITY};
|
use std::f64::{INFINITY, NAN, NEG_INFINITY};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_primitive_num() {
|
fn to_primitive_num() {
|
||||||
with_avm(6, |avm, context, _this| -> Result<(), Error> {
|
with_avm(6, |activation, context, _this| -> Result<(), Error> {
|
||||||
let true_value = Value::Bool(true);
|
let true_value = Value::Bool(true);
|
||||||
let undefined = Value::Undefined;
|
let undefined = Value::Undefined;
|
||||||
let false_value = Value::Bool(false);
|
let false_value = Value::Bool(false);
|
||||||
let null = Value::Null;
|
let null = Value::Null;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
true_value.to_primitive_num(avm, context).unwrap(),
|
true_value.to_primitive_num(activation, context).unwrap(),
|
||||||
true_value
|
true_value
|
||||||
);
|
);
|
||||||
assert_eq!(undefined.to_primitive_num(avm, context).unwrap(), undefined);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
false_value.to_primitive_num(avm, context).unwrap(),
|
undefined.to_primitive_num(activation, context).unwrap(),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
false_value.to_primitive_num(activation, context).unwrap(),
|
||||||
false_value
|
false_value
|
||||||
);
|
);
|
||||||
assert_eq!(null.to_primitive_num(avm, context).unwrap(), null);
|
assert_eq!(null.to_primitive_num(activation, context).unwrap(), null);
|
||||||
|
|
||||||
let (protos, global, _) = create_globals(context.gc_context);
|
let (protos, global, _) = create_globals(context.gc_context);
|
||||||
let vglobal = Value::Object(global);
|
let vglobal = Value::Object(global);
|
||||||
|
|
||||||
assert_eq!(vglobal.to_primitive_num(avm, context).unwrap(), undefined);
|
assert_eq!(
|
||||||
|
vglobal.to_primitive_num(activation, context).unwrap(),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
fn value_of_impl<'gc>(
|
fn value_of_impl<'gc>(
|
||||||
_: &mut Avm1<'gc>,
|
_: &mut Activation<'_, 'gc>,
|
||||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_: Object<'gc>,
|
_: Object<'gc>,
|
||||||
_: &[Value<'gc>],
|
_: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(5.0.into())
|
Ok(5.0.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,7 +667,9 @@ mod test {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Value::Object(o).to_primitive_num(avm, context).unwrap(),
|
Value::Object(o)
|
||||||
|
.to_primitive_num(activation, context)
|
||||||
|
.unwrap(),
|
||||||
Value::Number(5.0)
|
Value::Number(5.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -665,20 +680,20 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::float_cmp)]
|
#[allow(clippy::float_cmp)]
|
||||||
fn to_number_swf7() {
|
fn to_number_swf7() {
|
||||||
with_avm(7, |avm, context, _this| -> Result<(), Error> {
|
with_avm(7, |activation, context, _this| -> Result<(), Error> {
|
||||||
let t = Value::Bool(true);
|
let t = Value::Bool(true);
|
||||||
let u = Value::Undefined;
|
let u = Value::Undefined;
|
||||||
let f = Value::Bool(false);
|
let f = Value::Bool(false);
|
||||||
let n = Value::Null;
|
let n = Value::Null;
|
||||||
|
|
||||||
assert_eq!(t.coerce_to_f64(avm, context).unwrap(), 1.0);
|
assert_eq!(t.coerce_to_f64(activation, context).unwrap(), 1.0);
|
||||||
assert!(u.coerce_to_f64(avm, context).unwrap().is_nan());
|
assert!(u.coerce_to_f64(activation, context).unwrap().is_nan());
|
||||||
assert_eq!(f.coerce_to_f64(avm, context).unwrap(), 0.0);
|
assert_eq!(f.coerce_to_f64(activation, context).unwrap(), 0.0);
|
||||||
assert!(n.coerce_to_f64(avm, context).unwrap().is_nan());
|
assert!(n.coerce_to_f64(activation, context).unwrap().is_nan());
|
||||||
|
|
||||||
let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into());
|
let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into());
|
||||||
|
|
||||||
assert!(bo.coerce_to_f64(avm, context).unwrap().is_nan());
|
assert!(bo.coerce_to_f64(activation, context).unwrap().is_nan());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
@ -687,20 +702,20 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::float_cmp)]
|
#[allow(clippy::float_cmp)]
|
||||||
fn to_number_swf6() {
|
fn to_number_swf6() {
|
||||||
with_avm(6, |avm, context, _this| -> Result<(), Error> {
|
with_avm(6, |activation, context, _this| -> Result<(), Error> {
|
||||||
let t = Value::Bool(true);
|
let t = Value::Bool(true);
|
||||||
let u = Value::Undefined;
|
let u = Value::Undefined;
|
||||||
let f = Value::Bool(false);
|
let f = Value::Bool(false);
|
||||||
let n = Value::Null;
|
let n = Value::Null;
|
||||||
|
|
||||||
assert_eq!(t.coerce_to_f64(avm, context).unwrap(), 1.0);
|
assert_eq!(t.coerce_to_f64(activation, context).unwrap(), 1.0);
|
||||||
assert_eq!(u.coerce_to_f64(avm, context).unwrap(), 0.0);
|
assert_eq!(u.coerce_to_f64(activation, context).unwrap(), 0.0);
|
||||||
assert_eq!(f.coerce_to_f64(avm, context).unwrap(), 0.0);
|
assert_eq!(f.coerce_to_f64(activation, context).unwrap(), 0.0);
|
||||||
assert_eq!(n.coerce_to_f64(avm, context).unwrap(), 0.0);
|
assert_eq!(n.coerce_to_f64(activation, context).unwrap(), 0.0);
|
||||||
|
|
||||||
let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into());
|
let bo = Value::Object(ScriptObject::bare_object(context.gc_context).into());
|
||||||
|
|
||||||
assert_eq!(bo.coerce_to_f64(avm, context).unwrap(), 0.0);
|
assert_eq!(bo.coerce_to_f64(activation, context).unwrap(), 0.0);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
@ -708,27 +723,36 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn abstract_lt_num() {
|
fn abstract_lt_num() {
|
||||||
with_avm(8, |avm, context, _this| -> Result<(), Error> {
|
with_avm(8, |activation, context, _this| -> Result<(), Error> {
|
||||||
let a = Value::Number(1.0);
|
let a = Value::Number(1.0);
|
||||||
let b = Value::Number(2.0);
|
let b = Value::Number(2.0);
|
||||||
|
|
||||||
assert_eq!(a.abstract_lt(b, avm, context).unwrap(), Value::Bool(true));
|
assert_eq!(
|
||||||
|
a.abstract_lt(b, activation, context).unwrap(),
|
||||||
|
Value::Bool(true)
|
||||||
|
);
|
||||||
|
|
||||||
let nan = Value::Number(NAN);
|
let nan = Value::Number(NAN);
|
||||||
assert_eq!(a.abstract_lt(nan, avm, context).unwrap(), Value::Undefined);
|
assert_eq!(
|
||||||
|
a.abstract_lt(nan, activation, context).unwrap(),
|
||||||
|
Value::Undefined
|
||||||
|
);
|
||||||
|
|
||||||
let inf = Value::Number(INFINITY);
|
let inf = Value::Number(INFINITY);
|
||||||
assert_eq!(a.abstract_lt(inf, avm, context).unwrap(), Value::Bool(true));
|
assert_eq!(
|
||||||
|
a.abstract_lt(inf, activation, context).unwrap(),
|
||||||
|
Value::Bool(true)
|
||||||
|
);
|
||||||
|
|
||||||
let neg_inf = Value::Number(NEG_INFINITY);
|
let neg_inf = Value::Number(NEG_INFINITY);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
a.abstract_lt(neg_inf, avm, context).unwrap(),
|
a.abstract_lt(neg_inf, activation, context).unwrap(),
|
||||||
Value::Bool(false)
|
Value::Bool(false)
|
||||||
);
|
);
|
||||||
|
|
||||||
let zero = Value::Number(0.0);
|
let zero = Value::Number(0.0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
a.abstract_lt(zero, avm, context).unwrap(),
|
a.abstract_lt(zero, activation, context).unwrap(),
|
||||||
Value::Bool(false)
|
Value::Bool(false)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -738,36 +762,36 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn abstract_gt_num() {
|
fn abstract_gt_num() {
|
||||||
with_avm(8, |avm, context, _this| -> Result<(), Error> {
|
with_avm(8, |activation, context, _this| -> Result<(), Error> {
|
||||||
let a = Value::Number(1.0);
|
let a = Value::Number(1.0);
|
||||||
let b = Value::Number(2.0);
|
let b = Value::Number(2.0);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.abstract_lt(a.clone(), avm, context).unwrap(),
|
b.abstract_lt(a.clone(), activation, context).unwrap(),
|
||||||
Value::Bool(false)
|
Value::Bool(false)
|
||||||
);
|
);
|
||||||
|
|
||||||
let nan = Value::Number(NAN);
|
let nan = Value::Number(NAN);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
nan.abstract_lt(a.clone(), avm, context).unwrap(),
|
nan.abstract_lt(a.clone(), activation, context).unwrap(),
|
||||||
Value::Undefined
|
Value::Undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
let inf = Value::Number(INFINITY);
|
let inf = Value::Number(INFINITY);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
inf.abstract_lt(a.clone(), avm, context).unwrap(),
|
inf.abstract_lt(a.clone(), activation, context).unwrap(),
|
||||||
Value::Bool(false)
|
Value::Bool(false)
|
||||||
);
|
);
|
||||||
|
|
||||||
let neg_inf = Value::Number(NEG_INFINITY);
|
let neg_inf = Value::Number(NEG_INFINITY);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
neg_inf.abstract_lt(a.clone(), avm, context).unwrap(),
|
neg_inf.abstract_lt(a.clone(), activation, context).unwrap(),
|
||||||
Value::Bool(true)
|
Value::Bool(true)
|
||||||
);
|
);
|
||||||
|
|
||||||
let zero = Value::Number(0.0);
|
let zero = Value::Number(0.0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
zero.abstract_lt(a, avm, context).unwrap(),
|
zero.abstract_lt(a, activation, context).unwrap(),
|
||||||
Value::Bool(true)
|
Value::Bool(true)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -777,11 +801,14 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn abstract_lt_str() {
|
fn abstract_lt_str() {
|
||||||
with_avm(8, |avm, context, _this| -> Result<(), Error> {
|
with_avm(8, |activation, context, _this| -> Result<(), Error> {
|
||||||
let a = Value::String("a".to_owned());
|
let a = Value::String("a".to_owned());
|
||||||
let b = Value::String("b".to_owned());
|
let b = Value::String("b".to_owned());
|
||||||
|
|
||||||
assert_eq!(a.abstract_lt(b, avm, context).unwrap(), Value::Bool(true));
|
assert_eq!(
|
||||||
|
a.abstract_lt(b, activation, context).unwrap(),
|
||||||
|
Value::Bool(true)
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -789,11 +816,14 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn abstract_gt_str() {
|
fn abstract_gt_str() {
|
||||||
with_avm(8, |avm, context, _this| -> Result<(), Error> {
|
with_avm(8, |activation, context, _this| -> Result<(), Error> {
|
||||||
let a = Value::String("a".to_owned());
|
let a = Value::String("a".to_owned());
|
||||||
let b = Value::String("b".to_owned());
|
let b = Value::String("b".to_owned());
|
||||||
|
|
||||||
assert_eq!(b.abstract_lt(a, avm, context).unwrap(), Value::Bool(false));
|
assert_eq!(
|
||||||
|
b.abstract_lt(a, activation, context).unwrap(),
|
||||||
|
Value::Bool(false)
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! Object impl for boxed values
|
//! Object impl for boxed values
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::object::{ObjectPtr, TObject};
|
use crate::avm1::object::{ObjectPtr, TObject};
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
|
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
@ -40,7 +40,7 @@ impl<'gc> ValueObject<'gc> {
|
||||||
/// If a class exists for a given value type, this function automatically
|
/// If a class exists for a given value type, this function automatically
|
||||||
/// selects the correct prototype for it from the system prototypes list.
|
/// selects the correct prototype for it from the system prototypes list.
|
||||||
pub fn boxed(
|
pub fn boxed(
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
) -> Object<'gc> {
|
) -> Object<'gc> {
|
||||||
|
@ -48,9 +48,9 @@ impl<'gc> ValueObject<'gc> {
|
||||||
ob
|
ob
|
||||||
} else {
|
} else {
|
||||||
let proto = match &value {
|
let proto = match &value {
|
||||||
Value::Bool(_) => Some(avm.prototypes.boolean),
|
Value::Bool(_) => Some(activation.avm().prototypes.boolean),
|
||||||
Value::Number(_) => Some(avm.prototypes.number),
|
Value::Number(_) => Some(activation.avm().prototypes.number),
|
||||||
Value::String(_) => Some(avm.prototypes.string),
|
Value::String(_) => Some(activation.avm().prototypes.string),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,16 +65,28 @@ impl<'gc> ValueObject<'gc> {
|
||||||
// Constructor populates the boxed object with the value.
|
// Constructor populates the boxed object with the value.
|
||||||
match &value {
|
match &value {
|
||||||
Value::Bool(_) => {
|
Value::Bool(_) => {
|
||||||
let _ =
|
let _ = crate::avm1::globals::boolean::boolean(
|
||||||
crate::avm1::globals::boolean::boolean(avm, context, obj.into(), &[value]);
|
activation,
|
||||||
|
context,
|
||||||
|
obj.into(),
|
||||||
|
&[value],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Value::Number(_) => {
|
Value::Number(_) => {
|
||||||
let _ =
|
let _ = crate::avm1::globals::number::number(
|
||||||
crate::avm1::globals::number::number(avm, context, obj.into(), &[value]);
|
activation,
|
||||||
|
context,
|
||||||
|
obj.into(),
|
||||||
|
&[value],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Value::String(_) => {
|
Value::String(_) => {
|
||||||
let _ =
|
let _ = crate::avm1::globals::string::string(
|
||||||
crate::avm1::globals::string::string(avm, context, obj.into(), &[value]);
|
activation,
|
||||||
|
context,
|
||||||
|
obj.into(),
|
||||||
|
&[value],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -123,26 +135,29 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.0.read().base.get_local(name, avm, context, this)
|
self.0
|
||||||
|
.read()
|
||||||
|
.base
|
||||||
|
.get_local(name, activation, context, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(
|
fn set(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.0.read().base.set(name, value, avm, context)
|
self.0.read().base.set(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
|
@ -151,27 +166,26 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.base
|
.base
|
||||||
.call(avm, context, this, base_proto, args)
|
.call(activation, context, this, base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_setter(
|
fn call_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.base
|
.base
|
||||||
.call_setter(name, value, avm, context, this)
|
.call_setter(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
|
@ -181,11 +195,11 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().base.delete(avm, gc_context, name)
|
self.0.read().base.delete(activation, gc_context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_property(
|
fn add_property(
|
||||||
|
@ -204,7 +218,7 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -214,7 +228,7 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.base
|
.base
|
||||||
.add_property_with_case(avm, gc_context, name, get, set, attributes)
|
.add_property_with_case(activation, gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_value(
|
fn define_value(
|
||||||
|
@ -258,41 +272,50 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().base.has_property(avm, context, name)
|
self.0.read().base.has_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().base.has_own_property(avm, context, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.base
|
||||||
|
.has_own_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.read().base.has_own_virtual(avm, context, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.base
|
||||||
|
.has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.0.read().base.is_property_overwritable(avm, name)
|
self.0
|
||||||
|
.read()
|
||||||
|
.base
|
||||||
|
.is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.0.read().base.is_property_enumerable(avm, name)
|
self.0.read().base.is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
self.0.read().base.get_keys(avm)
|
self.0.read().base.get_keys(activation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string(&self) -> Cow<str> {
|
fn as_string(&self) -> Cow<str> {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! AVM1 object type to represent the attributes of XML nodes
|
//! AVM1 object type to represent the attributes of XML nodes
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::object::{ObjectPtr, TObject};
|
use crate::avm1::object::{ObjectPtr, TObject};
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
|
|
||||||
use crate::xml::{XMLName, XMLNode};
|
use crate::xml::{XMLName, XMLNode};
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::{Collect, MutationContext};
|
use gc_arena::{Collect, MutationContext};
|
||||||
|
@ -60,7 +60,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
@ -75,61 +75,61 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.node().set_attribute_value(
|
self.node().set_attribute_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
&XMLName::from_str(name),
|
&XMLName::from_str(name),
|
||||||
&value.coerce_to_string(avm, context)?,
|
&value.coerce_to_string(activation, context)?,
|
||||||
);
|
);
|
||||||
self.base().set(name, value, avm, context)
|
self.base().set(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base().call(avm, context, this, base_proto, args)
|
self.base()
|
||||||
|
.call(activation, context, this, base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_setter(
|
fn call_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
self.base().call_setter(name, value, activation, context)
|
||||||
self.base().call_setter(name, value, avm, context, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
//TODO: `new xmlnode.attributes()` returns undefined, not an object
|
//TODO: `new xmlnode.attributes()` returns undefined, not an object
|
||||||
log::warn!("Cannot create new XML Attributes object");
|
log::warn!("Cannot create new XML Attributes object");
|
||||||
Ok(Value::Undefined.coerce_to_object(avm, context))
|
Ok(Value::Undefined.coerce_to_object(activation, context))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.node()
|
self.node()
|
||||||
.delete_attribute(gc_context, &XMLName::from_str(name));
|
.delete_attribute(gc_context, &XMLName::from_str(name));
|
||||||
self.base().delete(avm, gc_context, name)
|
self.base().delete(activation, gc_context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_property(
|
fn add_property(
|
||||||
|
@ -146,7 +146,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -154,7 +154,7 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
|
||||||
attributes: EnumSet<Attribute>,
|
attributes: EnumSet<Attribute>,
|
||||||
) {
|
) {
|
||||||
self.base()
|
self.base()
|
||||||
.add_property_with_case(avm, gc_context, name, get, set, attributes)
|
.add_property_with_case(activation, gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_value(
|
fn define_value(
|
||||||
|
@ -189,16 +189,16 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_property(avm, context, name)
|
self.base().has_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -209,23 +209,23 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_own_virtual(avm, context, name)
|
self.base().has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_overwritable(avm, name)
|
self.base().is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_enumerable(avm, name)
|
self.base().is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
self.base().get_keys(avm)
|
self.base().get_keys(activation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string(&self) -> Cow<str> {
|
fn as_string(&self) -> Cow<str> {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! AVM1 object type to represent the attributes of XML nodes
|
//! AVM1 object type to represent the attributes of XML nodes
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::object::{ObjectPtr, TObject};
|
use crate::avm1::object::{ObjectPtr, TObject};
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
|
|
||||||
use crate::xml::{XMLDocument, XMLNode};
|
use crate::xml::{XMLDocument, XMLNode};
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::{Collect, MutationContext};
|
use gc_arena::{Collect, MutationContext};
|
||||||
|
@ -60,16 +60,19 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(mut node) = self.document().get_node_by_id(name) {
|
if let Some(mut node) = self.document().get_node_by_id(name) {
|
||||||
Ok(node
|
Ok(node
|
||||||
.script_object(context.gc_context, Some(avm.prototypes().xml_node))
|
.script_object(
|
||||||
|
context.gc_context,
|
||||||
|
Some(activation.avm().prototypes().xml_node),
|
||||||
|
)
|
||||||
.into())
|
.into())
|
||||||
} else {
|
} else {
|
||||||
self.base().get_local(name, avm, context, this)
|
self.base().get_local(name, activation, context, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,54 +80,54 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.base().set(name, value, avm, context)
|
self.base().set(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base().call(avm, context, this, base_proto, args)
|
self.base()
|
||||||
|
.call(activation, context, this, base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_setter(
|
fn call_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
self.base().call_setter(name, value, activation, context)
|
||||||
self.base().call_setter(name, value, avm, context, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
//TODO: `new xmlnode.attributes()` returns undefined, not an object
|
//TODO: `new xmlnode.attributes()` returns undefined, not an object
|
||||||
log::warn!("Cannot create new XML Attributes object");
|
log::warn!("Cannot create new XML Attributes object");
|
||||||
Ok(Value::Undefined.coerce_to_object(avm, context))
|
Ok(Value::Undefined.coerce_to_object(activation, context))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().delete(avm, gc_context, name)
|
self.base().delete(activation, gc_context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_property(
|
fn add_property(
|
||||||
|
@ -141,7 +144,7 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -149,7 +152,7 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
|
||||||
attributes: EnumSet<Attribute>,
|
attributes: EnumSet<Attribute>,
|
||||||
) {
|
) {
|
||||||
self.base()
|
self.base()
|
||||||
.add_property_with_case(avm, gc_context, name, get, set, attributes)
|
.add_property_with_case(activation, gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_value(
|
fn define_value(
|
||||||
|
@ -184,42 +187,42 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_property(avm, context, name)
|
self.base().has_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.document().get_node_by_id(name).is_some()
|
self.document().get_node_by_id(name).is_some()
|
||||||
|| self.base().has_own_property(avm, context, name)
|
|| self.base().has_own_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_own_virtual(avm, context, name)
|
self.base().has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_overwritable(avm, name)
|
self.base().is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_enumerable(avm, name)
|
self.base().is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
let mut keys = self.base().get_keys(avm);
|
let mut keys = self.base().get_keys(activation);
|
||||||
keys.extend(self.document().get_node_ids().into_iter());
|
keys.extend(self.document().get_node_ids().into_iter());
|
||||||
keys
|
keys
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! AVM1 object type to represent XML nodes
|
//! AVM1 object type to represent XML nodes
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::Executable;
|
use crate::avm1::function::Executable;
|
||||||
use crate::avm1::object::{ObjectPtr, TObject};
|
use crate::avm1::object::{ObjectPtr, TObject};
|
||||||
use crate::avm1::property::Attribute;
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::return_value::ReturnValue;
|
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, UpdateContext, Value};
|
|
||||||
use crate::xml::{XMLDocument, XMLNode};
|
use crate::xml::{XMLDocument, XMLNode};
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use gc_arena::{Collect, MutationContext};
|
use gc_arena::{Collect, MutationContext};
|
||||||
|
@ -61,49 +61,49 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
|
||||||
fn get_local(
|
fn get_local(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base().get_local(name, avm, context, this)
|
self.base().get_local(name, activation, context, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(
|
fn set(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.base().set(name, value, avm, context)
|
self.base().set(name, value, activation, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
base_proto: Option<Object<'gc>>,
|
base_proto: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
self.base().call(avm, context, this, base_proto, args)
|
self.base()
|
||||||
|
.call(activation, context, this, base_proto, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_setter(
|
fn call_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
) -> Option<Executable<'gc>> {
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
self.base().call_setter(name, value, activation, context)
|
||||||
self.base().call_setter(name, value, avm, context, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(
|
fn new(
|
||||||
&self,
|
&self,
|
||||||
_avm: &mut Avm1<'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
|
@ -113,11 +113,11 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
|
||||||
|
|
||||||
fn delete(
|
fn delete(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().delete(avm, gc_context, name)
|
self.base().delete(activation, gc_context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_property(
|
fn add_property(
|
||||||
|
@ -134,7 +134,7 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
|
||||||
|
|
||||||
fn add_property_with_case(
|
fn add_property_with_case(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
gc_context: MutationContext<'gc, '_>,
|
gc_context: MutationContext<'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
get: Executable<'gc>,
|
get: Executable<'gc>,
|
||||||
|
@ -142,7 +142,7 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
|
||||||
attributes: EnumSet<Attribute>,
|
attributes: EnumSet<Attribute>,
|
||||||
) {
|
) {
|
||||||
self.base()
|
self.base()
|
||||||
.add_property_with_case(avm, gc_context, name, get, set, attributes)
|
.add_property_with_case(activation, gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_value(
|
fn define_value(
|
||||||
|
@ -177,41 +177,41 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
|
||||||
|
|
||||||
fn has_property(
|
fn has_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_property(avm, context, name)
|
self.base().has_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(
|
fn has_own_property(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_own_property(avm, context, name)
|
self.base().has_own_property(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_virtual(
|
fn has_own_virtual(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.base().has_own_virtual(avm, context, name)
|
self.base().has_own_virtual(activation, context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_overwritable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_overwritable(avm, name)
|
self.base().is_property_overwritable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool {
|
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
|
||||||
self.base().is_property_enumerable(avm, name)
|
self.base().is_property_enumerable(activation, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec<String> {
|
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
|
||||||
self.base().get_keys(avm)
|
self.base().get_keys(activation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string(&self) -> Cow<str> {
|
fn as_string(&self) -> Cow<str> {
|
||||||
|
|
|
@ -20,6 +20,7 @@ mod morph_shape;
|
||||||
mod movie_clip;
|
mod movie_clip;
|
||||||
mod text;
|
mod text;
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::events::{ClipEvent, ClipEventResult};
|
use crate::events::{ClipEvent, ClipEventResult};
|
||||||
pub use bitmap::Bitmap;
|
pub use bitmap::Bitmap;
|
||||||
pub use button::Button;
|
pub use button::Button;
|
||||||
|
@ -913,7 +914,7 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>>
|
||||||
|
|
||||||
fn bind_text_field_variables(
|
fn bind_text_field_variables(
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
// Check all unbound text fields to see if they apply to this object.
|
// Check all unbound text fields to see if they apply to this object.
|
||||||
|
@ -921,7 +922,9 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>>
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
let mut len = context.unbound_text_fields.len();
|
let mut len = context.unbound_text_fields.len();
|
||||||
while i < len {
|
while i < len {
|
||||||
if context.unbound_text_fields[i].try_bind_text_field_variable(avm, context, false) {
|
if context.unbound_text_fields[i]
|
||||||
|
.try_bind_text_field_variable(activation, context, false)
|
||||||
|
{
|
||||||
context.unbound_text_fields.swap_remove(i);
|
context.unbound_text_fields.swap_remove(i);
|
||||||
len -= 1;
|
len -= 1;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! `EditText` display object and support code.
|
//! `EditText` display object and support code.
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::globals::text_field::attach_virtual_properties;
|
use crate::avm1::globals::text_field::attach_virtual_properties;
|
||||||
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
|
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
|
||||||
use crate::context::{RenderContext, UpdateContext};
|
use crate::context::{RenderContext, UpdateContext};
|
||||||
|
@ -502,7 +503,7 @@ impl<'gc> EditText<'gc> {
|
||||||
pub fn set_variable(
|
pub fn set_variable(
|
||||||
self,
|
self,
|
||||||
variable: Option<String>,
|
variable: Option<String>,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
// Clear previous binding.
|
// Clear previous binding.
|
||||||
|
@ -526,7 +527,7 @@ impl<'gc> EditText<'gc> {
|
||||||
let _ = self.set_text(text, context);
|
let _ = self.set_text(text, context);
|
||||||
|
|
||||||
self.0.write(context.gc_context).variable = variable;
|
self.0.write(context.gc_context).variable = variable;
|
||||||
self.try_bind_text_field_variable(avm, context, true);
|
self.try_bind_text_field_variable(activation, context, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a base text transform for this `EditText`, to be used for
|
/// Construct a base text transform for this `EditText`, to be used for
|
||||||
|
@ -698,7 +699,7 @@ impl<'gc> EditText<'gc> {
|
||||||
/// This is called when the text field is created, and, if the text field is in the unbound list, anytime a display object is created.
|
/// This is called when the text field is created, and, if the text field is in the unbound list, anytime a display object is created.
|
||||||
pub fn try_bind_text_field_variable(
|
pub fn try_bind_text_field_variable(
|
||||||
self,
|
self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
set_initial_value: bool,
|
set_initial_value: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -714,41 +715,46 @@ impl<'gc> EditText<'gc> {
|
||||||
|
|
||||||
let parent = self.parent().unwrap();
|
let parent = self.parent().unwrap();
|
||||||
|
|
||||||
avm.insert_stack_frame_for_display_object(
|
activation.run_with_child_frame_for_display_object(
|
||||||
parent,
|
parent,
|
||||||
context.swf.header().version,
|
context.swf.header().version,
|
||||||
context,
|
context,
|
||||||
);
|
|activation, context| {
|
||||||
|
if let Ok(Some((object, property))) =
|
||||||
|
activation.resolve_text_field_variable_path(context, parent, &variable)
|
||||||
|
{
|
||||||
|
// If this text field was just created, we immediately propagate the text to the variable (or vice versa).
|
||||||
|
if set_initial_value {
|
||||||
|
// If the property exists on the object, we overwrite the text with the property's value.
|
||||||
|
if object.has_property(activation, context, property) {
|
||||||
|
let value = object.get(property, activation, context).unwrap();
|
||||||
|
let _ = self.set_text(
|
||||||
|
value
|
||||||
|
.coerce_to_string(activation, context)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_owned(),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Otherwise, we initialize the proprty with the text field's text.
|
||||||
|
let _ =
|
||||||
|
object.set(property, self.text().into(), activation, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(Some((object, property))) =
|
if let Some(stage_object) = object.as_stage_object() {
|
||||||
avm.resolve_text_field_variable_path(context, parent, &variable)
|
self.0.write(context.gc_context).bound_stage_object =
|
||||||
{
|
Some(stage_object);
|
||||||
// If this text field was just created, we immediately propagate the text to the variable (or vice versa).
|
stage_object.register_text_field_binding(
|
||||||
if set_initial_value {
|
context.gc_context,
|
||||||
// If the property exists on the object, we overwrite the text with the property's value.
|
self,
|
||||||
if object.has_property(avm, context, property) {
|
property,
|
||||||
let value = object.get(property, avm, context).unwrap();
|
);
|
||||||
let _ = self.set_text(
|
bound = true;
|
||||||
value
|
}
|
||||||
.coerce_to_string(avm, context)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.into_owned(),
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Otherwise, we initialize the proprty with the text field's text.
|
|
||||||
let _ = object.set(property, self.text().into(), avm, context);
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
);
|
||||||
if let Some(stage_object) = object.as_stage_object() {
|
|
||||||
self.0.write(context.gc_context).bound_stage_object = Some(stage_object);
|
|
||||||
stage_object.register_text_field_binding(context.gc_context, self, property);
|
|
||||||
bound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
avm.retire_stack_frame(context, Value::Undefined);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bound
|
bound
|
||||||
|
@ -765,7 +771,7 @@ impl<'gc> EditText<'gc> {
|
||||||
///
|
///
|
||||||
pub fn propagate_text_binding(
|
pub fn propagate_text_binding(
|
||||||
self,
|
self,
|
||||||
avm: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) {
|
) {
|
||||||
if !self.0.read().firing_variable_binding {
|
if !self.0.read().firing_variable_binding {
|
||||||
|
@ -776,7 +782,7 @@ impl<'gc> EditText<'gc> {
|
||||||
let variable_path = variable.to_string();
|
let variable_path = variable.to_string();
|
||||||
drop(variable);
|
drop(variable);
|
||||||
|
|
||||||
if let Ok(Some((object, property))) = avm.resolve_text_field_variable_path(
|
if let Ok(Some((object, property))) = activation.resolve_text_field_variable_path(
|
||||||
context,
|
context,
|
||||||
self.parent().unwrap(),
|
self.parent().unwrap(),
|
||||||
&variable_path,
|
&variable_path,
|
||||||
|
@ -791,13 +797,14 @@ impl<'gc> EditText<'gc> {
|
||||||
|
|
||||||
// Note that this can call virtual setters, even though the opposite direction won't work
|
// Note that this can call virtual setters, even though the opposite direction won't work
|
||||||
// (virtual property changes do not affect the text field)
|
// (virtual property changes do not affect the text field)
|
||||||
avm.insert_stack_frame_for_display_object(
|
activation.run_with_child_frame_for_display_object(
|
||||||
self.parent().unwrap(),
|
self.parent().unwrap(),
|
||||||
context.swf.header().version,
|
context.swf.header().version,
|
||||||
context,
|
context,
|
||||||
|
|activation, context| {
|
||||||
|
let _ = object.set(property, text.into(), activation, context);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
let _ = object.set(property, text.into(), avm, context);
|
|
||||||
avm.retire_stack_frame(context, Value::Undefined);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.0.write(context.gc_context).firing_variable_binding = false;
|
self.0.write(context.gc_context).firing_variable_binding = false;
|
||||||
|
@ -861,12 +868,18 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
||||||
drop(text);
|
drop(text);
|
||||||
|
|
||||||
// If this text field has a variable set, initialize text field binding.
|
// If this text field has a variable set, initialize text field binding.
|
||||||
if !self.try_bind_text_field_variable(avm, context, true) {
|
avm.run_with_stack_frame_for_display_object(
|
||||||
context.unbound_text_fields.push(*self);
|
(*self).into(),
|
||||||
}
|
context.swf.version(),
|
||||||
|
context,
|
||||||
// People can bind to properties of TextFields the same as other display objects.
|
|activation, context| {
|
||||||
self.bind_text_field_variables(avm, context);
|
if !self.try_bind_text_field_variable(activation, context, true) {
|
||||||
|
context.unbound_text_fields.push(*self);
|
||||||
|
}
|
||||||
|
// People can bind to properties of TextFields the same as other display objects.
|
||||||
|
self.bind_text_field_variables(activation, context);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn object(&self) -> Value<'gc> {
|
fn object(&self) -> Value<'gc> {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
|
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
|
||||||
use crate::backend::audio::AudioStreamHandle;
|
use crate::backend::audio::AudioStreamHandle;
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::character::Character;
|
use crate::character::Character;
|
||||||
use crate::context::{ActionType, RenderContext, UpdateContext};
|
use crate::context::{ActionType, RenderContext, UpdateContext};
|
||||||
use crate::display_object::{
|
use crate::display_object::{
|
||||||
|
@ -373,14 +374,12 @@ impl<'gc> MovieClip<'gc> {
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
avm.insert_stack_frame_for_init_action(
|
avm.run_stack_frame_for_init_action(
|
||||||
*context.levels.get(&0).unwrap(),
|
*context.levels.get(&0).unwrap(),
|
||||||
context.swf.header().version,
|
context.swf.header().version,
|
||||||
slice,
|
slice,
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
let frame = avm.current_stack_frame().unwrap();
|
|
||||||
let _ = avm.run_current_frame(context, frame);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -976,10 +975,18 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
return Some(self_node);
|
return Some(self_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
let object = self.object().coerce_to_object(avm, context);
|
let mut activation = Activation::from_nothing(
|
||||||
|
avm,
|
||||||
|
context.swf.version(),
|
||||||
|
avm.global_object_cell(),
|
||||||
|
context.gc_context,
|
||||||
|
*context.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
|
let object = self.object().coerce_to_object(&mut activation, context);
|
||||||
|
|
||||||
if ClipEvent::BUTTON_EVENT_METHODS
|
if ClipEvent::BUTTON_EVENT_METHODS
|
||||||
.iter()
|
.iter()
|
||||||
.any(|handler| object.has_property(avm, context, handler))
|
.any(|handler| object.has_property(&mut activation, context, handler))
|
||||||
{
|
{
|
||||||
return Some(self_node);
|
return Some(self_node);
|
||||||
}
|
}
|
||||||
|
@ -1033,11 +1040,18 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
// If we are running within the AVM, this must be an immediate action.
|
// If we are running within the AVM, this must be an immediate action.
|
||||||
// If we are not, then this must be queued to be ran first-thing
|
// If we are not, then this must be queued to be ran first-thing
|
||||||
if instantiated_from_avm && self.0.read().avm1_constructor.is_some() {
|
if instantiated_from_avm && self.0.read().avm1_constructor.is_some() {
|
||||||
let constructor = self.0.read().avm1_constructor.unwrap();
|
let mut activation = Activation::from_nothing(
|
||||||
|
avm,
|
||||||
|
context.swf.version(),
|
||||||
|
avm.global_object_cell(),
|
||||||
|
context.gc_context,
|
||||||
|
*context.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let constructor = self.0.read().avm1_constructor.unwrap();
|
||||||
if let Ok(prototype) = constructor
|
if let Ok(prototype) = constructor
|
||||||
.get("prototype", avm, context)
|
.get("prototype", &mut activation, context)
|
||||||
.map(|v| v.coerce_to_object(avm, context))
|
.map(|v| v.coerce_to_object(&mut activation, context))
|
||||||
{
|
{
|
||||||
let object: Object<'gc> = StageObject::for_display_object(
|
let object: Object<'gc> = StageObject::for_display_object(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
|
@ -1046,16 +1060,17 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
if let Some(init_object) = init_object {
|
if let Some(init_object) = init_object {
|
||||||
for key in init_object.get_keys(avm) {
|
for key in init_object.get_keys(&mut activation) {
|
||||||
if let Ok(value) = init_object.get(&key, avm, context) {
|
if let Ok(value) = init_object.get(&key, &mut activation, context) {
|
||||||
let _ = object.set(&key, value, avm, context);
|
let _ = object.set(&key, value, &mut activation, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.0.write(context.gc_context).object = Some(object);
|
self.0.write(context.gc_context).object = Some(object);
|
||||||
let _ = constructor.call(avm, context, object, None, &[]);
|
let _ = constructor.call(&mut activation, context, object, None, &[]);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let object = StageObject::for_display_object(
|
let object = StageObject::for_display_object(
|
||||||
|
@ -1064,9 +1079,17 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
Some(context.system_prototypes.movie_clip),
|
Some(context.system_prototypes.movie_clip),
|
||||||
);
|
);
|
||||||
if let Some(init_object) = init_object {
|
if let Some(init_object) = init_object {
|
||||||
for key in init_object.get_keys(avm) {
|
let mut activation = Activation::from_nothing(
|
||||||
if let Ok(value) = init_object.get(&key, avm, context) {
|
avm,
|
||||||
let _ = object.set(&key, value, avm, context);
|
context.swf.version(),
|
||||||
|
avm.global_object_cell(),
|
||||||
|
context.gc_context,
|
||||||
|
*context.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for key in init_object.get_keys(&mut activation) {
|
||||||
|
if let Ok(value) = init_object.get(&key, &mut activation, context) {
|
||||||
|
let _ = object.set(&key, value, &mut activation, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1093,7 +1116,15 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.bind_text_field_variables(avm, context);
|
// If this text field has a variable set, initialize text field binding.
|
||||||
|
avm.run_with_stack_frame_for_display_object(
|
||||||
|
(*self).into(),
|
||||||
|
context.swf.version(),
|
||||||
|
context,
|
||||||
|
|activation, context| {
|
||||||
|
self.bind_text_field_variables(activation, context);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn object(&self) -> Value<'gc> {
|
fn object(&self) -> Value<'gc> {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Classes that store formatting options
|
//! Classes that store formatting options
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value};
|
use crate::avm1::activation::Activation;
|
||||||
|
use crate::avm1::{Object, ScriptObject, TObject, Value};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::html::iterators::TextSpanIter;
|
use crate::html::iterators::TextSpanIter;
|
||||||
use crate::tag_utils::SwfMovie;
|
use crate::tag_utils::SwfMovie;
|
||||||
|
@ -85,57 +86,57 @@ pub struct TextFormat {
|
||||||
fn getstr_from_avm1_object<'gc>(
|
fn getstr_from_avm1_object<'gc>(
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm1: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Option<String>, crate::avm1::error::Error<'gc>> {
|
) -> Result<Option<String>, crate::avm1::error::Error<'gc>> {
|
||||||
Ok(match object.get(name, avm1, uc)? {
|
Ok(match object.get(name, activation, uc)? {
|
||||||
Value::Undefined => None,
|
Value::Undefined => None,
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => Some(v.coerce_to_string(avm1, uc)?.to_string()),
|
v => Some(v.coerce_to_string(activation, uc)?.to_string()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getfloat_from_avm1_object<'gc>(
|
fn getfloat_from_avm1_object<'gc>(
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm1: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Option<f64>, crate::avm1::error::Error<'gc>> {
|
) -> Result<Option<f64>, crate::avm1::error::Error<'gc>> {
|
||||||
Ok(match object.get(name, avm1, uc)? {
|
Ok(match object.get(name, activation, uc)? {
|
||||||
Value::Undefined => None,
|
Value::Undefined => None,
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => Some(v.coerce_to_f64(avm1, uc)?),
|
v => Some(v.coerce_to_f64(activation, uc)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getbool_from_avm1_object<'gc>(
|
fn getbool_from_avm1_object<'gc>(
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm1: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Option<bool>, crate::avm1::error::Error<'gc>> {
|
) -> Result<Option<bool>, crate::avm1::error::Error<'gc>> {
|
||||||
Ok(match object.get(name, avm1, uc)? {
|
Ok(match object.get(name, activation, uc)? {
|
||||||
Value::Undefined => None,
|
Value::Undefined => None,
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => Some(v.as_bool(avm1.current_swf_version())),
|
v => Some(v.as_bool(activation.current_swf_version())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getfloatarray_from_avm1_object<'gc>(
|
fn getfloatarray_from_avm1_object<'gc>(
|
||||||
object: Object<'gc>,
|
object: Object<'gc>,
|
||||||
name: &str,
|
name: &str,
|
||||||
avm1: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Option<Vec<f64>>, crate::avm1::error::Error<'gc>> {
|
) -> Result<Option<Vec<f64>>, crate::avm1::error::Error<'gc>> {
|
||||||
Ok(match object.get(name, avm1, uc)? {
|
Ok(match object.get(name, activation, uc)? {
|
||||||
Value::Undefined => None,
|
Value::Undefined => None,
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => {
|
v => {
|
||||||
let mut output = Vec::new();
|
let mut output = Vec::new();
|
||||||
let v = v.coerce_to_object(avm1, uc);
|
let v = v.coerce_to_object(activation, uc);
|
||||||
|
|
||||||
for i in 0..v.length() {
|
for i in 0..v.length() {
|
||||||
output.push(v.array_element(i).coerce_to_f64(avm1, uc)?);
|
output.push(v.array_element(i).coerce_to_f64(activation, uc)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(output)
|
Some(output)
|
||||||
|
@ -197,37 +198,38 @@ impl TextFormat {
|
||||||
/// Construct a `TextFormat` from an object that is
|
/// Construct a `TextFormat` from an object that is
|
||||||
pub fn from_avm1_object<'gc>(
|
pub fn from_avm1_object<'gc>(
|
||||||
object1: Object<'gc>,
|
object1: Object<'gc>,
|
||||||
avm1: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Self, crate::avm1::error::Error<'gc>> {
|
) -> Result<Self, crate::avm1::error::Error<'gc>> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
font: getstr_from_avm1_object(object1, "font", avm1, uc)?,
|
font: getstr_from_avm1_object(object1, "font", activation, uc)?,
|
||||||
size: getfloat_from_avm1_object(object1, "size", avm1, uc)?,
|
size: getfloat_from_avm1_object(object1, "size", activation, uc)?,
|
||||||
color: getfloat_from_avm1_object(object1, "color", avm1, uc)?
|
color: getfloat_from_avm1_object(object1, "color", activation, uc)?
|
||||||
.map(|v| swf::Color::from_rgb(v as u32, 0xFF)),
|
.map(|v| swf::Color::from_rgb(v as u32, 0xFF)),
|
||||||
align: getstr_from_avm1_object(object1, "align", avm1, uc)?.and_then(|v| {
|
align: getstr_from_avm1_object(object1, "align", activation, uc)?.and_then(|v| match v
|
||||||
match v.to_lowercase().as_str() {
|
.to_lowercase()
|
||||||
"left" => Some(swf::TextAlign::Left),
|
.as_str()
|
||||||
"center" => Some(swf::TextAlign::Center),
|
{
|
||||||
"right" => Some(swf::TextAlign::Right),
|
"left" => Some(swf::TextAlign::Left),
|
||||||
"justify" => Some(swf::TextAlign::Justify),
|
"center" => Some(swf::TextAlign::Center),
|
||||||
_ => None,
|
"right" => Some(swf::TextAlign::Right),
|
||||||
}
|
"justify" => Some(swf::TextAlign::Justify),
|
||||||
|
_ => None,
|
||||||
}),
|
}),
|
||||||
bold: getbool_from_avm1_object(object1, "bold", avm1, uc)?,
|
bold: getbool_from_avm1_object(object1, "bold", activation, uc)?,
|
||||||
italic: getbool_from_avm1_object(object1, "italic", avm1, uc)?,
|
italic: getbool_from_avm1_object(object1, "italic", activation, uc)?,
|
||||||
underline: getbool_from_avm1_object(object1, "underline", avm1, uc)?,
|
underline: getbool_from_avm1_object(object1, "underline", activation, uc)?,
|
||||||
left_margin: getfloat_from_avm1_object(object1, "leftMargin", avm1, uc)?,
|
left_margin: getfloat_from_avm1_object(object1, "leftMargin", activation, uc)?,
|
||||||
right_margin: getfloat_from_avm1_object(object1, "rightMargin", avm1, uc)?,
|
right_margin: getfloat_from_avm1_object(object1, "rightMargin", activation, uc)?,
|
||||||
indent: getfloat_from_avm1_object(object1, "indent", avm1, uc)?,
|
indent: getfloat_from_avm1_object(object1, "indent", activation, uc)?,
|
||||||
block_indent: getfloat_from_avm1_object(object1, "blockIndent", avm1, uc)?,
|
block_indent: getfloat_from_avm1_object(object1, "blockIndent", activation, uc)?,
|
||||||
kerning: getbool_from_avm1_object(object1, "kerning", avm1, uc)?,
|
kerning: getbool_from_avm1_object(object1, "kerning", activation, uc)?,
|
||||||
leading: getfloat_from_avm1_object(object1, "leading", avm1, uc)?,
|
leading: getfloat_from_avm1_object(object1, "leading", activation, uc)?,
|
||||||
letter_spacing: getfloat_from_avm1_object(object1, "letterSpacing", avm1, uc)?,
|
letter_spacing: getfloat_from_avm1_object(object1, "letterSpacing", activation, uc)?,
|
||||||
tab_stops: getfloatarray_from_avm1_object(object1, "tabStops", avm1, uc)?,
|
tab_stops: getfloatarray_from_avm1_object(object1, "tabStops", activation, uc)?,
|
||||||
bullet: getbool_from_avm1_object(object1, "bullet", avm1, uc)?,
|
bullet: getbool_from_avm1_object(object1, "bullet", activation, uc)?,
|
||||||
url: getstr_from_avm1_object(object1, "url", avm1, uc)?,
|
url: getstr_from_avm1_object(object1, "url", activation, uc)?,
|
||||||
target: getstr_from_avm1_object(object1, "target", avm1, uc)?,
|
target: getstr_from_avm1_object(object1, "target", activation, uc)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,21 +352,24 @@ impl TextFormat {
|
||||||
/// Construct a `TextFormat` AVM1 object from this text format object.
|
/// Construct a `TextFormat` AVM1 object from this text format object.
|
||||||
pub fn as_avm1_object<'gc>(
|
pub fn as_avm1_object<'gc>(
|
||||||
&self,
|
&self,
|
||||||
avm1: &mut Avm1<'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
uc: &mut UpdateContext<'_, 'gc, '_>,
|
uc: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Object<'gc>, crate::avm1::error::Error<'gc>> {
|
) -> Result<Object<'gc>, crate::avm1::error::Error<'gc>> {
|
||||||
let object = ScriptObject::object(uc.gc_context, Some(avm1.prototypes().text_format));
|
let object = ScriptObject::object(
|
||||||
|
uc.gc_context,
|
||||||
|
Some(activation.avm().prototypes().text_format),
|
||||||
|
);
|
||||||
|
|
||||||
object.set(
|
object.set(
|
||||||
"font",
|
"font",
|
||||||
self.font.clone().map(|v| v.into()).unwrap_or(Value::Null),
|
self.font.clone().map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"size",
|
"size",
|
||||||
self.size.map(|v| v.into()).unwrap_or(Value::Null),
|
self.size.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
|
@ -373,7 +378,7 @@ impl TextFormat {
|
||||||
.clone()
|
.clone()
|
||||||
.map(|v| (((v.r as u32) << 16) + ((v.g as u32) << 8) + v.b as u32).into())
|
.map(|v| (((v.r as u32) << 16) + ((v.g as u32) << 8) + v.b as u32).into())
|
||||||
.unwrap_or(Value::Null),
|
.unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
|
@ -389,90 +394,91 @@ impl TextFormat {
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.unwrap_or(Value::Null),
|
.unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"bold",
|
"bold",
|
||||||
self.bold.map(|v| v.into()).unwrap_or(Value::Null),
|
self.bold.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"italic",
|
"italic",
|
||||||
self.italic.map(|v| v.into()).unwrap_or(Value::Null),
|
self.italic.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"underline",
|
"underline",
|
||||||
self.underline.map(|v| v.into()).unwrap_or(Value::Null),
|
self.underline.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"leftMargin",
|
"leftMargin",
|
||||||
self.left_margin.map(|v| v.into()).unwrap_or(Value::Null),
|
self.left_margin.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"rightMargin",
|
"rightMargin",
|
||||||
self.right_margin.map(|v| v.into()).unwrap_or(Value::Null),
|
self.right_margin.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"indent",
|
"indent",
|
||||||
self.indent.map(|v| v.into()).unwrap_or(Value::Null),
|
self.indent.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"blockIndent",
|
"blockIndent",
|
||||||
self.block_indent.map(|v| v.into()).unwrap_or(Value::Null),
|
self.block_indent.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"kerning",
|
"kerning",
|
||||||
self.kerning.map(|v| v.into()).unwrap_or(Value::Null),
|
self.kerning.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"leading",
|
"leading",
|
||||||
self.leading.map(|v| v.into()).unwrap_or(Value::Null),
|
self.leading.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"letterSpacing",
|
"letterSpacing",
|
||||||
self.letter_spacing.map(|v| v.into()).unwrap_or(Value::Null),
|
self.letter_spacing.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"bullet",
|
"bullet",
|
||||||
self.bullet.map(|v| v.into()).unwrap_or(Value::Null),
|
self.bullet.map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"url",
|
"url",
|
||||||
self.url.clone().map(|v| v.into()).unwrap_or(Value::Null),
|
self.url.clone().map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
object.set(
|
object.set(
|
||||||
"target",
|
"target",
|
||||||
self.target.clone().map(|v| v.into()).unwrap_or(Value::Null),
|
self.target.clone().map(|v| v.into()).unwrap_or(Value::Null),
|
||||||
avm1,
|
activation,
|
||||||
uc,
|
uc,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(ts) = &self.tab_stops {
|
if let Some(ts) = &self.tab_stops {
|
||||||
let tab_stops = ScriptObject::array(uc.gc_context, Some(avm1.prototypes().array));
|
let tab_stops =
|
||||||
|
ScriptObject::array(uc.gc_context, Some(activation.avm().prototypes().array));
|
||||||
|
|
||||||
tab_stops.set_length(uc.gc_context, ts.len());
|
tab_stops.set_length(uc.gc_context, ts.len());
|
||||||
|
|
||||||
|
@ -480,9 +486,9 @@ impl TextFormat {
|
||||||
tab_stops.set_array_element(index, (*tab).into(), uc.gc_context);
|
tab_stops.set_array_element(index, (*tab).into(), uc.gc_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
object.set("tabStops", tab_stops.into(), avm1, uc)?;
|
object.set("tabStops", tab_stops.into(), activation, uc)?;
|
||||||
} else {
|
} else {
|
||||||
object.set("tabStops", Value::Null, avm1, uc)?;
|
object.set("tabStops", Value::Null, activation, uc)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(object.into())
|
Ok(object.into())
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Management of async loaders
|
//! Management of async loaders
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::{Object, TObject, Value};
|
use crate::avm1::{Object, TObject, Value};
|
||||||
use crate::backend::navigator::OwnedFuture;
|
use crate::backend::navigator::OwnedFuture;
|
||||||
use crate::context::{ActionQueue, ActionType};
|
use crate::context::{ActionQueue, ActionType};
|
||||||
|
@ -314,7 +315,7 @@ impl<'gc> Loader<'gc> {
|
||||||
.replace_with_movie(uc.gc_context, None);
|
.replace_with_movie(uc.gc_context, None);
|
||||||
|
|
||||||
if let Some(broadcaster) = broadcaster {
|
if let Some(broadcaster) = broadcaster {
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
clip,
|
clip,
|
||||||
broadcaster,
|
broadcaster,
|
||||||
NEWEST_PLAYER_VERSION,
|
NEWEST_PLAYER_VERSION,
|
||||||
|
@ -322,7 +323,6 @@ impl<'gc> Loader<'gc> {
|
||||||
"broadcastMessage",
|
"broadcastMessage",
|
||||||
&["onLoadStart".into(), Value::Object(broadcaster)],
|
&["onLoadStart".into(), Value::Object(broadcaster)],
|
||||||
);
|
);
|
||||||
avm.run_stack_till_empty(uc)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -348,7 +348,7 @@ impl<'gc> Loader<'gc> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(broadcaster) = broadcaster {
|
if let Some(broadcaster) = broadcaster {
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
clip,
|
clip,
|
||||||
broadcaster,
|
broadcaster,
|
||||||
NEWEST_PLAYER_VERSION,
|
NEWEST_PLAYER_VERSION,
|
||||||
|
@ -361,7 +361,6 @@ impl<'gc> Loader<'gc> {
|
||||||
length.into(),
|
length.into(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
avm.run_stack_till_empty(uc)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mc = clip
|
let mut mc = clip
|
||||||
|
@ -386,7 +385,7 @@ impl<'gc> Loader<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(broadcaster) = broadcaster {
|
if let Some(broadcaster) = broadcaster {
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
clip,
|
clip,
|
||||||
broadcaster,
|
broadcaster,
|
||||||
NEWEST_PLAYER_VERSION,
|
NEWEST_PLAYER_VERSION,
|
||||||
|
@ -394,7 +393,6 @@ impl<'gc> Loader<'gc> {
|
||||||
"broadcastMessage",
|
"broadcastMessage",
|
||||||
&["onLoadComplete".into(), Value::Object(broadcaster)],
|
&["onLoadComplete".into(), Value::Object(broadcaster)],
|
||||||
);
|
);
|
||||||
avm.run_stack_till_empty(uc)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Loader::Movie { load_complete, .. }) =
|
if let Some(Loader::Movie { load_complete, .. }) =
|
||||||
|
@ -424,7 +422,7 @@ impl<'gc> Loader<'gc> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(broadcaster) = broadcaster {
|
if let Some(broadcaster) = broadcaster {
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
clip,
|
clip,
|
||||||
broadcaster,
|
broadcaster,
|
||||||
NEWEST_PLAYER_VERSION,
|
NEWEST_PLAYER_VERSION,
|
||||||
|
@ -436,7 +434,6 @@ impl<'gc> Loader<'gc> {
|
||||||
"LoadNeverCompleted".into(),
|
"LoadNeverCompleted".into(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
avm.run_stack_till_empty(uc)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Loader::Movie { load_complete, .. }) =
|
if let Some(Loader::Movie { load_complete, .. }) =
|
||||||
|
@ -477,8 +474,15 @@ impl<'gc> Loader<'gc> {
|
||||||
_ => return Err(Error::NotMovieLoader),
|
_ => return Err(Error::NotMovieLoader),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut activation = Activation::from_nothing(
|
||||||
|
avm,
|
||||||
|
uc.swf.version(),
|
||||||
|
avm.global_object_cell(),
|
||||||
|
uc.gc_context,
|
||||||
|
*uc.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
for (k, v) in form_urlencoded::parse(&data) {
|
for (k, v) in form_urlencoded::parse(&data) {
|
||||||
that.set(&k, v.into_owned().into(), avm, uc)?;
|
that.set(&k, v.into_owned().into(), &mut activation, uc)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -562,7 +566,7 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
let object =
|
let object =
|
||||||
node.script_object(uc.gc_context, Some(avm.prototypes().xml_node));
|
node.script_object(uc.gc_context, Some(avm.prototypes().xml_node));
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
active_clip,
|
active_clip,
|
||||||
object,
|
object,
|
||||||
NEWEST_PLAYER_VERSION,
|
NEWEST_PLAYER_VERSION,
|
||||||
|
@ -570,9 +574,8 @@ impl<'gc> Loader<'gc> {
|
||||||
"onHTTPStatus",
|
"onHTTPStatus",
|
||||||
&[200.into()],
|
&[200.into()],
|
||||||
);
|
);
|
||||||
avm.run_stack_till_empty(uc)?;
|
|
||||||
|
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
active_clip,
|
active_clip,
|
||||||
object,
|
object,
|
||||||
NEWEST_PLAYER_VERSION,
|
NEWEST_PLAYER_VERSION,
|
||||||
|
@ -580,7 +583,6 @@ impl<'gc> Loader<'gc> {
|
||||||
"onData",
|
"onData",
|
||||||
&[xmlstring.into()],
|
&[xmlstring.into()],
|
||||||
);
|
);
|
||||||
avm.run_stack_till_empty(uc)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
@ -600,7 +602,7 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
let object =
|
let object =
|
||||||
node.script_object(uc.gc_context, Some(avm.prototypes().xml_node));
|
node.script_object(uc.gc_context, Some(avm.prototypes().xml_node));
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
active_clip,
|
active_clip,
|
||||||
object,
|
object,
|
||||||
NEWEST_PLAYER_VERSION,
|
NEWEST_PLAYER_VERSION,
|
||||||
|
@ -608,9 +610,8 @@ impl<'gc> Loader<'gc> {
|
||||||
"onHTTPStatus",
|
"onHTTPStatus",
|
||||||
&[404.into()],
|
&[404.into()],
|
||||||
);
|
);
|
||||||
avm.run_stack_till_empty(uc)?;
|
|
||||||
|
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
active_clip,
|
active_clip,
|
||||||
object,
|
object,
|
||||||
NEWEST_PLAYER_VERSION,
|
NEWEST_PLAYER_VERSION,
|
||||||
|
@ -618,7 +619,6 @@ impl<'gc> Loader<'gc> {
|
||||||
"onData",
|
"onData",
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
avm.run_stack_till_empty(uc)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::debug::VariableDumper;
|
use crate::avm1::debug::VariableDumper;
|
||||||
use crate::avm1::globals::system::SystemProperties;
|
use crate::avm1::globals::system::SystemProperties;
|
||||||
use crate::avm1::listeners::SystemListener;
|
use crate::avm1::listeners::SystemListener;
|
||||||
use crate::avm1::object::Object;
|
use crate::avm1::object::Object;
|
||||||
use crate::avm1::{Activation, Avm1, TObject, Value};
|
use crate::avm1::{Avm1, TObject, Value};
|
||||||
use crate::backend::input::{InputBackend, MouseCursor};
|
use crate::backend::input::{InputBackend, MouseCursor};
|
||||||
use crate::backend::storage::StorageBackend;
|
use crate::backend::storage::StorageBackend;
|
||||||
use crate::backend::{
|
use crate::backend::{
|
||||||
|
@ -271,11 +272,18 @@ impl Player {
|
||||||
root.set_name(context.gc_context, "");
|
root.set_name(context.gc_context, "");
|
||||||
context.levels.insert(0, root);
|
context.levels.insert(0, root);
|
||||||
|
|
||||||
let object = root.object().coerce_to_object(avm, context);
|
let mut activation = Activation::from_nothing(
|
||||||
|
avm,
|
||||||
|
context.swf.version(),
|
||||||
|
avm.global_object_cell(),
|
||||||
|
context.gc_context,
|
||||||
|
*context.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
|
let object = root.object().coerce_to_object(&mut activation, context);
|
||||||
object.define_value(
|
object.define_value(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
"$version",
|
"$version",
|
||||||
context.system.get_version_string(avm).into(),
|
context.system.get_version_string(&mut activation).into(),
|
||||||
EnumSet::empty(),
|
EnumSet::empty(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -379,21 +387,32 @@ impl Player {
|
||||||
if self.input.is_key_down(KeyCode::Control) && self.input.is_key_down(KeyCode::Alt) {
|
if self.input.is_key_down(KeyCode::Control) && self.input.is_key_down(KeyCode::Alt) {
|
||||||
self.mutate_with_update_context(|avm, context| {
|
self.mutate_with_update_context(|avm, context| {
|
||||||
let mut dumper = VariableDumper::new(" ");
|
let mut dumper = VariableDumper::new(" ");
|
||||||
|
|
||||||
|
let mut activation = Activation::from_nothing(
|
||||||
|
avm,
|
||||||
|
context.swf.version(),
|
||||||
|
avm.global_object_cell(),
|
||||||
|
context.gc_context,
|
||||||
|
*context.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
dumper.print_variables(
|
dumper.print_variables(
|
||||||
"Global Variables:",
|
"Global Variables:",
|
||||||
"_global",
|
"_global",
|
||||||
&avm.global_object_cell(),
|
&activation.avm().global_object_cell(),
|
||||||
avm,
|
&mut activation,
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
let levels = context.levels.clone();
|
let levels = context.levels.clone();
|
||||||
for (level, display_object) in levels {
|
for (level, display_object) in levels {
|
||||||
let object = display_object.object().coerce_to_object(avm, context);
|
let object = display_object
|
||||||
|
.object()
|
||||||
|
.coerce_to_object(&mut activation, context);
|
||||||
dumper.print_variables(
|
dumper.print_variables(
|
||||||
&format!("Level #{}:", level),
|
&format!("Level #{}:", level),
|
||||||
&format!("_level{}", level),
|
&format!("_level{}", level),
|
||||||
&object,
|
&object,
|
||||||
avm,
|
&mut activation,
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -521,7 +540,7 @@ impl Player {
|
||||||
/// Update dragged object, if any.
|
/// Update dragged object, if any.
|
||||||
fn update_drag(&mut self) {
|
fn update_drag(&mut self) {
|
||||||
let mouse_pos = self.mouse_pos;
|
let mouse_pos = self.mouse_pos;
|
||||||
self.mutate_with_update_context(|_avm, context| {
|
self.mutate_with_update_context(|_activation, context| {
|
||||||
if let Some(drag_object) = &mut context.drag_object {
|
if let Some(drag_object) = &mut context.drag_object {
|
||||||
if drag_object.display_object.removed() {
|
if drag_object.display_object.removed() {
|
||||||
// Be sure to clear the drag if the object was removed.
|
// Be sure to clear the drag if the object was removed.
|
||||||
|
@ -608,12 +627,12 @@ impl Player {
|
||||||
/// This should only be called once. Further movie loads should preload the
|
/// This should only be called once. Further movie loads should preload the
|
||||||
/// specific `MovieClip` referenced.
|
/// specific `MovieClip` referenced.
|
||||||
fn preload(&mut self) {
|
fn preload(&mut self) {
|
||||||
self.mutate_with_update_context(|avm, context| {
|
self.mutate_with_update_context(|activation, context| {
|
||||||
let mut morph_shapes = fnv::FnvHashMap::default();
|
let mut morph_shapes = fnv::FnvHashMap::default();
|
||||||
let root = *context.levels.get(&0).expect("root level");
|
let root = *context.levels.get(&0).expect("root level");
|
||||||
root.as_movie_clip()
|
root.as_movie_clip()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.preload(avm, context, &mut morph_shapes);
|
.preload(activation, context, &mut morph_shapes);
|
||||||
|
|
||||||
// Finalize morph shapes.
|
// Finalize morph shapes.
|
||||||
for (id, static_data) in morph_shapes {
|
for (id, static_data) in morph_shapes {
|
||||||
|
@ -719,7 +738,7 @@ impl Player {
|
||||||
match actions.action_type {
|
match actions.action_type {
|
||||||
// DoAction/clip event code
|
// DoAction/clip event code
|
||||||
ActionType::Normal { bytecode } => {
|
ActionType::Normal { bytecode } => {
|
||||||
avm.insert_stack_frame_for_action(
|
avm.run_stack_frame_for_action(
|
||||||
actions.clip,
|
actions.clip,
|
||||||
context.swf.header().version,
|
context.swf.header().version,
|
||||||
bytecode,
|
bytecode,
|
||||||
|
@ -731,41 +750,29 @@ impl Player {
|
||||||
constructor: Some(constructor),
|
constructor: Some(constructor),
|
||||||
events,
|
events,
|
||||||
} => {
|
} => {
|
||||||
avm.insert_stack_frame(GcCell::allocate(
|
let mut activation = Activation::from_nothing(
|
||||||
|
avm,
|
||||||
|
context.swf.version(),
|
||||||
|
avm.global_object_cell(),
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
Activation::from_nothing(
|
*context.levels.get(&0).unwrap(),
|
||||||
context.swf.header().version,
|
);
|
||||||
avm.global_object_cell(),
|
|
||||||
context.gc_context,
|
|
||||||
actions.clip,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
if let Ok(prototype) = constructor
|
if let Ok(prototype) = constructor
|
||||||
.get("prototype", avm, context)
|
.get("prototype", &mut activation, context)
|
||||||
.map(|v| v.coerce_to_object(avm, context))
|
.map(|v| v.coerce_to_object(&mut activation, context))
|
||||||
{
|
{
|
||||||
if let Value::Object(object) = actions.clip.object() {
|
if let Value::Object(object) = actions.clip.object() {
|
||||||
object.set_proto(context.gc_context, Some(prototype));
|
object.set_proto(context.gc_context, Some(prototype));
|
||||||
for event in events {
|
for event in events {
|
||||||
avm.insert_stack_frame_for_action(
|
let _ = activation.run_child_frame_for_action(
|
||||||
actions.clip,
|
actions.clip,
|
||||||
context.swf.header().version,
|
context.swf.header().version,
|
||||||
event,
|
event,
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let _ = avm.run_stack_till_empty(context);
|
|
||||||
|
|
||||||
avm.insert_stack_frame(GcCell::allocate(
|
let _ = constructor.call(&mut activation, context, object, None, &[]);
|
||||||
context.gc_context,
|
|
||||||
Activation::from_nothing(
|
|
||||||
context.swf.header().version,
|
|
||||||
avm.global_object_cell(),
|
|
||||||
context.gc_context,
|
|
||||||
actions.clip,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
let _ = constructor.call(avm, context, object, None, &[]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -775,7 +782,7 @@ impl Player {
|
||||||
events,
|
events,
|
||||||
} => {
|
} => {
|
||||||
for event in events {
|
for event in events {
|
||||||
avm.insert_stack_frame_for_action(
|
avm.run_stack_frame_for_action(
|
||||||
actions.clip,
|
actions.clip,
|
||||||
context.swf.header().version,
|
context.swf.header().version,
|
||||||
event,
|
event,
|
||||||
|
@ -785,7 +792,7 @@ impl Player {
|
||||||
}
|
}
|
||||||
// Event handler method call (e.g. onEnterFrame)
|
// Event handler method call (e.g. onEnterFrame)
|
||||||
ActionType::Method { object, name, args } => {
|
ActionType::Method { object, name, args } => {
|
||||||
avm.insert_stack_frame_for_method(
|
avm.run_stack_frame_for_method(
|
||||||
actions.clip,
|
actions.clip,
|
||||||
object,
|
object,
|
||||||
context.swf.header().version,
|
context.swf.header().version,
|
||||||
|
@ -813,8 +820,6 @@ impl Player {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Execute the stack frame (if any).
|
|
||||||
let _ = avm.run_stack_till_empty(context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -997,10 +1002,18 @@ impl Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush_shared_objects(&mut self) {
|
pub fn flush_shared_objects(&mut self) {
|
||||||
self.update(|avm, update_context| {
|
self.update(|avm, context| {
|
||||||
let shared_objects = update_context.shared_objects.clone();
|
let mut activation = Activation::from_nothing(
|
||||||
|
avm,
|
||||||
|
context.swf.version(),
|
||||||
|
avm.global_object_cell(),
|
||||||
|
context.gc_context,
|
||||||
|
*context.levels.get(&0).unwrap(),
|
||||||
|
);
|
||||||
|
let shared_objects = context.shared_objects.clone();
|
||||||
for so in shared_objects.values() {
|
for so in shared_objects.values() {
|
||||||
let _ = crate::avm1::globals::shared_object::flush(avm, update_context, *so, &[]);
|
let _ =
|
||||||
|
crate::avm1::globals::shared_object::flush(&mut activation, context, *so, &[]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue