avm1: Rewrite `Date` implementation

The new implementation is simpler, and supports many AVM1 quirks not
supported before.

In addition, migrate `Date` to `NativeObject`.
This commit is contained in:
relrelb 2022-10-04 21:17:56 +03:00 committed by relrelb
parent a59114d569
commit 967ff56e3b
6 changed files with 556 additions and 970 deletions

View File

@ -25,7 +25,7 @@ pub(crate) mod color_transform;
pub(crate) mod context_menu; pub(crate) mod context_menu;
pub(crate) mod context_menu_item; pub(crate) mod context_menu_item;
pub mod convolution_filter; pub mod convolution_filter;
mod date; pub(crate) mod date;
pub mod displacement_map_filter; pub mod displacement_map_filter;
pub mod drop_shadow_filter; pub mod drop_shadow_filter;
pub(crate) mod error; pub(crate) mod error;
@ -516,7 +516,6 @@ pub struct SystemPrototypes<'gc> {
pub gradient_bevel_filter_constructor: Object<'gc>, pub gradient_bevel_filter_constructor: Object<'gc>,
pub gradient_glow_filter: Object<'gc>, pub gradient_glow_filter: Object<'gc>,
pub gradient_glow_filter_constructor: Object<'gc>, pub gradient_glow_filter_constructor: Object<'gc>,
pub date: Object<'gc>,
pub date_constructor: Object<'gc>, pub date_constructor: Object<'gc>,
pub bitmap_data: Object<'gc>, pub bitmap_data: Object<'gc>,
pub bitmap_data_constructor: Object<'gc>, pub bitmap_data_constructor: Object<'gc>,
@ -589,7 +588,6 @@ pub fn create_globals<'gc>(
function_proto, function_proto,
movie_clip_loader_proto, movie_clip_loader_proto,
); );
let date_proto = date::create_proto(gc_context, object_proto, function_proto);
let video_proto = video::create_proto(gc_context, object_proto, function_proto); let video_proto = video::create_proto(gc_context, object_proto, function_proto);
@ -689,7 +687,7 @@ pub fn create_globals<'gc>(
let string = string::create_string_object(gc_context, string_proto, function_proto); let string = string::create_string_object(gc_context, string_proto, function_proto);
let number = number::create_number_object(gc_context, number_proto, function_proto); let number = number::create_number_object(gc_context, number_proto, function_proto);
let boolean = boolean::create_boolean_object(gc_context, boolean_proto, function_proto); let boolean = boolean::create_boolean_object(gc_context, boolean_proto, function_proto);
let date = date::create_date_object(gc_context, date_proto, function_proto); let date = date::create_constructor(gc_context, object_proto, function_proto);
let flash = ScriptObject::new(gc_context, Some(object_proto)); let flash = ScriptObject::new(gc_context, Some(object_proto));
@ -1178,7 +1176,6 @@ pub fn create_globals<'gc>(
gradient_bevel_filter_constructor: gradient_bevel_filter, gradient_bevel_filter_constructor: gradient_bevel_filter,
gradient_glow_filter: gradient_glow_filter_proto, gradient_glow_filter: gradient_glow_filter_proto,
gradient_glow_filter_constructor: gradient_glow_filter, gradient_glow_filter_constructor: gradient_glow_filter,
date: date_proto,
date_constructor: date, date_constructor: date,
bitmap_data: bitmap_data_proto, bitmap_data: bitmap_data_proto,
bitmap_data_constructor: bitmap_data, bitmap_data_constructor: bitmap_data,

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,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::object::shared_object::SharedObject; use crate::avm1::object::shared_object::SharedObject;
use crate::avm1::object::NativeObject;
use crate::avm1::property::Attribute; use crate::avm1::property::Attribute;
use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::property_decl::{define_properties_on, Declaration};
use crate::avm1::{Object, ScriptObject, TObject, Value}; use crate::avm1::{Object, ScriptObject, TObject, Value};
@ -82,9 +83,8 @@ fn serialize_value<'gc>(
// TODO: What happens if an exception is thrown here? // TODO: What happens if an exception is thrown here?
let string = xml_node.into_string(activation).unwrap(); let string = xml_node.into_string(activation).unwrap();
Some(AmfValue::XML(string.to_utf8_lossy().into_owned(), true)) Some(AmfValue::XML(string.to_utf8_lossy().into_owned(), true))
} else if let Some(date) = o.as_date_object() { } else if let NativeObject::Date(date) = o.native() {
date.date_time() Some(AmfValue::Date(date.read().time(), None))
.map(|date_time| AmfValue::Date(date_time.timestamp_millis() as f64, None))
} else { } else {
let mut object_body = Vec::new(); let mut object_body = Vec::new();
recursive_serialize(activation, o, &mut object_body); recursive_serialize(activation, o, &mut object_body);

View File

@ -2,13 +2,13 @@
use crate::avm1::function::{Executable, ExecutionName, ExecutionReason, FunctionObject}; use crate::avm1::function::{Executable, ExecutionName, ExecutionReason, FunctionObject};
use crate::avm1::globals::color_transform::ColorTransformObject; use crate::avm1::globals::color_transform::ColorTransformObject;
use crate::avm1::globals::date::Date;
use crate::avm1::object::array_object::ArrayObject; use crate::avm1::object::array_object::ArrayObject;
use crate::avm1::object::bevel_filter::BevelFilterObject; use crate::avm1::object::bevel_filter::BevelFilterObject;
use crate::avm1::object::bitmap_data::BitmapDataObject; use crate::avm1::object::bitmap_data::BitmapDataObject;
use crate::avm1::object::blur_filter::BlurFilterObject; use crate::avm1::object::blur_filter::BlurFilterObject;
use crate::avm1::object::color_matrix_filter::ColorMatrixFilterObject; use crate::avm1::object::color_matrix_filter::ColorMatrixFilterObject;
use crate::avm1::object::convolution_filter::ConvolutionFilterObject; use crate::avm1::object::convolution_filter::ConvolutionFilterObject;
use crate::avm1::object::date_object::DateObject;
use crate::avm1::object::displacement_map_filter::DisplacementMapFilterObject; use crate::avm1::object::displacement_map_filter::DisplacementMapFilterObject;
use crate::avm1::object::drop_shadow_filter::DropShadowFilterObject; use crate::avm1::object::drop_shadow_filter::DropShadowFilterObject;
use crate::avm1::object::glow_filter::GlowFilterObject; use crate::avm1::object::glow_filter::GlowFilterObject;
@ -36,7 +36,6 @@ pub mod blur_filter;
pub mod color_matrix_filter; pub mod color_matrix_filter;
pub mod convolution_filter; pub mod convolution_filter;
mod custom_object; mod custom_object;
pub mod date_object;
pub mod displacement_map_filter; pub mod displacement_map_filter;
pub mod drop_shadow_filter; pub mod drop_shadow_filter;
pub mod glow_filter; pub mod glow_filter;
@ -56,6 +55,7 @@ pub mod xml_object;
#[collect(no_drop)] #[collect(no_drop)]
pub enum NativeObject<'gc> { pub enum NativeObject<'gc> {
None, None,
Date(GcCell<'gc, Date>),
ColorTransform(GcCell<'gc, ColorTransformObject>), ColorTransform(GcCell<'gc, ColorTransformObject>),
TextFormat(GcCell<'gc, TextFormat>), TextFormat(GcCell<'gc, TextFormat>),
} }
@ -87,7 +87,6 @@ pub enum NativeObject<'gc> {
ConvolutionFilterObject(ConvolutionFilterObject<'gc>), ConvolutionFilterObject(ConvolutionFilterObject<'gc>),
GradientBevelFilterObject(GradientBevelFilterObject<'gc>), GradientBevelFilterObject(GradientBevelFilterObject<'gc>),
GradientGlowFilterObject(GradientGlowFilterObject<'gc>), GradientGlowFilterObject(GradientGlowFilterObject<'gc>),
DateObject(DateObject<'gc>),
BitmapData(BitmapDataObject<'gc>), BitmapData(BitmapDataObject<'gc>),
} }
)] )]
@ -553,11 +552,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
None None
} }
/// Get the underlying `DateObject`, if it exists
fn as_date_object(&self) -> Option<DateObject<'gc>> {
None
}
/// Get the underlying `TransformObject`, if it exists /// Get the underlying `TransformObject`, if it exists
fn as_transform_object(&self) -> Option<TransformObject<'gc>> { fn as_transform_object(&self) -> Option<TransformObject<'gc>> {
None None

View File

@ -1,70 +0,0 @@
use crate::avm1::{Object, ScriptObject, TObject};
use crate::impl_custom_object;
use chrono::{DateTime, Utc};
use gc_arena::{Collect, GcCell, MutationContext};
use std::fmt;
#[derive(Clone, Copy, Collect)]
#[collect(no_drop)]
pub struct DateObject<'gc>(GcCell<'gc, DateObjectData<'gc>>);
#[derive(Collect)]
#[collect(no_drop)]
pub struct DateObjectData<'gc> {
/// The underlying script object.
base: ScriptObject<'gc>,
/// The DateTime represented by this object
#[collect(require_static)]
date_time: Option<DateTime<Utc>>,
}
impl fmt::Debug for DateObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let this = self.0.read();
f.debug_struct("DateObject")
.field("date_time", &this.date_time)
.finish()
}
}
impl<'gc> DateObject<'gc> {
pub fn empty(
gc_context: MutationContext<'gc, '_>,
proto: Option<Object<'gc>>,
) -> DateObject<'gc> {
Self::with_date_time(gc_context, proto, None)
}
pub fn with_date_time(
gc_context: MutationContext<'gc, '_>,
proto: Option<Object<'gc>>,
date_time: Option<DateTime<Utc>>,
) -> DateObject<'gc> {
DateObject(GcCell::allocate(
gc_context,
DateObjectData {
base: ScriptObject::new(gc_context, proto),
date_time,
},
))
}
pub fn date_time(self) -> Option<DateTime<Utc>> {
self.0.read().date_time
}
pub fn set_date_time(
self,
gc_context: MutationContext<'gc, '_>,
date_time: Option<DateTime<Utc>>,
) {
self.0.write(gc_context).date_time = date_time;
}
}
impl<'gc> TObject<'gc> for DateObject<'gc> {
impl_custom_object!(base {
bare_object(as_date_object -> DateObject::empty);
});
}

View File

@ -2,6 +2,7 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::function::ExecutionReason; use crate::avm1::function::ExecutionReason;
use crate::avm1::object::value_object::ValueObject; use crate::avm1::object::value_object::ValueObject;
use crate::avm1::object::NativeObject;
use crate::avm1::{Object, TObject}; use crate::avm1::{Object, TObject};
use crate::display_object::TDisplayObject; use crate::display_object::TDisplayObject;
use crate::ecma_conversions::{ use crate::ecma_conversions::{
@ -207,7 +208,8 @@ impl<'gc> Value<'gc> {
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let result = match self { let result = match self {
Value::Object(object) => { Value::Object(object) => {
let val = if activation.swf_version() > 5 && object.as_date_object().is_some() { let is_date = matches!(object.native(), NativeObject::Date(_));
let val = if activation.swf_version() > 5 && is_date {
// In SWFv6 and higher, Date objects call `toString`. // In SWFv6 and higher, Date objects call `toString`.
object.call_method( object.call_method(
"toString".into(), "toString".into(),