2024-01-06 11:27:15 +01:00
|
|
|
# These methods didn't have flags that would exclude them from being used
|
|
|
|
# in a double and they appear to break things if they are included.
|
2023-01-07 20:26:17 +01:00
|
|
|
const BLACKLIST = [
|
2023-01-20 23:34:39 +01:00
|
|
|
"get_script",
|
|
|
|
"has_method",
|
2023-01-07 20:26:17 +01:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Combins the meta for the method with additional information.
|
|
|
|
# * flag for whether the method is local
|
|
|
|
# * adds a 'default' property to all parameters that can be easily checked per
|
|
|
|
# parameter
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
class ParsedMethod:
|
2024-01-06 11:27:15 +01:00
|
|
|
const NO_DEFAULT = "__no__default__"
|
|
|
|
|
2023-01-07 20:26:17 +01:00
|
|
|
var _meta = {}
|
2023-01-20 23:34:39 +01:00
|
|
|
var meta = _meta:
|
|
|
|
get:
|
|
|
|
return _meta
|
|
|
|
set(val):
|
|
|
|
return
|
2023-01-07 20:26:17 +01:00
|
|
|
var is_local = false
|
|
|
|
|
2024-01-06 11:27:15 +01:00
|
|
|
var _parameters = []
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
func _init(metadata):
|
|
|
|
_meta = metadata
|
|
|
|
var start_default = _meta.args.size() - _meta.default_args.size()
|
|
|
|
for i in range(_meta.args.size()):
|
|
|
|
var arg = _meta.args[i]
|
|
|
|
# Add a "default" property to the metadata so we don't have to do
|
2024-01-06 11:27:15 +01:00
|
|
|
# weird default paramter position math again.
|
2023-01-20 23:34:39 +01:00
|
|
|
if i >= start_default:
|
|
|
|
arg["default"] = _meta.default_args[start_default - i]
|
2023-01-07 20:26:17 +01:00
|
|
|
else:
|
2023-01-20 23:34:39 +01:00
|
|
|
arg["default"] = NO_DEFAULT
|
2023-01-07 20:26:17 +01:00
|
|
|
_parameters.append(arg)
|
|
|
|
|
2024-01-06 11:27:15 +01:00
|
|
|
func is_eligible_for_doubling():
|
|
|
|
var has_bad_flag = (
|
|
|
|
_meta.flags & (METHOD_FLAG_OBJECT_CORE | METHOD_FLAG_VIRTUAL | METHOD_FLAG_STATIC)
|
|
|
|
)
|
|
|
|
return !has_bad_flag and BLACKLIST.find(_meta.name) == -1
|
|
|
|
|
|
|
|
func is_accessor():
|
|
|
|
return (
|
|
|
|
_meta.name.begins_with("@")
|
|
|
|
and (_meta.name.ends_with("_getter") or _meta.name.ends_with("_setter"))
|
|
|
|
)
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
func to_s():
|
|
|
|
var s = _meta.name + "("
|
|
|
|
|
|
|
|
for i in range(_meta.args.size()):
|
|
|
|
var arg = _meta.args[i]
|
2023-01-20 23:34:39 +01:00
|
|
|
if str(arg.default) != NO_DEFAULT:
|
2023-01-07 20:26:17 +01:00
|
|
|
var val = str(arg.default)
|
2023-01-20 23:34:39 +01:00
|
|
|
if val == "":
|
2023-01-07 20:26:17 +01:00
|
|
|
val = '""'
|
2023-01-20 23:34:39 +01:00
|
|
|
s += str(arg.name, " = ", val)
|
2023-01-07 20:26:17 +01:00
|
|
|
else:
|
|
|
|
s += str(arg.name)
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
if i != _meta.args.size() - 1:
|
|
|
|
s += ", "
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
s += ")"
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
class ParsedScript:
|
|
|
|
# All methods indexed by name.
|
|
|
|
var _methods_by_name = {}
|
2023-01-20 23:34:39 +01:00
|
|
|
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
var _script_path = null
|
2023-01-20 23:34:39 +01:00
|
|
|
var script_path = _script_path:
|
|
|
|
get:
|
|
|
|
return _script_path
|
|
|
|
set(val):
|
|
|
|
return
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
var _subpath = null
|
2023-01-20 23:34:39 +01:00
|
|
|
var subpath = null:
|
|
|
|
get:
|
|
|
|
return _subpath
|
|
|
|
set(val):
|
|
|
|
return
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
var _resource = null
|
2023-01-20 23:34:39 +01:00
|
|
|
var resource = null:
|
|
|
|
get:
|
|
|
|
return _resource
|
|
|
|
set(val):
|
|
|
|
return
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
var _native_instance = null
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
var is_native = false:
|
|
|
|
get:
|
|
|
|
return _native_instance != null
|
|
|
|
set(val):
|
|
|
|
return
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
func unreference():
|
2023-01-20 23:34:39 +01:00
|
|
|
if _native_instance != null:
|
2023-01-07 20:26:17 +01:00
|
|
|
_native_instance.free()
|
|
|
|
return super()
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
func _init(script_or_inst, inner_class = null):
|
2023-01-07 20:26:17 +01:00
|
|
|
var to_load = script_or_inst
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
if _utils.is_native_class(to_load):
|
2023-01-07 20:26:17 +01:00
|
|
|
_resource = to_load
|
|
|
|
_native_instance = to_load.new()
|
|
|
|
else:
|
2023-01-20 23:34:39 +01:00
|
|
|
if !script_or_inst is Resource:
|
2023-01-07 20:26:17 +01:00
|
|
|
to_load = load(script_or_inst.get_script().get_path())
|
|
|
|
|
|
|
|
_script_path = to_load.resource_path
|
2023-01-20 23:34:39 +01:00
|
|
|
if inner_class != null:
|
2023-01-07 20:26:17 +01:00
|
|
|
_subpath = _find_subpath(to_load, inner_class)
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
if inner_class == null:
|
2023-01-07 20:26:17 +01:00
|
|
|
_resource = to_load
|
|
|
|
else:
|
|
|
|
_resource = inner_class
|
|
|
|
to_load = inner_class
|
|
|
|
|
|
|
|
_parse_methods(to_load)
|
|
|
|
|
|
|
|
func _print_flags(meta):
|
2023-01-20 23:34:39 +01:00
|
|
|
print(
|
|
|
|
str(meta.name, ":").rpad(30),
|
|
|
|
str(meta.flags).rpad(4),
|
|
|
|
" = ",
|
|
|
|
_utils.dec2bistr(meta.flags, 10)
|
|
|
|
)
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
func _get_native_methods(base_type):
|
|
|
|
var to_return = []
|
2023-01-20 23:34:39 +01:00
|
|
|
if base_type != null:
|
|
|
|
var source = str("extends ", base_type)
|
2023-01-07 20:26:17 +01:00
|
|
|
var inst = _utils.create_script_from_source(source).new()
|
|
|
|
to_return = inst.get_method_list()
|
2023-01-20 23:34:39 +01:00
|
|
|
if !inst is RefCounted:
|
2023-01-07 20:26:17 +01:00
|
|
|
inst.free()
|
|
|
|
return to_return
|
|
|
|
|
|
|
|
func _parse_methods(thing):
|
|
|
|
var methods = []
|
2023-01-20 23:34:39 +01:00
|
|
|
if is_native:
|
2023-01-07 20:26:17 +01:00
|
|
|
methods = _native_instance.get_method_list()
|
|
|
|
else:
|
|
|
|
var base_type = thing.get_instance_base_type()
|
|
|
|
methods = _get_native_methods(base_type)
|
|
|
|
|
|
|
|
for m in methods:
|
2024-01-06 11:27:15 +01:00
|
|
|
var parsed = ParsedMethod.new(m)
|
|
|
|
_methods_by_name[m.name] = parsed
|
|
|
|
# _init must always be included so that we can initialize
|
|
|
|
# double_tools
|
|
|
|
if m.name == "_init":
|
|
|
|
parsed.is_local = true
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
# This loop will overwrite all entries in _methods_by_name with the local
|
|
|
|
# method object so there is only ever one listing for a function with
|
|
|
|
# the right "is_local" flag.
|
2023-01-20 23:34:39 +01:00
|
|
|
if !is_native:
|
2023-01-07 20:26:17 +01:00
|
|
|
methods = thing.get_script_method_list()
|
|
|
|
for m in methods:
|
|
|
|
var parsed_method = ParsedMethod.new(m)
|
|
|
|
parsed_method.is_local = true
|
|
|
|
_methods_by_name[m.name] = parsed_method
|
|
|
|
|
|
|
|
func _find_subpath(parent_script, inner):
|
|
|
|
var const_map = parent_script.get_script_constant_map()
|
|
|
|
var consts = const_map.keys()
|
|
|
|
var const_idx = 0
|
|
|
|
var found = false
|
|
|
|
var to_return = null
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
while const_idx < consts.size() and !found:
|
2023-01-07 20:26:17 +01:00
|
|
|
var key = consts[const_idx]
|
|
|
|
var const_val = const_map[key]
|
2023-01-20 23:34:39 +01:00
|
|
|
if typeof(const_val) == TYPE_OBJECT:
|
|
|
|
if const_val == inner:
|
2023-01-07 20:26:17 +01:00
|
|
|
found = true
|
|
|
|
to_return = key
|
|
|
|
else:
|
|
|
|
to_return = _find_subpath(const_val, inner)
|
2023-01-20 23:34:39 +01:00
|
|
|
if to_return != null:
|
|
|
|
to_return = str(key, ".", to_return)
|
2023-01-07 20:26:17 +01:00
|
|
|
found = true
|
|
|
|
|
|
|
|
const_idx += 1
|
|
|
|
|
|
|
|
return to_return
|
|
|
|
|
|
|
|
func get_method(name):
|
|
|
|
return _methods_by_name[name]
|
|
|
|
|
|
|
|
func get_super_method(name):
|
|
|
|
var to_return = get_method(name)
|
2023-01-20 23:34:39 +01:00
|
|
|
if to_return.is_local:
|
2023-01-07 20:26:17 +01:00
|
|
|
to_return = null
|
|
|
|
|
|
|
|
return to_return
|
|
|
|
|
|
|
|
func get_local_method(name):
|
|
|
|
var to_return = get_method(name)
|
2023-01-20 23:34:39 +01:00
|
|
|
if !to_return.is_local:
|
2023-01-07 20:26:17 +01:00
|
|
|
to_return = null
|
|
|
|
|
|
|
|
return to_return
|
|
|
|
|
|
|
|
func get_sorted_method_names():
|
|
|
|
var keys = _methods_by_name.keys()
|
|
|
|
keys.sort()
|
|
|
|
return keys
|
|
|
|
|
|
|
|
func get_local_method_names():
|
|
|
|
var names = []
|
|
|
|
for method in _methods_by_name:
|
2023-01-20 23:34:39 +01:00
|
|
|
if _methods_by_name[method].is_local:
|
2023-01-07 20:26:17 +01:00
|
|
|
names.append(method)
|
|
|
|
|
|
|
|
return names
|
|
|
|
|
|
|
|
func get_super_method_names():
|
|
|
|
var names = []
|
|
|
|
for method in _methods_by_name:
|
2023-01-20 23:34:39 +01:00
|
|
|
if !_methods_by_name[method].is_local:
|
2023-01-07 20:26:17 +01:00
|
|
|
names.append(method)
|
|
|
|
|
|
|
|
return names
|
|
|
|
|
|
|
|
func get_local_methods():
|
|
|
|
var to_return = []
|
|
|
|
for key in _methods_by_name:
|
|
|
|
var method = _methods_by_name[key]
|
2023-01-20 23:34:39 +01:00
|
|
|
if method.is_local:
|
2023-01-07 20:26:17 +01:00
|
|
|
to_return.append(method)
|
|
|
|
return to_return
|
|
|
|
|
|
|
|
func get_super_methods():
|
|
|
|
var to_return = []
|
|
|
|
for key in _methods_by_name:
|
|
|
|
var method = _methods_by_name[key]
|
2023-01-20 23:34:39 +01:00
|
|
|
if !method.is_local:
|
2023-01-07 20:26:17 +01:00
|
|
|
to_return.append(method)
|
|
|
|
return to_return
|
|
|
|
|
|
|
|
func get_extends_text():
|
|
|
|
var text = null
|
2023-01-20 23:34:39 +01:00
|
|
|
if is_native:
|
2023-01-07 20:26:17 +01:00
|
|
|
text = str("extends ", _native_instance.get_class())
|
|
|
|
else:
|
|
|
|
text = str("extends '", _script_path, "'")
|
2023-01-20 23:34:39 +01:00
|
|
|
if _subpath != null:
|
|
|
|
text += "." + _subpath
|
2023-01-07 20:26:17 +01:00
|
|
|
return text
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
var scripts = {}
|
2023-01-20 23:34:39 +01:00
|
|
|
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
2023-01-07 20:26:17 +01:00
|
|
|
|
|
|
|
|
|
|
|
func _get_instance_id(thing):
|
|
|
|
var inst_id = null
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
if _utils.is_native_class(thing):
|
|
|
|
var id_str = str(thing).replace("<", "").replace(">", "").split("#")[1]
|
2023-01-07 20:26:17 +01:00
|
|
|
inst_id = id_str.to_int()
|
2023-01-20 23:34:39 +01:00
|
|
|
elif typeof(thing) == TYPE_STRING:
|
|
|
|
if FileAccess.file_exists(thing):
|
2023-01-07 20:26:17 +01:00
|
|
|
inst_id = load(thing).get_instance_id()
|
|
|
|
else:
|
|
|
|
inst_id = thing.get_instance_id()
|
|
|
|
|
|
|
|
return inst_id
|
|
|
|
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
func parse(thing, inner_thing = null):
|
2023-01-07 20:26:17 +01:00
|
|
|
var key = -1
|
2023-01-20 23:34:39 +01:00
|
|
|
if inner_thing == null:
|
2023-01-07 20:26:17 +01:00
|
|
|
key = _get_instance_id(thing)
|
|
|
|
else:
|
|
|
|
key = _get_instance_id(inner_thing)
|
|
|
|
|
|
|
|
var parsed = null
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
if key != null:
|
|
|
|
if scripts.has(key):
|
2023-01-07 20:26:17 +01:00
|
|
|
parsed = scripts[key]
|
|
|
|
else:
|
|
|
|
var obj = instance_from_id(_get_instance_id(thing))
|
|
|
|
var inner = null
|
2023-01-20 23:34:39 +01:00
|
|
|
if inner_thing != null:
|
2023-01-07 20:26:17 +01:00
|
|
|
inner = instance_from_id(_get_instance_id(inner_thing))
|
|
|
|
|
2023-01-20 23:34:39 +01:00
|
|
|
if obj is Resource or _utils.is_native_class(obj):
|
2023-01-07 20:26:17 +01:00
|
|
|
parsed = ParsedScript.new(obj, inner)
|
|
|
|
scripts[key] = parsed
|
|
|
|
|
|
|
|
return parsed
|