Skip to content

Commit

Permalink
avm2: Port Boolean class to Actionscript
Browse files Browse the repository at this point in the history
This requires some minor changes to support it
  • Loading branch information
Lord-McSweeney authored and Lord-McSweeney committed Dec 16, 2024
1 parent e7cad7b commit f7cda11
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 174 deletions.
13 changes: 12 additions & 1 deletion core/src/avm2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
use std::rc::Rc;

use crate::avm2::class::{AllocatorFn, CustomConstructorFn};
use crate::avm2::e4x::XmlSettings;
use crate::avm2::error::{make_error_1014, make_error_1107, type_error, Error1014Type};
use crate::avm2::globals::{
init_builtin_system_classes, init_native_system_classes, SystemClassDefs, SystemClasses,
init_builtin_system_class_defs, init_builtin_system_classes, init_native_system_classes,
SystemClassDefs, SystemClasses,
};
use crate::avm2::method::{Method, NativeMethodImpl};
use crate::avm2::scope::ScopeChain;
Expand Down Expand Up @@ -175,6 +177,9 @@ pub struct Avm2<'gc> {
alias_to_class_map: FnvHashMap<AvmString<'gc>, ClassObject<'gc>>,
class_to_alias_map: FnvHashMap<Class<'gc>, AvmString<'gc>>,

#[collect(require_static)]
pub xml_settings: XmlSettings,

/// The api version of our root movie clip. Note - this is used as the
/// api version for swfs loaded via `Loader`, overriding the api version
/// specified in the loaded SWF. This is only used for API versioning (hiding
Expand Down Expand Up @@ -230,6 +235,8 @@ impl<'gc> Avm2<'gc> {
alias_to_class_map: Default::default(),
class_to_alias_map: Default::default(),

xml_settings: XmlSettings::new_default(),

// Set the lowest version for now - this will be overridden when we set our movie
root_api_version: ApiVersion::AllVersions,

Expand Down Expand Up @@ -707,6 +714,10 @@ impl<'gc> Avm2<'gc> {
.load_classes(&mut activation)
.expect("Classes should load");

// These Classes are absolutely critical to the runtime, so make sure
// we've registered them before anything else.
init_builtin_system_class_defs(&mut activation);

// The second script (script #1) is Toplevel.as, and includes important
// builtin classes such as Namespace, QName, and XML.
tunit
Expand Down
36 changes: 22 additions & 14 deletions core/src/avm2/e4x.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::avm2::error::{make_error_1010, make_error_1085, make_error_1118, type_error};
use crate::avm2::globals::slots::xml as xml_class_slots;
use crate::avm2::object::{E4XOrXml, FunctionObject, NamespaceObject};
use crate::avm2::{Activation, Error, Multiname, TObject, Value};
use crate::string::{AvmString, WStr, WString};
Expand Down Expand Up @@ -1659,21 +1658,10 @@ pub fn to_xml_string<'gc>(
xml: E4XOrXml<'gc>,
activation: &mut Activation<'_, 'gc>,
) -> AvmString<'gc> {
let pretty_printing = activation
.avm2()
.classes()
.xml
.get_slot(xml_class_slots::PRETTY_PRINTING)
.coerce_to_boolean();
let pretty_printing = activation.avm2().xml_settings.pretty_printing;

let pretty = if pretty_printing {
let pretty_indent = activation
.avm2()
.classes()
.xml
.get_slot(xml_class_slots::PRETTY_INDENT)
.coerce_to_i32(activation)
.expect("shouldn't error");
let pretty_indent = activation.avm2().xml_settings.pretty_indent;

// NOTE: Negative values are invalid and are ignored.
if pretty_indent < 0 {
Expand Down Expand Up @@ -1785,3 +1773,23 @@ pub fn maybe_escape_child<'gc>(

Ok(child)
}

pub struct XmlSettings {
pub ignore_comments: bool,
pub ignore_processing_instructions: bool,
pub ignore_whitespace: bool,
pub pretty_printing: bool,
pub pretty_indent: i32,
}

impl XmlSettings {
pub fn new_default() -> Self {
XmlSettings {
ignore_comments: true,
ignore_processing_instructions: true,
ignore_whitespace: true,
pretty_printing: true,
pretty_indent: 2,
}
}
}
23 changes: 11 additions & 12 deletions core/src/avm2/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ use crate::avm2::script::Script;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::vtable::VTable;
use crate::avm2::Avm2;
use crate::avm2::Error;
use crate::avm2::Namespace;
use crate::avm2::QName;
use crate::avm2::{Avm2, Error, Multiname, Namespace, QName};
use crate::tag_utils::{self, ControlFlow, SwfMovie, SwfSlice, SwfStream};
use gc_arena::Collect;
use std::sync::Arc;
Expand Down Expand Up @@ -572,7 +569,6 @@ pub fn load_player_globals<'gc>(
));

let string_class = string::create_class(activation);
let boolean_class = boolean::create_class(activation);
let number_class = number::create_class(activation);
let int_class = int::create_class(activation);
let uint_class = uint::create_class(activation);
Expand All @@ -597,7 +593,6 @@ pub fn load_player_globals<'gc>(
(public_ns, "Class", class_i_class),
(public_ns, "Function", fn_classdef),
(public_ns, "String", string_class),
(public_ns, "Boolean", boolean_class),
(public_ns, "Number", number_class),
(public_ns, "int", int_class),
(public_ns, "uint", uint_class),
Expand Down Expand Up @@ -740,7 +735,6 @@ pub fn load_player_globals<'gc>(
// Make sure to initialize superclasses *before* their subclasses!

avm2_system_class!(string, activation, string_class, script);
avm2_system_class!(boolean, activation, boolean_class, script);
avm2_system_class!(number, activation, number_class, script);
avm2_system_class!(int, activation, int_class, script);
avm2_system_class!(uint, activation, uint_class, script);
Expand Down Expand Up @@ -833,11 +827,12 @@ macro_rules! avm2_system_class_defs_playerglobal {
($activation:expr, [$(($package:expr, $class_name:expr, $field:ident)),* $(,)?]) => {
let activation = $activation;
$(
let domain = activation.domain();

// Lookup with the highest version, so we we see all defined classes here
let ns = Namespace::package($package, ApiVersion::VM_INTERNAL, activation.strings());
let name = QName::new(ns, $class_name);
let class_object = activation.domain().get_defined_value(activation, name).unwrap_or_else(|e| panic!("Failed to lookup {name:?}: {e:?}"));
let class_def = class_object.as_object().unwrap().as_class_object().unwrap().inner_class_definition();
let name = Multiname::new(ns, $class_name);
let class_def = domain.get_class(activation.context, &name).unwrap_or_else(|| panic!("Failed to lookup {name:?}"));
let sc = activation.avm2().system_class_defs.as_mut().unwrap();
sc.$field = class_def;
)*
Expand All @@ -848,11 +843,12 @@ pub fn init_builtin_system_classes(activation: &mut Activation<'_, '_>) {
avm2_system_classes_playerglobal!(
&mut *activation,
[
("", "Error", error),
("", "ArgumentError", argumenterror),
("", "QName", qname),
("", "Boolean", boolean),
("", "Error", error),
("", "EvalError", evalerror),
("", "Namespace", namespace),
("", "QName", qname),
("", "RangeError", rangeerror),
("", "ReferenceError", referenceerror),
("", "SecurityError", securityerror),
Expand All @@ -864,10 +860,13 @@ pub fn init_builtin_system_classes(activation: &mut Activation<'_, '_>) {
("", "XMLList", xml_list),
]
);
}

pub fn init_builtin_system_class_defs(activation: &mut Activation<'_, '_>) {
avm2_system_class_defs_playerglobal!(
&mut *activation,
[
("", "Boolean", boolean),
("", "Namespace", namespace),
("", "XML", xml),
("", "XMLList", xml_list),
Expand Down
52 changes: 49 additions & 3 deletions core/src/avm2/globals/Boolean.as
Original file line number Diff line number Diff line change
@@ -1,5 +1,51 @@
// This is a stub - the actual class is defined in `boolean.rs`
package {
public class Boolean {
}
[Ruffle(CustomConstructor)]
[Ruffle(CallHandler)]
public final class Boolean {
public function Boolean(value:* = void 0) {
// The Boolean constructor is implemented natively:
// this AS-defined method does nothing
}

prototype.toString = function():String {
if (this === Boolean.prototype) {
return "false";
}

if (!(this is Boolean)) {
throw new TypeError("Error #1004: Method Boolean.prototype.toString was invoked on an incompatible object.", 1004);
}

return this.AS3::toString();
};

prototype.valueOf = function():* {
if (this === Boolean.prototype) {
return false;
}

if (!(this is Boolean)) {
throw new TypeError("Error #1004: Method Boolean.prototype.valueOf was invoked on an incompatible object.", 1004);
}

return this;
};

prototype.setPropertyIsEnumerable("toString", false);
prototype.setPropertyIsEnumerable("valueOf", false);

AS3 function toString():String {
if (this) {
return "true";
} else {
return "false";
}
}

AS3 function valueOf():Boolean {
return this;
}

public static const length:int = 1;
}
}
2 changes: 2 additions & 0 deletions core/src/avm2/globals/Toplevel.as
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ package {

include "Error.as"

include "Boolean.as"

include "ArgumentError.as"
include "DefinitionError.as"
include "EvalError.as"
Expand Down
19 changes: 12 additions & 7 deletions core/src/avm2/globals/XML.as
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,20 @@ package {
AS3 native function notification():Function;
AS3 native function setNotification(f:Function):*;

public static var ignoreComments:Boolean = true;
public static var ignoreProcessingInstructions:Boolean = true;
public static var ignoreWhitespace:Boolean = true;
public static native function get ignoreComments():Boolean;
public static native function set ignoreComments(value:Boolean):void;

[Ruffle(InternalSlot)]
public static var prettyPrinting:Boolean = true;
public static native function get ignoreProcessingInstructions():Boolean;
public static native function set ignoreProcessingInstructions(value:Boolean):void;

[Ruffle(InternalSlot)]
public static var prettyIndent:int = 2;
public static native function get ignoreWhitespace():Boolean;
public static native function set ignoreWhitespace(value:Boolean):void;

public static native function get prettyPrinting():Boolean;
public static native function set prettyPrinting(value:Boolean):void;

public static native function get prettyIndent():int;
public static native function set prettyIndent(value:int):void;

prototype.hasComplexContent = function():Boolean {
var self:XML = this;
Expand Down
Loading

0 comments on commit f7cda11

Please sign in to comment.