summaryrefslogtreecommitdiff
path: root/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd
diff options
context:
space:
mode:
authorradiohotline <radoihotline@disroot.rg>2026-08-25 16:33:56 +0300
committerradiohotline <radoihotline@disroot.rg>2026-08-25 16:33:56 +0300
commita89e744ed0e8c759caf041a580112365bbfe4303 (patch)
treedd61e320904176995727e86a857d00c90d4933ba /addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd
parent6d53d8cac78250dc888aa49d5f9d4175c6652feb (diff)
escoria updateHEADmain
Diffstat (limited to 'addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd')
-rw-r--r--addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd286
1 files changed, 286 insertions, 0 deletions
diff --git a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd
new file mode 100644
index 0000000..7eae4b2
--- /dev/null
+++ b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd
@@ -0,0 +1,286 @@
+class_name GdFunctionDescriptor
+extends RefCounted
+
+var _is_virtual :bool
+var _is_static :bool
+var _is_engine :bool
+var _is_coroutine :bool
+var _name :String
+var _source_path: String
+var _line_number :int
+var _return_type :int
+var _return_class :String
+var _args : Array[GdFunctionArgument]
+var _varargs :Array[GdFunctionArgument]
+
+
+
+static func create(p_name: String, p_source_path: String, p_source_line: int, p_return_type: int, p_args: Array[GdFunctionArgument] = []) -> GdFunctionDescriptor:
+ var fd := GdFunctionDescriptor.new(p_name, p_source_line, false, false, false, p_return_type, "", p_args)
+ fd.enrich_file_info(p_source_path, p_source_line)
+ return fd
+
+static func create_static(p_name: String, p_source_path: String, p_source_line: int, p_return_type: int, p_args: Array[GdFunctionArgument] = []) -> GdFunctionDescriptor:
+ var fd := GdFunctionDescriptor.new(p_name, p_source_line, false, true, false, p_return_type, "", p_args)
+ fd.enrich_file_info(p_source_path, p_source_line)
+ return fd
+
+
+func _init(p_name :String,
+ p_line_number :int,
+ p_is_virtual :bool,
+ p_is_static :bool,
+ p_is_engine :bool,
+ p_return_type :int,
+ p_return_class :String,
+ p_args : Array[GdFunctionArgument],
+ p_varargs :Array[GdFunctionArgument] = []) -> void:
+ _name = p_name
+ _line_number = p_line_number
+ _return_type = p_return_type
+ _return_class = p_return_class
+ _is_virtual = p_is_virtual
+ _is_static = p_is_static
+ _is_engine = p_is_engine
+ _is_coroutine = false
+ _args = p_args
+ _varargs = p_varargs
+
+
+func with_return_class(clazz_name: String) -> GdFunctionDescriptor:
+ _return_class = clazz_name
+ return self
+
+
+func name() -> String:
+ return _name
+
+
+func source_path() -> String:
+ return _source_path
+
+
+func line_number() -> int:
+ return _line_number
+
+
+func is_virtual() -> bool:
+ return _is_virtual
+
+
+func is_static() -> bool:
+ return _is_static
+
+
+func is_engine() -> bool:
+ return _is_engine
+
+
+func is_vararg() -> bool:
+ return not _varargs.is_empty()
+
+
+func is_coroutine() -> bool:
+ return _is_coroutine
+
+
+func is_parameterized() -> bool:
+ for current in _args:
+ var arg :GdFunctionArgument = current
+ if arg.name() in GdFunctionArgument.ARG_PARAMETERIZED_TEST:
+ return true
+ return false
+
+
+func is_private() -> bool:
+ return name().begins_with("_") and not is_virtual()
+
+
+func return_type() -> int:
+ return _return_type
+
+
+func return_type_as_string() -> String:
+ if return_type() == TYPE_NIL:
+ return "void"
+ if (return_type() == TYPE_OBJECT or return_type() == GdObjects.TYPE_ENUM) and not _return_class.is_empty():
+ return _return_class
+ return GdObjects.type_as_string(return_type())
+
+
+func set_argument_value(arg_name: String, value: String) -> void:
+ var argument: GdFunctionArgument = _args.filter(func(arg: GdFunctionArgument) -> bool:
+ return arg.name() == arg_name
+ ).front()
+ if argument != null:
+ argument.set_value(value)
+
+
+func enrich_arguments(arguments: Array[Dictionary]) -> void:
+ for arg_index: int in arguments.size():
+ var arg: Dictionary = arguments[arg_index]
+ if arg["type"] != GdObjects.TYPE_VARARG:
+ var arg_name: String = arg["name"]
+ var arg_value: String = arg["value"]
+ set_argument_value(arg_name, arg_value)
+
+
+func enrich_file_info(p_source_path: String, p_line_number: int) -> void:
+ _source_path = p_source_path
+ _line_number = p_line_number
+
+
+func args() -> Array[GdFunctionArgument]:
+ return _args
+
+
+func varargs() -> Array[GdFunctionArgument]:
+ return _varargs
+
+
+func typed_args() -> String:
+ var collect := PackedStringArray()
+ for arg in args():
+ @warning_ignore("return_value_discarded")
+ collect.push_back(arg._to_string())
+ for arg in varargs():
+ @warning_ignore("return_value_discarded")
+ collect.push_back(arg._to_string())
+ return ", ".join(collect)
+
+
+func _to_string() -> String:
+ var fsignature := "virtual " if is_virtual() else ""
+ if _return_type == TYPE_NIL:
+ return fsignature + "[Line:%s] func %s(%s):" % [line_number(), name(), typed_args()]
+ var func_template := fsignature + "[Line:%s] func %s(%s) -> %s:"
+ if is_static():
+ func_template= "[Line:%s] static func %s(%s) -> %s:"
+ return func_template % [line_number(), name(), typed_args(), return_type_as_string()]
+
+
+# extract function description given by Object.get_method_list()
+static func extract_from(descriptor :Dictionary, is_engine_ := true) -> GdFunctionDescriptor:
+ var func_name: String = descriptor["name"]
+ var function_flags: int = descriptor["flags"]
+ var return_descriptor: Dictionary = descriptor["return"]
+ var clazz_name: String = return_descriptor["class_name"]
+ var is_virtual_: bool = function_flags & METHOD_FLAG_VIRTUAL
+ var is_static_: bool = function_flags & METHOD_FLAG_STATIC
+ var is_vararg_: bool = function_flags & METHOD_FLAG_VARARG
+
+ return GdFunctionDescriptor.new(
+ func_name,
+ -1,
+ is_virtual_,
+ is_static_,
+ is_engine_,
+ _extract_return_type(return_descriptor),
+ clazz_name,
+ _extract_args(descriptor),
+ _build_varargs(is_vararg_)
+ )
+
+# temporary exclude GlobalScope enums
+const enum_fix := [
+ "Side",
+ "Corner",
+ "Orientation",
+ "ClockDirection",
+ "HorizontalAlignment",
+ "VerticalAlignment",
+ "InlineAlignment",
+ "EulerOrder",
+ "Error",
+ "Key",
+ "MIDIMessage",
+ "MouseButton",
+ "MouseButtonMask",
+ "JoyButton",
+ "JoyAxis",
+ "PropertyHint",
+ "PropertyUsageFlags",
+ "MethodFlags",
+ "Variant.Type",
+ "Control.LayoutMode"]
+
+
+static func _extract_return_type(return_info :Dictionary) -> int:
+ var type :int = return_info["type"]
+ var usage :int = return_info["usage"]
+ if type == TYPE_INT and usage & PROPERTY_USAGE_CLASS_IS_ENUM:
+ return GdObjects.TYPE_ENUM
+ if type == TYPE_NIL and usage & PROPERTY_USAGE_NIL_IS_VARIANT:
+ return GdObjects.TYPE_VARIANT
+ if type == TYPE_NIL and usage == 6:
+ return GdObjects.TYPE_VOID
+ return type
+
+
+static func _extract_args(descriptor :Dictionary) -> Array[GdFunctionArgument]:
+ var args_ :Array[GdFunctionArgument] = []
+ var arguments :Array = descriptor["args"]
+ var defaults :Array = descriptor["default_args"]
+ # iterate backwards because the default values are stored from right to left
+ while not arguments.is_empty():
+ var arg :Dictionary = arguments.pop_back()
+ var arg_name := _argument_name(arg)
+ var arg_type := _argument_type(arg)
+ var arg_type_hint := _argument_hint(arg)
+ #var arg_class: StringName = arg["class_name"]
+ var default_value: Variant = GdFunctionArgument.UNDEFINED if defaults.is_empty() else defaults.pop_back()
+ args_.push_front(GdFunctionArgument.new(arg_name, arg_type, default_value, arg_type_hint))
+ return args_
+
+
+static func _build_varargs(p_is_vararg :bool) -> Array[GdFunctionArgument]:
+ var varargs_ :Array[GdFunctionArgument] = []
+ if not p_is_vararg:
+ return varargs_
+ varargs_.push_back(GdFunctionArgument.new("varargs", GdObjects.TYPE_VARARG, ''))
+ return varargs_
+
+
+static func _argument_name(arg :Dictionary) -> String:
+ return arg["name"]
+
+
+static func _argument_type(arg :Dictionary) -> int:
+ var type :int = arg["type"]
+ var usage :int = arg["usage"]
+
+ if type == TYPE_OBJECT:
+ if arg["class_name"] == "Node":
+ return GdObjects.TYPE_NODE
+ if arg["class_name"] == "Fuzzer":
+ return GdObjects.TYPE_FUZZER
+
+ # if the argument untyped we need to scan the assignef value type
+ if type == TYPE_NIL and usage == PROPERTY_USAGE_NIL_IS_VARIANT:
+ return GdObjects.TYPE_VARIANT
+ return type
+
+
+static func _argument_hint(arg :Dictionary) -> int:
+ var hint :int = arg["hint"]
+ var hint_string :String = arg["hint_string"]
+
+ match hint:
+ PROPERTY_HINT_ARRAY_TYPE:
+ return GdObjects.string_to_type(hint_string)
+ _:
+ return 0
+
+
+static func _argument_type_as_string(arg :Dictionary) -> String:
+ var type := _argument_type(arg)
+ match type:
+ TYPE_NIL:
+ return ""
+ TYPE_OBJECT:
+ var clazz_name :String = arg["class_name"]
+ if not clazz_name.is_empty():
+ return clazz_name
+ return ""
+ _:
+ return GdObjects.type_as_string(type)