diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index 698128483b3e..302086d05ea4 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -1470,12 +1470,9 @@ impl<'a, 'gc> Activation<'a, 'gc> { // InitArray pops no args and pushes undefined if num_elements is out of range. Value::Undefined } else { - ArrayObject::new( - self.gc(), - self.context.avm1.prototypes().array, - (0..num_elements as i32).map(|_| self.context.avm1.pop()), - ) - .into() + ArrayObject::builder(self) + .with((0..num_elements as i32).map(|_| self.context.avm1.pop())) + .into() }; self.context.avm1.push(result); diff --git a/core/src/avm1/flv.rs b/core/src/avm1/flv.rs index fdb21a7c469d..8850ea37d4bc 100644 --- a/core/src/avm1/flv.rs +++ b/core/src/avm1/flv.rs @@ -39,12 +39,9 @@ fn avm1_array_from_flv_values<'gc>( activation: &mut Activation<'_, 'gc>, values: Vec, ) -> Avm1Value<'gc> { - ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - values.iter().map(|v| v.clone().to_avm1_value(activation)), - ) - .into() + ArrayObject::builder(activation) + .with(values.iter().map(|v| v.clone().to_avm1_value(activation))) + .into() } pub trait FlvValueAvm1Ext<'gc> { diff --git a/core/src/avm1/function.rs b/core/src/avm1/function.rs index 1e2cd15b2a92..fbd04fea8c54 100644 --- a/core/src/avm1/function.rs +++ b/core/src/avm1/function.rs @@ -194,11 +194,7 @@ impl<'gc> Avm1Function<'gc> { return; } - let arguments = ArrayObject::new( - frame.gc(), - frame.context.avm1.prototypes().array, - args.iter().cloned(), - ); + let arguments = ArrayObject::builder(frame).with(args.iter().cloned()); arguments.define_value( frame.gc(), diff --git a/core/src/avm1/globals/array.rs b/core/src/avm1/globals/array.rs index 2bb617a59e9f..f4d5d348fe15 100644 --- a/core/src/avm1/globals/array.rs +++ b/core/src/avm1/globals/array.rs @@ -93,12 +93,9 @@ pub fn constructor<'gc>( array.set_length(activation, length.clamp_to_i32())?; Ok(array.into()) } else { - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - args.iter().cloned(), - ) - .into()) + Ok(ArrayObject::builder(activation) + .with(args.iter().cloned()) + .into()) } } @@ -302,12 +299,9 @@ pub fn slice<'gc>( make_index_absolute(end.coerce_to_i32(activation)?, length) }; - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - (start..end).map(|i| this.get_element(activation, i)), - ) - .into()) + Ok(ArrayObject::builder(activation) + .with((start..end).map(|i| this.get_element(activation, i))) + .into()) } pub fn splice<'gc>( @@ -367,12 +361,9 @@ pub fn splice<'gc>( } this.set_length(activation, length - delete_count + items.len() as i32)?; - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - result_elements, - ) - .into()) + Ok(ArrayObject::builder(activation) + .with(result_elements) + .into()) } pub fn concat<'gc>( @@ -402,12 +393,7 @@ pub fn concat<'gc>( elements.push(value); } } - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - elements, - ) - .into()) + Ok(ArrayObject::builder(activation).with(elements).into()) } pub fn to_string<'gc>( @@ -647,12 +633,9 @@ fn sort_internal<'gc>( if options.contains(SortOptions::RETURN_INDEXED_ARRAY) { // Array.RETURNINDEXEDARRAY returns an array containing the sorted indices, and does not modify // the original array. - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - elements.into_iter().map(|(index, _)| index.into()), - ) - .into()) + Ok(ArrayObject::builder(activation) + .with(elements.into_iter().map(|(index, _)| index.into())) + .into()) } else { // Standard sort modifies the original array, and returns it. // AS2 reference incorrectly states this returns nothing, but it returns the original array, sorted. @@ -738,7 +721,7 @@ pub fn create_proto<'gc>( proto: Object<'gc>, fn_proto: Object<'gc>, ) -> Object<'gc> { - let array = ArrayObject::empty_with_proto(context.gc(), proto); + let array = ArrayObject::builder_with_proto(context, proto).with([]); let object = array.raw_script_object(); define_properties_on(PROTO_DECLS, context, object, fn_proto); object.into() diff --git a/core/src/avm1/globals/as_broadcaster.rs b/core/src/avm1/globals/as_broadcaster.rs index 056e8357f176..eca5da6623ab 100644 --- a/core/src/avm1/globals/as_broadcaster.rs +++ b/core/src/avm1/globals/as_broadcaster.rs @@ -8,7 +8,7 @@ use crate::avm1::property::Attribute; use crate::avm1::property_decl::Declaration; use crate::avm1::{Activation, ArrayObject, Object, ScriptObject, Value}; use crate::string::{AvmString, StringContext}; -use gc_arena::{Collect, Mutation}; +use gc_arena::Collect; const OBJECT_DECLS: &[Declaration] = declare_properties! { "initialize" => method(initialize; DONT_ENUM | DONT_DELETE); @@ -62,11 +62,11 @@ pub struct BroadcasterFunctions<'gc> { impl<'gc> BroadcasterFunctions<'gc> { pub fn initialize( self, - gc_context: &Mutation<'gc>, + context: &StringContext<'gc>, broadcaster: Object<'gc>, array_proto: Object<'gc>, ) { - initialize_internal(gc_context, broadcaster, self, array_proto); + initialize_internal(context, broadcaster, self, array_proto); } } @@ -187,7 +187,7 @@ fn initialize<'gc>( if let Some(val) = args.get(0) { let broadcaster = val.coerce_to_object(activation); initialize_internal( - activation.gc(), + &activation.context.strings, broadcaster, activation.context.avm1.broadcaster_functions(), activation.context.avm1.prototypes().array, @@ -197,31 +197,33 @@ fn initialize<'gc>( } fn initialize_internal<'gc>( - gc_context: &Mutation<'gc>, + context: &StringContext<'gc>, broadcaster: Object<'gc>, functions: BroadcasterFunctions<'gc>, array_proto: Object<'gc>, ) { broadcaster.define_value( - gc_context, + context.gc(), "_listeners", - ArrayObject::empty_with_proto(gc_context, array_proto).into(), + ArrayObject::builder_with_proto(context, array_proto) + .with([]) + .into(), Attribute::DONT_ENUM, ); broadcaster.define_value( - gc_context, + context.gc(), "addListener", functions.add_listener.into(), Attribute::DONT_DELETE | Attribute::DONT_ENUM, ); broadcaster.define_value( - gc_context, + context.gc(), "removeListener", functions.remove_listener.into(), Attribute::DONT_DELETE | Attribute::DONT_ENUM, ); broadcaster.define_value( - gc_context, + context.gc(), "broadcastMessage", functions.broadcast_message.into(), Attribute::DONT_DELETE | Attribute::DONT_ENUM, diff --git a/core/src/avm1/globals/button.rs b/core/src/avm1/globals/button.rs index 270293c5450b..f19d3ed152d4 100644 --- a/core/src/avm1/globals/button.rs +++ b/core/src/avm1/globals/button.rs @@ -95,14 +95,13 @@ fn filters<'gc>( this: Avm1Button<'gc>, activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - this.filters() - .into_iter() - .map(|filter| bitmap_filter::filter_to_avm1(activation, filter)), - ) - .into()) + Ok(ArrayObject::builder(activation) + .with( + this.filters() + .into_iter() + .map(|filter| bitmap_filter::filter_to_avm1(activation, filter)), + ) + .into()) } fn set_filters<'gc>( diff --git a/core/src/avm1/globals/color_matrix_filter.rs b/core/src/avm1/globals/color_matrix_filter.rs index e364bf68678a..9b4c08a00394 100644 --- a/core/src/avm1/globals/color_matrix_filter.rs +++ b/core/src/avm1/globals/color_matrix_filter.rs @@ -65,12 +65,9 @@ impl<'gc> ColorMatrixFilter<'gc> { } fn matrix(&self, activation: &mut Activation<'_, 'gc>) -> Value<'gc> { - ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - self.0.read().matrix.iter().map(|&v| v.into()), - ) - .into() + ArrayObject::builder(activation) + .with(self.0.read().matrix.iter().map(|&v| v.into())) + .into() } fn set_matrix( diff --git a/core/src/avm1/globals/convolution_filter.rs b/core/src/avm1/globals/convolution_filter.rs index efaafa88285f..425cfe50ed1d 100644 --- a/core/src/avm1/globals/convolution_filter.rs +++ b/core/src/avm1/globals/convolution_filter.rs @@ -5,7 +5,6 @@ use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::object::NativeObject; use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::{Activation, ArrayObject, Error, Object, ScriptObject, TObject, Value}; -use crate::context::UpdateContext; use crate::string::StringContext; use gc_arena::{Collect, GcCell, Mutation}; use std::ops::Deref; @@ -167,12 +166,8 @@ impl<'gc> ConvolutionFilter<'gc> { Ok(()) } - fn matrix(&self, context: &mut UpdateContext<'gc>) -> ArrayObject<'gc> { - ArrayObject::new( - context.gc(), - context.avm1.prototypes().array, - self.0.read().matrix.iter().map(|&x| x.into()), - ) + fn matrix(&self, activation: &Activation<'_, 'gc>) -> ArrayObject<'gc> { + ArrayObject::builder(activation).with(self.0.read().matrix.iter().map(|&x| x.into())) } fn set_matrix( @@ -366,7 +361,7 @@ fn method<'gc>( this.set_matrix_y(activation, args.get(0))?; Value::Undefined } - GET_MATRIX => this.matrix(activation.context).into(), + GET_MATRIX => this.matrix(activation).into(), SET_MATRIX => { this.set_matrix(activation, args.get(0))?; Value::Undefined diff --git a/core/src/avm1/globals/file_reference.rs b/core/src/avm1/globals/file_reference.rs index 32bbbafe50b9..fa01ea50ef42 100644 --- a/core/src/avm1/globals/file_reference.rs +++ b/core/src/avm1/globals/file_reference.rs @@ -442,7 +442,7 @@ pub fn create_constructor<'gc>( ) -> Object<'gc> { let file_reference_proto = ScriptObject::new(context.gc(), Some(proto)); define_properties_on(PROTO_DECLS, context, file_reference_proto, fn_proto); - broadcaster_functions.initialize(context.gc(), file_reference_proto.into(), array_proto); + broadcaster_functions.initialize(context, file_reference_proto.into(), array_proto); let constructor = FunctionObject::constructor( context.gc(), Executable::Native(constructor), diff --git a/core/src/avm1/globals/gradient_filter.rs b/core/src/avm1/globals/gradient_filter.rs index 7801567183d9..0aa0d468f126 100644 --- a/core/src/avm1/globals/gradient_filter.rs +++ b/core/src/avm1/globals/gradient_filter.rs @@ -6,7 +6,6 @@ use crate::avm1::globals::bevel_filter::BevelFilterType; use crate::avm1::object::NativeObject; use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::{Activation, ArrayObject, Error, Object, ScriptObject, TObject, Value}; -use crate::context::UpdateContext; use crate::string::StringContext; use gc_arena::{Collect, GcCell, Mutation}; use ruffle_macros::istr; @@ -165,11 +164,9 @@ impl<'gc> GradientFilter<'gc> { Ok(()) } - fn colors(&self, context: &mut UpdateContext<'gc>) -> ArrayObject<'gc> { + fn colors(&self, activation: &Activation<'_, 'gc>) -> ArrayObject<'gc> { let read = self.0.read(); - ArrayObject::new( - context.gc(), - context.avm1.prototypes().array, + ArrayObject::builder(activation).with( read.colors[..read.num_colors] .iter() .map(|r| r.color.to_rgb().into()), @@ -203,11 +200,9 @@ impl<'gc> GradientFilter<'gc> { Ok(()) } - fn alphas(&self, context: &mut UpdateContext<'gc>) -> ArrayObject<'gc> { + fn alphas(&self, activation: &Activation<'_, 'gc>) -> ArrayObject<'gc> { let read = self.0.read(); - ArrayObject::new( - context.gc(), - context.avm1.prototypes().array, + ArrayObject::builder(activation).with( read.colors[..read.num_colors] .iter() .map(|r| (f64::from(r.color.a) / 255.0).into()), @@ -243,11 +238,9 @@ impl<'gc> GradientFilter<'gc> { Ok(()) } - fn ratios(&self, context: &mut UpdateContext<'gc>) -> ArrayObject<'gc> { + fn ratios(&self, activation: &Activation<'_, 'gc>) -> ArrayObject<'gc> { let read = self.0.read(); - ArrayObject::new( - context.gc(), - context.avm1.prototypes().array, + ArrayObject::builder(activation).with( read.colors[..read.num_colors] .iter() .map(|r| r.ratio.into()), @@ -474,17 +467,17 @@ fn method<'gc>( this.set_angle(activation, args.get(0))?; Value::Undefined } - GET_COLORS => this.colors(activation.context).into(), + GET_COLORS => this.colors(activation).into(), SET_COLORS => { this.set_colors(activation, args.get(0))?; Value::Undefined } - GET_ALPHAS => this.alphas(activation.context).into(), + GET_ALPHAS => this.alphas(activation).into(), SET_ALPHAS => { this.set_alphas(activation, args.get(0))?; Value::Undefined } - GET_RATIOS => this.ratios(activation.context).into(), + GET_RATIOS => this.ratios(activation).into(), SET_RATIOS => { this.set_ratios(activation, args.get(0))?; Value::Undefined diff --git a/core/src/avm1/globals/key.rs b/core/src/avm1/globals/key.rs index 1d3fe2fae041..aba3d4348442 100644 --- a/core/src/avm1/globals/key.rs +++ b/core/src/avm1/globals/key.rs @@ -89,7 +89,7 @@ pub fn create_key_object<'gc>( array_proto: Object<'gc>, ) -> Object<'gc> { let key = ScriptObject::new(context.gc(), Some(proto)); - broadcaster_functions.initialize(context.gc(), key.into(), array_proto); + broadcaster_functions.initialize(context, key.into(), array_proto); define_properties_on(OBJECT_DECLS, context, key, fn_proto); key.into() } diff --git a/core/src/avm1/globals/mouse.rs b/core/src/avm1/globals/mouse.rs index 33b4e5f97123..c2fdfe8fa40d 100644 --- a/core/src/avm1/globals/mouse.rs +++ b/core/src/avm1/globals/mouse.rs @@ -38,7 +38,7 @@ pub fn create_mouse_object<'gc>( array_proto: Object<'gc>, ) -> Object<'gc> { let mouse = ScriptObject::new(context.gc(), Some(proto)); - broadcaster_functions.initialize(context.gc(), mouse.into(), array_proto); + broadcaster_functions.initialize(context, mouse.into(), array_proto); define_properties_on(OBJECT_DECLS, context, mouse, fn_proto); mouse.into() } diff --git a/core/src/avm1/globals/movie_clip.rs b/core/src/avm1/globals/movie_clip.rs index 6c05f5afc975..5d647480040a 100644 --- a/core/src/avm1/globals/movie_clip.rs +++ b/core/src/avm1/globals/movie_clip.rs @@ -1784,14 +1784,13 @@ fn filters<'gc>( this: MovieClip<'gc>, activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - this.filters() - .into_iter() - .map(|filter| bitmap_filter::filter_to_avm1(activation, filter)), - ) - .into()) + Ok(ArrayObject::builder(activation) + .with( + this.filters() + .into_iter() + .map(|filter| bitmap_filter::filter_to_avm1(activation, filter)), + ) + .into()) } fn set_filters<'gc>( diff --git a/core/src/avm1/globals/movie_clip_loader.rs b/core/src/avm1/globals/movie_clip_loader.rs index 002a88844649..9ec6eb5a1358 100644 --- a/core/src/avm1/globals/movie_clip_loader.rs +++ b/core/src/avm1/globals/movie_clip_loader.rs @@ -24,11 +24,7 @@ pub fn constructor<'gc>( this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - let listeners = ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - [this.into()], - ); + let listeners = ArrayObject::builder(activation).with([this.into()]); this.define_value( activation.gc(), "_listeners", @@ -170,7 +166,7 @@ pub fn create_proto<'gc>( broadcaster_functions: BroadcasterFunctions<'gc>, ) -> Object<'gc> { let mcl_proto = ScriptObject::new(context.gc(), Some(proto)); - broadcaster_functions.initialize(context.gc(), mcl_proto.into(), array_proto); + broadcaster_functions.initialize(context, mcl_proto.into(), array_proto); define_properties_on(PROTO_DECLS, context, mcl_proto, fn_proto); mcl_proto.into() } diff --git a/core/src/avm1/globals/selection.rs b/core/src/avm1/globals/selection.rs index 1ca5ed58dc7f..977bca6c8ead 100644 --- a/core/src/avm1/globals/selection.rs +++ b/core/src/avm1/globals/selection.rs @@ -147,7 +147,7 @@ pub fn create_selection_object<'gc>( array_proto: Object<'gc>, ) -> Object<'gc> { let object = ScriptObject::new(context.gc(), Some(proto)); - broadcaster_functions.initialize(context.gc(), object.into(), array_proto); + broadcaster_functions.initialize(context, object.into(), array_proto); define_properties_on(OBJECT_DECLS, context, object, fn_proto); object.into() } diff --git a/core/src/avm1/globals/stage.rs b/core/src/avm1/globals/stage.rs index 0243e27e483f..e7c2eb3254ad 100644 --- a/core/src/avm1/globals/stage.rs +++ b/core/src/avm1/globals/stage.rs @@ -27,7 +27,7 @@ pub fn create_stage_object<'gc>( broadcaster_functions: BroadcasterFunctions<'gc>, ) -> Object<'gc> { let stage = ScriptObject::new(context.gc(), Some(proto)); - broadcaster_functions.initialize(context.gc(), stage.into(), array_proto); + broadcaster_functions.initialize(context, stage.into(), array_proto); define_properties_on(OBJECT_DECLS, context, stage, fn_proto); stage.into() } diff --git a/core/src/avm1/globals/string.rs b/core/src/avm1/globals/string.rs index ef7b29380a5e..07f2dc1c3479 100644 --- a/core/src/avm1/globals/string.rs +++ b/core/src/avm1/globals/string.rs @@ -277,31 +277,25 @@ fn split<'gc>( // but Flash does not. // e.g., split("foo", "") returns ["", "f", "o", "o", ""] in Rust but ["f, "o", "o"] in Flash. // Special case this to match Flash's behavior. - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - this.iter() - .take(limit) - .map(|c| AvmString::new(activation.gc(), WString::from_unit(c)).into()), - ) - .into()) + Ok(ArrayObject::builder(activation) + .with( + this.iter() + .take(limit) + .map(|c| activation.strings().make_char(c).into()), + ) + .into()) } else { - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - this.split(&delimiter) - .take(limit) - .map(|c| AvmString::new(activation.gc(), c).into()), - ) - .into()) + // TODO(moulins): make dependent AvmStrings instead of reallocating. + Ok(ArrayObject::builder(activation) + .with( + this.split(&delimiter) + .take(limit) + .map(|c| AvmString::new(activation.gc(), c).into()), + ) + .into()) } } else { - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - [this.into()], - ) - .into()) + Ok(ArrayObject::builder(activation).with([this.into()]).into()) } } diff --git a/core/src/avm1/globals/style_sheet.rs b/core/src/avm1/globals/style_sheet.rs index d5a2e50451f6..3d7207bd7ac8 100644 --- a/core/src/avm1/globals/style_sheet.rs +++ b/core/src/avm1/globals/style_sheet.rs @@ -138,14 +138,13 @@ fn get_style_names<'gc>( let css = this .get_stored("_css".into(), activation)? .coerce_to_object(activation); - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - css.get_keys(activation, false) - .into_iter() - .map(Value::String), - ) - .into()) + Ok(ArrayObject::builder(activation) + .with( + css.get_keys(activation, false) + .into_iter() + .map(Value::String), + ) + .into()) } fn load<'gc>( diff --git a/core/src/avm1/globals/system_ime.rs b/core/src/avm1/globals/system_ime.rs index aff63daf5cfc..3d9fcba51967 100644 --- a/core/src/avm1/globals/system_ime.rs +++ b/core/src/avm1/globals/system_ime.rs @@ -87,7 +87,7 @@ pub fn create<'gc>( array_proto: Object<'gc>, ) -> Object<'gc> { let ime = ScriptObject::new(context.gc(), Some(proto)); - broadcaster_functions.initialize(context.gc(), ime.into(), array_proto); + broadcaster_functions.initialize(context, ime.into(), array_proto); define_properties_on(OBJECT_DECLS, context, ime, fn_proto); ime.into() } diff --git a/core/src/avm1/globals/text_field.rs b/core/src/avm1/globals/text_field.rs index e07f88d88e68..6a53ce9c52d9 100644 --- a/core/src/avm1/globals/text_field.rs +++ b/core/src/avm1/globals/text_field.rs @@ -847,14 +847,13 @@ fn filters<'gc>( this: EditText<'gc>, activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { - Ok(ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - this.filters() - .into_iter() - .map(|filter| bitmap_filter::filter_to_avm1(activation, filter)), - ) - .into()) + Ok(ArrayObject::builder(activation) + .with( + this.filters() + .into_iter() + .map(|filter| bitmap_filter::filter_to_avm1(activation, filter)), + ) + .into()) } fn set_filters<'gc>( diff --git a/core/src/avm1/globals/text_format.rs b/core/src/avm1/globals/text_format.rs index 2e045937cc06..0325e2a8f5c1 100644 --- a/core/src/avm1/globals/text_format.rs +++ b/core/src/avm1/globals/text_format.rs @@ -375,12 +375,9 @@ fn tab_stops<'gc>(activation: &mut Activation<'_, 'gc>, text_format: &TextFormat .tab_stops .as_ref() .map_or(Value::Null, |tab_stops| { - ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - tab_stops.iter().map(|&x| x.into()), - ) - .into() + ArrayObject::builder(activation) + .with(tab_stops.iter().map(|&x| x.into())) + .into() }) } diff --git a/core/src/avm1/object/array_object.rs b/core/src/avm1/object/array_object.rs index 945e3dab1acb..5d2727235c71 100644 --- a/core/src/avm1/object/array_object.rs +++ b/core/src/avm1/object/array_object.rs @@ -1,8 +1,9 @@ use crate::avm1::property::Attribute; use crate::avm1::{Activation, Error, Object, ObjectPtr, ScriptObject, TObject, Value}; use crate::ecma_conversions::f64_to_wrapping_i32; -use crate::string::AvmString; +use crate::string::{AvmString, StringContext}; use gc_arena::{Collect, Mutation}; +use ruffle_macros::istr; use std::fmt; #[derive(Clone, Copy, Collect)] @@ -17,46 +18,52 @@ impl fmt::Debug for ArrayObject<'_> { } } -impl<'gc> ArrayObject<'gc> { - pub fn empty(activation: &Activation<'_, 'gc>) -> Self { - Self::new( - activation.gc(), - activation.context.avm1.prototypes().array, - [], - ) - } - - pub fn empty_with_proto(gc_context: &Mutation<'gc>, proto: Object<'gc>) -> Self { - Self::new_internal(gc_context, proto, []) - } - - pub fn new( - gc_context: &Mutation<'gc>, - array_proto: Object<'gc>, - elements: impl IntoIterator>, - ) -> Self { - Self::new_internal(gc_context, array_proto, elements) - } +/// Intermediate builder for constructing `ArrayObject`, +/// used to work around borrow-checker issues. +pub struct ArrayBuilder<'gc> { + mc: &'gc Mutation<'gc>, + proto: Object<'gc>, + length_prop: AvmString<'gc>, +} - fn new_internal( - gc_context: &Mutation<'gc>, - proto: Object<'gc>, - elements: impl IntoIterator>, - ) -> Self { - let base = ScriptObject::new(gc_context, Some(proto)); +impl<'gc> ArrayBuilder<'gc> { + pub fn with(self, elements: impl IntoIterator>) -> ArrayObject<'gc> { + let base = ScriptObject::new(self.mc, Some(self.proto)); let mut length: i32 = 0; for value in elements.into_iter() { - let length_str = AvmString::new_utf8(gc_context, length.to_string()); - base.define_value(gc_context, length_str, value, Attribute::empty()); + let length_str = AvmString::new_utf8(self.mc, length.to_string()); + base.define_value(self.mc, length_str, value, Attribute::empty()); length += 1; } base.define_value( - gc_context, - "length", + self.mc, + self.length_prop, length.into(), Attribute::DONT_ENUM | Attribute::DONT_DELETE, ); - Self(base) + ArrayObject(base) + } +} + +impl<'gc> ArrayObject<'gc> { + pub fn empty(activation: &Activation<'_, 'gc>) -> Self { + Self::builder(activation).with([]) + } + + pub fn builder(activation: &Activation<'_, 'gc>) -> ArrayBuilder<'gc> { + let proto = activation.context.avm1.prototypes().array; + Self::builder_with_proto(&activation.context.strings, proto) + } + + pub fn builder_with_proto( + context: &StringContext<'gc>, + proto: Object<'gc>, + ) -> ArrayBuilder<'gc> { + ArrayBuilder { + mc: context.gc(), + length_prop: istr!(context, "length"), + proto, + } } fn parse_index(name: AvmString<'gc>) -> Option { @@ -85,7 +92,7 @@ impl<'gc> TObject<'gc> for ArrayObject<'gc> { activation: &mut Activation<'_, 'gc>, this: Object<'gc>, ) -> Result<(), Error<'gc>> { - if &name == b"length" { + if name == istr!("length") { let new_length = value.coerce_to_i32(activation)?; self.set_length(activation, new_length)?; } else if let Some(index) = Self::parse_index(name) { @@ -103,7 +110,9 @@ impl<'gc> TObject<'gc> for ArrayObject<'gc> { activation: &mut Activation<'_, 'gc>, this: Object<'gc>, ) -> Result, Error<'gc>> { - Ok(Self::empty_with_proto(activation.gc(), this).into()) + Ok(Self::builder_with_proto(activation.strings(), this) + .with([]) + .into()) } fn as_array_object(&self) -> Option> { @@ -115,7 +124,8 @@ impl<'gc> TObject<'gc> for ArrayObject<'gc> { activation: &mut Activation<'_, 'gc>, new_length: i32, ) -> Result<(), Error<'gc>> { - if let Value::Number(old_length) = self.0.get_data("length".into(), activation) { + let old_length = self.0.get_data(istr!("length"), activation); + if let Value::Number(old_length) = old_length { for i in new_length.max(0)..f64_to_wrapping_i32(old_length) { self.delete_element(activation, i); } diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 4b667d780afd..deb9a068f274 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -2038,7 +2038,7 @@ impl<'gc> EditText<'gc> { fn initialize_as_broadcaster(&self, activation: &mut Avm1Activation<'_, 'gc>) { if let Avm1Value::Object(object) = self.object() { activation.context.avm1.broadcaster_functions().initialize( - activation.gc(), + &activation.context.strings, object, activation.context.avm1.prototypes().array, ); diff --git a/core/src/external.rs b/core/src/external.rs index 4a3c0a6ed5c2..37d58ffac5b8 100644 --- a/core/src/external.rs +++ b/core/src/external.rs @@ -183,14 +183,13 @@ impl Value { } object.into() } - Value::List(values) => Avm1ArrayObject::new( - activation.gc(), - activation.context.avm1.prototypes().array, - values - .iter() - .map(|value| value.to_owned().into_avm1(activation)), - ) - .into(), + Value::List(values) => Avm1ArrayObject::builder(activation) + .with( + values + .iter() + .map(|value| value.to_owned().into_avm1(activation)), + ) + .into(), } }