@@ -448,9 +448,10 @@ namespace jsb
448
448
449
449
template <bool HasReturnValueT>
450
450
static void call_builtin_function (Variant* self, const internal::FBuiltinMethodInfo& method_info,
451
- const v8::FunctionCallbackInfo<v8::Value>& info, v8::Isolate* isolate, const v8::Local<v8::Context>& context)
451
+ const v8::FunctionCallbackInfo<v8::Value>& info, v8::Isolate* isolate, const v8::Local<v8::Context>& context,
452
+ const bool utility = false )
452
453
{
453
- const int argc = info.Length ();
454
+ const int argc = utility ? info. Length () - 1 : info.Length ();
454
455
if (!method_info.check_argc (argc))
455
456
{
456
457
jsb_throw (isolate, " num of arguments does not meet the requirement" );
@@ -470,7 +471,7 @@ namespace jsb
470
471
{
471
472
if (index < argc)
472
473
{
473
- if (TypeConvert::js_to_gd_var (isolate, context, info[index], method_info.argument_types [index], args[index]))
474
+ if (TypeConvert::js_to_gd_var (isolate, context, info[utility ? index + 1 : index], method_info.argument_types [index], args[index]))
474
475
{
475
476
continue ;
476
477
}
@@ -488,14 +489,14 @@ namespace jsb
488
489
}
489
490
else
490
491
{
491
- if (TypeConvert::js_to_gd_var (isolate, context, info[index], args[index]))
492
+ if (TypeConvert::js_to_gd_var (isolate, context, info[utility ? index + 1 : index], args[index]))
492
493
{
493
494
continue ;
494
495
}
495
496
}
496
497
497
498
// revert all constructors
498
- const String error_message = jsb_errorf (" bad argument: %d" , index);
499
+ const String error_message = jsb_errorf (" bad argument: %d" , utility ? index + 1 : index);
499
500
while (index >= 0 ) { args[index--].~Variant (); }
500
501
impl::Helper::throw_error (isolate, error_message);
501
502
return ;
@@ -563,6 +564,30 @@ namespace jsb
563
564
call_builtin_function<HasReturnValueT>(nullptr , method_info, info, isolate, context);
564
565
}
565
566
567
+ template <Variant::Type VariantT, bool HasReturnValueT>
568
+ static void _utility_method (const v8::FunctionCallbackInfo<v8::Value>& info)
569
+ {
570
+ v8::Isolate* isolate = info.GetIsolate ();
571
+
572
+ if (info.Length () < 1 || info[0 ]->IsObject () || info[0 ]->IsNull ())
573
+ {
574
+ jsb_throw (isolate, " utility methods' first argument must be a JS primitive" );
575
+ return ;
576
+ }
577
+
578
+ const v8::Local<v8::Context> context = isolate->GetCurrentContext ();
579
+ const internal::FBuiltinMethodInfo& method_info = GetVariantInfoCollection (Environment::wrap (context)).methods [info.Data ().As <v8::Int32>()->Value ()];
580
+
581
+ Variant variant;
582
+ if (!TypeConvert::js_to_gd_var (isolate, context, info[0 ], VariantT, variant))
583
+ {
584
+ jsb_throw (isolate, " Failed to convert utility function JS primitive to the required Godot variant type" );
585
+ return ;
586
+ }
587
+
588
+ call_builtin_function<HasReturnValueT>(&variant, method_info, info, isolate, context, true );
589
+ }
590
+
566
591
static impl::ClassBuilder get_class_builder (const ClassRegister& p_env, const NativeClassID p_class_id, const StringName& p_class_name)
567
592
{
568
593
JSB_DEFINE_FAST_CONSTRUCTOR (Vector2, p_class_id, p_class_name);
@@ -892,6 +917,136 @@ namespace jsb
892
917
return class_info;
893
918
}
894
919
}
920
+
921
+ static void utility_noop_constructor (const v8::FunctionCallbackInfo<v8::Value>& _info)
922
+ {
923
+ }
924
+
925
+ // Expose primitive instance methods as static utility functions for variant types not exposed to JS e.g. String
926
+ static NativeClassInfoPtr reflect_bind_utilities (const ClassRegister& p_env, NativeClassID* r_class_id = nullptr )
927
+ {
928
+ const StringName& class_name = internal::NamingUtil::get_class_name (p_env.type_name );
929
+
930
+ if (class_name != p_env.type_name )
931
+ {
932
+ internal::StringNames::get_singleton ().add_replacement (p_env.type_name , class_name);
933
+ }
934
+
935
+ const NativeClassID class_id = p_env->add_native_class (NativeClassType::GodotPrimitive, class_name);
936
+
937
+ v8::Isolate* isolate = p_env->get_isolate ();
938
+ NativeClassInfoPtr class_info = p_env->get_native_class (class_id);
939
+ impl::ClassBuilder class_builder = impl::ClassBuilder::New<0 >(isolate, class_info->name , &utility_noop_constructor, *class_id);
940
+
941
+ auto static_builder = class_builder.Static ();
942
+
943
+ // methods
944
+ {
945
+ List<StringName> methods;
946
+ Variant::get_builtin_method_list (TYPE, &methods);
947
+
948
+ for (const StringName& name : methods)
949
+ {
950
+ const int argument_count = Variant::get_builtin_method_argument_count (TYPE, name);
951
+ const bool has_return_value = Variant::has_builtin_method_return_value (TYPE, name);
952
+ const Variant::Type return_type = Variant::get_builtin_method_return_type (TYPE, name);
953
+ String member_name = internal::NamingUtil::get_member_name (name);
954
+
955
+ if (member_name == " length" )
956
+ {
957
+ // We can't bind a property named .length to a function in JS because it clashes with a built-in
958
+ // property.
959
+ member_name = " length_" ;
960
+ }
961
+
962
+ // convert method info, and store
963
+ const int collection_index = (int ) GetVariantInfoCollection (p_env.env ).methods .size ();
964
+ GetVariantInfoCollection (p_env.env ).methods .append ({});
965
+ internal::FBuiltinMethodInfo& method_info = GetVariantInfoCollection (p_env.env ).methods .write [collection_index];
966
+ method_info.set_debug_name (member_name);
967
+ method_info.builtin_func = Variant::get_validated_builtin_method (TYPE, name);
968
+ method_info.return_type = return_type;
969
+ method_info.default_arguments = Variant::get_builtin_method_default_arguments (TYPE, name);
970
+ #if GODOT_4_5_OR_NEWER
971
+ method_info.argument_types .resize_initialized (argument_count);
972
+ #else
973
+ method_info.argument_types .resize_zeroed (argument_count);
974
+ #endif
975
+ method_info.is_vararg = Variant::is_builtin_method_vararg (TYPE, name);
976
+ for (int argument_index = 0 ; argument_index < argument_count; ++argument_index)
977
+ {
978
+ const Variant::Type type = Variant::get_builtin_method_argument_type (TYPE, name, argument_index);
979
+ method_info.argument_types .write [argument_index] = type;
980
+ }
981
+
982
+ // function wrapper
983
+ if (has_return_value)
984
+ {
985
+ if (Variant::is_builtin_method_static (TYPE, name))
986
+ {
987
+ static_builder.Method (member_name, _static_method<true >, collection_index);
988
+ }
989
+ else
990
+ {
991
+ static_builder.Method (member_name, _utility_method<TYPE, true >, collection_index);
992
+ }
993
+ }
994
+ else if (Variant::is_builtin_method_static (TYPE, name))
995
+ {
996
+ static_builder.Method (member_name, _static_method<false >, collection_index);
997
+ }
998
+ else
999
+ {
1000
+ static_builder.Method (member_name, _utility_method<TYPE, false >, collection_index);
1001
+ }
1002
+ }
1003
+
1004
+ ReflectAdditionalMethodRegister<T>::register_ (class_builder);
1005
+ }
1006
+
1007
+ // enums
1008
+ HashSet<StringName> enum_constants;
1009
+ {
1010
+ List<StringName> enums;
1011
+ Variant::get_enums_for_type (TYPE, &enums);
1012
+ for (const StringName& enum_name : enums)
1013
+ {
1014
+ String exposed_enum_name = internal::NamingUtil::get_enum_name (enum_name);
1015
+ auto enum_decl = static_builder.Enum (exposed_enum_name);
1016
+ List<StringName> enumerations;
1017
+ Variant::get_enumerations_for_enum (TYPE, enum_name, &enumerations);
1018
+ for (const StringName& enumeration : enumerations)
1019
+ {
1020
+ bool r_valid;
1021
+ const int enum_value = Variant::get_enum_value (TYPE, enum_name, enumeration, &r_valid);
1022
+ jsb_check (r_valid);
1023
+ enum_decl.Value (internal::NamingUtil::get_enum_value_name (enumeration), enum_value);
1024
+ enum_constants.insert (enumeration);
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ // constants
1030
+ {
1031
+ List<StringName> constants;
1032
+ Variant::get_constants_for_type (TYPE, &constants);
1033
+ for (const StringName& constant : constants)
1034
+ {
1035
+ // exclude all enum constants
1036
+ if (enum_constants.has (constant)) continue ;
1037
+ static_builder.LazyProperty (internal::NamingUtil::get_constant_name (constant), _get_constant_value_lazy);
1038
+ }
1039
+ }
1040
+
1041
+ {
1042
+ if (r_class_id) *r_class_id = class_id;
1043
+
1044
+ NativeClassInfoPtr class_info = p_env.env ->get_native_class (class_id);
1045
+ class_info->clazz = class_builder.Build ();
1046
+ jsb_check (!class_info->clazz .IsEmpty ());
1047
+ return class_info;
1048
+ }
1049
+ }
895
1050
};
896
1051
897
1052
void register_primitive_bindings_reflect (Environment* p_env)
@@ -902,6 +1057,7 @@ namespace jsb
902
1057
# include " jsb_primitive_types.def.h"
903
1058
#pragma pop_macro("DEF")
904
1059
1060
+ p_env->add_class_register (GetTypeInfo<String>::VARIANT_TYPE, &VariantBind<String>::reflect_bind_utilities);
905
1061
}
906
1062
}
907
1063
0 commit comments