diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index aedf16ffbf78..fdc94c263bc2 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -1559,7 +1559,7 @@ impl<'a, 'gc> Activation<'a, 'gc> { ScriptObject::catch_scope(self, &vname) } else { // for `finally` scopes, FP just creates a normal object. - self.avm2().classes().object.construct(self, &[])? + ScriptObject::new_object(self) }; self.push_stack(so); @@ -1813,7 +1813,7 @@ impl<'a, 'gc> Activation<'a, 'gc> { } fn op_new_object(&mut self, num_args: u32) -> Result, Error<'gc>> { - let object = self.context.avm2.classes().object.construct(self, &[])?; + let object = ScriptObject::new_object(self); for _ in 0..num_args { let value = self.pop_stack(); diff --git a/core/src/avm2/amf.rs b/core/src/avm2/amf.rs index 02a060bdb0da..dec582e0fa42 100644 --- a/core/src/avm2/amf.rs +++ b/core/src/avm2/amf.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use super::property::Property; use crate::avm2::bytearray::ByteArrayStorage; use crate::avm2::class::Class; -use crate::avm2::object::{ByteArrayObject, ClassObject, TObject, VectorObject}; +use crate::avm2::object::{ByteArrayObject, ClassObject, ScriptObject, TObject, VectorObject}; use crate::avm2::vector::VectorStorage; use crate::avm2::ArrayObject; use crate::avm2::ArrayStorage; @@ -507,11 +507,7 @@ pub fn deserialize_lso<'gc>( activation: &mut Activation<'_, 'gc>, lso: &Lso, ) -> Result, Error<'gc>> { - let obj = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let obj = ScriptObject::new_object(activation); for child in &lso.body { obj.set_string_property_local( diff --git a/core/src/avm2/globals/avmplus.rs b/core/src/avm2/globals/avmplus.rs index 3f54a6169687..16a71ef88005 100644 --- a/core/src/avm2/globals/avmplus.rs +++ b/core/src/avm2/globals/avmplus.rs @@ -2,7 +2,7 @@ use crate::avm2::class::Class; pub use crate::avm2::globals::flash::utils::get_qualified_class_name; use crate::avm2::metadata::Metadata; use crate::avm2::method::Method; -use crate::avm2::object::{ArrayObject, TObject}; +use crate::avm2::object::{ArrayObject, ScriptObject, TObject}; use crate::avm2::parameters::ParametersExt; use crate::avm2::property::Property; use crate::avm2::{Activation, Error, Multiname, Namespace, Object, Value}; @@ -22,11 +22,7 @@ pub fn describe_type_json<'gc>( let value = args[0]; let class_def = instance_class_describe_type(activation, value); - let object = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let object = ScriptObject::new_object(activation); let mut used_class_def = class_def; if flags.contains(DescribeTypeFlags::USE_ITRAITS) { @@ -93,11 +89,7 @@ fn describe_internal_body<'gc>( ) -> Result, Error<'gc>> { let mc = activation.gc(); - let traits = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let traits = ScriptObject::new_object(activation); let bases = ArrayObject::empty(activation)?.as_array_object().unwrap(); let interfaces = ArrayObject::empty(activation)?.as_array_object().unwrap(); @@ -216,11 +208,7 @@ fn describe_internal_body<'gc>( let trait_metadata = vtable.get_metadata_for_slot(slot_id); - let variable = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let variable = ScriptObject::new_object(activation); variable.set_string_property_local("name", prop_name.into(), activation)?; variable.set_string_property_local("type", prop_class_name.into(), activation)?; variable.set_string_property_local("access", access.into(), activation)?; @@ -278,11 +266,7 @@ fn describe_internal_body<'gc>( let trait_metadata = vtable.get_metadata_for_disp(disp_id); - let method_obj = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let method_obj = ScriptObject::new_object(activation); method_obj.set_string_property_local("name", prop_name.into(), activation)?; method_obj.set_string_property_local( @@ -360,11 +344,7 @@ fn describe_internal_body<'gc>( let accessor_type = display_name(activation.strings(), method_type); let declared_by = defining_class.dollar_removed_name(mc).to_qualified_name(mc); - let accessor_obj = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let accessor_obj = ScriptObject::new_object(activation); accessor_obj.set_string_property_local("name", prop_name.into(), activation)?; accessor_obj.set_string_property_local("access", access.into(), activation)?; accessor_obj.set_string_property_local("type", accessor_type.into(), activation)?; @@ -460,11 +440,7 @@ fn write_params<'gc>( for param in method.signature() { let param_type_name = display_name(activation.strings(), param.param_type_name); let optional = param.default_value.is_some(); - let param_obj = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let param_obj = ScriptObject::new_object(activation); param_obj.set_string_property_local("type", param_type_name.into(), activation)?; param_obj.set_string_property_local("optional", optional.into(), activation)?; params_array.push(param_obj.into()); diff --git a/core/src/avm2/globals/flash/display/loader_info.rs b/core/src/avm2/globals/flash/display/loader_info.rs index 3f3cd70ea39d..79123b1a9586 100644 --- a/core/src/avm2/globals/flash/display/loader_info.rs +++ b/core/src/avm2/globals/flash/display/loader_info.rs @@ -3,7 +3,7 @@ use crate::avm2::activation::Activation; use crate::avm2::bytearray::Endian; use crate::avm2::error::error; -use crate::avm2::object::{DomainObject, LoaderStream, Object, TObject}; +use crate::avm2::object::{DomainObject, LoaderStream, Object, ScriptObject, TObject}; use crate::avm2::value::Value; use crate::avm2::{AvmString, Error}; use crate::display_object::TDisplayObject; @@ -506,11 +506,7 @@ pub fn get_parameters<'gc>( LoaderStream::Swf(root, _) => root, }; - let params_obj = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let params_obj = ScriptObject::new_object(activation); let parameters = root.parameters(); for (k, v) in parameters.iter() { diff --git a/core/src/avm2/globals/flash/net/shared_object.rs b/core/src/avm2/globals/flash/net/shared_object.rs index 569a36d2c14c..84aa6eaf66d1 100644 --- a/core/src/avm2/globals/flash/net/shared_object.rs +++ b/core/src/avm2/globals/flash/net/shared_object.rs @@ -253,7 +253,7 @@ pub fn clear<'gc>( let shared_object = this.as_shared_object().unwrap(); // Clear the local data object. - shared_object.reset_data(activation)?; + shared_object.reset_data(activation); // Delete data from storage backend. let name = shared_object.name(); diff --git a/core/src/avm2/globals/flash/text/style_sheet.rs b/core/src/avm2/globals/flash/text/style_sheet.rs index 15e21a8775d6..1335e06a8c29 100644 --- a/core/src/avm2/globals/flash/text/style_sheet.rs +++ b/core/src/avm2/globals/flash/text/style_sheet.rs @@ -1,5 +1,6 @@ +use crate::avm2::object::{Object, ScriptObject, TObject}; use crate::avm2::parameters::ParametersExt; -use crate::avm2::{Activation, Error, Object, TObject, Value}; +use crate::avm2::{Activation, Error, Value}; use crate::html::{transform_dashes_to_camel_case, CssStream}; use crate::string::AvmString; use ruffle_wstr::{WStr, WString}; @@ -10,19 +11,12 @@ pub fn inner_parse_css<'gc>( args: &[Value<'gc>], ) -> Result, Error<'gc>> { let document = args.get_string(activation, 0)?; - let result = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let result = ScriptObject::new_object(activation); if let Ok(css) = CssStream::new(&document).parse() { for (selector, properties) in css.into_iter() { - let object = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let object = ScriptObject::new_object(activation); + for (key, value) in properties.into_iter() { object.set_string_property_local( AvmString::new(activation.gc(), transform_dashes_to_camel_case(key)), @@ -30,6 +24,7 @@ pub fn inner_parse_css<'gc>( activation, )?; } + result.set_string_property_local( AvmString::new(activation.gc(), selector), Value::Object(object), diff --git a/core/src/avm2/globals/json.rs b/core/src/avm2/globals/json.rs index 6058132f58ca..8c46b1a625ad 100644 --- a/core/src/avm2/globals/json.rs +++ b/core/src/avm2/globals/json.rs @@ -4,7 +4,7 @@ use crate::avm2::activation::Activation; use crate::avm2::array::ArrayStorage; use crate::avm2::error::{syntax_error, type_error}; use crate::avm2::globals::array::ArrayIter; -use crate::avm2::object::{ArrayObject, FunctionObject, Object, TObject}; +use crate::avm2::object::{ArrayObject, FunctionObject, Object, ScriptObject, TObject}; use crate::avm2::parameters::ParametersExt; use crate::avm2::value::Value; use crate::avm2::Error; @@ -33,8 +33,7 @@ fn deserialize_json_inner<'gc>( } } JsonValue::Object(js_obj) => { - let obj_class = activation.avm2().classes().object; - let obj = obj_class.construct(activation, &[])?; + let obj = ScriptObject::new_object(activation); for entry in js_obj.iter() { let key = AvmString::new_utf8(activation.context.gc_context, entry.0); let val = deserialize_json_inner(activation, entry.1.clone(), reviver)?; diff --git a/core/src/avm2/metadata.rs b/core/src/avm2/metadata.rs index f352e2cc9dd0..5248408a63f5 100644 --- a/core/src/avm2/metadata.rs +++ b/core/src/avm2/metadata.rs @@ -1,12 +1,12 @@ +use crate::avm2::array::ArrayStorage; +use crate::avm2::object::{ArrayObject, Object, ScriptObject, TObject}; use crate::avm2::script::TranslationUnit; -use crate::avm2::{Activation, Error}; +use crate::avm2::{Activation, Error, Value}; use crate::string::AvmString; use gc_arena::Collect; use swf::avm2::types::{Index as AbcIndex, Metadata as AbcMetadata}; -use super::{ArrayObject, ArrayStorage, Object, TObject, Value}; - // Represents a single key-value pair for a trait metadata. #[derive(Clone, Collect, Debug, Eq, PartialEq)] #[collect(no_drop)] @@ -77,22 +77,14 @@ impl<'gc> Metadata<'gc> { &self, activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { - let object = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let object = ScriptObject::new_object(activation); object.set_string_property_local("name", self.name.into(), activation)?; let values = self .items .iter() .map(|item| { - let value_object = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + let value_object = ScriptObject::new_object(activation); value_object.set_string_property_local("key", item.key.into(), activation)?; value_object.set_string_property_local("value", item.value.into(), activation)?; Ok(Some(value_object.into())) diff --git a/core/src/avm2/object/function_object.rs b/core/src/avm2/object/function_object.rs index b15ff9d1a2e0..23cefa332b77 100644 --- a/core/src/avm2/object/function_object.rs +++ b/core/src/avm2/object/function_object.rs @@ -123,12 +123,7 @@ impl<'gc> FunctionObject<'gc> { bound_class, ); - let es3_proto = activation - .avm2() - .classes() - .object - .construct(activation, &[]) - .expect("Object should construct"); + let es3_proto = ScriptObject::new_object(activation); FunctionObject(Gc::new( activation.context.gc_context, diff --git a/core/src/avm2/object/script_object.rs b/core/src/avm2/object/script_object.rs index 17f06ae9505a..14c76b69c32a 100644 --- a/core/src/avm2/object/script_object.rs +++ b/core/src/avm2/object/script_object.rs @@ -86,18 +86,30 @@ fn maybe_int_property(name: AvmString<'_>) -> DynamicKey<'_> { } impl<'gc> ScriptObject<'gc> { + /// Creates an instance of the Object class, exactly as if `new Object()` + /// were called, but without going through any construction or call + /// machinery (since it's unnecessary for the Object class). + pub fn new_object(activation: &mut Activation<'_, 'gc>) -> Object<'gc> { + let object_class = activation.avm2().classes().object; + + ScriptObject(Gc::new( + activation.gc(), + ScriptObjectData::new(object_class), + )) + .into() + } + /// Construct an instance with a possibly-none class and proto chain. /// NOTE: this is a low-level function. /// This should *not* be used unless you really need /// to do something low-level, weird or lazily initialize the object. /// You shouldn't let scripts observe this weirdness. /// - /// The "everyday" way to create a normal empty ScriptObject (AS "Object") is to call - /// `avm2.classes().object.construct(self, &[])`. - /// This is equivalent to AS3 `new Object()`. + /// The proper way to create a normal empty ScriptObject (AS "Object") is to call + /// `ScriptObject::new_object(activation)`. /// - /// (calling `custom_object(mc, object_class, object_class.prototype()`) - /// is technically also equivalent and faster, but not recommended outside lower-level Core code) + /// Calling `custom_object(mc, object_class, object_class.prototype()` is + /// technically also equivalent, but not recommended outside VM initialization code pub fn custom_object( mc: &Mutation<'gc>, class: Class<'gc>, diff --git a/core/src/avm2/object/shared_object_object.rs b/core/src/avm2/object/shared_object_object.rs index 5cdc603ba901..f3a6706d8540 100644 --- a/core/src/avm2/object/shared_object_object.rs +++ b/core/src/avm2/object/shared_object_object.rs @@ -3,7 +3,7 @@ use crate::avm2::activation::Activation; use crate::avm2::error::argument_error; use crate::avm2::object::script_object::ScriptObjectData; -use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject}; +use crate::avm2::object::{ClassObject, Object, ObjectPtr, ScriptObject, TObject}; use crate::avm2::Error; use gc_arena::barrier::unlock; use gc_arena::{lock::Lock, Collect, Gc, GcWeak}; @@ -73,12 +73,8 @@ impl<'gc> SharedObjectObject<'gc> { self.0.data.get() } - pub fn reset_data(&self, activation: &mut Activation<'_, 'gc>) -> Result<(), Error<'gc>> { - let empty_data = activation - .avm2() - .classes() - .object - .construct(activation, &[])?; + pub fn reset_data(&self, activation: &mut Activation<'_, 'gc>) { + let empty_data = ScriptObject::new_object(activation); unlock!( Gc::write(activation.gc(), self.0), @@ -86,8 +82,6 @@ impl<'gc> SharedObjectObject<'gc> { data ) .set(empty_data); - - Ok(()) } pub fn name(&self) -> &String {