Skip to content

Commit a44e385

Browse files
committed
avm2: Implement DxnsLate opcode
1 parent 102b5e7 commit a44e385

File tree

15 files changed

+364
-56
lines changed

15 files changed

+364
-56
lines changed

Cargo.lock

Lines changed: 11 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ bitflags = { workspace = true }
2929
smallvec = { workspace = true }
3030
num-traits = { workspace = true }
3131
num-derive = { workspace = true }
32-
quick-xml = "0.37.5"
32+
quick-xml = { git = "https://github.com/jarca0123/quick-xml.git", branch = "ruffle" }
3333
url = { workspace = true }
3434
weak-table = "0.3.2"
3535
percent-encoding = { workspace = true }

core/src/avm2/activation.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,17 @@ impl<'a, 'gc> Activation<'a, 'gc> {
652652
self.bound_class
653653
}
654654

655+
/// Get the default XML namespace for this activation.
656+
pub fn default_xml_namespace(&mut self) -> Option<Namespace<'gc>> {
657+
// Get the default XML namespace by walking up the call stack
658+
// This matches avmplus's MethodFrame::findDxns behavior
659+
self.context
660+
.avm2
661+
.call_stack()
662+
.borrow()
663+
.find_default_xml_namespace(self)
664+
}
665+
655666
pub fn run_actions(&mut self, method: Method<'gc>) -> Result<Value<'gc>, Error<'gc>> {
656667
// The method must be verified at this point
657668

@@ -2586,8 +2597,38 @@ impl<'a, 'gc> Activation<'a, 'gc> {
25862597

25872598
/// Implements `Op::DxnsLate`
25882599
fn op_dxns_late(&mut self) -> Result<(), Error<'gc>> {
2589-
let _ = self.pop_stack();
2590-
Err("Unimplemented opcode DxnsLate.".into())
2600+
let namespace_value = self.pop_stack();
2601+
2602+
// Convert the value to a namespace
2603+
let namespace = match namespace_value {
2604+
Value::Object(Object::NamespaceObject(ns)) => ns.namespace(),
2605+
Value::String(uri) => {
2606+
// Create a namespace from the URI string
2607+
Namespace::package(uri, self.avm2().root_api_version, self.strings())
2608+
}
2609+
Value::Undefined | Value::Null => {
2610+
// Empty namespace
2611+
Namespace::package(
2612+
AvmString::new_utf8(self.gc(), ""),
2613+
self.avm2().root_api_version,
2614+
self.strings(),
2615+
)
2616+
}
2617+
_ => {
2618+
// Try to coerce to string and create namespace
2619+
let uri = namespace_value.coerce_to_string(self)?;
2620+
Namespace::package(uri, self.avm2().root_api_version, self.strings())
2621+
}
2622+
};
2623+
2624+
// Set the default XML namespace for the current method frame
2625+
self.context
2626+
.avm2
2627+
.call_stack()
2628+
.borrow_mut(self.gc())
2629+
.set_default_xml_namespace(namespace);
2630+
2631+
Ok(())
25912632
}
25922633

25932634
/// Implements `Op::EscXAttr`

core/src/avm2/call_stack.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use crate::avm2::activation::Activation;
12
use crate::avm2::class::Class;
23
use crate::avm2::function::display_function;
34
use crate::avm2::method::Method;
5+
use crate::avm2::AvmString;
6+
use crate::avm2::Namespace;
47
use crate::string::WString;
58
use gc_arena::Collect;
69

@@ -9,6 +12,8 @@ use gc_arena::Collect;
912
pub struct CallNode<'gc> {
1013
method: Method<'gc>,
1114
class: Option<Class<'gc>>,
15+
/// The default XML namespace for this method frame, if set.
16+
default_xml_namespace: Option<Namespace<'gc>>,
1217
}
1318

1419
#[derive(Collect, Clone)]
@@ -23,7 +28,11 @@ impl<'gc> CallStack<'gc> {
2328
}
2429

2530
pub fn push(&mut self, method: Method<'gc>, class: Option<Class<'gc>>) {
26-
self.stack.push(CallNode { method, class })
31+
self.stack.push(CallNode {
32+
method,
33+
class,
34+
default_xml_namespace: None,
35+
});
2736
}
2837

2938
pub fn pop(&mut self) -> Option<CallNode<'gc>> {
@@ -62,6 +71,41 @@ impl<'gc> CallStack<'gc> {
6271
pub fn is_empty(&self) -> bool {
6372
self.stack.is_empty()
6473
}
74+
75+
/// Set the default XML namespace for the current method frame.
76+
/// This is called by the DxnsLate opcode.
77+
pub fn set_default_xml_namespace(&mut self, namespace: Namespace<'gc>) {
78+
if let Some(call_node) = self.stack.last_mut() {
79+
call_node.default_xml_namespace = Some(namespace);
80+
}
81+
}
82+
83+
/// Get the default XML namespace by walking up the call stack.
84+
/// This matches avmplus's MethodFrame::findDxns behavior.
85+
pub fn find_default_xml_namespace(
86+
&self,
87+
activation: &mut Activation<'_, 'gc>,
88+
) -> Option<Namespace<'gc>> {
89+
// Walk backwards through the call stack (most recent first)
90+
for call_node in self.stack.iter().rev() {
91+
// If this frame has an explicit DXNS declaration, return it
92+
if let Some(ns) = call_node.default_xml_namespace {
93+
return Some(ns);
94+
}
95+
96+
// If this method has the SET_DXNS flag, it means it has a local
97+
// default xml namespace declaration, so the default should be the
98+
// unnamed namespace (empty string) rather than inheriting from global scope
99+
if call_node.method.sets_dxns() {
100+
return Some(Namespace::package(
101+
AvmString::new_utf8(activation.gc(), ""),
102+
activation.avm2().root_api_version,
103+
activation.strings(),
104+
));
105+
}
106+
}
107+
None
108+
}
65109
}
66110

67111
impl Default for CallStack<'_> {

0 commit comments

Comments
 (0)