forked from py4n6/pytsk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
class_parser.py
4434 lines (3653 loc) · 149 KB
/
class_parser.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/python
#
# Copyright 2010, Michael Cohen <scudette@gmail.com>.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Documentation regarding the Python bounded code.
This code originally released as part of the AFF4 project
(http://code.google.com/p/aff4/).
Memory Management
=================
AFF4 uses a reference count system for memory management similar in
many ways to the native Python system. The basic idea is that memory
returned by the library always carries a new reference. When the
caller is done with the memory, they must call aff4_free() on the
memory, afterwhich the memory is considered invalid. The memory may
still not be freed at this point depending on its total reference
count.
New references may be taken to the same memory at any time using the
aff4_incref() function. This increases the reference count of the
object, and prevents it from being really freed until the correct
number of aff4_free() calls are made to it.
This idea is important for example in the following sequence:
FileLikeObject fd = resolver->create(resolver, "w");
RDFURN uri = fd->urn;
Now uri hold a reference to the urn attribute of fd, but that
attribute is actually owned by fd. If fd is freed in future, e.g. (the
close method actually frees the fd implicitely):
fd->close(fd);
Now the uri object is dangling. To prevent fd->urn from disappearing
when fd is freed, we need to take another reference to it:
FileLikeObject fd = resolver->create(resolver, "w");
RDFURN uri = fd->urn;
aff4_incref(uri);
fd->close(fd);
Now uri is valid (but fd is no longer valid). When we are finished
with uri we just call:
aff4_free(uri);
Python Integration
------------------
For every AFF4 object, we create a Python wrapper object of the
corresponding type. The wrapper object contains Python wrapper methods
to allow access to the AFF4 object methods, as well as getattr methods
for attributes. It is very important to allow Python to inherit from C
classes directly - this requires every internal C method call to be
diverted to the Python object.
The C object looks like this normally:
struct obj {
__class__ pointer to static struct initialised with C method pointers
... Some private members
... Attributes;
/* Following are the methods */
int (*method)(struct obj *self, ....);
};
I.e. when the method is called the struct.method member is
dereferenced to find the location of the function handling it, the
object is stuffed into the first arg, and the parameters are stuffed
into following args.
Directing Python calls
----------------------
The Python object which is created is a proxy for the c object. When
Python methods are called in the Python object, they need to be
directed into the C structure and a C call must be made, then the
return value must be reconverted into Python objects and returned into
Python. This occurs automatically by the wrapper:
struct PythonWrapper {
PyObject_HEAD
void *base;
};
When a Python method is called on this new Python type this is what happens:
1) The method name is looked up in the PyMethodDef struct as per normal.
2) If the method is recognised as a valid method the Python wrapper
function is called (pyCLASSNAME_method)
3) This method is broken into the general steps:
PyObject *pyCLASSNAME_method(PythonWrapper self, PyObject *args, PyObject *kwds) {
set up c declerations for all args - call .definition() on all the args and return type
parse argument using PyArg_ParseTupleAndKeywords
Precall preparations
Make the C call
Post call processing of the returned value (check for errors etc)
Convert the return value to a Python object using:
return_type.to_Python_object()
return the Python object or raise an exception
};
So the aim of the wrapper function is to convert Python args to C
args, find the C method corresponding to the method name by
dereferencing the c object and then call it.
The problem now is what happens when a C method internally calls
another method. This is a problem because the C method has no idea its
running within Python and so will just call the regular C method that
was there already. This makes it impossible to subclass the class and
update the C method with a Python method. What we really want is when
a C method is called internally, we want to end up calling the Python
object instead to allow a purely Python implementation to override the
C method.
This happens by way of a ProxiedMethod - A proxied method is in a
sense the reverse of the wrapper method:
return_type ProxyCLASSNAME_method(CLASSNAME self, ....) {
Take all C args and create Python objects from them
Dereference the object extension ((Object) self)->extension to
obtain the Python object which wraps this class.
If an extension does not exist, just call the method as normal,
otherwise make a Python call on the wrapper object.
Convert the returned Python object to a C type and return it.
};
To make all this work we have the following structures:
struct PythonWrapper {
PyObject_HEAD
struct CLASSNAME *base
- This is a copy of the item, with all function pointer
pointing at proxy functions. We can always get the original C
function pointers through base->__class__
- We also set the base object extension to be the Python
object: ((Object) base)->extension = PythonWrapper. This
allows us to get back the Python object from base.
};
When a Python method is invoked, we use cbase to find the C method
pointer, but we pass to it base:
self->base->__class__->method(self->base, ....)
base is a proper C object which had its methods dynamically replaced
with proxies. Now if an internal C method is called, the method will
dereference base and retrieve the proxied method. Calling the
proxied method will retreive the original Python object from the
object extension and make a Python call.
In the case where a method is not overridden by Python, internal C
method calls will generate an unnecessary conversion from C to Python
and then back to C.
Memory management in Python extension
-------------------------------------
When calling a method which returns a new reference, we just store the
reference in the "base" member of the Python object. When Python
garbage collects our Python object, we call aff4_free() on it.
The getattr method creates a new Python wrapper object of the correct
type, and sets its base attribute to point at the target AFF4
object. We then aff4_incref() the target to ensure that it does not
get freed until we are finished with it.
Python Object
-----
| P1 | C Object
| Base|-->+------+
| | | C1 |
| | | |
----- |Member|--------------+-->+----+
+------+ | | C2 |
| | |
Getattr ------- | | |
Member | P2 | | +----+
| Base |--+ New reference
-------
Python Object
Figure 1: Python object 1 owns C1's memory (when P1 is GC'ed C1 is
freed). A reference to a member of C1 is made via P1's
getattr method. The getattr method creates P2 to provide
access to C2 by setting base to C2's address. We need to
guarantee however, that C2 will not be freed suddenly
(e.g. if C1 is freed). We therefore increase C2's
reference count using aff4_incref();
"""
import io
import os
import pdb
import re
import sys
import lexer
DEBUG = 0
# The pytsk3 version.
VERSION = "20160904"
# These functions are used to manage library memory.
FREE = "aff4_free"
INCREF = "aff4_incref"
CURRENT_ERROR_FUNCTION = "aff4_get_current_error"
CONSTANTS_BLACKLIST = ["TSK3_H_"]
# Some constants.
DOCSTRING_RE = re.compile("[ ]*\n[ \t]+[*][ ]?")
def dispatch(name, type, *args, **kwargs):
if not type:
return PVoid(name)
m = re.match("struct ([a-zA-Z0-9]+)_t *", type)
if m:
type = m.group(1)
type_components = type.split()
attributes = set()
if type_components[0] in method_attributes:
attributes.add(type_components.pop(0))
type = " ".join(type_components)
result = type_dispatcher[type](name, type, *args, **kwargs)
result.attributes = attributes
return result
def log(msg):
if DEBUG > 0:
sys.stderr.write("{0:s}\n".format(msg))
def format_as_docstring(string):
# Remove C/C++ comment code statements.
string = DOCSTRING_RE.sub("\n", string)
byte_string = string.encode("unicode-escape")
# Escapes double quoted string. We need to run this after unicode-escape to
# prevent this operation to escape the escape character (\). In Python 3
# the replace method requires the arguments to be byte strings.
byte_string = byte_string.replace(b"\"", b"\\\"")
# Make sure to return the string a Unicode otherwise in Python 3 the string
# is prefixed with b when written or printed.
return byte_string.decode("utf-8")
class Module(object):
public_api = None
public_header = None
def __init__(self, name):
self.name = name
self.constants = set()
self.constants_blacklist = CONSTANTS_BLACKLIST
self.classes = {}
self.headers = "#include <Python.h>\n"
self.files = []
self.active_structs = set()
self.function_definitions = set()
init_string = ""
def initialization(self):
result = self.init_string + (
"\n"
"talloc_set_log_fn((void (*)(const char *)) printf);\n"
"// DEBUG: talloc_enable_leak_report();\n"
"// DEBUG: talloc_enable_leak_report_full();\n")
for cls in self.classes.values():
if cls.is_active():
result += cls.initialise()
return result
def add_constant(self, constant, type="numeric"):
"""This will be called to add #define constant macros."""
self.constants.add((constant, type))
def add_class(self, cls, handler):
self.classes[cls.class_name] = cls
# Make a wrapper in the type dispatcher so we can handle
# passing this class from/to Python
type_dispatcher[cls.class_name] = handler
def get_string(self):
"""Retrieves a string representation."""
result = "Module {0:s}\n".format(self.name)
classes_list = list(self.classes.values())
classes_list.sort(key=lambda cls: cls.class_name)
for cls in classes_list:
if cls.is_active():
result += " {0:s}\n".format(cls.get_string())
constants_list = list(self.constants)
constants_list.sort()
result += "Constants:\n"
for name, _ in constants_list:
result += " {0:s}\n".format(name)
return result
def private_functions(self):
"""Emits hard coded private functions for doing various things"""
values_dict = {
"classes_length": len(self.classes) + 1,
"get_current_error": CURRENT_ERROR_FUNCTION}
return """
/* The following is a static array mapping CLASS() pointers to their
* Python wrappers. This is used to allow the correct wrapper to be
* chosen depending on the object type found - regardless of the
* prototype.
*
* This is basically a safer way for us to cast the correct Python type
* depending on context rather than assuming a type based on the .h
* definition. For example consider the function
*
* AFFObject Resolver.open(uri, mode)
*
* The .h file implies that an AFFObject object is returned, but this is
* not true as most of the time an object of a derived class will be
* returned. In C we cast the returned value to the correct type. In the
* Python wrapper we just instantiate the correct Python object wrapper
* at runtime depending on the actual returned type. We use this lookup
* table to do so.
*/
static int TOTAL_CLASSES=0;
/* This is a global reference to this module so classes can call each
* other.
*/
static PyObject *g_module = NULL;
#define CONSTRUCT_INITIALIZE(class, virt_class, constructor, object, ...) \\
(class)(((virt_class) (&__ ## class))->constructor(object, ## __VA_ARGS__))
#undef BUFF_SIZE
#define BUFF_SIZE 10240
/* Python compatibility macros
*/
#if !defined( PyMODINIT_FUNC )
#if PY_MAJOR_VERSION >= 3
#define PyMODINIT_FUNC PyObject *
#else
#define PyMODINIT_FUNC void
#endif
#endif /* !defined( PyMODINIT_FUNC ) */
#if !defined( PyVarObject_HEAD_INIT )
#define PyVarObject_HEAD_INIT( type, size ) \\
PyObject_HEAD_INIT( type ) \\
size,
#endif /* !defined( PyVarObject_HEAD_INIT ) */
#if PY_MAJOR_VERSION >= 3
#define Py_TPFLAGS_HAVE_ITER 0
#endif
#if !defined( Py_TYPE )
#define Py_TYPE( object ) \\
( ( (PyObject *) object )->ob_type )
#endif /* !defined( Py_TYPE ) */
/* Generic wrapper type
*/
typedef struct Gen_wrapper_t *Gen_wrapper;
struct Gen_wrapper_t {{
PyObject_HEAD
void *base;
/* Value to indicate the base is a Python object.
*/
int base_is_python_object;
/* Value to indicate the base is managed internal.
*/
int base_is_internal;
PyObject *python_object1;
PyObject *python_object2;
}};
static struct python_wrapper_map_t {{
Object class_ref;
PyTypeObject *python_type;
void (*initialize_proxies)(Gen_wrapper self, void *item);
}} python_wrappers[{classes_length:d}];
/* Create the relevant wrapper from the item based on the lookup table.
*/
Gen_wrapper new_class_wrapper(Object item, int item_is_python_object) {{
Gen_wrapper result = NULL;
Object cls = NULL;
struct python_wrapper_map_t *python_wrapper = NULL;
int cls_index = 0;
// Return a Py_None object for a NULL pointer
if(item == NULL) {{
Py_IncRef((PyObject *) Py_None);
return (Gen_wrapper) Py_None;
}}
// Search for subclasses
for(cls = (Object) item->__class__; cls != cls->__super__; cls = cls->__super__) {{
for(cls_index = 0; cls_index < TOTAL_CLASSES; cls_index++) {{
python_wrapper = &(python_wrappers[cls_index]);
if(python_wrapper->class_ref == cls) {{
PyErr_Clear();
result = (Gen_wrapper) _PyObject_New(python_wrapper->python_type);
result->base = item;
result->base_is_python_object = item_is_python_object;
result->base_is_internal = 1;
result->python_object1 = NULL;
result->python_object2 = NULL;
python_wrapper->initialize_proxies(result, (void *) item);
return result;
}}
}}
}}
PyErr_Format(PyExc_RuntimeError, "Unable to find a wrapper for object %s", NAMEOF(item));
return NULL;
}}
static PyObject *resolve_exception(char **error_buff) {{
int *type = (int *){get_current_error:s}(error_buff);
switch(*type) {{
case EProgrammingError:
return PyExc_SystemError;
case EKeyError:
return PyExc_KeyError;
case ERuntimeError:
return PyExc_RuntimeError;
case EInvalidParameter:
return PyExc_TypeError;
case EWarning:
return PyExc_AssertionError;
case EIOError:
return PyExc_IOError;
default:
return PyExc_RuntimeError;
}}
}}
static int type_check(PyObject *obj, PyTypeObject *type) {{
PyTypeObject *tmp = NULL;
// Recurse through the inheritance tree and check if the types are expected
if(obj) {{
for(tmp = Py_TYPE(obj);
tmp && tmp != &PyBaseObject_Type;
tmp = tmp->tp_base) {{
if(tmp == type) return 1;
}}
}}
return 0;
}}
static int check_error() {{
char *buffer = NULL;
int *error_type = (int *)aff4_get_current_error(&buffer);
if(*error_type != EZero) {{
PyObject *exception = resolve_exception(&buffer);
if(buffer != NULL) {{
PyErr_Format(exception, "%s", buffer);
}} else {{
PyErr_Format(exception, "Unable to retrieve exception reason.");
}}
ClearError();
return 1;
}}
return 0;
}}
/* This function checks if a method was overridden in self over a
* method defined in type. This is used to determine if a Python class is
* extending this C type. If not, a proxy function is not written and C
* calls are made directly.
*
* This is an optimization to eliminate the need for a call into Python
* in the case where Python objects do not actually extend any methods.
*
* We basically just iterate over the MRO and determine if a method is
* defined in each level until we reach the base class.
*/
static int check_method_override(PyObject *self, PyTypeObject *type, char *method) {{
struct _typeobject *ob_type = NULL;
PyObject *mro = NULL;
PyObject *py_method = NULL;
PyObject *item_object = NULL;
PyObject *dict = NULL;
Py_ssize_t item_index = 0;
Py_ssize_t number_of_items = 0;
int found = 0;
ob_type = Py_TYPE(self);
if(ob_type == NULL ) {{
return 0;
}}
mro = ob_type->tp_mro;
#if PY_MAJOR_VERSION >= 3
py_method = PyUnicode_FromString(method);
#else
py_method = PyString_FromString(method);
#endif
number_of_items = PySequence_Size(mro);
for(item_index = 0; item_index < number_of_items; item_index++) {{
item_object = PySequence_GetItem(mro, item_index);
// Ok - we got to the base class - finish up
if(item_object == (PyObject *) type) {{
Py_DecRef(item_object);
break;
}}
/* Extract the dict and check if it contains the method (the
* dict is not a real dictionary so we can not use
* PyDict_Contains).
*/
dict = PyObject_GetAttrString(item_object, "__dict__");
if(dict != NULL && PySequence_Contains(dict, py_method)) {{
found = 1;
}}
Py_DecRef(dict);
Py_DecRef(item_object);
if(found != 0) {{
break;
}}
}}
Py_DecRef(py_method);
PyErr_Clear();
return found;
}}
/* Fetches the Python error (exception)
*/
void pytsk_fetch_error(void) {{
PyObject *exception_traceback = NULL;
PyObject *exception_type = NULL;
PyObject *exception_value = NULL;
PyObject *string_object = NULL;
char *str_c = NULL;
char *error_str = NULL;
int *error_type = (int *) {get_current_error:s}(&error_str);
#if PY_MAJOR_VERSION >= 3
PyObject *utf8_string_object = NULL;
#endif
// Fetch the exception state and convert it to a string:
PyErr_Fetch(&exception_type, &exception_value, &exception_traceback);
string_object = PyObject_Repr(exception_value);
#if PY_MAJOR_VERSION >= 3
utf8_string_object = PyUnicode_AsUTF8String(string_object);
if(utf8_string_object != NULL) {{
str_c = PyBytes_AsString(utf8_string_object);
}}
#else
str_c = PyString_AsString(string_object);
#endif
if(str_c != NULL) {{
strncpy(error_str, str_c, BUFF_SIZE-1);
error_str[BUFF_SIZE - 1] = 0;
*error_type = ERuntimeError;
}}
PyErr_Restore(exception_type, exception_value, exception_traceback);
#if PY_MAJOR_VERSION >= 3
if( utf8_string_object != NULL ) {{
Py_DecRef(utf8_string_object);
}}
#endif
Py_DecRef(string_object);
return;
}}
/* Copies a Python int or long object to an unsigned 64-bit value
*/
uint64_t integer_object_copy_to_uint64(PyObject *integer_object) {{
#if defined( HAVE_LONG_LONG )
PY_LONG_LONG long_value = 0;
#else
long long_value = 0;
#endif
int result = 0;
if(integer_object == NULL) {{
PyErr_Format(PyExc_ValueError, "Missing integer object");
return (uint64_t) -1;
}}
PyErr_Clear();
result = PyObject_IsInstance(integer_object, (PyObject *) &PyLong_Type);
if(result == -1) {{
pytsk_fetch_error();
return (uint64_t) -1;
}} else if(result != 0) {{
PyErr_Clear();
#if defined( HAVE_LONG_LONG )
long_value = PyLong_AsUnsignedLongLong(integer_object);
#else
long_value = PyLong_AsUnsignedLong(integer_object);
#endif
}}
#if PY_MAJOR_VERSION < 3
if(result == 0) {{
PyErr_Clear();
result = PyObject_IsInstance(integer_object, (PyObject *) &PyInt_Type);
if(result == -1) {{
pytsk_fetch_error();
return (uint64_t) -1;
}} else if(result != 0) {{
PyErr_Clear();
#if defined( HAVE_LONG_LONG )
long_value = PyInt_AsUnsignedLongLongMask(integer_object);
#else
long_value = PyInt_AsUnsignedLongMask(integer_object);
#endif
}}
}}
#endif /* PY_MAJOR_VERSION < 3 */
if(result == 0) {{
if(PyErr_Occurred()) {{
pytsk_fetch_error();
return (uint64_t) -1;
}}
}}
#if defined( HAVE_LONG_LONG )
#if ( SIZEOF_LONG_LONG > 8 )
if((long_value < (PY_LONG_LONG) 0) || (long_value > (PY_LONG_LONG) UINT64_MAX)) {{
#else
if(long_value < (PY_LONG_LONG) 0) {{
#endif
PyErr_Format(PyExc_ValueError, "Integer object value out of bounds");
return (uint64_t) -1;
}}
#else
#if ( SIZEOF_LONG > 8 )
if((long_value < (long) 0) || (long_value > (long) UINT64_MAX)) {{
#else
if(long_value < (PY_LONG_LONG) 0) {{
#endif
PyErr_Format(PyExc_ValueError, "Integer object value out of bounds");
return (uint64_t) -1;
}}
#endif
return (uint64_t) long_value;
}}
""".format(**values_dict)
def initialise_class(self, class_name, out, done=None):
if done and class_name in done:
return
done.add(class_name)
cls = self.classes[class_name]
"""Write out class initialisation code into the main init function."""
if cls.is_active():
base_class = self.classes.get(cls.base_class_name)
if base_class and base_class.is_active():
# We have a base class - ensure it gets written out
# first:
self.initialise_class(cls.base_class_name, out, done)
# Now assign ourselves as derived from them
out.write(
" {0:s}_Type.tp_base = &{1:s}_Type;".format(
cls.class_name, cls.base_class_name))
values_dict = {
"name": cls.class_name}
out.write((
" {name:s}_Type.tp_new = PyType_GenericNew;\n"
" if (PyType_Ready(&{name:s}_Type) < 0) {{\n"
" goto on_error;\n"
" }}\n"
" Py_IncRef((PyObject *)&{name:s}_Type);\n"
" PyModule_AddObject(module, \"{name:s}\", (PyObject *)&{name:s}_Type);\n").format(
**values_dict))
def write(self, out):
# Write the headers
if self.public_api:
self.public_api.write(
"#ifdef BUILDING_DLL\n"
"#include \"misc.h\"\n"
"#else\n"
"#include \"aff4_public.h\"\n"
"#endif\n")
# Prepare all classes
for cls in self.classes.values():
cls.prepare()
out.write((
"/*************************************************************\n"
" * Autogenerated module {0:s}\n"
" *\n"
" * This module was autogenerated from the following files:\n").format(
self.name))
for filename in self.files:
out.write(" * {0:s}\n".format(filename))
out.write(
" *\n"
" * This module implements the following classes:\n")
out.write(self.get_string())
out.write(
" ************************************************************/\n")
out.write(self.headers)
out.write(self.private_functions())
for cls in self.classes.values():
if cls.is_active():
out.write(
"/******************** {0:s} ***********************/".format(
cls.class_name))
cls.struct(out)
cls.prototypes(out)
out.write(
"/*****************************************************\n"
" * Implementation\n"
" ****************************************************/\n"
"\n")
for cls in self.classes.values():
if cls.is_active():
cls.PyMethodDef(out)
cls.PyGetSetDef(out)
cls.code(out)
cls.PyTypeObject(out)
# Write the module initializer
values_dict = {
"module": self.name,
"version": VERSION,
"version_length": len(VERSION)}
out.write((
"/* Retrieves the {module:s} version\n"
" * Returns a Python object if successful or NULL on error\n"
" */\n"
"PyObject *{module:s}_get_version(PyObject *self, PyObject *arguments) {{\n"
" const char *errors = NULL;\n"
" return(PyUnicode_DecodeUTF8(\"{version:s}\", (Py_ssize_t) {version_length:d}, errors));\n"
"}}\n"
"\n"
"static PyMethodDef {module:s}_module_methods[] = {{\n"
" {{ \"get_version\",\n"
" (PyCFunction) {module:s}_get_version,\n"
" METH_NOARGS,\n"
" \"get_version() -> String\\n\"\n"
" \"\\n\"\n"
" \"Retrieves the version.\" }},\n"
"\n"
" {{NULL, NULL, 0, NULL}} /* Sentinel */\n"
"}};\n"
"\n"
"#if PY_MAJOR_VERSION >= 3\n"
"\n"
"/* The {module:s} module definition\n"
" */\n"
"PyModuleDef {module:s}_module_definition = {{\n"
" PyModuleDef_HEAD_INIT,\n"
"\n"
" /* m_name */\n"
" \"{module:s}\",\n"
" /* m_doc */\n"
" \"Python {module:s} module.\",\n"
" /* m_size */\n"
" -1,\n"
" /* m_methods */\n"
" {module:s}_module_methods,\n"
" /* m_reload */\n"
" NULL,\n"
" /* m_traverse */\n"
" NULL,\n"
" /* m_clear */\n"
" NULL,\n"
" /* m_free */\n"
" NULL,\n"
"}};\n"
"\n"
"#endif /* PY_MAJOR_VERSION >= 3 */\n"
"\n"
"/* Initializes the {module:s} module\n"
" */\n"
"#if PY_MAJOR_VERSION >= 3\n"
"PyMODINIT_FUNC PyInit_{module:s}(void) {{\n"
"#else\n"
"PyMODINIT_FUNC init{module:s}(void) {{\n"
"#endif\n"
" PyGILState_STATE gil_state;\n"
"\n"
" PyObject *module = NULL;\n"
" PyObject *d = NULL;\n"
" PyObject *tmp = NULL;\n"
"\n"
" /* Create the module\n"
" * This function must be called before grabbing the GIL\n"
" * otherwise the module will segfault on a version mismatch\n"
" */\n"
"#if PY_MAJOR_VERSION >= 3\n"
" module = PyModule_Create(\n"
" &{module:s}_module_definition );\n"
"#else\n"
" module = Py_InitModule3(\n"
" \"{module:s}\",\n"
" {module:s}_module_methods,\n"
" \"Python {module:s} module.\" );\n"
"#endif\n"
" if (module == NULL) {{\n"
"#if PY_MAJOR_VERSION >= 3\n"
" return(NULL);\n"
"#else\n"
" return;\n"
"#endif\n"
" }}\n"
" d = PyModule_GetDict(module);\n"
"\n"
" /* Make sure threads are enabled */\n"
" PyEval_InitThreads();\n"
" gil_state = PyGILState_Ensure();\n"
"\n"
" g_module = module;\n").format(**values_dict))
# The trick is to initialise the classes in order of their
# inheritance. The following code will order initializations
# according to their inheritance tree
done = set()
for class_name in self.classes.keys():
self.initialise_class(class_name, out, done)
# Add the constants in here
for constant, type in self.constants:
if type == "integer":
out.write(
" tmp = PyLong_FromUnsignedLongLong((uint64_t) {0:s});\n".format(constant))
elif type == "string":
if constant == "TSK_VERSION_STR":
out.write((
"#if PY_MAJOR_VERSION >= 3\n"
" tmp = PyUnicode_FromString((char *){0:s});\n"
"#else\n"
" tmp = PyString_FromString((char *){0:s});\n"
"#endif\n").format(constant))
else:
out.write((
"#if PY_MAJOR_VERSION >= 3\n"
" tmp = PyBytes_FromString((char *){0:s});\n"
"#else\n"
" tmp = PyString_FromString((char *){0:s});\n"
"#endif\n").format(constant))
else:
out.write(
" /* I dont know how to convert {0:s} type {1:s} */\n".format(
constant, type))
continue
out.write((
" PyDict_SetItemString(d, \"{0:s}\", tmp);\n"
" Py_DecRef(tmp);\n").format(constant))
out.write(self.initialization())
out.write(
" PyGILState_Release(gil_state);\n"
"\n"
"#if PY_MAJOR_VERSION >= 3\n"
" return module;\n"
"#else\n"
" return;\n"
"#endif\n"
"\n"
"on_error:\n"
" PyGILState_Release(gil_state);\n"
"\n"
"#if PY_MAJOR_VERSION >= 3\n"
" return NULL;\n"
"#else\n"
" return;\n"
"#endif\n"
"}\n")
class Type(object):
interface = None
buildstr = "O"
sense = "IN"
error_value = "return 0;"
active = True
def __init__(self, name, type, *args, **kwargs):
super(Type, self).__init__()
self.name = name
self.type = type
self.attributes = set()
self.additional_args = kwargs
def comment(self):
return "{0:s} {1:s} ".format(self.type, self.name)
def get_string(self):
"""Retrieves a string representation."""
if self.name == "func_return":
return self.type
if "void" in self.type:
return ""
return "{0:s} : {1:s}".format(self.type, self.name)
def python_name(self):
return self.name
def python_proxy_post_call(self):
"""This is called after a proxy call"""
return ""