1#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
2
3/* ops that use CLs:
4op1:
5ZEND_FETCH_CONSTANT:
6ZEND_INIT_CTOR_CALL:
7ZEND_INIT_STATIC_METHOD_CALL:
8ZEND_INIT_METHOD_CALL:
9ZEND_IMPORT_CLASS:
10ZEND_IMPORT_FUNCTION:
11ZEND_IMPORT_CONST:
12ZEND_ADD_INTERFACE:
13ZEND_VERIFY_ABSTRACT_CLASS:
14ZEND_NEW:
15ZEND_CATCH:
16ZEND_INIT_FCALL_BY_NAME:
17
18op2:
19ZEND_UNSET_VAR:
20ZEND_ISSET_ISEMPTY_VAR:
21ZEND_FETCH_UNSET:
22ZEND_FETCH_IS:
23ZEND_FETCH_R:
24ZEND_FETCH_W:
25ZEND_FETCH_RW:
26ZEND_FETCH_FUNC_ARG:
27ZEND_ADD_INTERFACE:
28ZEND_INSTANCEOF:
29
30extended_value:
31ZEND_DECLARE_INHERITED_CLASS:
32
33ignore result
34INIT_METHOD_CALL:
35*/
36
37#define OP1_CONST_IS_CLASS 1
38#define OP2_CONST_IS_CLASS 2
39#define EXT_CONST_IS_CLASS 4
40#define RESULT_IS_UNUSED   8
41
42static const char op_const_means_class[256]  = {
43    /* 0 */
44    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45    /* 32 */
46    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
47    /* 64 */
48    0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2,
49    /* 96 */
50    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 9, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51    /* 128 */
52    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53    /* 160 */
54    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55    /* 192 */
56    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57    /* 224 */
58    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
59};
60#endif
61
62#define GET_AVAILABLE_T()       \
63    for (i = 0; i < T; i++) {   \
64        if (!taken_T[i]) {      \
65            break;              \
66        }                       \
67    }                           \
68    taken_T[i] = 1;             \
69    if (i > max) {              \
70        max = i;                \
71    }
72
73static void optimize_temporary_variables(zend_op_array *op_array)
74{
75    int T = op_array->T;
76    char *taken_T;          /* T index in use */
77    zend_op **start_of_T;   /* opline where T is first used */
78    char *valid_T;          /* Is the map_T valid */
79    int *map_T;             /* Map's the T to its new index */
80    zend_op *opline, *end;
81    int currT;
82    int i;
83    int max = -1;
84    int var_to_free = -1;
85
86    taken_T = (char *) emalloc(T);
87    start_of_T = (zend_op **) emalloc(T * sizeof(zend_op *));
88    valid_T = (char *) emalloc(T);
89    map_T = (int *) emalloc(T * sizeof(int));
90
91    end = op_array->opcodes;
92    opline = &op_array->opcodes[op_array->last - 1];
93
94    /* Find T definition points */
95    while (opline >= end) {
96#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
97        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) {
98            if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) {
99                start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline;
100            }
101        }
102#else
103        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
104            start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline;
105        }
106#endif
107        opline--;
108    }
109
110    memset(valid_T, 0, T);
111    memset(taken_T, 0, T);
112
113    end = op_array->opcodes;
114    opline = &op_array->opcodes[op_array->last - 1];
115
116    while (opline >= end) {
117        if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))
118#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
119            || ((op_const_means_class[opline->opcode] & OP1_CONST_IS_CLASS) && ZEND_OP1_TYPE(opline) == IS_CONST)
120#endif
121            ) {
122            currT = VAR_NUM(ZEND_OP1(opline).var);
123            if (!valid_T[currT]) {
124                GET_AVAILABLE_T();
125                map_T[currT] = i;
126                valid_T[currT] = 1;
127            }
128            ZEND_OP1(opline).var = NUM_VAR(map_T[currT]);
129        }
130
131        /* Skip OP_DATA */
132        if (opline->opcode == ZEND_OP_DATA &&
133            (opline-1)->opcode == ZEND_ASSIGN_DIM) {
134            opline--;
135            continue;
136        }
137
138        if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))
139#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
140            || ((op_const_means_class[opline->opcode] & OP2_CONST_IS_CLASS) && ZEND_OP2_TYPE(opline) == IS_CONST)
141#endif
142           ) {
143            currT = VAR_NUM(ZEND_OP2(opline).var);
144            if (!valid_T[currT]) {
145                GET_AVAILABLE_T();
146                map_T[currT] = i;
147                valid_T[currT] = 1;
148            }
149            ZEND_OP2(opline).var = NUM_VAR(map_T[currT]);
150        }
151
152#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
153        if ((op_const_means_class[opline->opcode] & EXT_CONST_IS_CLASS)) {
154#else
155        if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
156            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
157#endif
158            currT = VAR_NUM(opline->extended_value);
159            if (!valid_T[currT]) {
160                GET_AVAILABLE_T();
161                map_T[currT] = i;
162                valid_T[currT] = 1;
163            }
164            opline->extended_value = NUM_VAR(map_T[currT]);
165        }
166
167        /* Allocate OP_DATA->op2 after "operands", but before "result" */
168        if (opline->opcode == ZEND_ASSIGN_DIM &&
169            (opline + 1)->opcode == ZEND_OP_DATA &&
170            ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
171            currT = VAR_NUM(ZEND_OP2(opline + 1).var);
172            GET_AVAILABLE_T();
173            map_T[currT] = i;
174            valid_T[currT] = 1;
175            taken_T[i] = 0;
176            ZEND_OP2(opline + 1).var = NUM_VAR(i);
177            var_to_free = i;
178        }
179
180#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
181        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) {
182            if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) {
183#else
184        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
185#endif
186                currT = VAR_NUM(ZEND_RESULT(opline).var);
187                if (valid_T[currT]) {
188                    if (start_of_T[currT] == opline) {
189                        taken_T[map_T[currT]] = 0;
190                    }
191                    ZEND_RESULT(opline).var = NUM_VAR(map_T[currT]);
192                } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
193                    GET_AVAILABLE_T();
194
195                    if (RESULT_UNUSED(opline)) {
196                        taken_T[i] = 0;
197                    } else {
198                        /* Code which gets here is using a wrongly built opcode such as RECV() */
199                        map_T[currT] = i;
200                        valid_T[currT] = 1;
201                    }
202                    ZEND_RESULT(opline).var = NUM_VAR(i);
203                }
204#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
205            }
206#endif
207        }
208
209        if (var_to_free >= 0) {
210            taken_T[var_to_free] = 0;
211            var_to_free = -1;
212        }
213
214        opline--;
215    }
216
217    efree(taken_T);
218    efree(start_of_T);
219    efree(valid_T);
220    efree(map_T);
221    op_array->T = max + 1;
222}
223