mirror of https://github.com/coddrago/Heroku
130 lines
4.0 KiB
Python
130 lines
4.0 KiB
Python
# ©️ Dan Gazizullin, 2021-2023
|
|
# This file is a part of Hikka Userbot
|
|
# 🌐 https://github.com/hikariatama/Hikka
|
|
# You can redistribute it and/or modify it under the terms of the GNU AGPLv3
|
|
# 🔑 https://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
# ©️ Codrago, 2024-2025
|
|
# This file is a part of Heroku Userbot
|
|
# 🌐 https://github.com/coddrago/Heroku
|
|
# You can redistribute it and/or modify it under the terms of the GNU AGPLv3
|
|
# 🔑 https://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
import gc as _gc
|
|
import inspect
|
|
import logging
|
|
import types as _types
|
|
import typing
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def proxy0(data):
|
|
def proxy1():
|
|
return data
|
|
|
|
return proxy1
|
|
|
|
|
|
_CELLTYPE = type(proxy0(None).__closure__[0])
|
|
|
|
|
|
def replace_all_refs(replace_from: typing.Any, replace_to: typing.Any) -> typing.Any:
|
|
"""
|
|
:summary: Uses the :mod:`gc` module to replace all references to obj
|
|
:attr:`replace_from` with :attr:`replace_to` (it tries it's best,
|
|
anyway).
|
|
:param replace_from: The obj you want to replace.
|
|
:param replace_to: The new objject you want in place of the old one.
|
|
:returns: The replace_from
|
|
"""
|
|
# https://github.com/cart0113/pyjack/blob/dd1f9b70b71f48335d72f53ee0264cf70dbf4e28/pyjack.py
|
|
|
|
_gc.collect()
|
|
|
|
hit = False
|
|
for referrer in _gc.get_referrers(replace_from):
|
|
# FRAMES -- PASS THEM UP
|
|
if isinstance(referrer, _types.FrameType):
|
|
continue
|
|
|
|
# DICTS
|
|
if isinstance(referrer, dict):
|
|
cls = None
|
|
|
|
# THIS CODE HERE IS TO DEAL WITH DICTPROXY TYPES
|
|
if "__dict__" in referrer and "__weakref__" in referrer:
|
|
for cls in _gc.get_referrers(referrer):
|
|
if inspect.isclass(cls) and cls.__dict__ == referrer:
|
|
break
|
|
|
|
for key, value in referrer.items():
|
|
# REMEMBER TO REPLACE VALUES ...
|
|
if value is replace_from:
|
|
hit = True
|
|
value = replace_to
|
|
referrer[key] = value
|
|
if cls: # AGAIN, CLEANUP DICTPROXY PROBLEM
|
|
setattr(cls, key, replace_to)
|
|
# AND KEYS.
|
|
if key is replace_from:
|
|
hit = True
|
|
del referrer[key]
|
|
referrer[replace_to] = value
|
|
|
|
elif isinstance(referrer, list):
|
|
for i, value in enumerate(referrer):
|
|
if value is replace_from:
|
|
hit = True
|
|
referrer[i] = replace_to
|
|
|
|
elif isinstance(referrer, set):
|
|
referrer.remove(replace_from)
|
|
referrer.add(replace_to)
|
|
hit = True
|
|
|
|
elif isinstance(
|
|
referrer,
|
|
(
|
|
tuple,
|
|
frozenset,
|
|
),
|
|
):
|
|
new_tuple = []
|
|
for obj in referrer:
|
|
if obj is replace_from:
|
|
new_tuple.append(replace_to)
|
|
else:
|
|
new_tuple.append(obj)
|
|
replace_all_refs(referrer, type(referrer)(new_tuple))
|
|
|
|
elif isinstance(referrer, _CELLTYPE):
|
|
|
|
def _proxy0(data):
|
|
def proxy1():
|
|
return data
|
|
|
|
return proxy1
|
|
|
|
proxy = _proxy0(replace_to)
|
|
newcell = proxy.__closure__[0]
|
|
replace_all_refs(referrer, newcell)
|
|
|
|
elif isinstance(referrer, _types.FunctionType):
|
|
localsmap = {}
|
|
for key in ["code", "globals", "name", "defaults", "closure"]:
|
|
orgattr = getattr(referrer, f"__{key}__")
|
|
localsmap[key] = replace_to if orgattr is replace_from else orgattr
|
|
localsmap["argdefs"] = localsmap["defaults"]
|
|
del localsmap["defaults"]
|
|
newfn = _types.FunctionType(**localsmap)
|
|
replace_all_refs(referrer, newfn)
|
|
|
|
else:
|
|
logger.debug("%s is not supported.", referrer)
|
|
|
|
if hit is False:
|
|
raise AttributeError(f"Object '{replace_from}' not found")
|
|
|
|
return replace_from
|