diff --git a/core/src/avm2/class.rs b/core/src/avm2/class.rs index 3504799ee..e639842d2 100644 --- a/core/src/avm2/class.rs +++ b/core/src/avm2/class.rs @@ -19,7 +19,6 @@ use swf::avm2::types::{ Class as AbcClass, Instance as AbcInstance, Method as AbcMethod, MethodBody as AbcMethodBody, }; -use super::method::ParamConfig; use super::string::AvmString; bitflags! { @@ -338,11 +337,10 @@ impl<'gc> Class<'gc> { let method = Method::from_builtin_and_params( table_native_call_handler, name, - vec![ParamConfig::of_type( - "val", - Multiname::any(activation.context.gc_context), - )], - false, + // A 'callable' class doesn't have a signature - let the + // method do any needed coercions + vec![], + true, activation.context.gc_context, ); native_call_handler = Some(method); diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 9e9442a93..f50fb6a44 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -32,7 +32,7 @@ mod namespace; mod number; mod object; mod qname; -mod regexp; +mod reg_exp; mod string; mod toplevel; mod r#uint; @@ -523,7 +523,6 @@ pub fn load_player_globals<'gc>( )?; function(activation, "", "unescape", toplevel::unescape, script)?; - avm2_system_class!(regexp, activation, regexp::create_class(activation), script); avm2_system_class!(vector, activation, vector::create_class(activation), script); avm2_system_class!(date, activation, date::create_class(activation), script); @@ -604,6 +603,7 @@ fn load_playerglobal<'gc>( ("", "Error", error), ("", "ArgumentError", argumenterror), ("", "RangeError", rangeerror), + ("", "RegExp", regexp), ("", "ReferenceError", referenceerror), ("", "TypeError", typeerror), ("", "VerifyError", verifyerror), diff --git a/core/src/avm2/globals/RegExp.as b/core/src/avm2/globals/RegExp.as new file mode 100644 index 000000000..45ebb85f3 --- /dev/null +++ b/core/src/avm2/globals/RegExp.as @@ -0,0 +1,31 @@ +package { + [Ruffle(InstanceAllocator)] + [Ruffle(CallHandler)] + public dynamic class RegExp { + public function RegExp(re:* = undefined, flags:* = undefined) { + this.init(re, flags) + } + + private native function init(re:*, flags:*):void; + + public native function get dotall():Boolean; + public native function get extended():Boolean; + public native function get global():Boolean; + public native function get ignoreCase():Boolean; + public native function get multiline():Boolean; + public native function get lastIndex():int; + public native function set lastIndex(value:int):void; + public native function get source():String; + + AS3 native function exec(str:String = ""):Object; + AS3 native function test(str:String = ""):Boolean; + + prototype.exec = function(str:String = ""):Object { + return this.AS3::exec(str); + } + + prototype.test = function(str:String = ""):Boolean { + return this.AS3::test(str); + } + } +} \ No newline at end of file diff --git a/core/src/avm2/globals/globals.as b/core/src/avm2/globals/globals.as index e77a35b4d..1be0a0c38 100644 --- a/core/src/avm2/globals/globals.as +++ b/core/src/avm2/globals/globals.as @@ -11,6 +11,7 @@ include "TypeError.as" include "Math.as" include "RangeError.as" include "ReferenceError.as" +include "RegExp.as" include "SecurityError.as" include "SyntaxError.as" include "URIError.as" diff --git a/core/src/avm2/globals/regexp.rs b/core/src/avm2/globals/reg_exp.rs similarity index 68% rename from core/src/avm2/globals/regexp.rs rename to core/src/avm2/globals/reg_exp.rs index f36e26134..5d1b0dd88 100644 --- a/core/src/avm2/globals/regexp.rs +++ b/core/src/avm2/globals/reg_exp.rs @@ -1,32 +1,22 @@ //! `RegExp` impl -use crate::avm2::class::Class; use crate::avm2::error::type_error; -use crate::avm2::method::{Method, NativeMethodImpl, ParamConfig}; -use crate::avm2::object::{regexp_allocator, ArrayObject, FunctionObject, Object, TObject}; +use crate::avm2::object::{ArrayObject, Object, TObject}; use crate::avm2::regexp::RegExpFlags; use crate::avm2::value::Value; use crate::avm2::Error; -use crate::avm2::Multiname; -use crate::avm2::QName; use crate::avm2::{activation::Activation, array::ArrayStorage}; use crate::string::{AvmString, WString}; -use gc_arena::GcCell; -// All of these methods will be defined as both -// AS3 instance methods and methods on the `Array` class prototype. -const PUBLIC_INSTANCE_AND_PROTO_METHODS: &[(&str, NativeMethodImpl)] = - &[("exec", exec), ("test", test)]; +pub use crate::avm2::object::reg_exp_allocator; -/// Implements `RegExp`'s instance initializer. -pub fn instance_init<'gc>( +/// Implements `RegExp`'s `init` method, which is called from the constructor +pub fn init<'gc>( activation: &mut Activation<'_, 'gc>, this: Option>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { if let Some(this) = this { - activation.super_init(this, &[])?; - if let Some(mut regexp) = this.as_regexp_mut(activation.context.gc_context) { let source: AvmString<'gc> = match args.get(0) { Some(Value::Undefined) => "".into(), @@ -76,7 +66,7 @@ pub fn instance_init<'gc>( Ok(Value::Undefined) } -fn class_call<'gc>( +pub fn call_handler<'gc>( activation: &mut Activation<'_, 'gc>, _this: Option>, args: &[Value<'gc>], @@ -92,39 +82,8 @@ fn class_call<'gc>( return this_class.construct(activation, args).map(|o| o.into()); } -/// Implements `RegExp`'s class initializer. -pub fn class_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Option>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(this) = this { - let scope = activation.create_scopechain(); - let gc_context = activation.context.gc_context; - let this_class = this.as_class_object().unwrap(); - let regexp_proto = this_class.prototype(); - - for (name, method) in PUBLIC_INSTANCE_AND_PROTO_METHODS { - regexp_proto.set_string_property_local( - *name, - FunctionObject::from_method( - activation, - Method::from_builtin(*method, name, gc_context), - scope, - None, - Some(this_class), - ) - .into(), - activation, - )?; - regexp_proto.set_local_property_is_enumerable(gc_context, (*name).into(), false); - } - } - Ok(Value::Undefined) -} - /// Implements `RegExp.dotall` -pub fn dotall<'gc>( +pub fn get_dotall<'gc>( _activation: &mut Activation<'_, 'gc>, this: Option>, _args: &[Value<'gc>], @@ -139,7 +98,7 @@ pub fn dotall<'gc>( } /// Implements `RegExp.extended` -pub fn extended<'gc>( +pub fn get_extended<'gc>( _activation: &mut Activation<'_, 'gc>, this: Option>, _args: &[Value<'gc>], @@ -154,7 +113,7 @@ pub fn extended<'gc>( } /// Implements `RegExp.global` -pub fn global<'gc>( +pub fn get_global<'gc>( _activation: &mut Activation<'_, 'gc>, this: Option>, _args: &[Value<'gc>], @@ -169,7 +128,7 @@ pub fn global<'gc>( } /// Implements `RegExp.ignoreCase` -pub fn ignore_case<'gc>( +pub fn get_ignore_case<'gc>( _activation: &mut Activation<'_, 'gc>, this: Option>, _args: &[Value<'gc>], @@ -184,7 +143,7 @@ pub fn ignore_case<'gc>( } /// Implements `RegExp.multiline` -pub fn multiline<'gc>( +pub fn get_multiline<'gc>( _activation: &mut Activation<'_, 'gc>, this: Option>, _args: &[Value<'gc>], @@ -199,7 +158,7 @@ pub fn multiline<'gc>( } /// Implements `RegExp.lastIndex`'s getter -pub fn last_index<'gc>( +pub fn get_last_index<'gc>( _activation: &mut Activation<'_, 'gc>, this: Option>, _args: &[Value<'gc>], @@ -233,7 +192,7 @@ pub fn set_last_index<'gc>( } /// Implements `RegExp.source` -pub fn source<'gc>( +pub fn get_source<'gc>( _activation: &mut Activation<'_, 'gc>, this: Option>, _args: &[Value<'gc>], @@ -307,59 +266,3 @@ pub fn test<'gc>( Ok(Value::Undefined) } - -/// Construct `RegExp`'s class. -pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> GcCell<'gc, Class<'gc>> { - let mc = activation.context.gc_context; - let class = Class::new( - QName::new(activation.avm2().public_namespace, "RegExp"), - Some(Multiname::new(activation.avm2().public_namespace, "Object")), - Method::from_builtin_and_params( - instance_init, - "", - vec![ - ParamConfig::optional("re", Multiname::any(mc), Value::Undefined), - ParamConfig::optional("flags", Multiname::any(mc), Value::Undefined), - ], - false, - mc, - ), - Method::from_builtin(class_init, "", mc), - mc, - ); - - let mut write = class.write(mc); - write.set_instance_allocator(regexp_allocator); - write.set_call_handler(Method::from_builtin( - class_call, - "", - mc, - )); - - const PUBLIC_INSTANCE_PROPERTIES: &[( - &str, - Option, - Option, - )] = &[ - ("dotall", Some(dotall), None), - ("extended", Some(extended), None), - ("global", Some(global), None), - ("ignoreCase", Some(ignore_case), None), - ("multiline", Some(multiline), None), - ("lastIndex", Some(last_index), Some(set_last_index)), - ("source", Some(source), None), - ]; - write.define_builtin_instance_properties( - mc, - activation.avm2().public_namespace, - PUBLIC_INSTANCE_PROPERTIES, - ); - - write.define_builtin_instance_methods( - mc, - activation.avm2().as3_namespace, - PUBLIC_INSTANCE_AND_PROTO_METHODS, - ); - - class -} diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 4040ff92d..ecb668c8d 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -84,7 +84,7 @@ pub use crate::avm2::object::primitive_object::{primitive_allocator, PrimitiveOb pub use crate::avm2::object::program_3d_object::Program3DObject; pub use crate::avm2::object::proxy_object::{proxy_allocator, ProxyObject}; pub use crate::avm2::object::qname_object::{qname_allocator, QNameObject}; -pub use crate::avm2::object::regexp_object::{regexp_allocator, RegExpObject}; +pub use crate::avm2::object::regexp_object::{reg_exp_allocator, RegExpObject}; pub use crate::avm2::object::script_object::{ScriptObject, ScriptObjectData}; pub use crate::avm2::object::sound_object::{sound_allocator, QueuedPlay, SoundData, SoundObject}; pub use crate::avm2::object::soundchannel_object::{sound_channel_allocator, SoundChannelObject}; diff --git a/core/src/avm2/object/regexp_object.rs b/core/src/avm2/object/regexp_object.rs index 0318c47fd..82ac20e27 100644 --- a/core/src/avm2/object/regexp_object.rs +++ b/core/src/avm2/object/regexp_object.rs @@ -12,7 +12,7 @@ use gc_arena::{Collect, GcCell, MutationContext}; use std::cell::{Ref, RefMut}; /// A class instance allocator that allocates RegExp objects. -pub fn regexp_allocator<'gc>( +pub fn reg_exp_allocator<'gc>( class: ClassObject<'gc>, activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> {