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_item;
pub mod convolution_filter;
mod date;
pub(crate) mod date;
pub mod displacement_map_filter;
pub mod drop_shadow_filter;
pub(crate) mod error;
@ -516,7 +516,6 @@ pub struct SystemPrototypes<'gc> {
pub gradient_bevel_filter_constructor: Object<'gc>,
pub gradient_glow_filter: Object<'gc>,
pub gradient_glow_filter_constructor: Object<'gc>,
pub date: Object<'gc>,
pub date_constructor: Object<'gc>,
pub bitmap_data: Object<'gc>,
pub bitmap_data_constructor: Object<'gc>,
@ -589,7 +588,6 @@ pub fn create_globals<'gc>(
function_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);
@ -689,7 +687,7 @@ pub fn create_globals<'gc>(
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 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));
@ -1178,7 +1176,6 @@ pub fn create_globals<'gc>(
gradient_bevel_filter_constructor: gradient_bevel_filter,
gradient_glow_filter: gradient_glow_filter_proto,
gradient_glow_filter_constructor: gradient_glow_filter,
date: date_proto,
date_constructor: date,
bitmap_data: bitmap_data_proto,
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::function::{Executable, FunctionObject};
use crate::avm1::object::shared_object::SharedObject;
use crate::avm1::object::NativeObject;
use crate::avm1::property::Attribute;
use crate::avm1::property_decl::{define_properties_on, Declaration};
use crate::avm1::{Object, ScriptObject, TObject, Value};
@ -82,9 +83,8 @@ fn serialize_value<'gc>(
// TODO: What happens if an exception is thrown here?
let string = xml_node.into_string(activation).unwrap();
Some(AmfValue::XML(string.to_utf8_lossy().into_owned(), true))
} else if let Some(date) = o.as_date_object() {
date.date_time()
.map(|date_time| AmfValue::Date(date_time.timestamp_millis() as f64, None))
} else if let NativeObject::Date(date) = o.native() {
Some(AmfValue::Date(date.read().time(), None))
} else {
let mut object_body = Vec::new();
recursive_serialize(activation, o, &mut object_body);

View File

@ -2,13 +2,13 @@
use crate::avm1::function::{Executable, ExecutionName, ExecutionReason, FunctionObject};
use crate::avm1::globals::color_transform::ColorTransformObject;
use crate::avm1::globals::date::Date;
use crate::avm1::object::array_object::ArrayObject;
use crate::avm1::object::bevel_filter::BevelFilterObject;
use crate::avm1::object::bitmap_data::BitmapDataObject;
use crate::avm1::object::blur_filter::BlurFilterObject;
use crate::avm1::object::color_matrix_filter::ColorMatrixFilterObject;
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::drop_shadow_filter::DropShadowFilterObject;
use crate::avm1::object::glow_filter::GlowFilterObject;
@ -36,7 +36,6 @@ pub mod blur_filter;
pub mod color_matrix_filter;
pub mod convolution_filter;
mod custom_object;
pub mod date_object;
pub mod displacement_map_filter;
pub mod drop_shadow_filter;
pub mod glow_filter;
@ -56,6 +55,7 @@ pub mod xml_object;
#[collect(no_drop)]
pub enum NativeObject<'gc> {
None,
Date(GcCell<'gc, Date>),
ColorTransform(GcCell<'gc, ColorTransformObject>),
TextFormat(GcCell<'gc, TextFormat>),
}
@ -87,7 +87,6 @@ pub enum NativeObject<'gc> {
ConvolutionFilterObject(ConvolutionFilterObject<'gc>),
GradientBevelFilterObject(GradientBevelFilterObject<'gc>),
GradientGlowFilterObject(GradientGlowFilterObject<'gc>),
DateObject(DateObject<'gc>),
BitmapData(BitmapDataObject<'gc>),
}
)]
@ -553,11 +552,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
None
}
/// Get the underlying `DateObject`, if it exists
fn as_date_object(&self) -> Option<DateObject<'gc>> {
None
}
/// Get the underlying `TransformObject`, if it exists
fn as_transform_object(&self) -> Option<TransformObject<'gc>> {
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::function::ExecutionReason;
use crate::avm1::object::value_object::ValueObject;
use crate::avm1::object::NativeObject;
use crate::avm1::{Object, TObject};
use crate::display_object::TDisplayObject;
use crate::ecma_conversions::{
@ -207,7 +208,8 @@ impl<'gc> Value<'gc> {
) -> Result<Value<'gc>, Error<'gc>> {
let result = match self {
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`.
object.call_method(
"toString".into(),