Skip to content

Commit

Permalink
avm2: Add infallible ScriptObject::new_object and use it to constru…
Browse files Browse the repository at this point in the history
…ct Objects instead of usual construction pipeline
  • Loading branch information
Lord-McSweeney authored and Lord-McSweeney committed Dec 11, 2024
1 parent 051c5f0 commit a523ad4
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 93 deletions.
4 changes: 2 additions & 2 deletions core/src/avm2/activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1813,7 +1813,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
}

fn op_new_object(&mut self, num_args: u32) -> Result<FrameControl<'gc>, 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();
Expand Down
8 changes: 2 additions & 6 deletions core/src/avm2/amf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -507,11 +507,7 @@ pub fn deserialize_lso<'gc>(
activation: &mut Activation<'_, 'gc>,
lso: &Lso,
) -> Result<Object<'gc>, 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(
Expand Down
38 changes: 7 additions & 31 deletions core/src/avm2/globals/avmplus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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) {
Expand Down Expand Up @@ -93,11 +89,7 @@ fn describe_internal_body<'gc>(
) -> Result<Object<'gc>, 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();
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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());
Expand Down
8 changes: 2 additions & 6 deletions core/src/avm2/globals/flash/display/loader_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion core/src/avm2/globals/flash/net/shared_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
17 changes: 6 additions & 11 deletions core/src/avm2/globals/flash/text/style_sheet.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -10,26 +11,20 @@ pub fn inner_parse_css<'gc>(
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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)),
Value::String(AvmString::new(activation.gc(), value)),
activation,
)?;
}

result.set_string_property_local(
AvmString::new(activation.gc(), selector),
Value::Object(object),
Expand Down
5 changes: 2 additions & 3 deletions core/src/avm2/globals/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)?;
Expand Down
18 changes: 5 additions & 13 deletions core/src/avm2/metadata.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -77,22 +77,14 @@ impl<'gc> Metadata<'gc> {
&self,
activation: &mut Activation<'_, 'gc>,
) -> Result<Object<'gc>, 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()))
Expand Down
7 changes: 1 addition & 6 deletions core/src/avm2/object/function_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 17 additions & 5 deletions core/src/avm2/object/script_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
12 changes: 3 additions & 9 deletions core/src/avm2/object/shared_object_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -73,21 +73,15 @@ 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),
SharedObjectObjectData,
data
)
.set(empty_data);

Ok(())
}

pub fn name(&self) -> &String {
Expand Down

0 comments on commit a523ad4

Please sign in to comment.