avm2: Implement XML construction from XML and XMLList objects
This commit is contained in:
parent
79dfeaf715
commit
b140ce6d97
|
@ -9,6 +9,8 @@ use quick_xml::{
|
||||||
Reader,
|
Reader,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::avm2::{error::type_error, TObject};
|
||||||
|
|
||||||
use super::{object::E4XOrXml, string::AvmString, Activation, Error, Multiname, Value};
|
use super::{object::E4XOrXml, string::AvmString, Activation, Error, Multiname, Value};
|
||||||
use crate::string::{WStr, WString};
|
use crate::string::{WStr, WString};
|
||||||
|
|
||||||
|
@ -117,14 +119,29 @@ impl<'gc> E4XNode<'gc> {
|
||||||
/// The caller is responsible for validating that the number of top-level nodes
|
/// The caller is responsible for validating that the number of top-level nodes
|
||||||
/// is correct (for XML, there should be exactly one.)
|
/// is correct (for XML, there should be exactly one.)
|
||||||
pub fn parse(
|
pub fn parse(
|
||||||
value: Value<'gc>,
|
mut value: Value<'gc>,
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
) -> Result<Vec<Self>, Error<'gc>> {
|
) -> Result<Vec<Self>, Error<'gc>> {
|
||||||
let string = match value {
|
let string = match &value {
|
||||||
// The docs claim that this throws a TypeError, but it actually doesn't
|
// The docs claim that this throws a TypeError, but it actually doesn't
|
||||||
Value::Null | Value::Undefined => AvmString::default(),
|
Value::Null | Value::Undefined => AvmString::default(),
|
||||||
// The docs claim that only String, Number or Boolean are accepted, but that's also a lie
|
// The docs claim that only String, Number or Boolean are accepted, but that's also a lie
|
||||||
value => value.coerce_to_string(activation)?,
|
val => {
|
||||||
|
if let Some(obj) = val.as_object() {
|
||||||
|
if let Some(xml) = obj.as_xml_object() {
|
||||||
|
value = xml.call_public_property("toXMLString", &[], activation)?;
|
||||||
|
} else if let Some(list) = obj.as_xml_list_object() {
|
||||||
|
if list.length() == 1 {
|
||||||
|
value = list.children_mut(activation.context.gc_context)[0]
|
||||||
|
.get_or_create_xml(activation)
|
||||||
|
.call_public_property("toXMLString", &[], activation)?;
|
||||||
|
} else {
|
||||||
|
return Err(Error::AvmError(type_error(activation, "Error #1088: The markup in the document following the root element must be well-formed.", 1088)?));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value.coerce_to_string(activation)?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let data_utf8 = string.to_utf8_lossy();
|
let data_utf8 = string.to_utf8_lossy();
|
||||||
|
@ -342,15 +359,9 @@ impl<'gc> E4XNode<'gc> {
|
||||||
|
|
||||||
pub fn xml_to_xml_string(
|
pub fn xml_to_xml_string(
|
||||||
&self,
|
&self,
|
||||||
_activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
) -> Result<AvmString<'gc>, Error<'gc>> {
|
) -> Result<AvmString<'gc>, Error<'gc>> {
|
||||||
match &self.0.read().kind {
|
return to_xml_string(E4XOrXml::E4X(*self), activation);
|
||||||
E4XNodeKind::Text(text) => Ok(*text),
|
|
||||||
E4XNodeKind::Element { .. } => {
|
|
||||||
Err(format!("XML.toXMLString(): Not yet implemented element {:?}", self).into())
|
|
||||||
}
|
|
||||||
other => Err(format!("XML.toXMLString(): Not yet implemented for {other:?}").into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> Ref<'_, E4XNodeKind<'gc>> {
|
pub fn kind(&self) -> Ref<'_, E4XNodeKind<'gc>> {
|
||||||
|
|
|
@ -15,8 +15,8 @@ pub fn init<'gc>(
|
||||||
let this = this.unwrap().as_xml_object().unwrap();
|
let this = this.unwrap().as_xml_object().unwrap();
|
||||||
let value = args[0];
|
let value = args[0];
|
||||||
|
|
||||||
match E4XNode::parse(value, activation) {
|
let nodes = E4XNode::parse(value, activation)?;
|
||||||
Ok(nodes) => {
|
|
||||||
let node = match nodes.as_slice() {
|
let node = match nodes.as_slice() {
|
||||||
// XML defaults to an empty text node when nothing was parsed
|
// XML defaults to an empty text node when nothing was parsed
|
||||||
[] => E4XNode::text(activation.context.gc_context, AvmString::default()),
|
[] => E4XNode::text(activation.context.gc_context, AvmString::default()),
|
||||||
|
@ -32,13 +32,6 @@ pub fn init<'gc>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.set_node(activation.context.gc_context, node);
|
this.set_node(activation.context.gc_context, node);
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
return Err(Error::RustError(
|
|
||||||
format!("Failed to parse XML: {e:?}").into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
|
|
||||||
public function Main() {
|
public function Main() {
|
||||||
|
XML.prettyPrinting = false;
|
||||||
|
|
||||||
var byteArray: ByteArray = new ByteArray();
|
var byteArray: ByteArray = new ByteArray();
|
||||||
byteArray.writeUTFBytes("<foo><bar>test</bar></foo>");
|
byteArray.writeUTFBytes("<foo><bar>test</bar></foo>");
|
||||||
byteArray.position = 0;
|
byteArray.position = 0;
|
||||||
|
@ -20,6 +22,30 @@
|
||||||
objWithToString.toString = function() { return "<foo><bar>test</bar></foo>"; };
|
objWithToString.toString = function() { return "<foo><bar>test</bar></foo>"; };
|
||||||
trace("// new XML(objWithToString).bar");
|
trace("// new XML(objWithToString).bar");
|
||||||
trace(new XML(objWithToString).bar);
|
trace(new XML(objWithToString).bar);
|
||||||
|
|
||||||
|
var xmlObj = <outer></outer>;
|
||||||
|
var xmlCopy = new XML(xmlObj);
|
||||||
|
trace("xmlCopy().toXMLString(): " + xmlCopy.toXMLString());
|
||||||
|
trace("xmlObj === xmlCopy: " + (xmlObj === xmlCopy));
|
||||||
|
|
||||||
|
var emptyList = new XMLList();
|
||||||
|
try {
|
||||||
|
new XML(emptyList);
|
||||||
|
} catch (e) {
|
||||||
|
trace("Caught error: " + e);
|
||||||
|
trace(e.errorID);
|
||||||
|
}
|
||||||
|
|
||||||
|
var singleList = new XMLList("<outer><inner>Hello</inner><second>World</second></outer>");
|
||||||
|
trace("new XML(singleList): " + new XML(singleList));
|
||||||
|
|
||||||
|
var multiList = new XMLList("<first>Hello</first><second>World</second>");
|
||||||
|
try {
|
||||||
|
new XML(multiList);
|
||||||
|
} catch (e) {
|
||||||
|
trace("Caught error: " + e);
|
||||||
|
trace(e.errorID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,3 +3,10 @@ test
|
||||||
|
|
||||||
// new XML(objWithToString).bar
|
// new XML(objWithToString).bar
|
||||||
test
|
test
|
||||||
|
xmlCopy().toXMLString(): <outer/>
|
||||||
|
xmlObj === xmlCopy: false
|
||||||
|
Caught error: TypeError: Error #1088: The markup in the document following the root element must be well-formed.
|
||||||
|
1088
|
||||||
|
new XML(singleList): <outer><inner>Hello</inner><second>World</second></outer>
|
||||||
|
Caught error: TypeError: Error #1088: The markup in the document following the root element must be well-formed.
|
||||||
|
1088
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue