avm1: don't use &str for activation's path & variable logic
This commit is contained in:
parent
1d9d7e6942
commit
8863b54db0
|
@ -11,7 +11,7 @@ use crate::backend::navigator::{NavigationMethod, RequestOptions};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::display_object::{DisplayObject, MovieClip, TDisplayObject, TDisplayObjectContainer};
|
use crate::display_object::{DisplayObject, MovieClip, TDisplayObject, TDisplayObjectContainer};
|
||||||
use crate::ecma_conversions::f64_to_wrapping_u32;
|
use crate::ecma_conversions::f64_to_wrapping_u32;
|
||||||
use crate::string::{AvmString, BorrowWStr, WString};
|
use crate::string::{AvmString, BorrowWStr, WStr, WString};
|
||||||
use crate::tag_utils::SwfSlice;
|
use crate::tag_utils::SwfSlice;
|
||||||
use crate::vminterface::Instantiator;
|
use crate::vminterface::Instantiator;
|
||||||
use crate::{avm_error, avm_warn};
|
use crate::{avm_error, avm_warn};
|
||||||
|
@ -557,7 +557,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
Action::SetMember => self.action_set_member(),
|
Action::SetMember => self.action_set_member(),
|
||||||
Action::SetProperty => self.action_set_property(),
|
Action::SetProperty => self.action_set_property(),
|
||||||
Action::SetTarget(target) => {
|
Action::SetTarget(target) => {
|
||||||
self.action_set_target(&target.to_str_lossy(self.encoding()))
|
let target = WString::from_utf8_owned(target.to_string_lossy(self.encoding()));
|
||||||
|
self.action_set_target(target.borrow())
|
||||||
}
|
}
|
||||||
Action::SetTarget2 => self.action_set_target2(),
|
Action::SetTarget2 => self.action_set_target2(),
|
||||||
Action::SetVariable => self.action_set_variable(),
|
Action::SetVariable => self.action_set_variable(),
|
||||||
|
@ -759,12 +760,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
} else {
|
} else {
|
||||||
// An optional path to a MovieClip and a frame #/label, such as "/clip:framelabel".
|
// An optional path to a MovieClip and a frame #/label, such as "/clip:framelabel".
|
||||||
let frame_path = arg.coerce_to_string(self)?;
|
let frame_path = arg.coerce_to_string(self)?;
|
||||||
if let Some((clip, frame)) = self.resolve_variable_path(target, &frame_path)? {
|
if let Some((clip, frame)) = self.resolve_variable_path(target, frame_path.borrow())? {
|
||||||
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
|
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
|
||||||
|
let frame = frame.to_utf8_lossy(); // TODO: avoid this UTF8 conversion.
|
||||||
if let Ok(frame) = frame.parse().map(f64_to_wrapping_u32) {
|
if let Ok(frame) = frame.parse().map(f64_to_wrapping_u32) {
|
||||||
// First try to parse as a frame number.
|
// First try to parse as a frame number.
|
||||||
call_frame = Some((clip, frame));
|
call_frame = Some((clip, frame));
|
||||||
} else if let Some(frame) = clip.frame_label_to_number(frame) {
|
} else if let Some(frame) = clip.frame_label_to_number(&frame) {
|
||||||
// Otherwise, it's a frame label.
|
// Otherwise, it's a frame label.
|
||||||
call_frame = Some((clip, frame.into()));
|
call_frame = Some((clip, frame.into()));
|
||||||
}
|
}
|
||||||
|
@ -1908,7 +1910,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
Ok(FrameControl::Continue)
|
Ok(FrameControl::Continue)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action_set_target(&mut self, target: &str) -> Result<FrameControl<'gc>, Error<'gc>> {
|
fn action_set_target(&mut self, target: WStr<'_>) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||||
let base_clip = self.base_clip();
|
let base_clip = self.base_clip();
|
||||||
let new_target_clip;
|
let new_target_clip;
|
||||||
let root = base_clip.avm1_root(&self.context)?;
|
let root = base_clip.avm1_root(&self.context)?;
|
||||||
|
@ -1925,13 +1927,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
} else {
|
} else {
|
||||||
avm_warn!(self, "SetTarget failed: {} not found", target);
|
avm_warn!(self, "SetTarget failed: {} not found", target);
|
||||||
// TODO: Emulate AVM1 trace error message.
|
// TODO: Emulate AVM1 trace error message.
|
||||||
|
let path = if base_clip.removed() { None } else { Some(base_clip.path()) };
|
||||||
let message = format!(
|
let message = format!(
|
||||||
"Target not found: Target=\"{}\" Base=\"{}\"",
|
"Target not found: Target=\"{}\" Base=\"{}\"",
|
||||||
target,
|
target,
|
||||||
if base_clip.removed() {
|
match &path {
|
||||||
"?".to_string()
|
Some(p) => p.borrow(),
|
||||||
} else {
|
None => WStr::from_units(b"?"),
|
||||||
base_clip.path()
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
self.context.log.avm_trace(&message);
|
self.context.log.avm_trace(&message);
|
||||||
|
@ -1966,7 +1968,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
|
|
||||||
match target {
|
match target {
|
||||||
Value::String(target) => {
|
Value::String(target) => {
|
||||||
return self.action_set_target(&target);
|
return self.action_set_target(target.borrow());
|
||||||
}
|
}
|
||||||
Value::Undefined => {
|
Value::Undefined => {
|
||||||
// Reset.
|
// Reset.
|
||||||
|
@ -1980,12 +1982,12 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
} else {
|
} else {
|
||||||
// Other objects get coerced to string.
|
// Other objects get coerced to string.
|
||||||
let target = target.coerce_to_string(self)?;
|
let target = target.coerce_to_string(self)?;
|
||||||
return self.action_set_target(&target);
|
return self.action_set_target(target.borrow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let target = target.coerce_to_string(self)?;
|
let target = target.coerce_to_string(self)?;
|
||||||
return self.action_set_target(&target);
|
return self.action_set_target(target.borrow());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2151,7 +2153,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
let param = self.context.avm1.pop().coerce_to_object(self);
|
let param = self.context.avm1.pop().coerce_to_object(self);
|
||||||
let result = if let Some(display_object) = param.as_display_object() {
|
let result = if let Some(display_object) = param.as_display_object() {
|
||||||
let path = display_object.path();
|
let path = display_object.path();
|
||||||
AvmString::new(self.context.gc_context, path).into()
|
AvmString::new_ucs2(self.context.gc_context, path).into()
|
||||||
} else {
|
} else {
|
||||||
Value::Undefined
|
Value::Undefined
|
||||||
};
|
};
|
||||||
|
@ -2504,7 +2506,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
let root = start.avm1_root(&self.context)?;
|
let root = start.avm1_root(&self.context)?;
|
||||||
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.borrow(), false)?
|
||||||
.and_then(|o| o.as_display_object()))
|
.and_then(|o| o.as_display_object()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2521,8 +2523,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
&mut self,
|
&mut self,
|
||||||
root: DisplayObject<'gc>,
|
root: DisplayObject<'gc>,
|
||||||
start: Object<'gc>,
|
start: Object<'gc>,
|
||||||
// TODO(moulins): replace by Str<'_> once the API is good enough.
|
mut path: WStr<'_>,
|
||||||
path: &str,
|
|
||||||
mut first_element: bool,
|
mut first_element: 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.
|
||||||
|
@ -2532,9 +2533,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
|
|
||||||
// Starting / means an absolute path starting from root.
|
// Starting / means an absolute path starting from root.
|
||||||
// (`/bar` means `_root.bar`)
|
// (`/bar` means `_root.bar`)
|
||||||
let mut path = path.as_bytes();
|
let (mut object, mut is_slash_path) = if path.starts_with(b'/') {
|
||||||
let (mut object, mut is_slash_path) = if path[0] == b'/' {
|
path = path.slice(1..);
|
||||||
path = &path[1..];
|
|
||||||
(root.object().coerce_to_object(self), true)
|
(root.object().coerce_to_object(self), true)
|
||||||
} else {
|
} else {
|
||||||
(start, false)
|
(start, false)
|
||||||
|
@ -2546,17 +2546,19 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
while !path.is_empty() {
|
while !path.is_empty() {
|
||||||
// Skip any number of leading :
|
// Skip any number of leading :
|
||||||
// `foo`, `:foo`, and `:::foo` are all the same
|
// `foo`, `:foo`, and `:::foo` are all the same
|
||||||
while path.get(0) == Some(&b':') {
|
path = path.trim_start_matches(b':');
|
||||||
path = &path[1..];
|
|
||||||
}
|
|
||||||
|
|
||||||
let val = if let b".." | b"../" | b"..:" = &path[..std::cmp::min(path.len(), 3)] {
|
let prefix = path.slice(..path.len().min(3));
|
||||||
|
let val = if prefix == b".."
|
||||||
|
|| prefix == b"../"
|
||||||
|
|| prefix == b"..:"
|
||||||
|
{
|
||||||
// Check for ..
|
// Check for ..
|
||||||
// SWF-4 style _parent
|
// SWF-4 style _parent
|
||||||
if path.get(2) == Some(&b'/') {
|
if path.try_get(2) == Some(u16::from(b'/')) {
|
||||||
is_slash_path = true;
|
is_slash_path = true;
|
||||||
}
|
}
|
||||||
path = path.get(3..).unwrap_or(&[]);
|
path = path.try_slice(3..).unwrap_or_default();
|
||||||
if let Some(parent) = object.as_display_object().and_then(|o| o.avm1_parent()) {
|
if let Some(parent) = object.as_display_object().and_then(|o| o.avm1_parent()) {
|
||||||
parent.object()
|
parent.object()
|
||||||
} else {
|
} else {
|
||||||
|
@ -2571,10 +2573,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
// TODO: SWF4 is probably more restrictive.
|
// TODO: SWF4 is probably more restrictive.
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
while pos < path.len() {
|
while pos < path.len() {
|
||||||
match path[pos] {
|
match u8::try_from(path.get(pos)) {
|
||||||
b':' => break,
|
Ok(b':') => break,
|
||||||
b'.' if !is_slash_path => break,
|
Ok(b'.') if !is_slash_path => break,
|
||||||
b'/' => {
|
Ok(b'/') => {
|
||||||
is_slash_path = true;
|
is_slash_path = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2584,15 +2586,12 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slice out the identifier and step the cursor past the delimiter.
|
// Slice out the identifier and step the cursor past the delimiter.
|
||||||
let ident = &path[..pos];
|
let name = path.slice(..pos);
|
||||||
path = path.get(pos + 1..).unwrap_or(&[]);
|
path = path.try_slice(pos + 1..).unwrap_or_default();
|
||||||
|
|
||||||
// Guaranteed to be valid UTF-8.
|
if first_element && name == b"this" {
|
||||||
let name = unsafe { std::str::from_utf8_unchecked(ident) };
|
|
||||||
|
|
||||||
if first_element && name == "this" {
|
|
||||||
self.this_cell().into()
|
self.this_cell().into()
|
||||||
} else if first_element && name == "_root" {
|
} else if first_element && name == b"_root" {
|
||||||
self.root_object()?
|
self.root_object()?
|
||||||
} else {
|
} else {
|
||||||
// Get the value from the object.
|
// Get the value from the object.
|
||||||
|
@ -2601,13 +2600,11 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
if let Some(child) = object
|
if let Some(child) = object
|
||||||
.as_display_object()
|
.as_display_object()
|
||||||
.and_then(|o| o.as_container())
|
.and_then(|o| o.as_container())
|
||||||
.and_then(|o| {
|
.and_then(|o| o.child_by_name(name, case_sensitive))
|
||||||
o.child_by_name(WString::from_utf8(name).borrow(), case_sensitive)
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
child.object()
|
child.object()
|
||||||
} else {
|
} else {
|
||||||
let name = AvmString::new(self.context.gc_context, name);
|
let name = AvmString::new_ucs2(self.context.gc_context, name.into());
|
||||||
object.get(name, self).unwrap()
|
object.get(name, self).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2634,29 +2631,14 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
pub fn resolve_variable_path<'s>(
|
pub fn resolve_variable_path<'s>(
|
||||||
&mut self,
|
&mut self,
|
||||||
start: DisplayObject<'gc>,
|
start: DisplayObject<'gc>,
|
||||||
// TODO(moulins): replace by Str<'_> once the API is good enough.
|
path: WStr<'s>,
|
||||||
path: &'s str,
|
) -> Result<Option<(Object<'gc>, WStr<'s>)>, Error<'gc>> {
|
||||||
) -> Result<Option<(Object<'gc>, &'s str)>, Error<'gc>> {
|
|
||||||
// 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.
|
||||||
// We also check for a / to skip some unnecessary work later.
|
if let Some(separator) = path.rfind(b":.".as_ref()) {
|
||||||
let mut has_slash = false;
|
|
||||||
let mut var_iter = path.as_bytes().rsplitn(2, |c| match c {
|
|
||||||
b':' | b'.' => true,
|
|
||||||
b'/' => {
|
|
||||||
has_slash = true;
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let b = var_iter.next();
|
|
||||||
let a = var_iter.next();
|
|
||||||
if let (Some(path), Some(var_name)) = (a, b) {
|
|
||||||
// We have a . or :, so this is a path to an object plus a variable name.
|
// We have a . or :, so this is a path to an object plus a variable name.
|
||||||
// We resolve it directly on the targeted object.
|
// We resolve it directly on the targeted object.
|
||||||
let path = unsafe { std::str::from_utf8_unchecked(path) };
|
let (path, var_name) = (path.slice(..separator), path.slice(separator + 1..));
|
||||||
let var_name = unsafe { std::str::from_utf8_unchecked(var_name) };
|
|
||||||
|
|
||||||
let mut current_scope = Some(self.scope_cell());
|
let mut current_scope = Some(self.scope_cell());
|
||||||
while let Some(scope) = current_scope {
|
while let Some(scope) = current_scope {
|
||||||
|
@ -2708,24 +2690,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
|
|
||||||
// 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.
|
||||||
// We also check for a / to skip some unnecessary work later.
|
if let Some(separator) = path.rfind(b":.".as_ref()) {
|
||||||
let mut has_slash = false;
|
|
||||||
let mut var_iter = path.as_bytes().rsplitn(2, |c| match c {
|
|
||||||
b':' | b'.' => true,
|
|
||||||
b'/' => {
|
|
||||||
has_slash = true;
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let b = var_iter.next();
|
|
||||||
let a = var_iter.next();
|
|
||||||
if let (Some(path), Some(var_name)) = (a, b) {
|
|
||||||
// We have a . or :, so this is a path to an object plus a variable name.
|
// We have a . or :, so this is a path to an object plus a variable name.
|
||||||
// We resolve it directly on the targeted object.
|
// We resolve it directly on the targeted object.
|
||||||
let path = unsafe { std::str::from_utf8_unchecked(path) };
|
let (path, var_name) = (path.slice(..separator), path.slice(separator + 1..));
|
||||||
let var_name = unsafe { std::str::from_utf8_unchecked(var_name) };
|
|
||||||
|
|
||||||
let mut current_scope = Some(self.scope_cell());
|
let mut current_scope = Some(self.scope_cell());
|
||||||
while let Some(scope) = current_scope {
|
while let Some(scope) = current_scope {
|
||||||
|
@ -2733,7 +2701,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
if let Some(object) =
|
if let Some(object) =
|
||||||
self.resolve_target_path(avm1_root, *scope.read().locals(), path, true)?
|
self.resolve_target_path(avm1_root, *scope.read().locals(), path, true)?
|
||||||
{
|
{
|
||||||
let var_name = AvmString::new(self.context.gc_context, var_name);
|
let var_name = AvmString::new_ucs2(self.context.gc_context, var_name.into());
|
||||||
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)?));
|
||||||
}
|
}
|
||||||
|
@ -2745,14 +2713,16 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it doesn't have a trailing variable, it can still be a slash path.
|
// If it doesn't have a trailing variable, it can still be a slash path.
|
||||||
// We can skip this step if we didn't find a slash above.
|
if path.contains(b'/') {
|
||||||
if has_slash {
|
|
||||||
let mut current_scope = Some(self.scope_cell());
|
let mut current_scope = Some(self.scope_cell());
|
||||||
while let Some(scope) = current_scope {
|
while let Some(scope) = current_scope {
|
||||||
let avm1_root = start.avm1_root(&self.context)?;
|
let avm1_root = start.avm1_root(&self.context)?;
|
||||||
if let Some(object) =
|
if let Some(object) = self.resolve_target_path(
|
||||||
self.resolve_target_path(avm1_root, *scope.read().locals(), &path, false)?
|
avm1_root,
|
||||||
{
|
*scope.read().locals(),
|
||||||
|
path.borrow(),
|
||||||
|
false,
|
||||||
|
)? {
|
||||||
return Ok(CallableValue::UnCallable(object.into()));
|
return Ok(CallableValue::UnCallable(object.into()));
|
||||||
}
|
}
|
||||||
current_scope = scope.read().parent_cell();
|
current_scope = scope.read().parent_cell();
|
||||||
|
@ -2801,16 +2771,12 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
|
|
||||||
// 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.
|
||||||
let mut var_iter = path.as_bytes().rsplitn(2, |&c| c == b':' || c == b'.');
|
let separator = path.rfind(b":.".as_ref());
|
||||||
let b = var_iter.next();
|
|
||||||
let a = var_iter.next();
|
|
||||||
|
|
||||||
if let (Some(path), Some(var_name)) = (a, b) {
|
if let Some(sep) = separator {
|
||||||
// We have a . or :, so this is a path to an object plus a variable name.
|
// We have a . or :, so this is a path to an object plus a variable name.
|
||||||
// We resolve it directly on the targeted object.
|
// We resolve it directly on the targeted object.
|
||||||
let path = unsafe { std::str::from_utf8_unchecked(path) };
|
let (path, var_name) = (path.slice(..sep), path.slice(sep + 1..));
|
||||||
let var_name = unsafe { std::str::from_utf8_unchecked(var_name) };
|
|
||||||
let var_name = AvmString::new(self.context.gc_context, var_name);
|
|
||||||
|
|
||||||
let mut current_scope = Some(self.scope_cell());
|
let mut current_scope = Some(self.scope_cell());
|
||||||
while let Some(scope) = current_scope {
|
while let Some(scope) = current_scope {
|
||||||
|
@ -2818,6 +2784,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
if let Some(object) =
|
if let Some(object) =
|
||||||
self.resolve_target_path(avm1_root, *scope.read().locals(), path, true)?
|
self.resolve_target_path(avm1_root, *scope.read().locals(), path, true)?
|
||||||
{
|
{
|
||||||
|
let var_name = AvmString::new_ucs2(self.context.gc_context, var_name.into());
|
||||||
object.set(var_name, value, self)?;
|
object.set(var_name, value, self)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ pub fn to_string<'gc>(
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
Ok(AvmString::new(activation.context.gc_context, display_object.path()).into())
|
Ok(AvmString::new_ucs2(activation.context.gc_context, display_object.path()).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1004,7 +1004,7 @@ pub fn goto_frame<'gc>(
|
||||||
// This can direct other clips than the one this method was called on!
|
// This can direct other clips than the one this method was called on!
|
||||||
let frame_path = val.coerce_to_string(activation)?;
|
let frame_path = val.coerce_to_string(activation)?;
|
||||||
if let Some((clip, frame)) =
|
if let Some((clip, frame)) =
|
||||||
activation.resolve_variable_path(movie_clip.into(), &frame_path)?
|
activation.resolve_variable_path(movie_clip.into(), frame_path.borrow())?
|
||||||
{
|
{
|
||||||
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
|
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
|
||||||
// TODO(moulins): we need WStr::parse for avoiding allocation here.
|
// TODO(moulins): we need WStr::parse for avoiding allocation here.
|
||||||
|
|
|
@ -782,7 +782,7 @@ fn set_rotation<'gc>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target<'gc>(activation: &mut Activation<'_, 'gc, '_>, this: DisplayObject<'gc>) -> Value<'gc> {
|
fn target<'gc>(activation: &mut Activation<'_, 'gc, '_>, this: DisplayObject<'gc>) -> Value<'gc> {
|
||||||
AvmString::new(activation.context.gc_context, this.slash_path()).into()
|
AvmString::new_ucs2(activation.context.gc_context, this.slash_path()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frames_loaded<'gc>(
|
fn frames_loaded<'gc>(
|
||||||
|
@ -794,8 +794,8 @@ fn frames_loaded<'gc>(
|
||||||
.map_or(Value::Undefined, Value::from)
|
.map_or(Value::Undefined, Value::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name<'gc>(activation: &mut Activation<'_, 'gc, '_>, this: DisplayObject<'gc>) -> Value<'gc> {
|
fn name<'gc>(_activation: &mut Activation<'_, 'gc, '_>, this: DisplayObject<'gc>) -> Value<'gc> {
|
||||||
AvmString::new(activation.context.gc_context, this.name().to_string()).into()
|
this.name().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_name<'gc>(
|
fn set_name<'gc>(
|
||||||
|
@ -817,7 +817,7 @@ fn drop_target<'gc>(
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|| "".into(),
|
|| "".into(),
|
||||||
|drop_target| {
|
|drop_target| {
|
||||||
AvmString::new(activation.context.gc_context, drop_target.slash_path()).into()
|
AvmString::new_ucs2(activation.context.gc_context, drop_target.slash_path()).into()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::context::{RenderContext, UpdateContext};
|
||||||
use crate::drawing::Drawing;
|
use crate::drawing::Drawing;
|
||||||
use crate::player::NEWEST_PLAYER_VERSION;
|
use crate::player::NEWEST_PLAYER_VERSION;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::string::AvmString;
|
use crate::string::{AvmString, BorrowWStr, WString};
|
||||||
use crate::tag_utils::SwfMovie;
|
use crate::tag_utils::SwfMovie;
|
||||||
use crate::transform::Transform;
|
use crate::transform::Transform;
|
||||||
use crate::types::{Degrees, Percent};
|
use crate::types::{Degrees, Percent};
|
||||||
|
@ -789,34 +789,34 @@ pub trait TDisplayObject<'gc>:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the dot-syntax path to this display object, e.g. `_level0.foo.clip`
|
/// Returns the dot-syntax path to this display object, e.g. `_level0.foo.clip`
|
||||||
fn path(&self) -> String {
|
fn path(&self) -> WString {
|
||||||
if let Some(parent) = self.avm1_parent() {
|
if let Some(parent) = self.avm1_parent() {
|
||||||
let mut path = parent.path();
|
let mut path = parent.path();
|
||||||
path.push('.');
|
path.push_byte(b'.');
|
||||||
path.push_str(&*self.name());
|
path.push_str(self.name().borrow());
|
||||||
path
|
path
|
||||||
} else {
|
} else {
|
||||||
format!("_level{}", self.depth())
|
WString::from_utf8_owned(format!("_level{}", self.depth()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the Flash 4 slash-syntax path to this display object, e.g. `/foo/clip`.
|
/// Returns the Flash 4 slash-syntax path to this display object, e.g. `/foo/clip`.
|
||||||
/// Returned by the `_target` property in AVM1.
|
/// Returned by the `_target` property in AVM1.
|
||||||
fn slash_path(&self) -> String {
|
fn slash_path(&self) -> WString {
|
||||||
fn build_slash_path(object: DisplayObject<'_>) -> String {
|
fn build_slash_path(object: DisplayObject<'_>) -> WString {
|
||||||
if let Some(parent) = object.avm1_parent() {
|
if let Some(parent) = object.avm1_parent() {
|
||||||
let mut path = build_slash_path(parent);
|
let mut path = build_slash_path(parent);
|
||||||
path.push('/');
|
path.push_byte(b'/');
|
||||||
path.push_str(&*object.name());
|
path.push_str(object.name().borrow());
|
||||||
path
|
path
|
||||||
} else {
|
} else {
|
||||||
let level = object.depth();
|
let level = object.depth();
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
// _level0 does not append its name in slash syntax.
|
// _level0 does not append its name in slash syntax.
|
||||||
String::new()
|
WString::new()
|
||||||
} else {
|
} else {
|
||||||
// Other levels do append their name.
|
// Other levels do append their name.
|
||||||
format!("_level{}", level)
|
WString::from_utf8_owned(format!("_level{}", level))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -825,7 +825,7 @@ pub trait TDisplayObject<'gc>:
|
||||||
build_slash_path((*self).into())
|
build_slash_path((*self).into())
|
||||||
} else {
|
} else {
|
||||||
// _target of _level0 should just be '/'.
|
// _target of _level0 should just be '/'.
|
||||||
'/'.to_string()
|
WString::from_unit(b'/'.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::font::{round_down_to_pixel, Glyph, TextRenderSettings};
|
||||||
use crate::html::{BoxBounds, FormatSpans, LayoutBox, LayoutContent, TextFormat};
|
use crate::html::{BoxBounds, FormatSpans, LayoutBox, LayoutContent, TextFormat};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::shape_utils::DrawCommand;
|
use crate::shape_utils::DrawCommand;
|
||||||
use crate::string::{utils as string_utils, AvmString};
|
use crate::string::{utils as string_utils, AvmString, BorrowWStr, WString};
|
||||||
use crate::tag_utils::SwfMovie;
|
use crate::tag_utils::SwfMovie;
|
||||||
use crate::transform::Transform;
|
use crate::transform::Transform;
|
||||||
use crate::vminterface::{AvmObject, AvmType, Instantiator};
|
use crate::vminterface::{AvmObject, AvmType, Instantiator};
|
||||||
|
@ -1047,7 +1047,7 @@ impl<'gc> EditText<'gc> {
|
||||||
|
|
||||||
// Avoid double-borrows by copying the string.
|
// Avoid double-borrows by copying the string.
|
||||||
// TODO: Can we avoid this somehow? Maybe when we have a better string type.
|
// TODO: Can we avoid this somehow? Maybe when we have a better string type.
|
||||||
let variable = (*var_path).to_string();
|
let variable_path = WString::from_utf8(&var_path);
|
||||||
drop(var_path);
|
drop(var_path);
|
||||||
|
|
||||||
let parent = self.avm1_parent().unwrap();
|
let parent = self.avm1_parent().unwrap();
|
||||||
|
@ -1058,9 +1058,10 @@ impl<'gc> EditText<'gc> {
|
||||||
activation.context.swf.version(),
|
activation.context.swf.version(),
|
||||||
|activation| {
|
|activation| {
|
||||||
if let Ok(Some((object, property))) =
|
if let Ok(Some((object, property))) =
|
||||||
activation.resolve_variable_path(parent, &variable)
|
activation.resolve_variable_path(parent, variable_path.borrow())
|
||||||
{
|
{
|
||||||
let property = AvmString::new(activation.context.gc_context, property);
|
let property =
|
||||||
|
AvmString::new_ucs2(activation.context.gc_context, property.into());
|
||||||
|
|
||||||
// If this text field was just created, we immediately propagate the text to the variable (or vice versa).
|
// If this text field was just created, we immediately propagate the text to the variable (or vice versa).
|
||||||
if set_initial_value {
|
if set_initial_value {
|
||||||
|
@ -1124,11 +1125,11 @@ impl<'gc> EditText<'gc> {
|
||||||
if let Some(variable) = self.variable() {
|
if let Some(variable) = self.variable() {
|
||||||
// Avoid double-borrows by copying the string.
|
// Avoid double-borrows by copying the string.
|
||||||
// TODO: Can we avoid this somehow? Maybe when we have a better string type.
|
// TODO: Can we avoid this somehow? Maybe when we have a better string type.
|
||||||
let variable_path = variable.to_string();
|
let variable_path = WString::from_utf8(&variable);
|
||||||
drop(variable);
|
drop(variable);
|
||||||
|
|
||||||
if let Ok(Some((object, property))) =
|
if let Ok(Some((object, property))) = activation
|
||||||
activation.resolve_variable_path(self.avm1_parent().unwrap(), &variable_path)
|
.resolve_variable_path(self.avm1_parent().unwrap(), variable_path.borrow())
|
||||||
{
|
{
|
||||||
let html_text = self.html_text(&mut activation.context);
|
let html_text = self.html_text(&mut activation.context);
|
||||||
|
|
||||||
|
@ -1139,7 +1140,8 @@ impl<'gc> EditText<'gc> {
|
||||||
self.avm1_parent().unwrap(),
|
self.avm1_parent().unwrap(),
|
||||||
activation.context.swf.version(),
|
activation.context.swf.version(),
|
||||||
|activation| {
|
|activation| {
|
||||||
let property = AvmString::new(activation.context.gc_context, property);
|
let property =
|
||||||
|
AvmString::new_ucs2(activation.context.gc_context, property.into());
|
||||||
let _ = object.set(
|
let _ = object.set(
|
||||||
property,
|
property,
|
||||||
AvmString::new(activation.context.gc_context, html_text).into(),
|
AvmString::new(activation.context.gc_context, html_text).into(),
|
||||||
|
|
Loading…
Reference in New Issue