Skip to content

Commit ca50662

Browse files
committed
avm2: Implement Dxns and DxnsLate opcodes
1 parent ba02cfd commit ca50662

File tree

7 files changed

+106
-19
lines changed

7 files changed

+106
-19
lines changed

core/src/avm2.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ pub struct Avm2<'gc> {
119119
/// Scopes currently present of the scope stack.
120120
scope_stack: Vec<Scope<'gc>>,
121121

122+
/// Default XML namespace stack for E4X operations.
123+
/// The last element is the current default namespace.
124+
default_xml_namespace_stack: Vec<AvmString<'gc>>,
125+
122126
/// The current call stack of the player.
123127
call_stack: GcRefLock<'gc, CallStack<'gc>>,
124128

@@ -214,6 +218,7 @@ impl<'gc> Avm2<'gc> {
214218
player_runtime,
215219
stack: Vec::with_capacity(PREALLOCATED_STACK_SIZE),
216220
scope_stack: Vec::new(),
221+
default_xml_namespace_stack: Vec::new(),
217222
call_stack: GcRefLock::new(mc, CallStack::new().into()),
218223
playerglobals_domain,
219224
stage_domain,
@@ -818,6 +823,25 @@ impl<'gc> Avm2<'gc> {
818823
self.scope_stack.pop();
819824
}
820825

826+
/// Push a default XML namespace onto the default XML namespace stack.
827+
pub fn push_default_xml_namespace(&mut self, namespace: AvmString<'gc>) {
828+
self.default_xml_namespace_stack.push(namespace);
829+
}
830+
831+
/// Pop a default XML namespace from the default XML namespace stack.
832+
pub fn pop_default_xml_namespace(&mut self) {
833+
self.default_xml_namespace_stack.pop();
834+
}
835+
836+
/// Get the current default XML namespace.
837+
/// Returns an empty string if no default XML namespace is set.
838+
pub fn current_default_xml_namespace(&self, strings: &StringContext<'gc>) -> AvmString<'gc> {
839+
self.default_xml_namespace_stack
840+
.last()
841+
.copied()
842+
.unwrap_or_else(|| AvmString::new_ascii_static(strings.gc(), b""))
843+
}
844+
821845
#[cfg(feature = "avm_debug")]
822846
#[inline]
823847
pub fn show_debug_output(&self) -> bool {

core/src/avm2/activation.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ pub struct Activation<'a, 'gc: 'a> {
7777
/// The index where the scope frame starts.
7878
scope_depth: usize,
7979

80+
/// The index where the default XML namespace frame starts.
81+
/// Used to implement proper function-level scoping for default XML namespace.
82+
default_xml_namespace_depth: usize,
83+
8084
/// In avmplus, some behavior differs slightly depending on whether the JIT
8185
/// or the interpreter is used. Most methods are executed in "JIT mode", but
8286
/// in some cases "interpreter mode" is used instead: for example, script
@@ -125,6 +129,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
125129
bound_class: None,
126130
stack_depth: context.avm2.stack.len(),
127131
scope_depth: context.avm2.scope_stack.len(),
132+
default_xml_namespace_depth: context.avm2.default_xml_namespace_stack.len(),
128133
is_interpreter: false,
129134
context,
130135
}
@@ -149,6 +154,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
149154
bound_class: None,
150155
stack_depth: context.avm2.stack.len(),
151156
scope_depth: context.avm2.scope_stack.len(),
157+
default_xml_namespace_depth: context.avm2.default_xml_namespace_stack.len(),
152158
is_interpreter: false,
153159
context,
154160
}
@@ -177,6 +183,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
177183
bound_class: Some(script.global_class()),
178184
stack_depth: context.avm2.stack.len(),
179185
scope_depth: context.avm2.scope_stack.len(),
186+
default_xml_namespace_depth: 0, // Scripts inherit global default XML namespace
180187
is_interpreter: true, // Script initializers are always in interpreter mode
181188
context,
182189
};
@@ -377,6 +384,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
377384
self.bound_class = bound_class;
378385
self.stack_depth = self.context.avm2.stack.len();
379386
self.scope_depth = self.context.avm2.scope_stack.len();
387+
self.default_xml_namespace_depth = self.context.avm2.default_xml_namespace_stack.len();
380388
self.is_interpreter = false;
381389

382390
// Resolve parameters and return type
@@ -483,6 +491,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
483491
bound_class,
484492
stack_depth: context.avm2.stack.len(),
485493
scope_depth: context.avm2.scope_stack.len(),
494+
default_xml_namespace_depth: context.avm2.default_xml_namespace_stack.len(), // Builtin functions start with isolated scope
486495
is_interpreter: false,
487496
context,
488497
}
@@ -626,6 +635,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
626635
pub fn cleanup(&mut self) {
627636
self.clear_stack_and_locals();
628637
self.clear_scope();
638+
self.clear_default_xml_namespace();
629639
}
630640

631641
/// Clears the operand stack used by this activation.
@@ -650,6 +660,13 @@ impl<'a, 'gc> Activation<'a, 'gc> {
650660
self.avm2().scope_stack.truncate(scope_depth);
651661
}
652662

663+
/// Clears the default XML namespace stack used by this activation.
664+
#[inline]
665+
fn clear_default_xml_namespace(&mut self) {
666+
let default_xml_namespace_depth = self.default_xml_namespace_depth;
667+
self.avm2().default_xml_namespace_stack.truncate(default_xml_namespace_depth);
668+
}
669+
653670
/// Get the superclass of the class that defined the currently-executing
654671
/// method, if it exists.
655672
///
@@ -664,6 +681,12 @@ impl<'a, 'gc> Activation<'a, 'gc> {
664681
self.bound_class
665682
}
666683

684+
/// Get the current default XML namespace.
685+
/// Returns an empty string if no default XML namespace is set.
686+
pub fn default_xml_namespace(&self) -> AvmString<'gc> {
687+
self.context.avm2.current_default_xml_namespace(self.strings_ref())
688+
}
689+
667690
/// Retrieve a method entry from the current ABC file's method table.
668691
fn table_method(
669692
&mut self,
@@ -845,7 +868,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
845868
Op::BkptLine { line_num } => self.op_bkpt_line(*line_num),
846869
Op::Timestamp => self.op_timestamp(),
847870
Op::TypeOf => self.op_type_of(),
848-
Op::Dxns { .. } => self.op_dxns(),
871+
Op::Dxns { string } => self.op_dxns(*string),
849872
Op::DxnsLate => self.op_dxns_late(),
850873
Op::EscXAttr => self.op_esc_xattr(),
851874
Op::EscXElem => self.op_esc_elem(),
@@ -2627,14 +2650,21 @@ impl<'a, 'gc> Activation<'a, 'gc> {
26272650
}
26282651

26292652
/// Implements `Op::Dxns`
2630-
fn op_dxns(&mut self) -> Result<(), Error<'gc>> {
2631-
Err("Unimplemented opcode Dxns.".into())
2653+
fn op_dxns(&mut self, string: AvmAtom<'gc>) -> Result<(), Error<'gc>> {
2654+
let namespace_uri: AvmString<'gc> = string.into();
2655+
self.avm2().push_default_xml_namespace(namespace_uri);
2656+
2657+
Ok(())
26322658
}
26332659

26342660
/// Implements `Op::DxnsLate`
26352661
fn op_dxns_late(&mut self) -> Result<(), Error<'gc>> {
2636-
let _ = self.pop_stack();
2637-
Err("Unimplemented opcode DxnsLate.".into())
2662+
let namespace_value = self.pop_stack();
2663+
let namespace_uri = namespace_value.coerce_to_string(self)?;
2664+
2665+
self.avm2().push_default_xml_namespace(namespace_uri);
2666+
2667+
Ok(())
26382668
}
26392669

26402670
/// Implements `Op::EscXAttr`

core/src/avm2/e4x.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::avm2::error::{make_error_1010, make_error_1085, make_error_1118, type_error};
22
use crate::avm2::object::{E4XOrXml, FunctionObject, NamespaceObject};
3-
use crate::avm2::{Activation, Error, Multiname, TObject, Value};
3+
use crate::avm2::{Activation, Error, Multiname, Namespace, TObject, Value};
44
use crate::string::{AvmString, StringContext, WStr, WString};
55
use crate::xml::custom_unescape;
66

@@ -1084,7 +1084,18 @@ impl<'gc> E4XNode<'gc> {
10841084
ResolveResult::Unknown(ns) => {
10851085
return Err(make_unknown_ns_error(activation, ns, name));
10861086
}
1087-
ResolveResult::Unbound => None,
1087+
ResolveResult::Unbound => {
1088+
// Use default XML namespace if available
1089+
let default_ns_uri = activation.default_xml_namespace();
1090+
if default_ns_uri.is_empty() {
1091+
None
1092+
} else {
1093+
Some(E4XNamespace {
1094+
prefix: None,
1095+
uri: default_ns_uri,
1096+
})
1097+
}
1098+
}
10881099
};
10891100

10901101
let result = E4XNode(Gc::new(
@@ -1673,7 +1684,17 @@ pub fn string_to_multiname<'gc>(
16731684
} else if &*name == b"*" {
16741685
Multiname::any()
16751686
} else {
1676-
Multiname::new(activation.avm2().namespaces.public_all(), name)
1687+
let default_ns_uri = activation.default_xml_namespace();
1688+
if default_ns_uri.is_empty() {
1689+
Multiname::new(activation.avm2().namespaces.public_all(), name)
1690+
} else {
1691+
let namespace = Namespace::package(
1692+
default_ns_uri,
1693+
activation.avm2().root_api_version,
1694+
activation.strings()
1695+
);
1696+
Multiname::new(namespace, name)
1697+
}
16771698
}
16781699
}
16791700

core/src/avm2/globals/q_name.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,18 @@ pub fn q_name_constructor<'gc>(
9292

9393
if &*local != b"*" {
9494
this.set_local_name(activation.gc(), local);
95-
Some(activation.avm2().find_public_namespace())
95+
96+
// Use default XML namespace if set, otherwise use public namespace
97+
let default_ns_uri = activation.default_xml_namespace();
98+
if default_ns_uri.is_empty() {
99+
Some(activation.avm2().find_public_namespace())
100+
} else {
101+
Some(Namespace::package(
102+
default_ns_uri,
103+
activation.avm2().root_api_version,
104+
activation.strings(),
105+
))
106+
}
96107
} else {
97108
None
98109
}

core/src/avm2/object/xml_object.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -715,15 +715,18 @@ fn handle_input_multiname<'gc>(
715715
.local_name()
716716
.map(|name| string_to_multiname(activation, name))
717717
{
718-
// Copy the namespaces from the previous name,
719-
// but make sure to definitely include the public namespace.
720-
if !new_name.is_any_namespace() {
721-
let mut ns = Vec::new();
722-
ns.extend(name.namespace_set());
723-
if !name.contains_public_namespace() {
724-
ns.push(activation.avm2().namespaces.public_all());
718+
let default_ns_uri = activation.default_xml_namespace();
719+
if default_ns_uri.is_empty() || new_name.contains_public_namespace() {
720+
// Copy the namespaces from the previous name,
721+
// but make sure to definitely include the public namespace.
722+
if !new_name.is_any_namespace() {
723+
let mut ns = Vec::new();
724+
ns.extend(name.namespace_set());
725+
if !name.contains_public_namespace() {
726+
ns.push(activation.avm2().namespaces.public_all());
727+
}
728+
new_name.set_ns(NamespaceSet::new(ns, activation.gc()));
725729
}
726-
new_name.set_ns(NamespaceSet::new(ns, activation.gc()));
727730
}
728731

729732
return new_name;
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
num_ticks = 1
2-
known_failure = true # https://github.com/ruffle-rs/ruffle/issues/12355
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
num_ticks = 1
2-
known_failure = true

0 commit comments

Comments
 (0)