Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avm2: Work towards starting to remove PrimitiveObject #18198

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
46 changes: 28 additions & 18 deletions core/src/avm2/activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,21 +761,17 @@ impl<'a, 'gc> Activation<'a, 'gc> {
let verified_info = method.verified_info.borrow();
let exception_list = &verified_info.as_ref().unwrap().exceptions;

// Use `coerce_to_object` so that we handle primitives correctly.
let err_object = error.coerce_to_object(self);
let last_ip = self.ip - 1;
for e in exception_list {
if last_ip >= e.from_offset as i32 && last_ip < e.to_offset as i32 {
let mut matches = false;
// A typeless catch block (e.g. `catch(er) { ... }`) will
// always match.
if e.target_class.is_none() {
matches = true;
} else if let Ok(err_object) = err_object {
let target_class = e.target_class.expect("Just confirmed to be non-None");

matches = err_object.is_of_type(target_class);
}
let matches = if let Some(target_class) = e.target_class {
// This ensures null and undefined don't match
error.is_of_type(self, target_class)
} else {
// A typeless catch block (i.e. `catch(err:*) { ... }`) will
// always match.
true
};

if matches {
#[cfg(feature = "avm_debug")]
Expand Down Expand Up @@ -1719,7 +1715,11 @@ impl<'a, 'gc> Activation<'a, 'gc> {
}

fn op_get_slot(&mut self, index: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
let object = self.pop_stack().coerce_to_object_or_typeerror(self, None)?;
let object = self
.pop_stack()
.null_check(self, None)?
.as_object()
.expect("Cannot get_slot on primitive");
let value = object.get_slot(index);

self.push_stack(value);
Expand All @@ -1729,7 +1729,11 @@ impl<'a, 'gc> Activation<'a, 'gc> {

fn op_set_slot(&mut self, index: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
let value = self.pop_stack();
let object = self.pop_stack().coerce_to_object_or_typeerror(self, None)?;
let object = self
.pop_stack()
.null_check(self, None)?
.as_object()
.expect("Cannot set_slot on primitive");

object.set_slot(index, value, self)?;

Expand All @@ -1738,7 +1742,11 @@ impl<'a, 'gc> Activation<'a, 'gc> {

fn op_set_slot_no_coerce(&mut self, index: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
let value = self.pop_stack();
let object = self.pop_stack().coerce_to_object_or_typeerror(self, None)?;
let object = self
.pop_stack()
.null_check(self, None)?
.as_object()
.expect("Cannot set_slot on primitive");

object.set_slot_no_coerce(index, value, self.context.gc_context);

Expand Down Expand Up @@ -1994,16 +2002,18 @@ impl<'a, 'gc> Activation<'a, 'gc> {
fn op_check_filter(&mut self) -> Result<FrameControl<'gc>, Error<'gc>> {
let xml = self.avm2().class_defs().xml;
let xml_list = self.avm2().class_defs().xml_list;
let value = self.pop_stack().coerce_to_object_or_typeerror(self, None)?;
let value = self.pop_stack().null_check(self, None)?;

if value.is_of_type(xml) || value.is_of_type(xml_list) {
if value.is_of_type(self, xml) || value.is_of_type(self, xml_list) {
self.push_stack(value);
} else {
let class_name = value.instance_of_class_name(self);

return Err(Error::AvmError(type_error(
self,
&format!(
"Error #1123: Filter operator not supported on type {}.",
value.instance_of_class_name(self.context.gc_context)
class_name
),
1123,
)?));
Expand Down
21 changes: 19 additions & 2 deletions core/src/avm2/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod int;
mod json;
mod math;
mod namespace;
mod null;
mod number;
mod object;
mod q_name;
Expand Down Expand Up @@ -169,6 +170,7 @@ pub struct SystemClasses<'gc> {
pub dictionary: ClassObject<'gc>,
pub id3info: ClassObject<'gc>,
pub textrun: ClassObject<'gc>,
pub sharedobject: ClassObject<'gc>,
}

#[derive(Clone, Collect)]
Expand All @@ -177,6 +179,7 @@ pub struct SystemClassDefs<'gc> {
pub object: Class<'gc>,
pub class: Class<'gc>,
pub function: Class<'gc>,
pub null: Class<'gc>,
pub void: Class<'gc>,

pub array: Class<'gc>,
Expand Down Expand Up @@ -325,16 +328,24 @@ impl<'gc> SystemClasses<'gc> {
dictionary: object,
id3info: object,
textrun: object,
sharedobject: object,
}
}
}

impl<'gc> SystemClassDefs<'gc> {
fn new(object: Class<'gc>, class: Class<'gc>, function: Class<'gc>, void: Class<'gc>) -> Self {
fn new(
object: Class<'gc>,
class: Class<'gc>,
function: Class<'gc>,
null: Class<'gc>,
void: Class<'gc>,
) -> Self {
SystemClassDefs {
object,
class,
function,
null,
void,

// temporary initialization
Expand Down Expand Up @@ -531,10 +542,14 @@ pub fn load_player_globals<'gc>(
// Function is more of a "normal" class than the other two, so we can create it normally.
let fn_classdef = function::create_class(activation, object_i_class, class_i_class);

// This is a weird internal class in avmplus, but it allows for implementing
// `describeType(null)` in a cleaner way
let null_def = null::create_class(activation);

// void doesn't have a ClassObject
let void_def = void::create_class(activation);

// Register the classes in the domain, now (except for the global class)
// Register the classes in the domain, now (except for the global and null classes)
domain.export_class(object_i_class.name(), object_i_class, mc);
domain.export_class(class_i_class.name(), class_i_class, mc);
domain.export_class(fn_classdef.name(), fn_classdef, mc);
Expand All @@ -544,6 +559,7 @@ pub fn load_player_globals<'gc>(
object_i_class,
class_i_class,
fn_classdef,
null_def,
void_def,
));

Expand Down Expand Up @@ -915,6 +931,7 @@ pub fn init_native_system_classes(activation: &mut Activation<'_, '_>) {
("flash.net", "URLVariables", urlvariables),
("flash.net", "FileReference", filereference),
("flash.net", "FileFilter", filefilter),
("flash.net", "SharedObject", sharedobject),
("flash.utils", "ByteArray", bytearray),
("flash.utils", "Dictionary", dictionary),
("flash.system", "ApplicationDomain", application_domain),
Expand Down
127 changes: 25 additions & 102 deletions core/src/avm2/globals/avmplus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,9 @@ pub fn describe_type_json<'gc>(
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let flags = DescribeTypeFlags::from_bits(args.get_u32(activation, 1)?).expect("Invalid flags!");
if args[0] == Value::Null {
return describe_type_json_null(activation, flags);
}

let value = args[0].coerce_to_object(activation)?;
let class_def = value.instance_class();
let value = args[0];
let class_def = instance_class_describe_type(activation, value);
let object = activation
.avm2()
.classes()
Expand Down Expand Up @@ -54,7 +51,11 @@ pub fn describe_type_json<'gc>(
object.set_public_property("isFinal", used_class_def.is_final().into(), activation)?;
object.set_public_property(
"isStatic",
value.as_class_object().is_some().into(),
value
.as_object()
.and_then(|o| o.as_class_object())
.is_some()
.into(),
activation,
)?;

Expand Down Expand Up @@ -85,102 +86,6 @@ bitflags::bitflags! {
}
}

fn describe_type_json_null<'gc>(
activation: &mut Activation<'_, 'gc>,
flags: DescribeTypeFlags,
) -> Result<Value<'gc>, Error<'gc>> {
if flags.contains(DescribeTypeFlags::USE_ITRAITS) {
return Ok(Value::Null);
}
let object = activation
.avm2()
.classes()
.object
.construct(activation, &[])?;

object.set_public_property("name", "null".into(), activation)?;
object.set_public_property("isDynamic", false.into(), activation)?;
object.set_public_property("isFinal", true.into(), activation)?;
object.set_public_property("isStatic", false.into(), activation)?;

let traits = activation
.avm2()
.classes()
.object
.construct(activation, &[])?;

if flags.contains(DescribeTypeFlags::INCLUDE_TRAITS) {
traits.set_public_property(
"bases",
if flags.contains(DescribeTypeFlags::INCLUDE_BASES) {
ArrayObject::empty(activation)?.into()
} else {
Value::Null
},
activation,
)?;
traits.set_public_property(
"interfaces",
if flags.contains(DescribeTypeFlags::INCLUDE_INTERFACES) {
ArrayObject::empty(activation)?.into()
} else {
Value::Null
},
activation,
)?;
traits.set_public_property(
"variables",
if flags.contains(DescribeTypeFlags::INCLUDE_VARIABLES) {
ArrayObject::empty(activation)?.into()
} else {
Value::Null
},
activation,
)?;
traits.set_public_property(
"accessors",
if flags.contains(DescribeTypeFlags::INCLUDE_ACCESSORS) {
ArrayObject::empty(activation)?.into()
} else {
Value::Null
},
activation,
)?;
traits.set_public_property(
"methods",
if flags.contains(DescribeTypeFlags::INCLUDE_METHODS) {
ArrayObject::empty(activation)?.into()
} else {
Value::Null
},
activation,
)?;
traits.set_public_property(
"metadata",
if flags.contains(DescribeTypeFlags::INCLUDE_METADATA) {
ArrayObject::empty(activation)?.into()
} else {
Value::Null
},
activation,
)?;
traits.set_public_property(
"constructor",
if flags.contains(DescribeTypeFlags::INCLUDE_CONSTRUCTOR) {
ArrayObject::empty(activation)?.into()
} else {
Value::Null
},
activation,
)?;
object.set_public_property("traits", traits.into(), activation)?;
} else {
object.set_public_property("traits", Value::Null, activation)?;
}

Ok(object.into())
}

fn describe_internal_body<'gc>(
activation: &mut Activation<'_, 'gc>,
class_def: Class<'gc>,
Expand Down Expand Up @@ -573,3 +478,21 @@ fn write_metadata<'gc>(
}
Ok(())
}

/// Like `Value::instance_class`, but supports Value::Null and Value::Undefined,
/// and returns `int` for Value::Integer instead of `Number`.
///
/// Used for `describeType`, `getQualifiedClassName`, and `getQualifiedSuperClassName`.
pub fn instance_class_describe_type<'gc>(
activation: &mut Activation<'_, 'gc>,
value: Value<'gc>,
) -> Class<'gc> {
let class_defs = activation.avm2().class_defs();

match value {
Value::Null => class_defs.null,
Value::Undefined => class_defs.void,
Value::Integer(_) => class_defs.int,
_ => value.instance_class(activation),
}
}
14 changes: 7 additions & 7 deletions core/src/avm2/globals/flash/display/bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,15 @@ pub fn set_bitmap_data<'gc>(
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap) = this.as_display_object().and_then(|dobj| dobj.as_bitmap()) {
let bitmap_data = args.get(0).unwrap_or(&Value::Null);
let bitmap_data = if matches!(bitmap_data, Value::Null) {
BitmapDataWrapper::dummy(activation.context.gc_context)
let bitmap_data = args.try_get_object(activation, 0);

let bitmap_data = if let Some(bitmap_data) = bitmap_data {
bitmap_data.as_bitmap_data().expect("Must be a BitmapData")
} else {
bitmap_data
.coerce_to_object(activation)?
.as_bitmap_data()
.ok_or_else(|| Error::RustError("Argument was not a BitmapData".into()))?
// Passing null results in a dummy BitmapData being set.
BitmapDataWrapper::dummy(activation.gc())
};

bitmap.set_bitmap_data(activation.context, bitmap_data);
}

Expand Down
13 changes: 3 additions & 10 deletions core/src/avm2/globals/flash/display/bitmap_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,17 +469,14 @@ pub fn set_pixels<'gc>(
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let rectangle = args.get_object(activation, 0, "rect")?;
let bytearray = args.get_object(activation, 1, "inputByteArray")?;

let bytearray = args
.get(1)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation)?;
if let Some(bitmap_data) = this.as_bitmap_data() {
let (x, y, width, height) = get_rectangle_x_y_width_height(activation, rectangle)?;

let mut ba_write = bytearray
.as_bytearray_mut()
.ok_or("ArgumentError: Parameter must be a bytearray")?;
.expect("Parameter must be a bytearray");

operations::set_pixels_from_byte_array(
activation.context.gc_context,
Expand Down Expand Up @@ -559,13 +556,9 @@ pub fn copy_channel<'gc>(
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data() {
bitmap_data.check_valid(activation)?;
let source_bitmap = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation)?;

let source_bitmap = args.get_object(activation, 0, "sourceBitmapData")?;
let source_rect = args.get_object(activation, 1, "sourceRect")?;

let dest_point = args.get_object(activation, 2, "destPoint")?;

let dest_x = dest_point
Expand Down
Loading