core: Trying to access a named shape should resolve to the parent as they have no object representation
This commit is contained in:
parent
4c1c3cc105
commit
771f568509
|
@ -1127,7 +1127,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
let object_val = self.context.avm1.pop();
|
let object_val = self.context.avm1.pop();
|
||||||
let object = object_val.coerce_to_object(self);
|
let object = object_val.coerce_to_object(self);
|
||||||
|
|
||||||
let result = object.get(name, self)?;
|
let result = object.get_non_slash_path(name, self)?;
|
||||||
self.stack_push(result);
|
self.stack_push(result);
|
||||||
|
|
||||||
Ok(FrameControl::Continue)
|
Ok(FrameControl::Continue)
|
||||||
|
@ -2563,7 +2563,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
let root = start.avm1_root();
|
let root = start.avm1_root();
|
||||||
let start = start.object().coerce_to_object(self);
|
let start = start.object().coerce_to_object(self);
|
||||||
Ok(self
|
Ok(self
|
||||||
.resolve_target_path(root, start, &path, false)?
|
.resolve_target_path(root, start, &path, false, true)?
|
||||||
.and_then(|o| o.as_display_object()))
|
.and_then(|o| o.as_display_object()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2582,6 +2582,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
start: Object<'gc>,
|
start: Object<'gc>,
|
||||||
mut path: &WStr,
|
mut path: &WStr,
|
||||||
mut first_element: bool,
|
mut first_element: bool,
|
||||||
|
path_has_slash: bool,
|
||||||
) -> Result<Option<Object<'gc>>, Error<'gc>> {
|
) -> Result<Option<Object<'gc>>, Error<'gc>> {
|
||||||
// Empty path resolves immediately to start clip.
|
// Empty path resolves immediately to start clip.
|
||||||
if path.is_empty() {
|
if path.is_empty() {
|
||||||
|
@ -2656,10 +2657,25 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
.and_then(|o| o.as_container())
|
.and_then(|o| o.as_container())
|
||||||
.and_then(|o| o.child_by_name(name, case_sensitive))
|
.and_then(|o| o.child_by_name(name, case_sensitive))
|
||||||
{
|
{
|
||||||
child.object()
|
// If an object doesn't have an object representation, e.g. Graphic, then trying to access it
|
||||||
|
// Returns the parent instead
|
||||||
|
if path_has_slash {
|
||||||
|
child.object()
|
||||||
|
} else if let crate::display_object::DisplayObject::Graphic(_) = child {
|
||||||
|
child
|
||||||
|
.parent()
|
||||||
|
.map(|p| p.object())
|
||||||
|
.unwrap_or(Value::Undefined)
|
||||||
|
} else {
|
||||||
|
child.object()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let name = AvmString::new(self.context.gc_context, name);
|
let name = AvmString::new(self.context.gc_context, name);
|
||||||
object.get(name, self).unwrap()
|
if path_has_slash {
|
||||||
|
object.get(name, self).unwrap()
|
||||||
|
} else {
|
||||||
|
object.get_non_slash_path(name, self).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2689,6 +2705,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
start: DisplayObject<'gc>,
|
start: DisplayObject<'gc>,
|
||||||
path: &'s WStr,
|
path: &'s WStr,
|
||||||
) -> Result<Option<(Object<'gc>, &'s WStr)>, Error<'gc>> {
|
) -> Result<Option<(Object<'gc>, &'s WStr)>, Error<'gc>> {
|
||||||
|
let path_has_slash = path.contains(b'/');
|
||||||
|
|
||||||
// Find the right-most : or . in the path.
|
// Find the right-most : or . in the path.
|
||||||
// If we have one, we must resolve as a target path.
|
// If we have one, we must resolve as a target path.
|
||||||
if let Some(separator) = path.rfind(b":.".as_ref()) {
|
if let Some(separator) = path.rfind(b":.".as_ref()) {
|
||||||
|
@ -2699,9 +2717,13 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
let mut current_scope = Some(self.scope());
|
let mut current_scope = Some(self.scope());
|
||||||
while let Some(scope) = current_scope {
|
while let Some(scope) = current_scope {
|
||||||
let avm1_root = start.avm1_root();
|
let avm1_root = start.avm1_root();
|
||||||
if let Some(object) =
|
if let Some(object) = self.resolve_target_path(
|
||||||
self.resolve_target_path(avm1_root, *scope.locals(), path, true)?
|
avm1_root,
|
||||||
{
|
*scope.locals(),
|
||||||
|
path,
|
||||||
|
true,
|
||||||
|
path_has_slash,
|
||||||
|
)? {
|
||||||
return Ok(Some((object, var_name)));
|
return Ok(Some((object, var_name)));
|
||||||
}
|
}
|
||||||
current_scope = scope.parent();
|
current_scope = scope.parent();
|
||||||
|
@ -2744,6 +2766,8 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
// Resolve a variable path for a GetVariable action.
|
// Resolve a variable path for a GetVariable action.
|
||||||
let start = self.target_clip_or_root();
|
let start = self.target_clip_or_root();
|
||||||
|
|
||||||
|
let path_has_slash = path.contains(b'/');
|
||||||
|
|
||||||
// Find the right-most : or . in the path.
|
// Find the right-most : or . in the path.
|
||||||
// If we have one, we must resolve as a target path.
|
// If we have one, we must resolve as a target path.
|
||||||
if let Some(separator) = path.rfind(b":.".as_ref()) {
|
if let Some(separator) = path.rfind(b":.".as_ref()) {
|
||||||
|
@ -2754,9 +2778,13 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
let mut current_scope = Some(self.scope());
|
let mut current_scope = Some(self.scope());
|
||||||
while let Some(scope) = current_scope {
|
while let Some(scope) = current_scope {
|
||||||
let avm1_root = start.avm1_root();
|
let avm1_root = start.avm1_root();
|
||||||
if let Some(object) =
|
if let Some(object) = self.resolve_target_path(
|
||||||
self.resolve_target_path(avm1_root, *scope.locals(), path, true)?
|
avm1_root,
|
||||||
{
|
*scope.locals(),
|
||||||
|
path,
|
||||||
|
true,
|
||||||
|
path_has_slash,
|
||||||
|
)? {
|
||||||
let var_name = AvmString::new(self.context.gc_context, var_name);
|
let var_name = AvmString::new(self.context.gc_context, var_name);
|
||||||
if object.has_property(self, var_name) {
|
if object.has_property(self, var_name) {
|
||||||
return Ok(CallableValue::Callable(object, object.get(var_name, self)?));
|
return Ok(CallableValue::Callable(object, object.get(var_name, self)?));
|
||||||
|
@ -2774,7 +2802,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
while let Some(scope) = current_scope {
|
while let Some(scope) = current_scope {
|
||||||
let avm1_root = start.avm1_root();
|
let avm1_root = start.avm1_root();
|
||||||
if let Some(object) =
|
if let Some(object) =
|
||||||
self.resolve_target_path(avm1_root, *scope.locals(), &path, false)?
|
self.resolve_target_path(avm1_root, *scope.locals(), &path, false, true)?
|
||||||
{
|
{
|
||||||
return Ok(CallableValue::UnCallable(object.into()));
|
return Ok(CallableValue::UnCallable(object.into()));
|
||||||
}
|
}
|
||||||
|
@ -2841,7 +2869,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
while let Some(scope) = current_scope {
|
while let Some(scope) = current_scope {
|
||||||
let avm1_root = start.avm1_root();
|
let avm1_root = start.avm1_root();
|
||||||
if let Some(object) =
|
if let Some(object) =
|
||||||
self.resolve_target_path(avm1_root, *scope.locals(), path, true)?
|
self.resolve_target_path(avm1_root, *scope.locals(), path, true, true)?
|
||||||
{
|
{
|
||||||
let var_name = AvmString::new(self.context.gc_context, var_name);
|
let var_name = AvmString::new(self.context.gc_context, var_name);
|
||||||
object.set(var_name, value, self)?;
|
object.set(var_name, value, self)?;
|
||||||
|
@ -3075,7 +3103,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
if target.is_empty() {
|
if target.is_empty() {
|
||||||
new_target_clip = Some(base_clip);
|
new_target_clip = Some(base_clip);
|
||||||
} else if let Some(clip) = self
|
} else if let Some(clip) = self
|
||||||
.resolve_target_path(root, start, target, false)?
|
.resolve_target_path(root, start, target, false, true)?
|
||||||
.and_then(|o| o.as_display_object())
|
.and_then(|o| o.as_display_object())
|
||||||
.filter(|_| !self.base_clip.avm1_removed())
|
.filter(|_| !self.base_clip.avm1_removed())
|
||||||
// All properties invalid if base clip is removed.
|
// All properties invalid if base clip is removed.
|
||||||
|
|
|
@ -582,10 +582,10 @@ fn sort_on_compare<'a, 'gc>(fields: &'a [(AvmString<'gc>, SortOptions)]) -> Comp
|
||||||
if let [Value::Object(a), Value::Object(b)] = [a, b] {
|
if let [Value::Object(a), Value::Object(b)] = [a, b] {
|
||||||
for (field_name, options) in fields {
|
for (field_name, options) in fields {
|
||||||
let a_prop = a
|
let a_prop = a
|
||||||
.get_local_stored(*field_name, activation)
|
.get_local_stored(*field_name, activation, false)
|
||||||
.unwrap_or(Value::Undefined);
|
.unwrap_or(Value::Undefined);
|
||||||
let b_prop = b
|
let b_prop = b
|
||||||
.get_local_stored(*field_name, activation)
|
.get_local_stored(*field_name, activation, false)
|
||||||
.unwrap_or(Value::Undefined);
|
.unwrap_or(Value::Undefined);
|
||||||
|
|
||||||
let result = sort_compare(activation, &a_prop, &b_prop, *options)?;
|
let result = sort_compare(activation, &a_prop, &b_prop, *options)?;
|
||||||
|
|
|
@ -391,7 +391,7 @@ fn clone<'gc>(
|
||||||
if !bitmap_data.disposed() {
|
if !bitmap_data.disposed() {
|
||||||
return Ok(new_bitmap_data(
|
return Ok(new_bitmap_data(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
this.get_local_stored("__proto__", activation),
|
this.get_local_stored("__proto__", activation, false),
|
||||||
operations::clone(bitmap_data),
|
operations::clone(bitmap_data),
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
|
@ -756,8 +756,8 @@ fn hit_test<'gc>(
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_object(activation);
|
.coerce_to_object(activation);
|
||||||
let top_left = if let (Some(x), Some(y)) = (
|
let top_left = if let (Some(x), Some(y)) = (
|
||||||
first_point.get_local_stored("x", activation),
|
first_point.get_local_stored("x", activation, false),
|
||||||
first_point.get_local_stored("y", activation),
|
first_point.get_local_stored("y", activation, false),
|
||||||
) {
|
) {
|
||||||
(x.coerce_to_i32(activation)?, y.coerce_to_i32(activation)?)
|
(x.coerce_to_i32(activation)?, y.coerce_to_i32(activation)?)
|
||||||
} else {
|
} else {
|
||||||
|
@ -787,8 +787,8 @@ fn hit_test<'gc>(
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_object(activation);
|
.coerce_to_object(activation);
|
||||||
let second_point = if let (Some(x), Some(y)) = (
|
let second_point = if let (Some(x), Some(y)) = (
|
||||||
second_point.get_local_stored("x", activation),
|
second_point.get_local_stored("x", activation, false),
|
||||||
second_point.get_local_stored("y", activation),
|
second_point.get_local_stored("y", activation, false),
|
||||||
) {
|
) {
|
||||||
(x.coerce_to_i32(activation)?, y.coerce_to_i32(activation)?)
|
(x.coerce_to_i32(activation)?, y.coerce_to_i32(activation)?)
|
||||||
} else {
|
} else {
|
||||||
|
@ -814,10 +814,10 @@ fn hit_test<'gc>(
|
||||||
// Determine what kind of Object we have, point or rectangle.
|
// Determine what kind of Object we have, point or rectangle.
|
||||||
// Duck-typed dumb objects are allowed.
|
// Duck-typed dumb objects are allowed.
|
||||||
let compare_fields = (
|
let compare_fields = (
|
||||||
compare_object.get_local_stored("x", activation),
|
compare_object.get_local_stored("x", activation, false),
|
||||||
compare_object.get_local_stored("y", activation),
|
compare_object.get_local_stored("y", activation, false),
|
||||||
compare_object.get_local_stored("width", activation),
|
compare_object.get_local_stored("width", activation, false),
|
||||||
compare_object.get_local_stored("height", activation),
|
compare_object.get_local_stored("height", activation, false),
|
||||||
);
|
);
|
||||||
match compare_fields {
|
match compare_fields {
|
||||||
// BitmapData vs. point
|
// BitmapData vs. point
|
||||||
|
@ -1144,10 +1144,10 @@ fn pixel_dissolve<'gc>(
|
||||||
.coerce_to_object(activation);
|
.coerce_to_object(activation);
|
||||||
let (src_min_x, src_min_y, src_width, src_height) =
|
let (src_min_x, src_min_y, src_width, src_height) =
|
||||||
if let (Some(x), Some(y), Some(width), Some(height)) = (
|
if let (Some(x), Some(y), Some(width), Some(height)) = (
|
||||||
source_rect.get_local_stored("x", activation),
|
source_rect.get_local_stored("x", activation, false),
|
||||||
source_rect.get_local_stored("y", activation),
|
source_rect.get_local_stored("y", activation, false),
|
||||||
source_rect.get_local_stored("width", activation),
|
source_rect.get_local_stored("width", activation, false),
|
||||||
source_rect.get_local_stored("height", activation),
|
source_rect.get_local_stored("height", activation, false),
|
||||||
) {
|
) {
|
||||||
(
|
(
|
||||||
x.coerce_to_f64(activation)? as i32,
|
x.coerce_to_f64(activation)? as i32,
|
||||||
|
@ -1365,7 +1365,7 @@ fn compare<'gc>(
|
||||||
match operations::compare(this_bitmap_data, other_bitmap_data) {
|
match operations::compare(this_bitmap_data, other_bitmap_data) {
|
||||||
Some(bitmap_data) => Ok(new_bitmap_data(
|
Some(bitmap_data) => Ok(new_bitmap_data(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
this.get_local_stored("__proto__", activation),
|
this.get_local_stored("__proto__", activation, false),
|
||||||
bitmap_data,
|
bitmap_data,
|
||||||
)
|
)
|
||||||
.into()),
|
.into()),
|
||||||
|
@ -1406,7 +1406,7 @@ fn load_bitmap<'gc>(
|
||||||
);
|
);
|
||||||
Ok(new_bitmap_data(
|
Ok(new_bitmap_data(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
this.get_local_stored("prototype", activation),
|
this.get_local_stored("prototype", activation, false),
|
||||||
bitmap_data,
|
bitmap_data,
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
|
|
|
@ -67,7 +67,7 @@ pub fn clone<'gc>(
|
||||||
),
|
),
|
||||||
_ => return Ok(Value::Undefined),
|
_ => return Ok(Value::Undefined),
|
||||||
};
|
};
|
||||||
let proto = this.get_local_stored("__proto__", activation);
|
let proto = this.get_local_stored("__proto__", activation, false);
|
||||||
Ok(create_instance(activation, native, proto).into())
|
Ok(create_instance(activation, native, proto).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,9 +177,9 @@ impl<'gc> DisplacementMapFilter<'gc> {
|
||||||
value: Option<&Value<'gc>>,
|
value: Option<&Value<'gc>>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Some(Value::Object(object)) = value {
|
if let Some(Value::Object(object)) = value {
|
||||||
if let Some(x) = object.get_local_stored("x", activation) {
|
if let Some(x) = object.get_local_stored("x", activation, false) {
|
||||||
let x = x.coerce_to_f64(activation)?.clamp_to_i32();
|
let x = x.coerce_to_f64(activation)?.clamp_to_i32();
|
||||||
if let Some(y) = object.get_local_stored("y", activation) {
|
if let Some(y) = object.get_local_stored("y", activation, false) {
|
||||||
let y = y.coerce_to_f64(activation)?.clamp_to_i32();
|
let y = y.coerce_to_f64(activation)?.clamp_to_i32();
|
||||||
self.0.write(activation.context.gc_context).map_point = Point::new(x, y);
|
self.0.write(activation.context.gc_context).map_point = Point::new(x, y);
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,12 +114,12 @@ pub fn object_to_matrix_or_default<'gc>(
|
||||||
) -> Result<Matrix, Error<'gc>> {
|
) -> Result<Matrix, Error<'gc>> {
|
||||||
if let (Some(a), Some(b), Some(c), Some(d), Some(tx), Some(ty)) = (
|
if let (Some(a), Some(b), Some(c), Some(d), Some(tx), Some(ty)) = (
|
||||||
// These lookups do not search the prototype chain and ignore virtual properties.
|
// These lookups do not search the prototype chain and ignore virtual properties.
|
||||||
object.get_local_stored("a", activation),
|
object.get_local_stored("a", activation, false),
|
||||||
object.get_local_stored("b", activation),
|
object.get_local_stored("b", activation, false),
|
||||||
object.get_local_stored("c", activation),
|
object.get_local_stored("c", activation, false),
|
||||||
object.get_local_stored("d", activation),
|
object.get_local_stored("d", activation, false),
|
||||||
object.get_local_stored("tx", activation),
|
object.get_local_stored("tx", activation, false),
|
||||||
object.get_local_stored("ty", activation),
|
object.get_local_stored("ty", activation, false),
|
||||||
) {
|
) {
|
||||||
let a = a.coerce_to_f64(activation)? as f32;
|
let a = a.coerce_to_f64(activation)? as f32;
|
||||||
let b = b.coerce_to_f64(activation)? as f32;
|
let b = b.coerce_to_f64(activation)? as f32;
|
||||||
|
|
|
@ -148,7 +148,7 @@ fn object_to_rectangle<'gc>(
|
||||||
const NAMES: &[&str] = &["x", "y", "width", "height"];
|
const NAMES: &[&str] = &["x", "y", "width", "height"];
|
||||||
let mut values = [0; 4];
|
let mut values = [0; 4];
|
||||||
for (&name, value) in NAMES.iter().zip(&mut values) {
|
for (&name, value) in NAMES.iter().zip(&mut values) {
|
||||||
*value = match object.get_local_stored(name, activation) {
|
*value = match object.get_local_stored(name, activation, false) {
|
||||||
Some(value) => value.coerce_to_i32(activation)?,
|
Some(value) => value.coerce_to_i32(activation)?,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
}
|
}
|
||||||
|
@ -1371,10 +1371,10 @@ fn local_to_global<'gc>(
|
||||||
// It does not search the prototype chain and ignores virtual properties.
|
// It does not search the prototype chain and ignores virtual properties.
|
||||||
if let (Value::Number(x), Value::Number(y)) = (
|
if let (Value::Number(x), Value::Number(y)) = (
|
||||||
point
|
point
|
||||||
.get_local_stored("x", activation)
|
.get_local_stored("x", activation, false)
|
||||||
.unwrap_or(Value::Undefined),
|
.unwrap_or(Value::Undefined),
|
||||||
point
|
point
|
||||||
.get_local_stored("y", activation)
|
.get_local_stored("y", activation, false)
|
||||||
.unwrap_or(Value::Undefined),
|
.unwrap_or(Value::Undefined),
|
||||||
) {
|
) {
|
||||||
let local = Point::from_pixels(x, y);
|
let local = Point::from_pixels(x, y);
|
||||||
|
@ -1537,10 +1537,10 @@ fn global_to_local<'gc>(
|
||||||
// It does not search the prototype chain and ignores virtual properties.
|
// It does not search the prototype chain and ignores virtual properties.
|
||||||
if let (Value::Number(x), Value::Number(y)) = (
|
if let (Value::Number(x), Value::Number(y)) = (
|
||||||
point
|
point
|
||||||
.get_local_stored("x", activation)
|
.get_local_stored("x", activation, false)
|
||||||
.unwrap_or(Value::Undefined),
|
.unwrap_or(Value::Undefined),
|
||||||
point
|
point
|
||||||
.get_local_stored("y", activation)
|
.get_local_stored("y", activation, false)
|
||||||
.unwrap_or(Value::Undefined),
|
.unwrap_or(Value::Undefined),
|
||||||
) {
|
) {
|
||||||
let global = Point::from_pixels(x, y);
|
let global = Point::from_pixels(x, y);
|
||||||
|
|
|
@ -89,8 +89,28 @@ pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy {
|
||||||
&self,
|
&self,
|
||||||
name: impl Into<AvmString<'gc>>,
|
name: impl Into<AvmString<'gc>>,
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
is_slash_path: bool,
|
||||||
) -> Option<Value<'gc>> {
|
) -> Option<Value<'gc>> {
|
||||||
self.raw_script_object().get_local_stored(name, activation)
|
self.raw_script_object()
|
||||||
|
.get_local_stored(name, activation, is_slash_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a named property from the object, or its prototype.
|
||||||
|
fn get_non_slash_path(
|
||||||
|
&self,
|
||||||
|
name: impl Into<AvmString<'gc>>,
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
// TODO: Extract logic to a `lookup` function.
|
||||||
|
let (this, proto) = if let Some(super_object) = self.as_super_object() {
|
||||||
|
(super_object.this(), super_object.proto(activation))
|
||||||
|
} else {
|
||||||
|
((*self).into(), Value::Object((*self).into()))
|
||||||
|
};
|
||||||
|
match search_prototype(proto, name.into(), activation, this, false)? {
|
||||||
|
Some((value, _depth)) => Ok(value),
|
||||||
|
None => Ok(Value::Undefined),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve a named property from the object, or its prototype.
|
/// Retrieve a named property from the object, or its prototype.
|
||||||
|
@ -105,7 +125,7 @@ pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy {
|
||||||
} else {
|
} else {
|
||||||
((*self).into(), Value::Object((*self).into()))
|
((*self).into(), Value::Object((*self).into()))
|
||||||
};
|
};
|
||||||
match search_prototype(proto, name.into(), activation, this)? {
|
match search_prototype(proto, name.into(), activation, this, true)? {
|
||||||
Some((value, _depth)) => Ok(value),
|
Some((value, _depth)) => Ok(value),
|
||||||
None => Ok(Value::Undefined),
|
None => Ok(Value::Undefined),
|
||||||
}
|
}
|
||||||
|
@ -127,7 +147,7 @@ pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy {
|
||||||
return Err(Error::PrototypeRecursionLimit);
|
return Err(Error::PrototypeRecursionLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(value) = p.get_local_stored(name, activation) {
|
if let Some(value) = p.get_local_stored(name, activation, true) {
|
||||||
return Ok(value);
|
return Ok(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,10 +277,11 @@ pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (method, depth) = match search_prototype(Value::Object(this), name, activation, this)? {
|
let (method, depth) =
|
||||||
Some((Value::Object(method), depth)) => (method, depth),
|
match search_prototype(Value::Object(this), name, activation, this, false)? {
|
||||||
_ => return Ok(Value::Undefined),
|
Some((Value::Object(method), depth)) => (method, depth),
|
||||||
};
|
_ => return Ok(Value::Undefined),
|
||||||
|
};
|
||||||
|
|
||||||
// If the method was found on the object itself, change `depth` as-if
|
// If the method was found on the object itself, change `depth` as-if
|
||||||
// the method was found on the object's prototype.
|
// the method was found on the object's prototype.
|
||||||
|
@ -669,6 +690,7 @@ pub fn search_prototype<'gc>(
|
||||||
name: AvmString<'gc>,
|
name: AvmString<'gc>,
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
|
is_slash_path: bool,
|
||||||
) -> Result<Option<(Value<'gc>, u8)>, Error<'gc>> {
|
) -> Result<Option<(Value<'gc>, u8)>, Error<'gc>> {
|
||||||
let mut depth = 0;
|
let mut depth = 0;
|
||||||
|
|
||||||
|
@ -697,7 +719,7 @@ pub fn search_prototype<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(value) = p.get_local_stored(name, activation) {
|
if let Some(value) = p.get_local_stored(name, activation, is_slash_path) {
|
||||||
return Ok(Some((value, depth)));
|
return Ok(Some((value, depth)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: impl Into<AvmString<'gc>>,
|
name: impl Into<AvmString<'gc>>,
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
_is_slash_path: bool,
|
||||||
) -> Option<Value<'gc>> {
|
) -> Option<Value<'gc>> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
|
|
|
@ -193,13 +193,14 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
name: impl Into<AvmString<'gc>>,
|
name: impl Into<AvmString<'gc>>,
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
is_slash_path: bool,
|
||||||
) -> Option<Value<'gc>> {
|
) -> Option<Value<'gc>> {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
let obj = self.0.read();
|
let obj = self.0.read();
|
||||||
|
|
||||||
// Property search order for DisplayObjects:
|
// Property search order for DisplayObjects:
|
||||||
// 1) Actual properties on the underlying object
|
// 1) Actual properties on the underlying object
|
||||||
if let Some(value) = obj.base.get_local_stored(name, activation) {
|
if let Some(value) = obj.base.get_local_stored(name, activation, is_slash_path) {
|
||||||
return Some(value);
|
return Some(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +218,15 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
.as_container()
|
.as_container()
|
||||||
.and_then(|o| o.child_by_name(&name, activation.is_case_sensitive()))
|
.and_then(|o| o.child_by_name(&name, activation.is_case_sensitive()))
|
||||||
{
|
{
|
||||||
return Some(child.object());
|
return if is_slash_path {
|
||||||
|
Some(child.object())
|
||||||
|
// If an object doesn't have an object representation, e.g. Graphic, then trying to access it
|
||||||
|
// Returns the parent instead
|
||||||
|
} else if let crate::display_object::DisplayObject::Graphic(_) = child {
|
||||||
|
child.parent().map(|p| p.object())
|
||||||
|
} else {
|
||||||
|
Some(child.object())
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4) Display object properties such as `_x`, `_y` (never case sensitive)
|
// 4) Display object properties such as `_x`, `_y` (never case sensitive)
|
||||||
|
|
|
@ -75,6 +75,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
&self,
|
&self,
|
||||||
_name: impl Into<AvmString<'gc>>,
|
_name: impl Into<AvmString<'gc>>,
|
||||||
_activation: &mut Activation<'_, 'gc>,
|
_activation: &mut Activation<'_, 'gc>,
|
||||||
|
_is_slash_path: bool,
|
||||||
) -> Option<Value<'gc>> {
|
) -> Option<Value<'gc>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -124,7 +125,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let this = self.0.this;
|
let this = self.0.this;
|
||||||
let (method, depth) =
|
let (method, depth) =
|
||||||
match search_prototype(self.proto(activation), name, activation, this)? {
|
match search_prototype(self.proto(activation), name, activation, this, false)? {
|
||||||
Some((Value::Object(method), depth)) => (method, depth),
|
Some((Value::Object(method), depth)) => (method, depth),
|
||||||
_ => return Ok(Value::Undefined),
|
_ => return Ok(Value::Undefined),
|
||||||
};
|
};
|
||||||
|
|
|
@ -140,7 +140,7 @@ impl<'gc> Scope<'gc> {
|
||||||
if self.locals().has_property(activation, name) {
|
if self.locals().has_property(activation, name) {
|
||||||
return self
|
return self
|
||||||
.locals()
|
.locals()
|
||||||
.get(name, activation)
|
.get_non_slash_path(name, activation)
|
||||||
.map(|v| CallableValue::Callable(self.locals_cell(), v));
|
.map(|v| CallableValue::Callable(self.locals_cell(), v));
|
||||||
}
|
}
|
||||||
if let Some(scope) = self.parent() {
|
if let Some(scope) = self.parent() {
|
||||||
|
@ -155,7 +155,7 @@ impl<'gc> Scope<'gc> {
|
||||||
return activation
|
return activation
|
||||||
.root_object()
|
.root_object()
|
||||||
.coerce_to_object(activation)
|
.coerce_to_object(activation)
|
||||||
.get(name, activation)
|
.get_non_slash_path(name, activation)
|
||||||
.map(|v| CallableValue::Callable(self.locals_cell(), v));
|
.map(|v| CallableValue::Callable(self.locals_cell(), v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
root: _level0
|
||||||
|
_root.foo: foo1
|
||||||
|
_root.real: _level0.real
|
||||||
|
_root.real.foo: foo2
|
||||||
|
get(root.real.foo): foo2
|
||||||
|
get(root.real/foo): undefined
|
||||||
|
get(root.real:foo): foo2
|
||||||
|
_root.empty: _level0
|
||||||
|
_root.empty.foo: foo1
|
||||||
|
get(root.empty.foo): foo1
|
||||||
|
get(root.empty/foo): undefined
|
||||||
|
get(root.empty:foo): foo1
|
||||||
|
_root.nothere: undefined
|
||||||
|
_root.nothere.foo: undefined
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
num_frames = 1
|
Loading…
Reference in New Issue