You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +08:00
Correct MANIFEST->MANIFEST.in more lint
This commit is contained in:
22
MANIFEST
22
MANIFEST
@@ -1,22 +0,0 @@
|
||||
README
|
||||
compile_tests
|
||||
setup.cfg
|
||||
setup.py
|
||||
test/*
|
||||
uncompyle2/__init__.py
|
||||
uncompyle2/disas.py
|
||||
uncompyle2/magics.py
|
||||
uncompyle2/parser.py
|
||||
uncompyle2/scanner.py
|
||||
uncompyle2/scanner25.py
|
||||
uncompyle2/scanner26.py
|
||||
uncompyle2/scanner27.py
|
||||
uncompyle2/spark.py
|
||||
uncompyle2/verify.py
|
||||
uncompyle2/walker.py
|
||||
uncompyle2/opcode/__init__.py
|
||||
uncompyle2/opcode/opcode_23.py
|
||||
uncompyle2/opcode/opcode_24.py
|
||||
uncompyle2/opcode/opcode_25.py
|
||||
uncompyle2/opcode/opcode_26.py
|
||||
uncompyle2/opcode/opcode_27.py
|
5
MANIFEST.in
Normal file
5
MANIFEST.in
Normal file
@@ -0,0 +1,5 @@
|
||||
include README.rst
|
||||
include __pkginfo__.py
|
||||
recursive-include uncompyle6
|
||||
include script/uncompyle6
|
||||
recursive-include/test/*
|
12
PKG-INFO
Executable file → Normal file
12
PKG-INFO
Executable file → Normal file
@@ -1,10 +1,10 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: uncompyle2
|
||||
Version: 1.1
|
||||
Metadata-Version: 2.0
|
||||
Name: uncompyle6
|
||||
Version: 2.0.1
|
||||
Summary: Python byte-code to source-code converter
|
||||
Home-page: http://github.com/Mysterie/uncompyle2
|
||||
Author: Mysterie
|
||||
Author-email: kajusska@gmail.com
|
||||
Home-page: http://github.com/rocky/uncompyle6
|
||||
Author: Rocky
|
||||
Author-email: rb@dustyfeet.com
|
||||
License: GPLv3
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
|
0
README.rst
Executable file → Normal file
0
README.rst
Executable file → Normal file
@@ -21,7 +21,7 @@ hasfree = []
|
||||
|
||||
opmap = {}
|
||||
opname = [''] * 256
|
||||
for op in range(256): opname[op] = '<' + `op` + '>'
|
||||
for op in range(256): opname[op] = '<' + repr(op) + '>'
|
||||
del op
|
||||
|
||||
def def_op(name, op):
|
||||
|
@@ -47,7 +47,7 @@ def updateGlobal():
|
||||
globals().update({'PJIT': opmap['JUMP_IF_TRUE']})
|
||||
globals().update({'JA': opmap['JUMP_ABSOLUTE']})
|
||||
globals().update({'JF': opmap['JUMP_FORWARD']})
|
||||
globals().update({k.replace('+', '_'): v for (k,v) in opmap.items()})
|
||||
globals().update({k.replace('+', '_'): v for (k, v) in opmap.items()})
|
||||
globals().update({'JUMP_OPs': map(lambda op: opname[op], hasjrel + hasjabs)})
|
||||
|
||||
# Instruction opcodes for compiled code
|
||||
|
@@ -43,9 +43,9 @@ def updateGlobal():
|
||||
globals().update({'PJIT': opmap['POP_JUMP_IF_TRUE']})
|
||||
globals().update({'JA': opmap['JUMP_ABSOLUTE']})
|
||||
globals().update({'JF': opmap['JUMP_FORWARD']})
|
||||
globals().update({k.replace('+','_'):v for (k,v) in opmap.items()})
|
||||
globals().update({k.replace('+', '_'): v for (k, v) in opmap.items()})
|
||||
globals().update({'JUMP_OPs': map(lambda op: opname[op], hasjrel + hasjabs)})
|
||||
|
||||
|
||||
# Instruction opcodes for compiled code
|
||||
# Blank lines correspond to available opcodes
|
||||
|
||||
|
@@ -232,7 +232,7 @@ class Scanner25(scan.Scanner):
|
||||
# del POP_TOP
|
||||
if self.code[i+opsize] == POP_TOP:
|
||||
if self.code[i+opsize] == self.code[i+opsize+1] and self.code[i+opsize] == self.code[i+opsize+2] \
|
||||
and opcode in (JF,JA) and self.code[i+opsize] != self.code[i+opsize+3]:
|
||||
and opcode in (JF, JA) and self.code[i+opsize] != self.code[i+opsize+3]:
|
||||
pass
|
||||
else:
|
||||
toDel += [i+opsize]
|
||||
@@ -334,10 +334,10 @@ class Scanner25(scan.Scanner):
|
||||
while chckDel < chckStp-3:
|
||||
toDel += [chckDel]
|
||||
chckDel += self.op_size(self.code[chckDel])
|
||||
if (self.code[chckStp-3] in (STORE_NAME,STORE_FAST)
|
||||
and self.code[chckStp+3] in (LOAD_NAME,LOAD_FAST)
|
||||
and self.code[chckStp+6] in (DELETE_NAME,DELETE_FAST)):
|
||||
toDel += [chckStp-3,chckStp+3,chckStp+6]
|
||||
if (self.code[chckStp-3] in (STORE_NAME, STORE_FAST)
|
||||
and self.code[chckStp+3] in (LOAD_NAME, LOAD_FAST)
|
||||
and self.code[chckStp+6] in (DELETE_NAME, DELETE_FAST)):
|
||||
toDel += [chckStp-3, chckStp+3, chckStp+6]
|
||||
# SETUP_WITH opcode dosen't exist in 2.5 but is necessary for the grammar
|
||||
self.code[chckStore] = JUMP_ABSOLUTE # ugly hack
|
||||
self.restructJump(chckStore, i)
|
||||
@@ -466,7 +466,7 @@ class Scanner25(scan.Scanner):
|
||||
i=0
|
||||
while i < len(self.code): # we can't use op_range for the moment
|
||||
op = self.code[i]
|
||||
if(op in (PJIF,PJIT)):
|
||||
if(op in (PJIF, PJIT)):
|
||||
target = self.get_argument(i)
|
||||
target += i + 3
|
||||
self.restructJump(i, target)
|
||||
@@ -477,7 +477,7 @@ class Scanner25(scan.Scanner):
|
||||
i=0
|
||||
while i < len(self.code): # we can't use op_range for the moment
|
||||
op = self.code[i]
|
||||
if(op in (PJIF,PJIT)):
|
||||
if(op in (PJIF, PJIT)):
|
||||
target = self.get_target(i)
|
||||
if self.code[target] == JA:
|
||||
target = self.get_target(target)
|
||||
@@ -500,7 +500,7 @@ class Scanner25(scan.Scanner):
|
||||
|
||||
def build_stmt_indices(self):
|
||||
code = self.code
|
||||
start = 0;
|
||||
start = 0
|
||||
end = len(code)
|
||||
|
||||
stmt_opcodes = {
|
||||
@@ -627,7 +627,7 @@ class Scanner25(scan.Scanner):
|
||||
start = _start
|
||||
end = _end
|
||||
parent = s
|
||||
## We need to know how many new structures were added in this run
|
||||
# We need to know how many new structures were added in this run
|
||||
|
||||
if op == SETUP_LOOP:
|
||||
start = pos+3
|
||||
@@ -771,9 +771,9 @@ class Scanner25(scan.Scanner):
|
||||
pass
|
||||
elif code[pre[pre[rtarget]]] == RETURN_VALUE \
|
||||
and self.remove_mid_line_ifs([pos]) \
|
||||
and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], \
|
||||
(PJIF, PJIT), target))) \
|
||||
| set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], \
|
||||
and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]],
|
||||
(PJIF, PJIT), target)))
|
||||
| set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]],
|
||||
(PJIF, PJIT, JA), pre[rtarget], True))))):
|
||||
pass
|
||||
else:
|
||||
@@ -813,7 +813,7 @@ class Scanner25(scan.Scanner):
|
||||
and self.get_target(target) == self.get_target(next):
|
||||
self.fixed_jumps[pos] = pre[next]
|
||||
return
|
||||
#don't add a struct for a while test, it's already taken care of
|
||||
# don't add a struct for a while test, it's already taken care of
|
||||
if pos in self.ignore_if:
|
||||
return
|
||||
|
||||
@@ -821,11 +821,11 @@ class Scanner25(scan.Scanner):
|
||||
and pre[rtarget] != pos and pre[pre[rtarget]] != pos \
|
||||
and not (code[rtarget] == JA and code[rtarget+3] == POP_BLOCK and code[pre[pre[rtarget]]] != JA):
|
||||
rtarget = pre[rtarget]
|
||||
#does the if jump just beyond a jump op, then this is probably an if statement
|
||||
# does the if jump just beyond a jump op, then this is probably an if statement
|
||||
if code[pre[rtarget]] in (JA, JF):
|
||||
if_end = self.get_target(pre[rtarget])
|
||||
|
||||
#is this a loop not an if?
|
||||
# is this a loop not an if?
|
||||
if (if_end < pre[rtarget]) and (code[pre[if_end]] == SETUP_LOOP):
|
||||
if(if_end > start):
|
||||
return
|
||||
@@ -864,8 +864,8 @@ class Scanner25(scan.Scanner):
|
||||
self.structs = [{'type': 'root',
|
||||
'start': 0,
|
||||
'end': n-1}]
|
||||
self.loops = [] ## All loop entry points
|
||||
self.fixed_jumps = {} ## Map fixed jumps to their real destination
|
||||
self.loops = [] # All loop entry points
|
||||
self.fixed_jumps = {} # Map fixed jumps to their real destination
|
||||
self.ignore_if = set()
|
||||
self.build_stmt_indices()
|
||||
self.not_continue = set()
|
||||
@@ -875,7 +875,7 @@ class Scanner25(scan.Scanner):
|
||||
for i in self.op_range(0, n):
|
||||
op = code[i]
|
||||
|
||||
## Determine structures and fix jumps for 2.3+
|
||||
# Determine structures and fix jumps for 2.3+
|
||||
self.detect_structure(i, op)
|
||||
|
||||
if self.op_hasArgument(op):
|
||||
@@ -884,10 +884,10 @@ class Scanner25(scan.Scanner):
|
||||
if label is None:
|
||||
if op in hasjrel and op != FOR_ITER:
|
||||
label = i + 3 + oparg
|
||||
#elif op in hasjabs: Pas de gestion des jump abslt
|
||||
#if op in (PJIF, PJIT): Or pop a faire
|
||||
#if (oparg > i):
|
||||
#label = oparg
|
||||
# elif op in hasjabs: Pas de gestion des jump abslt
|
||||
# if op in (PJIF, PJIT): Or pop a faire
|
||||
# if (oparg > i):
|
||||
# label = oparg
|
||||
if label is not None and label != -1:
|
||||
targets[label] = targets.get(label, []) + [i]
|
||||
elif op == END_FINALLY and i in self.fixed_jumps:
|
||||
|
@@ -137,7 +137,7 @@ class Scanner26(scan.Scanner):
|
||||
continue
|
||||
if op in hasconst:
|
||||
const = co.co_consts[oparg]
|
||||
if type(const) == types.CodeType:
|
||||
if isinstance(const, types.CodeType):
|
||||
oparg = const
|
||||
if const.co_name == '<lambda>':
|
||||
assert op_name == 'LOAD_CONST'
|
||||
@@ -234,7 +234,7 @@ class Scanner26(scan.Scanner):
|
||||
# del POP_TOP
|
||||
if self.code[i+opsize] == POP_TOP:
|
||||
if self.code[i+opsize] == self.code[i+opsize+1] and self.code[i+opsize] == self.code[i+opsize+2] \
|
||||
and opcode in (JF,JA) and self.code[i+opsize] != self.code[i+opsize+3]:
|
||||
and opcode in (JF, JA) and self.code[i+opsize] != self.code[i+opsize+3]:
|
||||
pass
|
||||
else:
|
||||
toDel += [i+opsize]
|
||||
@@ -262,15 +262,16 @@ class Scanner26(scan.Scanner):
|
||||
return [i+opsize]
|
||||
# modification of list structure
|
||||
if opcode == BUILD_LIST:
|
||||
if self.code[i+opsize] == DUP_TOP and self.code[i+opsize+1] in (STORE_NAME,STORE_FAST):
|
||||
if (self.code[i+opsize] == DUP_TOP and
|
||||
self.code[i+opsize+1] in (STORE_NAME, STORE_FAST)):
|
||||
# del DUP/STORE_NAME x
|
||||
toDel = [i+opsize,i+opsize+1]
|
||||
toDel = [i+opsize, i+opsize+1]
|
||||
nameDel = self.get_argument(i+opsize+1)
|
||||
start = i+opsize+1
|
||||
end = start
|
||||
# del LOAD_NAME x
|
||||
while end < len(self.code):
|
||||
end = self.first_instr(end, len(self.code), (LOAD_NAME,LOAD_FAST))
|
||||
end = self.first_instr(end, len(self.code), (LOAD_NAME, LOAD_FAST))
|
||||
if nameDel == self.get_argument(end):
|
||||
toDel += [end]
|
||||
break
|
||||
@@ -280,7 +281,7 @@ class Scanner26(scan.Scanner):
|
||||
end += self.op_size(LOAD_FAST)
|
||||
# log JA/POP_TOP to del and update PJIF
|
||||
while start < end:
|
||||
start = self.first_instr(start, end, (PJIF,PJIT))
|
||||
start = self.first_instr(start, end, (PJIF, PJIT))
|
||||
if start is None: break
|
||||
target = self.get_target(start)
|
||||
if self.code[target] == POP_TOP and self.code[target-3] == JA:
|
||||
@@ -292,7 +293,8 @@ class Scanner26(scan.Scanner):
|
||||
# del DELETE_NAME x
|
||||
start = end
|
||||
while end < len(self.code):
|
||||
end = self.first_instr(end, len(self.code), (DELETE_NAME,DELETE_FAST))
|
||||
end = self.first_instr(end, len(self.code),
|
||||
(DELETE_NAME, DELETE_FAST))
|
||||
if nameDel == self.get_argument(end):
|
||||
toDel += [end]
|
||||
break
|
||||
@@ -308,7 +310,7 @@ class Scanner26(scan.Scanner):
|
||||
end = self.first_instr(i, len(self.code), RETURN_VALUE)
|
||||
end = self.first_instr(i, end, YIELD_VALUE)
|
||||
if end and self.code[end+1] == POP_TOP and self.code[end+2] == JA and self.code[end+5] == POP_BLOCK:
|
||||
return [i,end+5]
|
||||
return [i, end+5]
|
||||
# with stmt
|
||||
if opcode == WITH_CLEANUP:
|
||||
allRot = self.all_instr(0, i, (ROT_TWO))
|
||||
@@ -318,7 +320,7 @@ class Scanner26(scan.Scanner):
|
||||
and self.code[rot-4] == DUP_TOP:
|
||||
chckRot = rot
|
||||
assert chckRot > 0
|
||||
toDel = [chckRot-4,chckRot-3,chckRot]
|
||||
toDel = [chckRot-4, chckRot-3, chckRot]
|
||||
chckStp = -1
|
||||
allSetup = self.all_instr(chckRot+1, i, (SETUP_FINALLY))
|
||||
for stp in allSetup:
|
||||
@@ -330,9 +332,10 @@ class Scanner26(scan.Scanner):
|
||||
while chckDel < chckStp-3:
|
||||
toDel += [chckDel]
|
||||
chckDel += self.op_size(self.code[chckDel])
|
||||
if self.code[chckStp-3] in (STORE_NAME,STORE_FAST) and self.code[chckStp+3] in (LOAD_NAME,LOAD_FAST) \
|
||||
and self.code[chckStp+6] in (DELETE_NAME,DELETE_FAST):
|
||||
toDel += [chckStp-3,chckStp+3,chckStp+6]
|
||||
if (self.code[chckStp-3] in (STORE_NAME, STORE_FAST) and
|
||||
self.code[chckStp+3] in (LOAD_NAME, LOAD_FAST)
|
||||
and self.code[chckStp+6] in (DELETE_NAME, DELETE_FAST)):
|
||||
toDel += [chckStp-3, chckStp+3, chckStp+6]
|
||||
# SETUP_WITH opcode dosen't exist in 2.6 but is necessary for the grammar
|
||||
self.code[chckRot+1] = JUMP_ABSOLUTE # ugly hack
|
||||
self.restructJump(chckRot+1, i)
|
||||
@@ -461,7 +464,7 @@ class Scanner26(scan.Scanner):
|
||||
i=0
|
||||
while i < len(self.code): # we can't use op_range for the moment
|
||||
op = self.code[i]
|
||||
if(op in (PJIF,PJIT)):
|
||||
if(op in (PJIF, PJIT)):
|
||||
target = self.get_argument(i)
|
||||
target += i + 3
|
||||
self.restructJump(i, target)
|
||||
@@ -472,7 +475,7 @@ class Scanner26(scan.Scanner):
|
||||
i=0
|
||||
while i < len(self.code): # we can't use op_range for the moment
|
||||
op = self.code[i]
|
||||
if(op in (PJIF,PJIT)):
|
||||
if(op in (PJIF, PJIT)):
|
||||
target = self.get_target(i)
|
||||
if self.code[target] == JA:
|
||||
target = self.get_target(target)
|
||||
@@ -707,7 +710,7 @@ class Scanner26(scan.Scanner):
|
||||
i = end
|
||||
while i < len(self.code) and self.code[i] != END_FINALLY:
|
||||
jmp = self.next_except_jump(i)
|
||||
if jmp == None: # check
|
||||
if jmp is None: # check
|
||||
i = self.next_stmt[i]
|
||||
continue
|
||||
if self.code[jmp] == RETURN_VALUE:
|
||||
@@ -769,9 +772,9 @@ class Scanner26(scan.Scanner):
|
||||
pass
|
||||
elif code[pre[pre[rtarget]]] == RETURN_VALUE \
|
||||
and self.remove_mid_line_ifs([pos]) \
|
||||
and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], \
|
||||
(PJIF, PJIT), target))) \
|
||||
| set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], \
|
||||
and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]],
|
||||
(PJIF, PJIT), target)))
|
||||
| set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]],
|
||||
(PJIF, PJIT, JA), pre[rtarget], True))))):
|
||||
pass
|
||||
else:
|
||||
|
@@ -121,7 +121,8 @@ class GenericParser:
|
||||
# thee not with this; nor shall thee toucheth the _preprocess
|
||||
# argument to addRule.
|
||||
#
|
||||
def preprocess(self, rule, func): return rule, func
|
||||
def preprocess(self, rule, func):
|
||||
return rule, func
|
||||
|
||||
def addRule(self, doc, func, _preprocess=1):
|
||||
fn = func
|
||||
@@ -505,7 +506,7 @@ class GenericParser:
|
||||
lhs, rhs = rule
|
||||
for pitem in sets[parent]:
|
||||
pstate, pparent = pitem
|
||||
#k = self.goto(pstate, lhs)
|
||||
# k = self.goto(pstate, lhs)
|
||||
k = self.edges.get((pstate, lhs), None)
|
||||
if k is not None:
|
||||
why = (item, i, rule)
|
||||
@@ -519,10 +520,10 @@ class GenericParser:
|
||||
cur.append(new)
|
||||
self.links[key].append((pptr, why))
|
||||
# INLINED ----------^
|
||||
#nk = self.goto(k, None)
|
||||
# nk = self.goto(k, None)
|
||||
nk = self.edges.get((k, None), None)
|
||||
if nk is not None:
|
||||
#self.add(cur, (nk, i))
|
||||
# self.add(cur, (nk, i))
|
||||
# INLINED ---------v
|
||||
new = (nk, i)
|
||||
if new not in cur:
|
||||
@@ -583,7 +584,7 @@ class GenericParser:
|
||||
attr[i] = tokens[k-1]
|
||||
key = (item, k)
|
||||
item, k = self.predecessor(key, None)
|
||||
#elif self.isnullable(sym):
|
||||
# elif self.isnullable(sym):
|
||||
elif self._NULLABLE == sym[0:len(self._NULLABLE)]:
|
||||
attr[i] = self.deriveEpsilon(sym)
|
||||
else:
|
||||
@@ -597,8 +598,8 @@ class GenericParser:
|
||||
def ambiguity(self, rules):
|
||||
#
|
||||
# XXX - problem here and in collectRules() if the same rule
|
||||
# appears in >1 method. Also undefined results if rules
|
||||
# causing the ambiguity appear in the same method.
|
||||
# appears in >1 method. Also undefined results if rules
|
||||
# causing the ambiguity appear in the same method.
|
||||
#
|
||||
sortlist = []
|
||||
name2index = {}
|
||||
@@ -648,7 +649,8 @@ class GenericASTBuilder(GenericParser):
|
||||
children.append(self.terminal(arg))
|
||||
return self.nonterminal(lhs, children)
|
||||
|
||||
def terminal(self, token): return token
|
||||
def terminal(self, token):
|
||||
return token
|
||||
|
||||
def nonterminal(self, type, args):
|
||||
rv = self.AST(type)
|
||||
|
@@ -34,7 +34,7 @@ BIN_OP_FUNCS = {
|
||||
|
||||
JUMP_OPs = None
|
||||
|
||||
#--- exceptions ---
|
||||
# --- exceptions ---
|
||||
|
||||
class VerifyCmpError(Exception):
|
||||
pass
|
||||
@@ -82,7 +82,7 @@ class CmpErrorCode(VerifyCmpError):
|
||||
|
||||
def __str__(self):
|
||||
s = reduce(lambda s, t: "%s%-37s\t%-37s\n" % (s, t[0], t[1]),
|
||||
list(map(lambda a,b: (a, b),
|
||||
list(map(lambda a, b: (a, b),
|
||||
self.tokens[0],
|
||||
self.tokens[1])),
|
||||
'Code differs in %s\n' % str(self.name))
|
||||
@@ -287,8 +287,8 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''):
|
||||
elif member == 'co_consts':
|
||||
# partial optimization can make the co_consts look different,
|
||||
# so we'll just compare the code consts
|
||||
codes1 = ( c for c in code_obj1.co_consts if type(c) == types.CodeType )
|
||||
codes2 = ( c for c in code_obj2.co_consts if type(c) == types.CodeType )
|
||||
codes1 = ( c for c in code_obj1.co_consts if isinstance(c, types.CodeType) )
|
||||
codes2 = ( c for c in code_obj2.co_consts if isinstance(c, types.CodeType) )
|
||||
|
||||
for c1, c2 in zip(codes1, codes2):
|
||||
cmp_code_objects(version, c1, c2, name=name)
|
||||
@@ -296,8 +296,8 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''):
|
||||
# all other members must be equal
|
||||
if getattr(code_obj1, member) != getattr(code_obj2, member):
|
||||
raise CmpErrorMember(name, member,
|
||||
getattr(code_obj1,member),
|
||||
getattr(code_obj2,member))
|
||||
getattr(code_obj1, member),
|
||||
getattr(code_obj2, member))
|
||||
|
||||
class Token(scanner.Token):
|
||||
"""Token class with changed semantics for 'cmp()'."""
|
||||
@@ -306,7 +306,7 @@ class Token(scanner.Token):
|
||||
t = self.type # shortcut
|
||||
loads = ('LOAD_NAME', 'LOAD_GLOBAL', 'LOAD_CONST')
|
||||
if t in loads and o.type in loads:
|
||||
if self.pattr == 'None' and o.pattr == None:
|
||||
if self.pattr == 'None' and o.pattr is None:
|
||||
return 0
|
||||
if t == 'BUILD_TUPLE_0' and o.type == 'LOAD_CONST' and o.pattr == ():
|
||||
return 0
|
||||
|
Reference in New Issue
Block a user