-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtypecheck.py
115 lines (110 loc) · 3.84 KB
/
typecheck.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#coding=utf-8
from types import CodeType
from types import FunctionType
from opcode import opmap
from opcode import opname
import dis
def typcheck(v,typ):
#print( "typcheck:",v,type(v),typ)
if isinstance(v,typ):
#print( v, typ )
return v
else:
raise TypeError("\n\t \033[0;31;43m Fail isinstance ({},{}) \033[0m".format(repr(v),repr(typ)))
def check(func):
_globals_ = func.__globals__
temp = {}
old_annotations = {}
temp.update(func.__annotations__)
old_annotations.update(func.__annotations__)
typs = temp
ret = typs.pop('return')
_code_ = func.__code__
o_varnames = list(_code_.co_varnames)
o_names = list(_code_.co_names)
o_consts = list(_code_.co_consts)
n_consts = o_consts + list( set( list(typs.values()) + [ret]) ) + [typcheck]
#print( "new consts:",n_consts )
info = {}
for k,v in typs.items():
info[o_varnames.index(k)] = v
o_binlst = list( _code_.co_code )
o_argcount = _code_.co_argcount
mlst = [ ]
n = o_argcount
for i in range(n):
mlst += [opmap["LOAD_CONST"],len(n_consts)-1,
opmap["LOAD_FAST"],i,
opmap["LOAD_CONST"],n_consts.index( info[i] ),
opmap["CALL_FUNCTION"],2,
opmap["POP_TOP"],0 ]
jumps = [opmap["POP_JUMP_IF_FALSE"],
opmap["POP_JUMP_IF_TRUE"],
opmap["JUMP_IF_TRUE_OR_POP"],
opmap["JUMP_IF_FALSE_OR_POP"],
opmap["JUMP_ABSOLUTE"]]
jump_forward = opmap["JUMP_FORWARD"]
OffSet = len(mlst)
# ------------
# func ^ stack top
# result |
last = [ opmap["LOAD_CONST"],len(n_consts)-1,
opmap["ROT_TWO"],0,# Swaps the two top-most stack items.
opmap["LOAD_CONST"],n_consts.index( ret ),
opmap["CALL_FUNCTION"],2,
opmap["RETURN_VALUE"],0]
new_binlst = o_binlst
lset = len(last) - 2
old_addrs = list()
for i in range(len(o_binlst)):
if o_binlst[i] in jumps:
old_addrs+= [ o_binlst[i+1] ]
lst = new_binlst
for i in old_addrs:
new_binlst[i] = [-1,new_binlst[i]]
new_binlst[i+1] = [-1,new_binlst[i+1]]
acc = [ ]
while lst :
op,arg = lst[0],lst[1]
if op == opmap["RETURN_VALUE"]:
acc += last
else:
acc += [op,arg]
lst = lst[2:]
new_binlst = mlst + acc
new_addrs = list()
for i in range(len(new_binlst)):
op = new_binlst[i]
if isinstance(op,list):
new_addrs += [i]
new_binlst[i] = new_binlst[i][1]
new_binlst[i+1] = new_binlst[i+1][1]
for i in range(len(new_binlst)):
op = new_binlst[i]
if op in jumps:
new_binlst[i+1] = new_addrs[0]
new_addrs = new_addrs[1:]
lstbin = new_binlst
code = CodeType(_code_.co_argcount, # argcount
_code_.co_kwonlyargcount, # kwonlyargcount
_code_.co_nlocals, # nlocals
_code_.co_stacksize, # stacksize
_code_.co_flags, # flags
bytes(lstbin), # codestring
tuple(n_consts), # consts
_code_.co_names, # names
_code_.co_varnames, # varnames
_code_.co_filename, # filename
_code_.co_name, # name
_code_.co_firstlineno, # firstlineno
_code_.co_lnotab, # lnotab
_code_.co_freevars, # freevars
_code_.co_cellvars, # cellvars
)
#dis.dis(code)
#dis.show_code(code)
#print( lstbin )
func = FunctionType(code,_globals_)
func.__annotations__= old_annotations
return func
__all__ = ["check"]