Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion core/src/avm1/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,23 @@ impl<'gc> FunctionObject<'gc> {
let native = NativeObject::Function(Gc::new(context.gc(), self));
obj.set_native(context.gc(), native);

// In swfv5, __proto__ property of function objects is undefined by default.
obj.define_value(
context.gc(),
istr!(context, "__proto__"),
fn_proto.into(),
Attribute::DONT_ENUM | Attribute::DONT_DELETE | Attribute::VERSION_6,
);

// This should be correct, because f.hasOwnProperty("constructor") returns true in flashplayer.
let constr_val = fn_proto.get_data_raw(istr!(context, "constructor"));
obj.define_value(
context.gc(),
istr!(context, "constructor"),
constr_val,
Attribute::DONT_ENUM | Attribute::DONT_DELETE,
);

if let Some(prototype) = prototype {
prototype.define_value(
context.gc(),
Expand All @@ -490,7 +507,7 @@ impl<'gc> FunctionObject<'gc> {
context.gc(),
istr!(context, "prototype"),
prototype.into(),
Attribute::empty(),
Attribute::DONT_ENUM,
);
}

Expand Down
29 changes: 29 additions & 0 deletions core/src/avm1/globals.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::property_decl::DeclContext;
use crate::avm1::Attribute;
use crate::avm1::{Object, Value};
use crate::display_object::{DisplayObject, TDisplayObject, TDisplayObjectContainer};
use crate::string::{AvmString, StringContext, WStr, WString};
use gc_arena::Collect;
use ruffle_macros::istr;
use std::str;

mod accessibility;
Expand Down Expand Up @@ -516,6 +518,33 @@

let object = object::create_class(context);
let function = function::create_class(context);

let patch_constructor = |obj: Object<'gc>| {
obj.define_value(
context.gc(),
istr!(context.strings, "constructor"),
function.constr.into(),
Attribute::DONT_ENUM | Attribute::DONT_DELETE,
);
};

let patch_proto_methods = |proto: Object<'gc>| {
for val in proto.get_all_property_data() {
if let Value::Object(o) = val {
if o.as_function().is_some() {
patch_constructor(o);
}
}

Check warning on line 537 in core/src/avm1/globals.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (537)
}
};

// function.proto does not need to be patched because its constructor was set in function::build.
patch_constructor(object.constr);
patch_constructor(function.constr);

patch_proto_methods(object.proto);
patch_proto_methods(function.proto);

let (broadcaster_fns, as_broadcaster) = as_broadcaster::create_class(context, object.proto);

let flash = Object::new(context.strings, Some(object.proto));
Expand Down
4 changes: 2 additions & 2 deletions core/src/avm1/globals/as_broadcaster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use gc_arena::Collect;
use ruffle_macros::istr;

const OBJECT_DECLS: StaticDeclarations = declare_static_properties! {
"initialize" => method(initialize; DONT_ENUM | DONT_DELETE);
"initialize" => function(initialize; DONT_ENUM | DONT_DELETE);
"addListener" => function(add_listener; DONT_ENUM | DONT_DELETE);
"removeListener" => function(remove_listener; DONT_ENUM | DONT_DELETE);
"broadcastMessage" => function(broadcast_message; DONT_ENUM | DONT_DELETE);
Expand All @@ -28,7 +28,7 @@ pub fn create_class<'gc>(

let decls = OBJECT_DECLS(context);
let mut define_as_object = |index: usize| -> Object<'gc> {
match decls[index].define_on(context.strings, class.constr, context.fn_proto) {
match decls[index].define_on(context, class.constr) {
Value::Object(o) => o,
_ => panic!("expected object for broadcaster function"),
}
Expand Down
17 changes: 16 additions & 1 deletion core/src/avm1/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
use crate::avm1::globals::xml_socket::XmlSocket;
use crate::avm1::object::super_object::SuperObject;
use crate::avm1::xml::XmlNode;
use crate::avm1::{Activation, Error, Value};
use crate::avm1::{Activation, Attribute, Error, Value};
use crate::bitmap::bitmap_data::BitmapData;
use crate::display_object::{
Avm1Button, DisplayObject, EditText, MovieClip, TDisplayObject as _, Video,
Expand Down Expand Up @@ -214,6 +214,21 @@
return Ok(());
}

if name == istr!("__proto__") {
if let NativeObject::Function(_) = self.native() {
let attributes = if activation.swf_version() < 6 {

Check warning on line 219 in core/src/avm1/object.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (219)
// Explicit overwriting in swfv5 makes __proto__ of function objects visible to swfv5.
Attribute::DONT_ENUM | Attribute::DONT_DELETE

Check warning on line 221 in core/src/avm1/object.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (221)
} else {
// After explicit overwriting in swfv6 and above, __proto__ of function objects remains invisible to swfv5.
Attribute::DONT_ENUM | Attribute::DONT_DELETE | Attribute::VERSION_6

Check warning on line 224 in core/src/avm1/object.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (224)
};

self.define_value(activation.gc(), name, value, attributes);
return Ok(());

Check warning on line 228 in core/src/avm1/object.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (227–228)
}
}

let mut value = value;
let (this, mut proto) = if let Some(super_object) = self.as_super_object() {
(super_object.this(), super_object.proto(activation))
Expand Down
34 changes: 34 additions & 0 deletions core/src/avm1/object/script_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,26 @@
.map_or(Value::Undefined, |property| property.data())
}

/// Gets the value of a data property without activation, ignoring attributes.
pub fn get_data_raw(self, name: AvmString<'gc>) -> Value<'gc> {
self.0
.borrow()
.properties
.get(name, false)
.map(|p| p.data())
.unwrap_or(Value::Undefined)
}

/// Gets values of all data properties stored on this object, ignoring attributes.
pub fn get_all_property_data(self) -> Vec<Value<'gc>> {
self.0
.borrow()
.properties
.iter()
.map(|(_, p)| p.data())
.collect()
}

/// Sets a data property on this object, ignoring attributes.
///
/// Doesn't look up the prototype chain and ignores virtual properties, but still might
Expand Down Expand Up @@ -661,6 +681,20 @@
return zuper.proto(activation);
}

// In swfv5, __proto__ property of function objects is undefined by default.
if activation.swf_version() < 6 {
if let NativeObject::Function(_) = self.native_no_super() {
let read = self.0.borrow();
if let Some(prop) = read.properties.get(istr!("__proto__"), false) {
if !prop.allow_swf_version(activation.swf_version()) {
return Value::Undefined;
}
return prop.data();
}
return Value::Undefined;

Check warning on line 694 in core/src/avm1/object/script_object.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (693–694)
}
}

self.get_data(istr!("__proto__"), activation)
}

Expand Down
52 changes: 33 additions & 19 deletions core/src/avm1/property_decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#[inline(never)]
pub fn define_properties_on(&mut self, this: Object<'gc>, decls: &[Declaration<'gc>]) {
for decl in decls {
decl.define_on(self.strings, this, self.fn_proto);
decl.define_on(self, this);
}
}

Expand Down Expand Up @@ -138,21 +138,18 @@
/// Defines the field represented by this declaration on a [`Object`].
/// Returns the value defined on the object, or `undefined` if this declaration
/// defined a property.
pub fn define_on(
&self,
context: &mut StringContext<'gc>,
this: Object<'gc>,
fn_proto: Object<'gc>,
) -> Value<'gc> {
pub fn define_on(&self, context: &mut DeclContext<'_, 'gc>, this: Object<'gc>) -> Value<'gc> {
let mc = context.gc();

let name = context.intern_static(WStr::from_units(self.name));
let name = context.strings.intern_static(WStr::from_units(self.name));
let value = match self.kind {
DeclKind::Property { getter, setter } => {
// Property objects are unobservable by user code, so a bare function is enough.
let getter = FunctionObject::native(getter).build(context, fn_proto, None);
let setter = setter
.map(|setter| FunctionObject::native(setter).build(context, fn_proto, None));
let getter =
FunctionObject::native(getter).build(context.strings, context.fn_proto, None);
let setter = setter.map(|setter| {
FunctionObject::native(setter).build(context.strings, context.fn_proto, None)
});
this.add_property(mc, name.into(), getter, setter, self.attributes);
return Value::Undefined;
}
Expand All @@ -162,25 +159,42 @@
setter,
} => {
// Property objects are unobservable by user code, so a bare function is enough.
let getter =
FunctionObject::table_native(native, getter).build(context, fn_proto, None);
let getter = FunctionObject::table_native(native, getter).build(
context.strings,
context.fn_proto,
None,
);
let setter = setter.map(|setter| {
FunctionObject::table_native(native, setter).build(context, fn_proto, None)
FunctionObject::table_native(native, setter).build(
context.strings,
context.fn_proto,
None,
)
});
this.add_property(mc, name.into(), getter, setter, self.attributes);
return Value::Undefined;
}
DeclKind::Method(f) | DeclKind::Function(f) => {
let p = matches!(self.kind, DeclKind::Function(_)).then_some(fn_proto);
FunctionObject::native(f).build(context, fn_proto, p).into()
let p = if matches!(self.kind, DeclKind::Function(_)) {
Some(Object::new(context.strings, Some(context.object_proto)))
} else {
None
};
FunctionObject::native(f)
.build(context.strings, context.fn_proto, p)
.into()
}
DeclKind::TableMethod(f, index) | DeclKind::TableFunction(f, index) => {
let p = matches!(self.kind, DeclKind::Function(_)).then_some(fn_proto);
let p = if matches!(self.kind, DeclKind::TableFunction(_, _)) {
Some(Object::new(context.strings, Some(context.object_proto)))

Check warning on line 189 in core/src/avm1/property_decl.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (189)
} else {
None
};
FunctionObject::table_native(f, index)
.build(context, fn_proto, p)
.build(context.strings, context.fn_proto, p)
.into()
}
DeclKind::String(s) => context.intern_static(WStr::from_units(s)).into(),
DeclKind::String(s) => context.strings.intern_static(WStr::from_units(s)).into(),
DeclKind::Bool(b) => b.into(),
DeclKind::Int(i) => i.into(),
DeclKind::Float(f) => f.into(),
Expand Down
Loading
Loading