]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | /* Parse tree node implementation */\r |
2 | \r | |
3 | #include "Python.h"\r | |
4 | #include "node.h"\r | |
5 | #include "errcode.h"\r | |
6 | \r | |
7 | node *\r | |
8 | PyNode_New(int type)\r | |
9 | {\r | |
10 | node *n = (node *) PyObject_MALLOC(1 * sizeof(node));\r | |
11 | if (n == NULL)\r | |
12 | return NULL;\r | |
13 | n->n_type = type;\r | |
14 | n->n_str = NULL;\r | |
15 | n->n_lineno = 0;\r | |
16 | n->n_nchildren = 0;\r | |
17 | n->n_child = NULL;\r | |
18 | return n;\r | |
19 | }\r | |
20 | \r | |
21 | /* See comments at XXXROUNDUP below. Returns -1 on overflow. */\r | |
22 | static int\r | |
23 | fancy_roundup(int n)\r | |
24 | {\r | |
25 | /* Round up to the closest power of 2 >= n. */\r | |
26 | int result = 256;\r | |
27 | assert(n > 128);\r | |
28 | while (result < n) {\r | |
29 | result <<= 1;\r | |
30 | if (result <= 0)\r | |
31 | return -1;\r | |
32 | }\r | |
33 | return result;\r | |
34 | }\r | |
35 | \r | |
36 | /* A gimmick to make massive numbers of reallocs quicker. The result is\r | |
37 | * a number >= the input. In PyNode_AddChild, it's used like so, when\r | |
38 | * we're about to add child number current_size + 1:\r | |
39 | *\r | |
40 | * if XXXROUNDUP(current_size) < XXXROUNDUP(current_size + 1):\r | |
41 | * allocate space for XXXROUNDUP(current_size + 1) total children\r | |
42 | * else:\r | |
43 | * we already have enough space\r | |
44 | *\r | |
45 | * Since a node starts out empty, we must have\r | |
46 | *\r | |
47 | * XXXROUNDUP(0) < XXXROUNDUP(1)\r | |
48 | *\r | |
49 | * so that we allocate space for the first child. One-child nodes are very\r | |
50 | * common (presumably that would change if we used a more abstract form\r | |
51 | * of syntax tree), so to avoid wasting memory it's desirable that\r | |
52 | * XXXROUNDUP(1) == 1. That in turn forces XXXROUNDUP(0) == 0.\r | |
53 | *\r | |
54 | * Else for 2 <= n <= 128, we round up to the closest multiple of 4. Why 4?\r | |
55 | * Rounding up to a multiple of an exact power of 2 is very efficient, and\r | |
56 | * most nodes with more than one child have <= 4 kids.\r | |
57 | *\r | |
58 | * Else we call fancy_roundup() to grow proportionately to n. We've got an\r | |
59 | * extreme case then (like test_longexp.py), and on many platforms doing\r | |
60 | * anything less than proportional growth leads to exorbitant runtime\r | |
61 | * (e.g., MacPython), or extreme fragmentation of user address space (e.g.,\r | |
62 | * Win98).\r | |
63 | *\r | |
64 | * In a run of compileall across the 2.3a0 Lib directory, Andrew MacIntyre\r | |
65 | * reported that, with this scheme, 89% of PyObject_REALLOC calls in\r | |
66 | * PyNode_AddChild passed 1 for the size, and 9% passed 4. So this usually\r | |
67 | * wastes very little memory, but is very effective at sidestepping\r | |
68 | * platform-realloc disasters on vulnerable platforms.\r | |
69 | *\r | |
70 | * Note that this would be straightforward if a node stored its current\r | |
71 | * capacity. The code is tricky to avoid that.\r | |
72 | */\r | |
73 | #define XXXROUNDUP(n) ((n) <= 1 ? (n) : \\r | |
74 | (n) <= 128 ? (((n) + 3) & ~3) : \\r | |
75 | fancy_roundup(n))\r | |
76 | \r | |
77 | \r | |
78 | int\r | |
79 | PyNode_AddChild(register node *n1, int type, char *str, int lineno, int col_offset)\r | |
80 | {\r | |
81 | const int nch = n1->n_nchildren;\r | |
82 | int current_capacity;\r | |
83 | int required_capacity;\r | |
84 | node *n;\r | |
85 | \r | |
86 | if (nch == INT_MAX || nch < 0)\r | |
87 | return E_OVERFLOW;\r | |
88 | \r | |
89 | current_capacity = XXXROUNDUP(nch);\r | |
90 | required_capacity = XXXROUNDUP(nch + 1);\r | |
91 | if (current_capacity < 0 || required_capacity < 0)\r | |
92 | return E_OVERFLOW;\r | |
93 | if (current_capacity < required_capacity) {\r | |
94 | if (required_capacity > PY_SIZE_MAX / sizeof(node)) {\r | |
95 | return E_NOMEM;\r | |
96 | }\r | |
97 | n = n1->n_child;\r | |
98 | n = (node *) PyObject_REALLOC(n,\r | |
99 | required_capacity * sizeof(node));\r | |
100 | if (n == NULL)\r | |
101 | return E_NOMEM;\r | |
102 | n1->n_child = n;\r | |
103 | }\r | |
104 | \r | |
105 | n = &n1->n_child[n1->n_nchildren++];\r | |
106 | n->n_type = type;\r | |
107 | n->n_str = str;\r | |
108 | n->n_lineno = lineno;\r | |
109 | n->n_col_offset = col_offset;\r | |
110 | n->n_nchildren = 0;\r | |
111 | n->n_child = NULL;\r | |
112 | return 0;\r | |
113 | }\r | |
114 | \r | |
115 | /* Forward */\r | |
116 | static void freechildren(node *);\r | |
117 | \r | |
118 | \r | |
119 | void\r | |
120 | PyNode_Free(node *n)\r | |
121 | {\r | |
122 | if (n != NULL) {\r | |
123 | freechildren(n);\r | |
124 | PyObject_FREE(n);\r | |
125 | }\r | |
126 | }\r | |
127 | \r | |
128 | static void\r | |
129 | freechildren(node *n)\r | |
130 | {\r | |
131 | int i;\r | |
132 | for (i = NCH(n); --i >= 0; )\r | |
133 | freechildren(CHILD(n, i));\r | |
134 | if (n->n_child != NULL)\r | |
135 | PyObject_FREE(n->n_child);\r | |
136 | if (STR(n) != NULL)\r | |
137 | PyObject_FREE(STR(n));\r | |
138 | }\r |