1 | #include "Python.h"
|
2 |
|
3 | // Notes on thread-local state that objects use (PyThreadState)
|
4 | //
|
5 | // Py_EnterRecursiveCall / Py_LeaveRecursiveCall -- so we don't blow the stack
|
6 | // Py_ReprEnter / Py_ReprLeave -- for printing [...] in self-referential structures
|
7 | //
|
8 | // PyErr_Occurred -- check for exceptions, stubbed in pgenmain
|
9 | // PyErr_CheckSignals -- for OS signals, stubbed in sigcheck.c, and here
|
10 | //
|
11 | // Notes on builtins like int(). See Python/bltinmodule.c.
|
12 | //
|
13 | // #define SETBUILTIN(NAME, OBJECT) \
|
14 | // if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0) \
|
15 | // return NULL; \
|
16 | // ADD_TO_ALL(OBJECT)
|
17 | //
|
18 | // SETBUILTIN("int", &PyInt_Type);
|
19 | //
|
20 | // It creates the __builtin__ module, which has a __dict__. And then the types
|
21 | // are here. So you can just call a type directly? Sure.
|
22 |
|
23 | // #include "longintrepr.h" // 32 bytes
|
24 |
|
25 | #include <stdarg.h> // va_list, etc.
|
26 | #include <stdio.h> // vprintf
|
27 |
|
28 | void Log(const char* fmt, ...) {
|
29 | va_list args;
|
30 | va_start(args, fmt);
|
31 | vprintf(fmt, args);
|
32 | va_end(args);
|
33 | printf("\n");
|
34 | }
|
35 |
|
36 |
|
37 | int Py_VerboseFlag; /* pythonrun.c, does only intobject.c use it? */
|
38 |
|
39 | PyThreadState *_PyThreadState_Current = NULL;
|
40 |
|
41 | struct _inittab _PyImport_Inittab[] = {
|
42 | /* Sentinel */
|
43 | {0, 0}
|
44 | };
|
45 |
|
46 | void
|
47 | Py_FatalError(const char *msg)
|
48 | {
|
49 | fprintf(stderr, "Fatal Python error: %s\n", msg);
|
50 | abort();
|
51 | }
|
52 |
|
53 | /* Even smaller than Python/sigcheck.c */
|
54 | int
|
55 | PyErr_CheckSignals(void)
|
56 | {
|
57 | return 0;
|
58 | }
|
59 |
|
60 | /* APIs to write to sys.stdout or sys.stderr using a printf-like interface.
|
61 | Adapted from code submitted by Just van Rossum.
|
62 |
|
63 | PySys_WriteStdout(format, ...)
|
64 | PySys_WriteStderr(format, ...)
|
65 |
|
66 | The first function writes to sys.stdout; the second to sys.stderr. When
|
67 | there is a problem, they write to the real (C level) stdout or stderr;
|
68 | no exceptions are raised.
|
69 |
|
70 | Both take a printf-style format string as their first argument followed
|
71 | by a variable length argument list determined by the format string.
|
72 |
|
73 | *** WARNING ***
|
74 |
|
75 | The format should limit the total size of the formatted output string to
|
76 | 1000 bytes. In particular, this means that no unrestricted "%s" formats
|
77 | should occur; these should be limited using "%.<N>s where <N> is a
|
78 | decimal number calculated so that <N> plus the maximum size of other
|
79 | formatted text does not exceed 1000 bytes. Also watch out for "%f",
|
80 | which can print hundreds of digits for very large numbers.
|
81 |
|
82 | */
|
83 |
|
84 | static void
|
85 | mywrite(char *name, FILE *fp, const char *format, va_list va)
|
86 | {
|
87 | #ifdef OBJECTS_ONLY
|
88 | assert(0);
|
89 | #else
|
90 | PyObject *file;
|
91 | PyObject *error_type, *error_value, *error_traceback;
|
92 |
|
93 | PyErr_Fetch(&error_type, &error_value, &error_traceback);
|
94 | file = PySys_GetObject(name);
|
95 | if (file == NULL || PyFile_AsFile(file) == fp)
|
96 | vfprintf(fp, format, va);
|
97 | else {
|
98 | char buffer[1001];
|
99 | const int written = PyOS_vsnprintf(buffer, sizeof(buffer),
|
100 | format, va);
|
101 | if (PyFile_WriteString(buffer, file) != 0) {
|
102 | PyErr_Clear();
|
103 | fputs(buffer, fp);
|
104 | }
|
105 | if (written < 0 || (size_t)written >= sizeof(buffer)) {
|
106 | const char *truncated = "... truncated";
|
107 | if (PyFile_WriteString(truncated, file) != 0) {
|
108 | PyErr_Clear();
|
109 | fputs(truncated, fp);
|
110 | }
|
111 | }
|
112 | }
|
113 | PyErr_Restore(error_type, error_value, error_traceback);
|
114 | #endif
|
115 | }
|
116 |
|
117 | /*
|
118 | void
|
119 | PySys_WriteStdout(const char *format, ...)
|
120 | {
|
121 | va_list va;
|
122 |
|
123 | va_start(va, format);
|
124 | mywrite("stdout", stdout, format, va);
|
125 | va_end(va);
|
126 | }
|
127 | */
|
128 |
|
129 | void
|
130 | PySys_WriteStderr(const char *format, ...)
|
131 | {
|
132 | va_list va;
|
133 |
|
134 | va_start(va, format);
|
135 | mywrite("stderr", stderr, format, va);
|
136 | va_end(va);
|
137 | }
|
138 |
|
139 |
|
140 | #define FILE_BEGIN_ALLOW_THREADS(fobj)
|
141 | #define FILE_END_ALLOW_THREADS(fobj)
|
142 |
|
143 | static PyObject *
|
144 | err_closed(void)
|
145 | {
|
146 | PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
|
147 | return NULL;
|
148 | }
|
149 |
|
150 | /* Interfaces to write objects/strings to file-like objects */
|
151 |
|
152 | int
|
153 | PyFile_WriteObject(PyObject *v, PyObject *f, int flags)
|
154 | {
|
155 | #ifdef OBJECTS_ONLY
|
156 | assert(0);
|
157 | #else
|
158 | PyObject *writer, *value, *args, *result;
|
159 | if (f == NULL) {
|
160 | PyErr_SetString(PyExc_TypeError, "writeobject with NULL file");
|
161 | return -1;
|
162 | }
|
163 | else if (PyFile_Check(f)) {
|
164 | PyFileObject *fobj = (PyFileObject *) f;
|
165 | #ifdef Py_USING_UNICODE
|
166 | PyObject *enc = fobj->f_encoding;
|
167 | int result;
|
168 | #endif
|
169 | if (fobj->f_fp == NULL) {
|
170 | err_closed();
|
171 | return -1;
|
172 | }
|
173 | #ifdef Py_USING_UNICODE
|
174 | if ((flags & Py_PRINT_RAW) &&
|
175 | PyUnicode_Check(v) && enc != Py_None) {
|
176 | char *cenc = PyString_AS_STRING(enc);
|
177 | char *errors = fobj->f_errors == Py_None ?
|
178 | "strict" : PyString_AS_STRING(fobj->f_errors);
|
179 | value = PyUnicode_AsEncodedString(v, cenc, errors);
|
180 | if (value == NULL)
|
181 | return -1;
|
182 | } else {
|
183 | value = v;
|
184 | Py_INCREF(value);
|
185 | }
|
186 | result = file_PyObject_Print(value, fobj, flags);
|
187 | Py_DECREF(value);
|
188 | return result;
|
189 | #else
|
190 | return file_PyObject_Print(v, fobj, flags);
|
191 | #endif
|
192 | }
|
193 | writer = PyObject_GetAttrString(f, "write");
|
194 | if (writer == NULL)
|
195 | return -1;
|
196 | if (flags & Py_PRINT_RAW) {
|
197 | if (PyUnicode_Check(v)) {
|
198 | value = v;
|
199 | Py_INCREF(value);
|
200 | } else
|
201 | value = PyObject_Str(v);
|
202 | }
|
203 | else
|
204 | value = PyObject_Repr(v);
|
205 | if (value == NULL) {
|
206 | Py_DECREF(writer);
|
207 | return -1;
|
208 | }
|
209 | args = PyTuple_Pack(1, value);
|
210 | if (args == NULL) {
|
211 | Py_DECREF(value);
|
212 | Py_DECREF(writer);
|
213 | return -1;
|
214 | }
|
215 | result = PyEval_CallObject(writer, args);
|
216 | Py_DECREF(args);
|
217 | Py_DECREF(value);
|
218 | Py_DECREF(writer);
|
219 | if (result == NULL)
|
220 | return -1;
|
221 | Py_DECREF(result);
|
222 | #endif
|
223 | return 0;
|
224 | }
|
225 |
|
226 | int
|
227 | PyFile_WriteString(const char *s, PyObject *f)
|
228 | {
|
229 | #ifdef OBJECTS_ONLY
|
230 | assert(0);
|
231 | #else
|
232 | if (f == NULL) {
|
233 | /* Should be caused by a pre-existing error */
|
234 | if (!PyErr_Occurred())
|
235 | PyErr_SetString(PyExc_SystemError,
|
236 | "null file for PyFile_WriteString");
|
237 | return -1;
|
238 | }
|
239 | else if (PyFile_Check(f)) {
|
240 | PyFileObject *fobj = (PyFileObject *) f;
|
241 | FILE *fp = PyFile_AsFile(f);
|
242 | if (fp == NULL) {
|
243 | err_closed();
|
244 | return -1;
|
245 | }
|
246 | FILE_BEGIN_ALLOW_THREADS(fobj)
|
247 | fputs(s, fp);
|
248 | FILE_END_ALLOW_THREADS(fobj)
|
249 | return 0;
|
250 | }
|
251 | else if (!PyErr_Occurred()) {
|
252 | PyObject *v = PyString_FromString(s);
|
253 | int err;
|
254 | if (v == NULL)
|
255 | return -1;
|
256 | err = PyFile_WriteObject(v, f, Py_PRINT_RAW);
|
257 | Py_DECREF(v);
|
258 | return err;
|
259 | }
|
260 | else
|
261 | return -1;
|
262 | #endif
|
263 | }
|
264 |
|
265 |
|
266 | /* Stub of function in Python/_warnings.c */
|
267 | int
|
268 | PyErr_WarnEx(PyObject *category, const char *text, Py_ssize_t stack_level)
|
269 | {
|
270 | return 0;
|
271 | }
|
272 |
|
273 | /* from Python/ceval.c */
|
274 |
|
275 | #define Py_DEFAULT_RECURSION_LIMIT 1000
|
276 | /* a lot of code uses this too */
|
277 | int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
|
278 |
|
279 | int
|
280 | Py_GetRecursionLimit(void)
|
281 | {
|
282 | return Py_DEFAULT_RECURSION_LIMIT;
|
283 | }
|
284 |
|
285 | void
|
286 | Py_SetRecursionLimit(int new_limit)
|
287 | {
|
288 | }
|
289 |
|
290 | int
|
291 | _Py_CheckRecursiveCall(const char *where)
|
292 | {
|
293 | return 0;
|
294 | }
|
295 |
|
296 | /* Extract a slice index from a PyInt or PyLong or an object with the
|
297 | nb_index slot defined, and store in *pi.
|
298 | Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX,
|
299 | and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1.
|
300 | Return 0 on error, 1 on success.
|
301 | */
|
302 | /* Note: If v is NULL, return success without storing into *pi. This
|
303 | is because_PyEval_SliceIndex() is called by apply_slice(), which can be
|
304 | called by the SLICE opcode with v and/or w equal to NULL.
|
305 | */
|
306 | int
|
307 | _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi)
|
308 | {
|
309 | if (v != NULL) {
|
310 | Py_ssize_t x;
|
311 | if (PyInt_Check(v)) {
|
312 | /* XXX(nnorwitz): I think PyInt_AS_LONG is correct,
|
313 | however, it looks like it should be AsSsize_t.
|
314 | There should be a comment here explaining why.
|
315 | */
|
316 | x = PyInt_AS_LONG(v);
|
317 | }
|
318 | else if (PyIndex_Check(v)) {
|
319 | x = PyNumber_AsSsize_t(v, NULL);
|
320 | if (x == -1 && PyErr_Occurred())
|
321 | return 0;
|
322 | }
|
323 | else {
|
324 | PyErr_SetString(PyExc_TypeError,
|
325 | "slice indices must be integers or "
|
326 | "None or have an __index__ method");
|
327 | return 0;
|
328 | }
|
329 | *pi = x;
|
330 | }
|
331 | return 1;
|
332 | }
|
333 |
|
334 | /* External interface to call any callable object.
|
335 | The arg must be a tuple or NULL. The kw must be a dict or NULL. */
|
336 |
|
337 | PyObject *
|
338 | PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw)
|
339 | {
|
340 | PyObject *result;
|
341 |
|
342 | if (arg == NULL) {
|
343 | arg = PyTuple_New(0);
|
344 | if (arg == NULL)
|
345 | return NULL;
|
346 | }
|
347 | else if (!PyTuple_Check(arg)) {
|
348 | PyErr_SetString(PyExc_TypeError,
|
349 | "argument list must be a tuple");
|
350 | return NULL;
|
351 | }
|
352 | else
|
353 | Py_INCREF(arg);
|
354 |
|
355 | if (kw != NULL && !PyDict_Check(kw)) {
|
356 | PyErr_SetString(PyExc_TypeError,
|
357 | "keyword list must be a dictionary");
|
358 | Py_DECREF(arg);
|
359 | return NULL;
|
360 | }
|
361 |
|
362 | result = PyObject_Call(func, arg, kw);
|
363 | Py_DECREF(arg);
|
364 | return result;
|
365 | }
|
366 |
|
367 | /* NOTE: Can't use a switch here because it's not an integer constant!
|
368 | *
|
369 | * Instead of "with tagswitch", do "with typeswitch"?
|
370 | * */
|
371 |
|
372 | void Type(PyObject* obj) {
|
373 | struct _typeobject* type = obj->ob_type;
|
374 |
|
375 | if (type == &PyBool_Type) {
|
376 | Log("bool");
|
377 | } else if (type == &PyLong_Type) {
|
378 | Log("long");
|
379 | } else if (type == &PyFloat_Type) {
|
380 | Log("float");
|
381 | } else if (type == &PyString_Type) {
|
382 | Log("string");
|
383 | } else if (type == &PyTuple_Type) {
|
384 | Log("tuple");
|
385 | } else if (type == &PyList_Type) {
|
386 | Log("list");
|
387 | } else if (type == &PyDict_Type) {
|
388 | Log("dict");
|
389 | } else {
|
390 | Log("unknown type");
|
391 | }
|
392 | }
|
393 |
|
394 | void PrintList(reprfunc list_repr, PyObject* list) {
|
395 | PyObject* r = list_repr(list);
|
396 | assert(r != NULL);
|
397 | PyStringObject* rstr = (PyStringObject*) r;
|
398 |
|
399 | fprintf(stderr, "list = %.*s\n", (int)rstr->ob_size, rstr->ob_sval);
|
400 | }
|
401 |
|
402 | int main(int argc, char **argv) {
|
403 | PyObject* bool1 = PyBool_FromLong(1);
|
404 |
|
405 | reprfunc bool_repr = PyBool_Type.tp_repr;
|
406 | PyObject* b = bool_repr(bool1);
|
407 |
|
408 | PyStringObject* br = (PyStringObject*) b;
|
409 |
|
410 | fprintf(stderr, "true = %.*s\n", (int)br->ob_size, br->ob_sval);
|
411 |
|
412 |
|
413 | PyObject* long1 = PyLong_FromLong(42);
|
414 | PyObject* long2 = PyLong_FromLong(1);
|
415 |
|
416 | binaryfunc long_add = PyLong_Type.tp_as_number->nb_add;
|
417 | PyObject* long_sum = long_add(long1, long2);
|
418 |
|
419 | reprfunc long_repr = PyLong_Type.tp_repr;
|
420 | PyObject* r = long_repr(long_sum);
|
421 |
|
422 | PyStringObject* rstr = (PyStringObject*) r;
|
423 |
|
424 | fprintf(stderr, "42 + 1 = %.*s\n", (int)rstr->ob_size, rstr->ob_sval);
|
425 |
|
426 |
|
427 | PyObject* float1 = PyFloat_FromDouble(42.1);
|
428 | PyObject* float2 = PyFloat_FromDouble(1.2);
|
429 |
|
430 | binaryfunc float_add = PyFloat_Type.tp_as_number->nb_add;
|
431 | PyObject* float_sum = float_add(float1, float2);
|
432 |
|
433 | reprfunc float_repr = PyFloat_Type.tp_repr;
|
434 | PyObject* r3 = float_repr(float_sum);
|
435 |
|
436 | PyStringObject* rstr3 = (PyStringObject*) r3;
|
437 |
|
438 | fprintf(stderr, "42.1 + 1.2 = %.*s\n", (int)rstr3->ob_size, rstr3->ob_sval);
|
439 |
|
440 |
|
441 | PyObject* str1 = PyString_FromString("foo ");
|
442 | PyObject* str2 = PyString_FromString("bar");
|
443 |
|
444 | binaryfunc str_add = PyString_Type.tp_as_sequence->sq_concat;
|
445 | PyObject* concat = str_add(str1, str2);
|
446 |
|
447 | reprfunc str_repr = PyString_Type.tp_repr;
|
448 | PyObject* r2 = str_repr(concat);
|
449 |
|
450 | PyStringObject* rstr2 = (PyStringObject*) r2;
|
451 |
|
452 | fprintf(stderr, "foo + bar = %.*s\n", (int)rstr2->ob_size, rstr2->ob_sval);
|
453 |
|
454 | // List
|
455 |
|
456 | PyObject* list = PyList_New(3);
|
457 | PyList_SetItem(list, 0, long_sum);
|
458 | PyList_SetItem(list, 1, float_sum);
|
459 | /* Test the printing of cyclic data structures */
|
460 | PyList_SetItem(list, 2, list);
|
461 |
|
462 | reprfunc list_repr = PyList_Type.tp_repr;
|
463 | PyObject* r5 = list_repr(list);
|
464 | assert(r5 != NULL);
|
465 | PyStringObject* rstr5 = (PyStringObject*) r5;
|
466 |
|
467 | fprintf(stderr, "list = %.*s\n", (int)rstr5->ob_size, rstr5->ob_sval);
|
468 |
|
469 | Log("");
|
470 |
|
471 | // Sort and reverse list
|
472 | PyObject* list2 = PyList_New(3);
|
473 | PyList_SetItem(list2, 0, long1);
|
474 | PyList_SetItem(list2, 1, long2);
|
475 | PyList_SetItem(list2, 2, long_sum);
|
476 | PrintList(list_repr, list2);
|
477 |
|
478 | // These are internal API calls. listsort() and listreverse() are in the
|
479 | // method table, and do less work.
|
480 | PyList_Sort(list2);
|
481 | PrintList(list_repr, list2);
|
482 |
|
483 | PyList_Reverse(list2);
|
484 | PrintList(list_repr, list2);
|
485 |
|
486 | /* Works if you make it non-static
|
487 | listreverse(list2);
|
488 | PrintList(list_repr, list2);
|
489 | */
|
490 |
|
491 | Log("");
|
492 |
|
493 | // Tuple
|
494 |
|
495 | PyObject* tuple = PyTuple_New(3);
|
496 | PyTuple_SetItem(tuple, 0, long_sum);
|
497 | PyTuple_SetItem(tuple, 1, float_sum);
|
498 | PyTuple_SetItem(tuple, 2, concat);
|
499 |
|
500 | reprfunc tuple_repr = PyTuple_Type.tp_repr;
|
501 | PyObject* r4 = tuple_repr(tuple);
|
502 | assert(r4 != NULL);
|
503 |
|
504 | PyStringObject* rstr4 = (PyStringObject*) r4;
|
505 |
|
506 | fprintf(stderr, "tuple = %.*s\n", (int)rstr4->ob_size, rstr4->ob_sval);
|
507 |
|
508 | // Dict
|
509 |
|
510 | PyObject* dict = PyDict_New();
|
511 | PyDict_SetItem(dict, concat, long_sum);
|
512 |
|
513 | reprfunc dict_repr = PyDict_Type.tp_repr;
|
514 | PyObject* d = dict_repr(dict);
|
515 | assert(d != NULL);
|
516 |
|
517 | PyStringObject* dr = (PyStringObject*) d;
|
518 |
|
519 | fprintf(stderr, "dict = %.*s\n", (int)dr->ob_size, dr->ob_sval);
|
520 |
|
521 | // suceess
|
522 | PyObject* val = PyDict_GetItem(dict, concat);
|
523 | Log("val = %p", val);
|
524 |
|
525 | // fail
|
526 | PyObject* val2 = PyDict_GetItem(dict, long1);
|
527 | Log("val2 = %p", val2);
|
528 | // TODO: How to get exception?
|
529 |
|
530 | Type(long_sum);
|
531 | Type(float_sum);
|
532 | Type(list);
|
533 | Type(dict);
|
534 |
|
535 | Log("sizeof(PyBoolObject) = %zu", sizeof(PyBoolObject)); // 24
|
536 | // 32 bytes, needs header
|
537 | //Log("sizeof(PyLongObject) = %zu", sizeof(PyLongObject));
|
538 | Log("sizeof(PyFloatObject) = %zu", sizeof(PyFloatObject)); // 24
|
539 | Log("sizeof(PyStringObject) = %zu", sizeof(PyStringObject)); // 40
|
540 | Log("sizeof(PyTupleObject) = %zu", sizeof(PyTupleObject)); // 32
|
541 | Log("sizeof(PyListObject) = %zu", sizeof(PyListObject)); // 40
|
542 | Log("sizeof(PyDictObject) = %zu", sizeof(PyDictObject)); // 248
|
543 |
|
544 |
|
545 | // TODO:
|
546 | // - Iterate over dicts and lists
|
547 | // - Call methods -- how?
|
548 | // - .keys() and .value() -- see addmethods()
|
549 | // - respect calling convention flags for L.sort() and L.reverse()
|
550 | // - Test exceptions like IndexError, KeyError, and more
|
551 | // - Other types: slices? What about range and enumeration?
|
552 | // - repr(None)? How? Py_None
|
553 | // object.c has PyNone_Type, but it's not in a header!
|
554 | // - builtins like int() str()
|
555 | return 0;
|
556 | }
|