avm2: Port `Math` to ActionScript
This commit is contained in:
parent
4e9cc919a1
commit
d152cc3e64
|
@ -1,7 +1,7 @@
|
|||
//! An internal Ruffle utility to build our playerglobal
|
||||
//! `library.swf`
|
||||
|
||||
use convert_case::{Case, Casing};
|
||||
use convert_case::{Boundary, Case, Casing};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use std::fs::File;
|
||||
|
@ -109,7 +109,13 @@ fn flash_to_rust_path(path: &str) -> String {
|
|||
// so 'URLLoader' becomes 'url_loader'
|
||||
let components = path
|
||||
.split('.')
|
||||
.map(|component| component.to_case(Case::Snake))
|
||||
.map(|component| {
|
||||
component
|
||||
.from_case(Case::Camel)
|
||||
// Do not split on a letter followed by a digit, so e.g. `atan2` won't become `atan_2`.
|
||||
.without_boundaries(&[Boundary::UpperDigit, Boundary::LowerDigit])
|
||||
.to_case(Case::Snake)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
// Form a Rust path from the snake-case components
|
||||
components.join("::")
|
||||
|
|
|
@ -432,7 +432,6 @@ pub fn load_player_globals<'gc>(
|
|||
object_class,
|
||||
)?;
|
||||
|
||||
class(activation, math::create_class(mc), script)?;
|
||||
class(activation, json::create_class(mc), script)?;
|
||||
avm2_system_class!(regexp, activation, regexp::create_class(mc), script);
|
||||
avm2_system_class!(vector, activation, vector::create_class(mc), script);
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package {
|
||||
public final class Math {
|
||||
public static const E: Number = 2.718281828459045;
|
||||
public static const LN10: Number = 2.302585092994046;
|
||||
public static const LN2: Number = 0.6931471805599453;
|
||||
public static const LOG10E: Number = 0.4342944819032518;
|
||||
public static const LOG2E: Number = 1.442695040888963387;
|
||||
public static const PI: Number = 3.141592653589793;
|
||||
public static const SQRT1_2: Number = 0.7071067811865476;
|
||||
public static const SQRT2: Number = 1.4142135623730951;
|
||||
|
||||
public static native function abs(x: Number): Number;
|
||||
public static native function acos(x: Number): Number;
|
||||
public static native function asin(x: Number): Number;
|
||||
public static native function atan(x: Number): Number;
|
||||
public static native function ceil(x: Number): Number;
|
||||
public static native function cos(x: Number): Number;
|
||||
public static native function exp(x: Number): Number;
|
||||
public static native function floor(x: Number): Number;
|
||||
public static native function log(x: Number): Number;
|
||||
public static native function round(x: Number): Number;
|
||||
public static native function sin(x: Number): Number;
|
||||
public static native function sqrt(x: Number): Number;
|
||||
public static native function tan(x: Number): Number;
|
||||
public static native function atan2(y: Number, x: Number): Number;
|
||||
public static native function pow(x: Number, y: Number): Number;
|
||||
|
||||
// This is a hacky way to specify `-Infinity` as a default value.
|
||||
private static const NegInfinity: Number = -1 / 0;
|
||||
public static native function max(x: Number = NegInfinity, y: Number = NegInfinity, ...rest): Number;
|
||||
|
||||
// TODO: Remove this once `Infinity` is properly defined in ActionScript.
|
||||
private static const Infinity: Number = 1 / 0;
|
||||
public static native function min(x: Number = Infinity, y: Number = Infinity, ...rest): Number;
|
||||
|
||||
public static native function random(): Number;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
// need to come before subclasses.
|
||||
|
||||
include "Error.as"
|
||||
include "Math.as"
|
||||
|
||||
include "flash/accessibility/AccessibilityProperties.as"
|
||||
include "flash/crypto.as"
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
//! `Math` impl
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::{Class, ClassAttributes};
|
||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||
use crate::avm2::object::Object;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use crate::avm2::Namespace;
|
||||
use crate::avm2::QName;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use rand::Rng;
|
||||
|
||||
macro_rules! math_wrap_std {
|
||||
($std:expr) => {
|
||||
|activation, _this, args| -> Result<Value<'_>, Error> {
|
||||
macro_rules! wrap_std {
|
||||
($name:ident, $std:expr) => {
|
||||
pub fn $name<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(input) = args.get(0) {
|
||||
Ok($std(input.coerce_to_number(activation)?).into())
|
||||
} else {
|
||||
|
@ -23,77 +22,20 @@ macro_rules! math_wrap_std {
|
|||
};
|
||||
}
|
||||
|
||||
/// Implements `Math`'s instance initializer.
|
||||
pub fn instance_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
// TODO: Replace with actual error type.
|
||||
Err("TypeError: Error #1076: Math is not a constructor.".into())
|
||||
}
|
||||
wrap_std!(abs, f64::abs);
|
||||
wrap_std!(acos, f64::acos);
|
||||
wrap_std!(asin, f64::asin);
|
||||
wrap_std!(atan, f64::atan);
|
||||
wrap_std!(ceil, f64::ceil);
|
||||
wrap_std!(cos, f64::cos);
|
||||
wrap_std!(exp, f64::exp);
|
||||
wrap_std!(floor, f64::floor);
|
||||
wrap_std!(log, f64::ln);
|
||||
wrap_std!(sin, f64::sin);
|
||||
wrap_std!(sqrt, f64::sqrt);
|
||||
wrap_std!(tan, f64::tan);
|
||||
|
||||
/// Implements `Math`'s class initializer.
|
||||
pub fn class_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Construct `Math`'s class.
|
||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||
let class = Class::new(
|
||||
QName::new(Namespace::public(), "Math"),
|
||||
Some(QName::new(Namespace::public(), "Object").into()),
|
||||
Method::from_builtin(instance_init, "<Math instance initializer>", mc),
|
||||
Method::from_builtin(class_init, "<Math class initializer>", mc),
|
||||
mc,
|
||||
);
|
||||
|
||||
let mut write = class.write(mc);
|
||||
write.set_attributes(ClassAttributes::FINAL | ClassAttributes::SEALED);
|
||||
|
||||
use std::f64::consts::*;
|
||||
const CONSTANTS: &[(&str, f64)] = &[
|
||||
("E", E),
|
||||
("LN10", LN_10),
|
||||
("LN2", LN_2),
|
||||
("LOG10E", LOG10_E),
|
||||
("LOG2E", LOG2_E),
|
||||
("PI", PI),
|
||||
("SQRT1_2", FRAC_1_SQRT_2),
|
||||
("SQRT2", SQRT_2),
|
||||
];
|
||||
write.define_public_constant_number_class_traits(CONSTANTS);
|
||||
|
||||
const PUBLIC_CLASS_METHODS: &[(&str, NativeMethodImpl)] = &[
|
||||
("atan2", atan2),
|
||||
("max", max),
|
||||
("min", min),
|
||||
("pow", pow),
|
||||
("random", random),
|
||||
("round", round),
|
||||
("abs", math_wrap_std!(f64::abs)),
|
||||
("acos", math_wrap_std!(f64::acos)),
|
||||
("asin", math_wrap_std!(f64::asin)),
|
||||
("atan", math_wrap_std!(f64::atan)),
|
||||
("ceil", math_wrap_std!(f64::ceil)),
|
||||
("cos", math_wrap_std!(f64::cos)),
|
||||
("exp", math_wrap_std!(f64::exp)),
|
||||
("floor", math_wrap_std!(f64::floor)),
|
||||
("log", math_wrap_std!(f64::ln)),
|
||||
("sin", math_wrap_std!(f64::sin)),
|
||||
("sqrt", math_wrap_std!(f64::sqrt)),
|
||||
("tan", math_wrap_std!(f64::tan)),
|
||||
];
|
||||
write.define_public_builtin_class_methods(mc, PUBLIC_CLASS_METHODS);
|
||||
|
||||
class
|
||||
}
|
||||
|
||||
fn round<'gc>(
|
||||
pub fn round<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
|
@ -108,7 +50,7 @@ fn round<'gc>(
|
|||
Ok(f64::NAN.into())
|
||||
}
|
||||
|
||||
fn atan2<'gc>(
|
||||
pub fn atan2<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
|
@ -124,7 +66,7 @@ fn atan2<'gc>(
|
|||
Ok(f64::atan2(y, x).into())
|
||||
}
|
||||
|
||||
fn max<'gc>(
|
||||
pub fn max<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
|
@ -141,7 +83,7 @@ fn max<'gc>(
|
|||
Ok(cur_max.into())
|
||||
}
|
||||
|
||||
fn min<'gc>(
|
||||
pub fn min<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
|
@ -158,7 +100,7 @@ fn min<'gc>(
|
|||
Ok(cur_min.into())
|
||||
}
|
||||
|
||||
fn pow<'gc>(
|
||||
pub fn pow<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
|
|
Loading…
Reference in New Issue