-
Notifications
You must be signed in to change notification settings - Fork 30
/
interpreter.py
2235 lines (2020 loc) · 90.8 KB
/
interpreter.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python3
"""
PyCParser - interpreter
by Albert Zeyer, 2011
code under BSD 2-Clause License
"""
from __future__ import print_function
import ctypes
import _ctypes
import ast
import sys
import inspect
from weakref import ref, WeakValueDictionary
from collections import OrderedDict
from . import cparser
from .cparser import *
from .cwrapper import CStateWrapper
from .cparser_utils import long, unicode
from .interpreter_utils import ast_bin_op_to_func
from . import goto
from .sortedcontainers.sortedset import SortedSet
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] >= 3
def iterIdentifierNames():
S = "abcdefghijklmnopqrstuvwxyz0123456789"
n = 0
while True:
v = []
x = n
while x > 0 or len(v) == 0:
v = [x % len(S)] + v
x //= len(S)
yield "".join([S[x] for x in v])
n += 1
def iterIdWithPostfixes(name):
if name is None:
for postfix in iterIdentifierNames():
yield "__dummy_" + postfix
return
yield name
for postfix in iterIdentifierNames():
yield name + "_" + postfix
import keyword
PyReservedNames = set(dir(__builtins__) + keyword.kwlist + ["ctypes", "helpers"])
def isValidVarName(name):
return name not in PyReservedNames
class GlobalScope:
StateScopeDicts = ["vars", "typedefs", "funcs"]
def __init__(self, interpreter, stateStruct):
self.interpreter = interpreter
self.stateStruct = stateStruct
self.identifiers = {} # name -> CVarDecl | ...
self.names = {} # id(decl) -> name
self.vars = {} # name -> value
def _findId(self, name):
for D in self.StateScopeDicts:
d = getattr(self.stateStruct, D)
o = d.get(name)
if o is not None: return o
return None
def findIdentifier(self, name):
o = self.identifiers.get(name, None)
if o is not None: return o
o = self._findId(name)
if o is None: return None
self.identifiers[name] = o
self.names[id(o)] = name
return o
def findName(self, decl):
name = self.names.get(id(decl), None)
if name is not None: return name
o = self.findIdentifier(decl.name)
if o is None: return None
# Note: `o` might be a different object than `decl`.
# This can happen if `o` is the extern declaration and `decl`
# is the actual variable. Anyway, this is fine.
return o.name
def _getDeclTypeBodyAstAndType(self, decl):
assert isinstance(decl, CVarDecl)
if decl.body is not None:
anonFuncEnv = FuncEnv(self)
bodyAst, bodyType = astAndTypeForStatement(anonFuncEnv, decl.body)
else:
bodyAst, bodyType = None, None
decl_type = decl.type
# Arrays might have implicit length. If the len is not specified
# explicitely, try to get it from the body.
if isinstance(decl_type, CArrayType):
arrayLen = None
if decl_type.arrayLen:
arrayLen = getConstValue(self.stateStruct, decl_type.arrayLen)
assert isinstance(arrayLen, (int, long))
assert arrayLen > 0
if isinstance(bodyType, (tuple, list)):
if arrayLen:
assert arrayLen >= len(bodyType)
else:
arrayLen = len(bodyType)
assert arrayLen > 0
elif isinstance(bodyType, CArrayType):
_arrayLen = getConstValue(self.stateStruct, bodyType.arrayLen)
assert isinstance(_arrayLen, (int, long))
if arrayLen:
assert arrayLen >= _arrayLen
else:
arrayLen = _arrayLen
else:
assert bodyType is None, "not expected: %r" % bodyType
assert arrayLen, "array without explicit len and without body"
if not decl_type.arrayLen:
decl_type.arrayLen = CNumber(arrayLen)
return decl_type, bodyAst, bodyType
def _getEmptyValueAst(self, decl_type):
return getAstNode_newTypeInstance(FuncEnv(self), decl_type)
def _getVarBodyValueAst(self, decl, decl_type, bodyAst, bodyType):
assert isinstance(decl, CVarDecl)
if decl.body is None:
return None
v = decl.body.getConstValue(self.stateStruct)
if v is not None and v == 0:
return None # no need to initialize it
if not isinstance(decl_type, CArrayType) and isPointerType(decl_type) \
and not isPointerType(bodyType):
assert v is not None and v == 0, "Global: Initializing pointer type " + str(
decl_type) + " only supported with 0 value but we got " + str(v) + " from " + str(decl.body)
return None
else:
valueAst = getAstNode_newTypeInstance(FuncEnv(self), decl_type, bodyAst, bodyType)
return valueAst
def getVar(self, name):
if name in self.vars: return self.vars[name]
decl = self.findIdentifier(name)
if self.interpreter.debug_print_getVar: print("+ getVar %s" % decl)
assert isinstance(decl, CVarDecl)
# Note: To avoid infinite loops, we must first create the object.
# This is to avoid infinite loops, in case that the initializer
# access the var itself.
decl_type, bodyAst, bodyType = self._getDeclTypeBodyAstAndType(decl)
def getEmpty():
emptyValueAst = self._getEmptyValueAst(decl_type)
v_empty = evalValueAst(self, emptyValueAst, "<PyCParser_globalvar_%s_init_empty>" % name)
self.interpreter._storePtr(ctypes.pointer(v_empty))
return v_empty
self.vars[name] = getEmpty()
bodyValueAst = self._getVarBodyValueAst(decl, decl_type, bodyAst, bodyType)
if bodyValueAst is not None:
value = evalValueAst(self, bodyValueAst, "<PyCParser_globalvar_" + name + "_init_value>")
self.interpreter.helpers.assign(self.vars[name], value)
return self.vars[name]
def evalValueAst(funcEnv, valueAst, srccode_name=None):
if srccode_name is None: srccode_name = "<PyCParser_dynamic_eval>"
if False: # directly via AST
valueExprAst = ast.Expression(valueAst)
ast.fix_missing_locations(valueExprAst)
valueCode = compile(valueExprAst, srccode_name, "eval")
else:
src = _unparse(valueAst)
_set_linecache(srccode_name, src)
valueCode = compile(src, srccode_name, "eval")
v = eval(valueCode, funcEnv.interpreter.globalsDict)
return v
class GlobalsWrapper:
def __init__(self, globalScope):
"""
:type globalScope: GlobalScope
"""
self.globalScope = globalScope
def __setattr__(self, name, value):
self.__dict__[name] = value
def __getattr__(self, name):
decl = self.globalScope.findIdentifier(name)
if decl is None: raise KeyError
if isinstance(decl, CVarDecl):
v = self.globalScope.getVar(name)
elif isinstance(decl, CWrapValue):
v = decl.value
elif isinstance(decl, CFunc):
v = self.globalScope.interpreter.getFunc(name)
elif isinstance(decl, (CTypedef,CStruct,CUnion,CEnum)):
v = getCType(decl, self.globalScope.stateStruct)
elif isinstance(decl, CFuncPointerDecl):
v = getCType(decl, self.globalScope.stateStruct)
else:
assert False, "didn't expected " + str(decl)
self.__dict__[name] = v
return v
def __repr__(self):
return "<" + self.__class__.__name__ + " " + repr(self.__dict__) + ">"
class GlobalsTypeWrapper:
def __init__(self, globalScope, attrib):
self.globalScope = globalScope
self.attrib = attrib
def __setattr__(self, name, value):
self.__dict__[name] = value
def __getattr__(self, name):
collection = getattr(self.globalScope.stateStruct, self.attrib)
decl = collection.get(name)
if decl is None: raise AttributeError
v = getCType(decl, self.globalScope.stateStruct)
self.__dict__[name] = v
return v
def __repr__(self):
return "<" + self.__class__.__name__ + " " + repr(self.__dict__) + ">"
class FuncEnv:
def __init__(self, globalScope):
self.globalScope = globalScope
self.interpreter = globalScope.interpreter
self.vars = {} # name -> varDecl
self.varNames = {} # id(varDecl) -> name
self.localTypes = {} # type -> var-name
self.scopeStack = [] # type: typing.List[FuncCodeblockScope]
self.needGotoHandling = False
self.astNode = ast.FunctionDef(
args=ast.arguments(args=[], vararg=None, kwarg=None, defaults=[]),
body=[], decorator_list=[])
def get_name(self): return self.astNode.name
def __repr__(self):
try: return "<" + self.__class__.__name__ + " of " + self.get_name() + ">"
except Exception: return "<" + self.__class__.__name__ + " in invalid state>"
def _registerNewVar(self, varName, varDecl):
if varDecl is not None:
assert id(varDecl) not in self.varNames
for name in iterIdWithPostfixes(varName):
if not isValidVarName(name): continue
if name in self.interpreter.globalsDict: continue
if self.searchVarName(name): continue
self.vars[name] = varDecl
if varDecl is not None:
self.varNames[id(varDecl)] = name
return name
def searchVarName(self, varName):
if varName in self.vars: return True
return self.globalScope.findIdentifier(varName) is not None
def registerNewVar(self, varName, varDecl=None):
return self.scopeStack[-1].registerNewVar(varName, varDecl)
def registerNewUnscopedVarName(self, varName, initNone=True):
"""
:type varName: str
:return: Python var name
:rtype: str
This will register a new var which is available in all scopes
and we init it with None at the very beginning.
"""
varName = self._registerNewVar(varName, None)
if initNone:
a = ast.Assign()
a.targets = [ast.Name(id=varName, ctx=ast.Store())]
a.value = ast.Name(id="None", ctx=ast.Load())
# Add at the very front because this var might not be assigned otherwise when there is a goto.
self.scopeStack[0].body.insert(0, a)
return varName
def registerLocalTypedef(self, typedef):
assert isinstance(typedef, CTypedef)
if typedef in self.localTypes: return
varName = self.registerNewUnscopedVarName(typedef.name or "anon_type", initNone=False)
self.localTypes[typedef] = varName
a = ast.Assign()
a.targets = [ast.Name(id=varName, ctx=ast.Store())]
a.value = getAstNodeForVarType(self, typedef.type)
self.scopeStack[-1].body.append(a)
def getAstNodeForVarDecl(self, varDecl):
assert varDecl is not None
if id(varDecl) in self.varNames:
# local var
name = self.varNames[id(varDecl)]
assert name is not None
return ast.Name(id=name, ctx=ast.Load())
# we expect this is a global
name = self.globalScope.findName(varDecl)
assert name is not None, str(varDecl) + " is expected to be a global var"
return getAstNodeAttrib("g", name)
def _unregisterVar(self, varName):
varDecl = self.vars[varName]
if varDecl is not None:
del self.varNames[id(varDecl)]
del self.vars[varName]
def pushScope(self, bodyStmntList):
"""
:param list bodyStmntList:
:rtype: FuncCodeblockScope
"""
scope = FuncCodeblockScope(funcEnv=self, body=bodyStmntList)
self.scopeStack += [scope]
return scope
def popScope(self):
scope = self.scopeStack.pop()
scope.finishMe()
def getBody(self):
"""
:rtype: list
"""
return self.scopeStack[-1].body
NoneAstNode = ast.Name(id="None", ctx=ast.Load())
def getAstNodeAttrib(value, attrib, ctx=ast.Load()):
a = ast.Attribute(ctx=ctx)
if isinstance(value, (str,unicode)):
a.value = ast.Name(id=str(value), ctx=ctx)
elif isinstance(value, ast.AST):
a.value = value
else:
assert False, str(value) + " has invalid type"
assert attrib is not None
a.attr = str(attrib)
return a
class DidNotFindCTypesBasicType(Exception): pass
def getAstNodeForCTypesBasicType(t):
if t is None: return NoneAstNode
if t is CVoidType: return NoneAstNode
if not inspect.isclass(t) and isinstance(t, CVoidType): return NoneAstNode
if inspect.isclass(t) and issubclass(t, CVoidType): return None
if not inspect.isclass(t): raise DidNotFindCTypesBasicType("not a class")
if issubclass(t, ctypes._Pointer):
base_type = t._type_
a = getAstNodeAttrib("ctypes", "POINTER")
return makeAstNodeCall(a, getAstNodeForCTypesBasicType(base_type))
if not issubclass(t, ctypes._SimpleCData): raise DidNotFindCTypesBasicType("unknown type")
t_name = t.__name__
if t_name.startswith("wrapCTypeClass_"): t_name = t_name[len("wrapCTypeClass_"):]
assert issubclass(t, getattr(ctypes, t_name))
t = getattr(ctypes, t_name) # get original type
if needWrapCTypeClass(t):
return getAstNodeAttrib("ctypes_wrapped", t_name)
return getAstNodeAttrib("ctypes", t_name)
def getAstNodeForVarType(funcEnv, t):
interpreter = funcEnv.interpreter
if isinstance(t, CBuiltinType):
return getAstNodeForCTypesBasicType(State.CBuiltinTypes[t.builtinType])
elif isinstance(t, CStdIntType):
return getAstNodeForCTypesBasicType(State.StdIntTypes[t.name])
elif isinstance(t, CEnum):
# Just use the related int type.
stdtype = t.getMinCIntType()
assert stdtype is not None
return getAstNodeForCTypesBasicType(State.StdIntTypes[stdtype])
elif isinstance(t, CPointerType):
if t.pointerOf == CBuiltinType(("void",)):
return getAstNodeAttrib("ctypes_wrapped", "c_void_p")
a = getAstNodeAttrib("ctypes", "POINTER")
return makeAstNodeCall(a, getAstNodeForVarType(funcEnv, t.pointerOf))
elif isinstance(t, CTypedef):
if t in funcEnv.localTypes:
return ast.Name(id=funcEnv.localTypes[t], ctx=ast.Load())
return getAstNodeAttrib("g", t.name)
elif isinstance(t, CStruct):
if t.name is None:
# This is an anonymous struct. E.g. like in:
# `struct A { struct { int x; } a; };`
# Wrap it via CWrapValue.
# TODO: is this the best solution? We could refer it to the named parent. If there is one.
v = getAstForWrapValue(interpreter, CWrapValue(getCType(t, interpreter.globalScope.stateStruct)))
return getAstNodeAttrib(v, "value")
# TODO: this assumes the was previously declared globally.
return getAstNodeAttrib("structs", t.name)
elif isinstance(t, CUnion):
assert t.name is not None
return getAstNodeAttrib("unions", t.name)
elif isinstance(t, CArrayType):
arrayOf = getAstNodeForVarType(funcEnv, t.arrayOf)
v = getConstValue(interpreter.globalScope.stateStruct, t.arrayLen)
assert isinstance(v, (int,long))
arrayLen = ast.Num(n=v)
return ast.BinOp(left=arrayOf, op=ast.Mult(), right=arrayLen)
elif isinstance(t, (CFuncPointerDecl, CFunc)):
return makeAstNodeCall(
getAstNodeAttrib("ctypes", "CFUNCTYPE"),
makeAstNodeCall(
getAstNodeAttrib("helpers", "fixReturnType"),
getAstNodeForVarType(funcEnv, t.type)
),
*[getAstNodeForVarType(funcEnv, a.type) for a in t.args]
)
elif isinstance(t, CWrapValue):
return getAstNodeForVarType(funcEnv, t.getCType(None))
elif isinstance(t, CWrapFuncType):
return getAstNodeForVarType(funcEnv, t.func)
else:
try: return getAstNodeForCTypesBasicType(t)
except DidNotFindCTypesBasicType: pass
assert False, "cannot handle " + str(t)
def findHelperFunc(f):
for k in dir(Helpers):
v = getattr(Helpers, k)
if v == f: return k
return None
def makeAstNodeCall(func, *args):
if not isinstance(func, ast.AST):
name = findHelperFunc(func)
assert name is not None, str(func) + " unknown"
func = getAstNodeAttrib("helpers", name)
return ast.Call(func=func, args=list(args), keywords=[], starargs=None, kwargs=None)
def makeCastToVoidP(v):
astVoidPT = getAstNodeAttrib("ctypes_wrapped", "c_void_p")
astCast = getAstNodeAttrib("ctypes", "cast")
return makeAstNodeCall(astCast, v, astVoidPT)
def makeCastToVoidP_value(v):
castToPtr = makeCastToVoidP(v)
astValue = getAstNodeAttrib(castToPtr, "value")
return ast.BoolOp(op=ast.Or(), values=[astValue, ast.Num(0)])
def getAstNode_valueFromObj(stateStruct, objAst, objType, isPartOfCOp=False):
if isPartOfCOp: # usually ==, != or so.
# Some types need special handling. We cast them to integer.
if isinstance(objType, CFuncPointerDecl):
return makeCastToVoidP_value(objAst) # return address
if isinstance(objType, CWrapFuncType):
return makeFuncPtrValue(objAst, objType)
if isinstance(objType, CFuncPointerDecl):
# It's already the value. See also CWrapFuncType below.
return objAst
elif isPointerType(objType):
from inspect import isclass
if not isclass(objType) or not issubclass(objType, ctypes.c_void_p):
# Only c_void_p supports to get the pointer-value via the value-attrib.
astVoidP = makeCastToVoidP(objAst)
else:
astVoidP = objAst
astValue = getAstNodeAttrib(astVoidP, "value")
return ast.BoolOp(op=ast.Or(), values=[astValue, ast.Num(0)])
elif isValueType(objType):
astValue = getAstNodeAttrib(objAst, "value")
return astValue
elif isinstance(objType, CEnum):
# We expect that this is just the int type.
return getAstNodeAttrib(objAst, "value")
elif isinstance(objType, CArrayType):
# cast array to ptr
return makeCastToVoidP_value(objAst)
elif isinstance(objType, CTypedef):
t = objType.type
return getAstNode_valueFromObj(stateStruct, objAst, t, isPartOfCOp=isPartOfCOp)
elif isinstance(objType, CWrapValue):
# It's already the value. See astAndTypeForStatement().
return getAstNode_valueFromObj(stateStruct, objAst, objType.getCType(stateStruct), isPartOfCOp=isPartOfCOp)
elif isinstance(objType, CWrapFuncType):
# It's already the value. See astAndTypeForStatement(). And CFuncPointerDecl above.
return objAst
elif isinstance(objType, (CStruct, CUnion)):
# Note that this is not always useable as a value.
# It cannot be used in a copy constructor because there is no such thing.
return objAst
elif isinstance(objType, CVariadicArgsType):
# We handle this special anyway.
return objAst
else:
assert False, "bad type: " + str(objType)
def _makeVal(funcEnv, f_arg_type, s_arg_ast, s_arg_type):
interpreter = funcEnv.interpreter
stateStruct = interpreter.globalScope.stateStruct
while isinstance(f_arg_type, CTypedef):
f_arg_type = f_arg_type.type
while isinstance(s_arg_type, CTypedef):
s_arg_type = s_arg_type.type
if isinstance(s_arg_type, (tuple, list)): # CCurlyArrayArgs
arrayLen = len(s_arg_type)
typeAst = getAstNodeForVarType(funcEnv, f_arg_type)
assert isinstance(s_arg_ast, ast.Tuple)
assert len(s_arg_ast.elts) == len(s_arg_type)
# There is a bit of inconsistency between basic types init
# (like c_int), which must get a value (int),
# and ctypes.Structure/ctypes.ARRAY, which for some field can either
# get a value (int) or a c_int. For pointers, it must get
# the var, not the value.
# This is mostly the same as for calling functions.
f_args = []
if isinstance(f_arg_type, (CStruct,CUnion)):
if not f_arg_type.body:
assert f_arg_type.name
if isinstance(f_arg_type, CStruct):
f_arg_type = interpreter.globalScope.stateStruct.structs[f_arg_type.name]
elif isinstance(f_arg_type, CUnion):
f_arg_type = interpreter.globalScope.stateStruct.unions[f_arg_type.name]
for c in f_arg_type.body.contentlist:
if not isinstance(c, CVarDecl): continue
f_args += [c.type]
elif isinstance(f_arg_type, CArrayType):
f_args += [f_arg_type.arrayOf] * arrayLen
else:
assert False, "did not expect type %r" % f_arg_type
assert len(s_arg_type) <= len(f_args)
# Somewhat like autoCastArgs():
s_args = []
for _f_arg_type, _s_arg_ast, _s_arg_type in zip(f_args, s_arg_ast.elts, s_arg_type):
_s_arg_ast = _makeVal(funcEnv, _f_arg_type, _s_arg_ast, _s_arg_type)
s_args += [_s_arg_ast]
return makeAstNodeCall(typeAst, *s_args)
f_arg_ctype = getCType(f_arg_type, stateStruct)
if isinstance(s_arg_type, CArrayType) and not s_arg_type.arrayLen:
# It can happen that we don't know the array-len yet.
# Then, getCType() will fail.
# However, it's probably enough here to just use the pointer-type instead.
s_arg_type = CPointerType(s_arg_type.arrayOf)
s_arg_ctype = getCType(s_arg_type, stateStruct)
use_value = False
if stateStruct.IndirectSimpleCTypes and needWrapCTypeClass(f_arg_ctype):
# We cannot use e.g. c_int, because the Structure uses another wrapped field type.
# However, using the value itself should be fine in those cases.
use_value = True
if use_value:
s_arg_ast = getAstNode_valueFromObj(stateStruct, s_arg_ast, s_arg_type)
else:
need_cast = s_arg_ctype != f_arg_ctype
if isinstance(s_arg_type, CWrapFuncType):
# The new type instance might add some checks.
need_cast = True
if need_cast:
s_arg_ast = getAstNode_newTypeInstance(funcEnv, f_arg_type, s_arg_ast, s_arg_type)
return s_arg_ast
def getAstNode_newTypeInstance(funcEnv, objType, argAst=None, argType=None):
"""
Create a new instance of type `objType`.
It can optionally be initialized with `argAst` (already AST) which is of type `argType`.
If `argType` is None, `argAst` is supposed to be a value (e.g. via getAstNode_valueFromObj).
:type interpreter: Interpreter
"""
interpreter = funcEnv.interpreter
origObjType = objType
while isinstance(objType, CTypedef):
objType = objType.type
while isinstance(argType, CTypedef):
argType = argType.type
if isinstance(objType, CBuiltinType) and objType.builtinType == ("void",):
# It's like a void cast. Return None.
if argAst is None:
return NoneAstNode
tup = ast.Tuple(elts=(argAst, NoneAstNode), ctx=ast.Load())
return getAstNodeArrayIndex(tup, 1)
arrayLen = None
if isinstance(objType, CArrayType):
arrayOf = getAstNodeForVarType(funcEnv, objType.arrayOf)
if objType.arrayLen:
arrayLen = getConstValue(interpreter.globalScope.stateStruct, objType.arrayLen)
assert arrayLen is not None
if isinstance(argType, (tuple, list)):
assert arrayLen == len(argType)
else:
# Handle array type extra here for the case when array-len is not specified.
assert argType is not None
if isinstance(argType, (tuple, list)):
arrayLen = len(argType)
else:
assert isinstance(argType, CArrayType)
arrayLen = getConstValue(interpreter.globalScope.stateStruct, argType.arrayLen)
assert arrayLen is not None
# Write back to type so that future getCType calls will succeed.
objType.arrayLen = CNumber(arrayLen)
typeAst = ast.BinOp(left=arrayOf, op=ast.Mult(), right=ast.Num(n=arrayLen))
else:
typeAst = getAstNodeForVarType(funcEnv, origObjType)
if isinstance(argType, (tuple, list)): # CCurlyArrayArgs
assert isinstance(argAst, ast.Tuple)
assert len(argAst.elts) == len(argType)
# There is a bit of inconsistency between basic types init
# (like c_int), which must get a value (int),
# and ctypes.Structure/ctypes.ARRAY, which for some field can either
# get a value (int) or a c_int. For pointers, it must get
# the var, not the value.
# This is mostly the same as for calling functions.
f_args = []
while isinstance(objType, CTypedef):
objType = objType.type
if isinstance(objType, CStruct):
for c in objType.body.contentlist:
if not isinstance(c, CVarDecl): continue
f_args += [c.type]
elif isinstance(objType, CArrayType):
f_args += [objType.arrayOf] * arrayLen
else:
assert False, "did not expect type %r" % objType
assert len(argType) <= len(f_args)
# Somewhat like autoCastArgs():
s_args = []
for f_arg_type, s_arg_ast, s_arg_type in zip(f_args, argAst.elts, argType):
s_arg_ast = _makeVal(funcEnv, f_arg_type, s_arg_ast, s_arg_type)
s_args += [s_arg_ast]
return makeAstNodeCall(typeAst, *s_args)
if isinstance(objType, CArrayType) and isinstance(argType, CArrayType):
return ast.Call(func=typeAst, args=[], keywords=[], starargs=argAst, kwargs=None)
if isinstance(argType, CWrapFuncType):
if isVoidPtrType(objType):
vAst = getAstNode_newTypeInstance(
funcEnv, CFuncPointerDecl(type=argType.func.type, args=argType.func.args),
argAst=argAst, argType=argType)
astCast = getAstNodeAttrib("ctypes", "cast")
return makeAstNodeCall(astCast, vAst, typeAst)
if isinstance(objType, CWrapFuncType):
return argAst
assert isinstance(objType, CFuncPointerDecl) # what other case could there be?
return makeAstNodeCall(getAstNodeAttrib("helpers", "makeFuncPtr"), typeAst, argAst)
if isinstance(objType, CPointerType) and usePyRefForType(objType.pointerOf):
# We expect a PyRef.
return makeAstNodeCall(getAstNodeAttrib("helpers", "PyRef"),
*([getAstNodeAttrib(argAst, "ref")] if argAst else []))
if isPointerType(objType, checkWrapValue=True) and isPointerType(argType, checkWrapValue=True):
# We can have it simpler. This is even important in some cases
# were the pointer instance is temporary and the object
# would get freed otherwise!
astCast = getAstNodeAttrib("ctypes", "cast")
return makeAstNodeCall(astCast, argAst, typeAst)
if isSameType(interpreter.globalScope.stateStruct, objType, ctypes.c_void_p) and \
isinstance(argType, CFuncPointerDecl):
# We treat CFuncPointerDecl not as a normal pointer.
# However, we allow casts to c_void_p.
astCast = getAstNodeAttrib("ctypes", "cast")
return makeAstNodeCall(astCast, argAst, typeAst)
if isinstance(objType, CFuncPointerDecl) and isinstance(argType, CFuncPointerDecl):
# We did not allow a pointer-to-func-ptr cast above.
# But we allow func-ptr-to-func-ptr.
astCast = getAstNodeAttrib("ctypes", "cast")
return makeAstNodeCall(astCast, argAst, typeAst)
args = []
if argAst is not None:
if isinstance(argAst, (ast.Str, ast.Num)):
args += [argAst]
elif argType is not None:
args += [getAstNode_valueFromObj(interpreter._cStateWrapper, argAst, argType)]
else:
# expect that it is the AST for the value.
# there is no really way to 'assert' this.
args += [argAst]
if isPointerType(objType, checkWrapValue=True) and argAst is not None:
# Note that we already covered the case where both objType and argType
# are pointer types, and we get a ctypes pointer object.
# In that case, we can use ctypes.cast, which is more or less safe.
# Note what this case here means:
# We get an integer from somewhere, and interpret is as a pointer.
# So, if there is a bug in how we got this integer, this can
# potentially lead to an invalid pointer and hard to find bug.
# Also, if the memory was allocated before by Python,
# normally the ctypes pointer handling would keep a reference
# to the underlying Python object.
# When we however just get the raw pointer address as an integer
# and then convert that back to a pointer at this place,
# it doesn't know about the underlying Python objects.
# When the underlying Python objects will get out-of-scope
# at some later point, which we cannot control here,
# this again would lead to hard to find bugs.
assert len(args) == 1
return makeAstNodeCall(getAstNodeAttrib("intp", "_getPtr"), args[0], typeAst)
#astVoidPT = getAstNodeAttrib("ctypes", "c_void_p")
#astCast = getAstNodeAttrib("ctypes", "cast")
#astVoidP = makeAstNodeCall(astVoidPT, *args)
#return makeAstNodeCall(astCast, astVoidP, typeAst)
if isIntType(objType) and args:
# Introduce a Python int-cast, because ctypes will fail if it is a float or so.
assert len(args) == 1
args = [makeAstNodeCall(ast.Name(id="int", ctx=ast.Load()), *args)]
if isinstance(objType, (CStruct, CUnion)) and argAst:
# We get the object itself. We expect that this is supposed to be a copy.
# However, there is no such thing as a copy constructor.
assert len(args) == 1
return makeAstNodeCall(Helpers.assign, makeAstNodeCall(typeAst), *args)
if isinstance(objType, CVariadicArgsType):
if argAst:
return makeAstNodeCall(Helpers.VarArgs, argAst)
assert isinstance(funcEnv.astNode, ast.FunctionDef)
# TODO: Normally, we would assign the var via va_start().
# However, we just always initialize with the varargs tuple also already here
# because we have the ref to the real varargs here.
# See globalincludewrappers.
return makeAstNodeCall(
Helpers.VarArgs,
ast.Name(id=funcEnv.astNode.args.vararg or "None", ctx=ast.Load()),
ast.Name(id="intp", ctx=ast.Load()))
return makeAstNodeCall(typeAst, *args)
class FuncCodeblockScope:
def __init__(self, funcEnv, body):
"""
:param FuncEnv funcEnv:
:param list body:
"""
self.varNames = set()
self.funcEnv = funcEnv
self.body = body
def registerNewVar(self, varName, varDecl):
varName = self.funcEnv._registerNewVar(varName, varDecl)
assert varName is not None
self.varNames.add(varName)
a = ast.Assign()
a.targets = [ast.Name(id=varName, ctx=ast.Store())]
if varDecl is None:
a.value = ast.Name(id="None", ctx=ast.Load())
elif isinstance(varDecl, CFuncArgDecl):
# Note: We just assume that the parameter has the correct/same type.
a.value = getAstNode_newTypeInstance(self.funcEnv, varDecl.type, ast.Name(id=varName, ctx=ast.Load()), varDecl.type)
elif isinstance(varDecl, CVarDecl):
if varDecl.body is not None:
bodyAst, t = astAndTypeForStatement(self.funcEnv, varDecl.body)
v = getConstValue(self.funcEnv.globalScope.stateStruct, varDecl.body)
if v is not None and not v:
# If we want to init with 0, we can skip this because we are always zero initialized.
bodyAst = t = None
a.value = getAstNode_newTypeInstance(self.funcEnv, varDecl.type, bodyAst, t)
else:
a.value = getAstNode_newTypeInstance(self.funcEnv, varDecl.type)
elif isinstance(varDecl, CFunc):
# TODO: register func, ...
a.value = ast.Name(id="None", ctx=ast.Load())
else:
assert False, "didn't expected " + str(varDecl)
self.body.append(a)
return varName
def _astForDeleteVar(self, varName):
assert varName is not None
return ast.Delete(targets=[ast.Name(id=varName, ctx=ast.Del())])
def finishMe(self):
astCmds = []
for varName in self.varNames:
astCmds += [self._astForDeleteVar(varName)]
self.funcEnv._unregisterVar(varName)
self.varNames.clear()
self.body.extend(astCmds)
OpUnary = {
"~": ast.Invert,
"!": ast.Not,
"+": ast.UAdd,
"-": ast.USub,
}
OpBin = {
"+": ast.Add,
"-": ast.Sub,
"*": ast.Mult,
"/": ast.Div, # we cast after the div to right type
"%": ast.Mod,
"<<": ast.LShift,
">>": ast.RShift,
"|": ast.BitOr,
"^": ast.BitXor,
"&": ast.BitAnd,
}
OpBinBool = {
"&&": ast.And,
"||": ast.Or,
}
OpBinCmp = {
"==": ast.Eq,
"!=": ast.NotEq,
"<": ast.Lt,
"<=": ast.LtE,
">": ast.Gt,
">=": ast.GtE,
}
OpAugAssign = {"%s=" % k: v for (k, v) in OpBin.items()}
OpBinFuncsByOp = {op: ast_bin_op_to_func(op) for op in OpBin.values()}
class Helpers:
def __init__(self, interpreter):
self.interpreter = interpreter
@staticmethod
def prefixInc(a):
a.value += 1
return a
@staticmethod
def prefixDec(a):
a.value -= 1
return a
@staticmethod
def postfixInc(a):
b = Helpers.copy(a)
a.value += 1
return b
@staticmethod
def postfixDec(a):
b = Helpers.copy(a)
a.value -= 1
return b
@staticmethod
def prefixIncPtr(a):
aPtr = ctypes.cast(ctypes.pointer(a), ctypes.POINTER(ctypes.c_void_p))
aPtr.contents.value += ctypes.sizeof(a._type_)
return a
@staticmethod
def prefixDecPtr(a):
aPtr = ctypes.cast(ctypes.pointer(a), ctypes.POINTER(ctypes.c_void_p))
aPtr.contents.value -= ctypes.sizeof(a._type_)
return a
@staticmethod
def postfixIncPtr(a):
b = Helpers.copy(a)
aPtr = ctypes.cast(ctypes.pointer(a), ctypes.POINTER(ctypes.c_void_p))
aPtr.contents.value += ctypes.sizeof(a._type_)
return b
@staticmethod
def postfixDecPtr(a):
b = Helpers.copy(a)
aPtr = ctypes.cast(ctypes.pointer(a), ctypes.POINTER(ctypes.c_void_p))
aPtr.contents.value -= ctypes.sizeof(a._type_)
return b
@staticmethod
def copy(a):
if isinstance(a, ctypes.c_void_p):
return ctypes.cast(a, wrapCTypeClass(ctypes.c_void_p))
if isinstance(a, ctypes._Pointer):
return ctypes.cast(a, a.__class__)
if isinstance(a, ctypes.Array):
return ctypes.pointer(a[0]) # should keep _b_base_
# This would not:
# return ctypes.cast(a, ctypes.POINTER(a._type_))
if isinstance(a, ctypes._SimpleCData):
# Safe, should not be a pointer.
return a.__class__(a.value)
raise NotImplementedError("cannot copy %r" % a)
@staticmethod
def assign(a, bValue):
if isinstance(a, Helpers.VarArgs):
a.assign(bValue)
elif isinstance(a, type(bValue)):
# WARNING: This can be dangerous/unsafe.
# It will correctly copy the content. However, we might loose any Python obj refs,
# from body_value._objects.
# TODO: Fix this somehow? Better use a helper func which goes over the structure.
ctypes.pointer(a)[0] = bValue
elif isinstance(a, (ctypes.c_void_p, ctypes._SimpleCData)):
assert hasattr(a, "value")
a.value = bValue
else:
assert False, "assign: not handled: %r of type %r" % (a, type(a))
return a
@staticmethod
def assignPtr(a, bValue):
# WARNING: This can be dangerous/unsafe.
# It will correctly copy the content. However, we might loose any Python obj refs.
# TODO: Fix this somehow?
_ctype_ptr_set_value(a, bValue)
return a
def getValueGeneric(self, b):
if isinstance(b, (ctypes._Pointer, ctypes._CFuncPtr, ctypes.Array, ctypes.c_void_p)):
self.interpreter._storePtr(b)
if isinstance(b, (ctypes._Pointer, ctypes._CFuncPtr, ctypes.Array)):
b = ctypes.cast(b, ctypes.c_void_p)
if isinstance(b, (ctypes.c_void_p, ctypes._SimpleCData)):
b = b.value
return b
def assignGeneric(self, a, bValue):
from inspect import isfunction
if isinstance(a, ctypes._CFuncPtr):
if isfunction(bValue):
bValue = self.makeFuncPtr(type(a), bValue)
assert isinstance(bValue, ctypes._CFuncPtr)
return self.assign(a, bValue)
elif isPointerType(type(a), alsoArray=False):
bValue = self.getValueGeneric(bValue)
assert isinstance(bValue, (int, long))
return self.assignPtr(a, bValue)
else:
bValue = self.getValueGeneric(bValue)
assert isinstance(bValue, (int, long, float))
return self.assign(a, bValue)
@staticmethod
def augAssign(a, op, bValue):
if isinstance(a, (ctypes.c_void_p, ctypes._SimpleCData)):
a.value = OpBinFuncs[op](a.value, bValue)
else:
assert False, "augAssign: not handled: %r of type %r" % (a, type(a))
return a
def augAssignPtr(self, a, op, bValue):
# `a` is itself a pointer.
assert op in ("+=","-=")
op = OpBinFuncs[op]
bValue *= ctypes.sizeof(a._type_)
# Should be safe as long as `a` already contains all the refs.
aPtr = ctypes.cast(ctypes.pointer(a), ctypes.POINTER(ctypes.c_void_p))
aPtr.contents.value = op(aPtr.contents.value, bValue)
a = self.interpreter._storePtr(a, offset=op(0, bValue))
return a
def ptrArithmetic(self, a, op, bValue):
assert op in ("+","-")
return self.augAssignPtr(self.copy(a), op + "=", bValue)
def fixReturnType(self, t):
# Note: This behavior must match CFuncPointerDecl.getCType()
# so that we stay compatible.
if t is None: return None
if issubclass(t, ctypes._Pointer):
# A Python func wrapped in CFuncType cannot handle any pointer type
# other than void-ptr.
t = wrapCTypeClass(ctypes.c_void_p)
stateStruct = self.interpreter.globalScope.stateStruct
return getCTypeWrapped(t, stateStruct)
def makeFuncPtr(self, funcCType, func):
assert inspect.isfunction(func)
if getattr(func, "C_funcPtr", None):
return func.C_funcPtr
# We store the pointer in the func itself
# so that it don't get out of scope (because of casts).
func.C_funcPtr = funcCType(func)
func.C_funcPtrStorage = PointerStorage(ptr=func.C_funcPtr, value=func)
self.interpreter._storePtr(func.C_funcPtr, value=func.C_funcPtrStorage)
return func.C_funcPtr
def checkedFuncPtrCall(self, f, *args):
if _ctype_ptr_get_value(f) == 0:
raise Exception("checkedFuncPtrCall: tried to call NULL ptr")