source: trunk/utils.c @ 212

Last change on this file since 212 was 212, checked in by moo, 8 years ago

fixed #41, early class binding

File size: 16.1 KB
Line 
1
2#include "xcache.h"
3#include "utils.h"
4#ifdef ZEND_ENGINE_2_1
5#include "zend_vm.h"
6#endif
7#include "opcode_spec.h"
8#undef NDEBUG
9#include "assert.h"
10
11#ifndef ZEND_VM_SET_OPCODE_HANDLER
12#   define ZEND_VM_SET_OPCODE_HANDLER(opline) do { } while (0)
13#endif
14
15#define OP_ZVAL_DTOR(op) do { \
16    (op).u.constant.is_ref = 0; \
17    zval_dtor(&(op).u.constant); \
18} while(0)
19xc_compile_result_t *xc_compile_result_init(xc_compile_result_t *cr, /* {{{ */
20        zend_op_array *op_array,
21        HashTable *function_table,
22        HashTable *class_table)
23{
24    if (cr) {
25        cr->alloc = 0;
26    }
27    else {
28        cr = emalloc(sizeof(xc_compile_result_t));
29        cr->alloc = 1;
30    }
31    cr->op_array       = op_array;
32    cr->function_table = function_table;
33    cr->class_table    = class_table;
34    return cr;
35}
36/* }}} */
37xc_compile_result_t *xc_compile_result_init_cur(xc_compile_result_t *cr, zend_op_array *op_array TSRMLS_DC) /* {{{ */
38{
39    return xc_compile_result_init(cr, op_array, CG(function_table), CG(class_table));
40}
41/* }}} */
42void xc_compile_result_free(xc_compile_result_t *cr) /* {{{ */
43{
44    if (cr->alloc) {
45        efree(cr);
46    }
47}
48/* }}} */
49
50int xc_apply_function(zend_function *zf, apply_func_t applyer TSRMLS_DC) /* {{{ */
51{
52    switch (zf->type) {
53    case ZEND_USER_FUNCTION:
54    case ZEND_EVAL_CODE:
55        return applyer(&zf->op_array TSRMLS_CC);
56        break;
57
58    case ZEND_INTERNAL_FUNCTION:
59    case ZEND_OVERLOADED_FUNCTION:
60        break;
61
62    EMPTY_SWITCH_DEFAULT_CASE();
63    }
64    return 0;
65}
66/* }}} */
67typedef struct {
68    apply_func_t applyer;
69    zend_class_entry *ce;
70} xc_apply_method_info;
71int xc_apply_method(zend_function *zf, xc_apply_method_info *mi TSRMLS_DC) /* {{{ */
72{
73    /* avoid duplicate apply for shadowed method */
74#ifdef ZEND_ENGINE_2
75    if (mi->ce != zf->common.scope) {
76        /* fprintf(stderr, "avoided duplicate %s\n", zf->common.function_name); */
77        return 0;
78    }
79#else
80    char *name = zf->common.function_name;
81    int name_s = strlen(name) + 1;
82    zend_class_entry *ce;
83    zend_function *ptr;
84
85    for (ce = mi->ce->parent; ce; ce = ce->parent) {
86        if (zend_hash_find(&ce->function_table, name, name_s, (void **) &ptr) == SUCCESS) {
87            if (ptr->op_array.refcount == zf->op_array.refcount) {
88                return 0;
89            }
90        }
91    }
92#endif
93    return xc_apply_function(zf, mi->applyer TSRMLS_CC);
94}
95/* }}} */
96#if 0
97int xc_apply_class(zend_class_entry *ce, apply_func_t applyer TSRMLS_DC) /* {{{ */
98{
99    xc_apply_method_info mi;
100
101    mi.applyer = applyer;
102    mi.ce      = ce;
103    zend_hash_apply_with_argument(&(ce->function_table), (apply_func_arg_t) xc_apply_method, &mi TSRMLS_CC);
104    return 0;
105}
106/* }}} */
107#endif
108static int xc_apply_cest(xc_cest_t *cest, apply_func_t applyer TSRMLS_DC) /* {{{ */
109{
110    xc_apply_method_info mi;
111
112    mi.applyer = applyer;
113    mi.ce      = CestToCePtr(*cest);
114    zend_hash_apply_with_argument(&(CestToCePtr(*cest)->function_table), (apply_func_arg_t) xc_apply_method, &mi TSRMLS_CC);
115    return 0;
116}
117/* }}} */
118int xc_apply_op_array(xc_compile_result_t *cr, apply_func_t applyer TSRMLS_DC) /* {{{ */
119{
120    zend_hash_apply_with_argument(cr->function_table, (apply_func_arg_t) xc_apply_function, applyer TSRMLS_CC);
121    zend_hash_apply_with_argument(cr->class_table, (apply_func_arg_t) xc_apply_cest, applyer TSRMLS_CC);
122
123    return applyer(cr->op_array TSRMLS_CC);
124}
125/* }}} */
126
127int xc_undo_pass_two(zend_op_array *op_array TSRMLS_DC) /* {{{ */
128{
129    zend_op *opline, *end;
130
131    if (!op_array->done_pass_two) {
132        return 0;
133    }
134
135    opline = op_array->opcodes;
136    end = opline + op_array->last;
137    while (opline < end) {
138#ifdef ZEND_ENGINE_2_1
139        switch (opline->opcode) {
140            case ZEND_JMP:
141                opline->op1.u.opline_num = opline->op1.u.jmp_addr - op_array->opcodes;
142                assert(opline->op1.u.opline_num < op_array->last);
143                break;
144            case ZEND_JMPZ:
145            case ZEND_JMPNZ:
146            case ZEND_JMPZ_EX:
147            case ZEND_JMPNZ_EX:
148                opline->op2.u.opline_num = opline->op2.u.jmp_addr - op_array->opcodes;
149                assert(opline->op2.u.opline_num < op_array->last);
150                break;
151        }
152#endif
153        opline++;
154    }
155    op_array->done_pass_two = 0;
156
157    return 0;
158}
159/* }}} */
160int xc_redo_pass_two(zend_op_array *op_array TSRMLS_DC) /* {{{ */
161{
162    zend_op *opline, *end;
163
164    if (op_array->done_pass_two) {
165        return 0;
166    }
167
168    /*
169    op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, sizeof(zend_op)*op_array->last);
170    op_array->size = op_array->last;
171    */
172
173    opline = op_array->opcodes;
174    end = opline + op_array->last;
175    while (opline < end) {
176        if (opline->op1.op_type == IS_CONST) {
177            opline->op1.u.constant.is_ref = 1;
178            opline->op1.u.constant.refcount = 2; /* Make sure is_ref won't be reset */
179        }
180        if (opline->op2.op_type == IS_CONST) {
181            opline->op2.u.constant.is_ref = 1;
182            opline->op2.u.constant.refcount = 2;
183        }
184#ifdef ZEND_ENGINE_2_1
185        switch (opline->opcode) {
186            case ZEND_JMP:
187                assert(opline->op1.u.opline_num < op_array->last);
188                opline->op1.u.jmp_addr = op_array->opcodes + opline->op1.u.opline_num;
189                break;
190            case ZEND_JMPZ:
191            case ZEND_JMPNZ:
192            case ZEND_JMPZ_EX:
193            case ZEND_JMPNZ_EX:
194                assert(opline->op2.u.opline_num < op_array->last);
195                opline->op2.u.jmp_addr = op_array->opcodes + opline->op2.u.opline_num;
196                break;
197        }
198        ZEND_VM_SET_OPCODE_HANDLER(opline);
199#endif
200        opline++;
201    }
202
203    op_array->done_pass_two = 1;
204    return 0;
205}
206/* }}} */
207
208#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
209static void xc_fix_opcode_ex_znode(int tofix, xc_op_spec_t spec, znode *znode, int type TSRMLS_DC) /* {{{ */
210{
211#ifdef ZEND_ENGINE_2
212    if ((znode->op_type != IS_UNUSED && (spec == OPSPEC_UCLASS || spec == OPSPEC_CLASS)) ||
213            spec == OPSPEC_FETCH) {
214        if (tofix) {
215            switch (znode->op_type) {
216            case IS_VAR:
217            case IS_TMP_VAR:
218                break;
219
220            default:
221                /* TODO: data lost, find a way to keep it */
222                /* assert(znode->op_type == IS_CONST); */
223                znode->op_type = IS_TMP_VAR;
224            }
225        }
226    }
227    switch (znode->op_type) {
228    case IS_TMP_VAR:
229    case IS_VAR:
230        if (tofix) {
231            znode->u.var /= sizeof(temp_variable);
232        }
233        else {
234            znode->u.var *= sizeof(temp_variable);
235        }
236    }
237#endif
238}
239/* }}} */
240
241static void xc_fix_opcode_ex(zend_op_array *op_array, int tofix TSRMLS_DC) /* {{{ */
242{
243    zend_op *opline;
244    zend_uint i;
245
246    opline = op_array->opcodes;
247    for (i = 0; i < op_array->last; i ++, opline ++) {
248        /* 3rd optimizer may have ... */
249        if (opline->opcode < xc_get_opcode_spec_count()) {
250            const xc_opcode_spec_t *spec;
251            spec = xc_get_opcode_spec(opline->opcode);
252
253            xc_fix_opcode_ex_znode(tofix, spec->op1, &opline->op1, 0 TSRMLS_CC);
254            xc_fix_opcode_ex_znode(tofix, spec->op2, &opline->op2, 1 TSRMLS_CC);
255            xc_fix_opcode_ex_znode(tofix, spec->res, &opline->result, 2 TSRMLS_CC);
256        }
257    }
258}
259/* }}} */
260int xc_fix_opcode(zend_op_array *op_array TSRMLS_DC) /* {{{ */
261{
262    xc_fix_opcode_ex(op_array, 1 TSRMLS_CC);
263    return 0;
264}
265/* }}} */
266int xc_undo_fix_opcode(zend_op_array *op_array TSRMLS_DC) /* {{{ */
267{
268    xc_fix_opcode_ex(op_array, 0 TSRMLS_CC);
269
270    return 0;
271}
272/* }}} */
273#endif
274
275int xc_foreach_early_binding_class(zend_op_array *op_array, void (*callback)(zend_op *opline, int oplineno, void *data TSRMLS_DC), void *data TSRMLS_DC) /* {{{ */
276{
277    zend_op *opline, *begin, *end, *next = NULL;
278    xc_cest_t cest;
279
280    opline = begin = op_array->opcodes;
281    end = opline + op_array->last;
282    while (opline < end) {
283        switch (opline->opcode) {
284            case ZEND_JMP:
285                next = begin + opline->op1.u.opline_num;
286                break;
287
288            case ZEND_JMPZNZ:
289                next = begin + max(opline->op2.u.opline_num, opline->extended_value);
290                break;
291
292            case ZEND_JMPZ:
293            case ZEND_JMPNZ:
294            case ZEND_JMPZ_EX:
295            case ZEND_JMPNZ_EX:
296                next = begin + opline->op2.u.opline_num;
297                break;
298
299            case ZEND_RETURN:
300                opline = end;
301                break;
302
303            case ZEND_DECLARE_INHERITED_CLASS:
304                callback(opline, opline - begin, data TSRMLS_CC);
305                break;
306        }
307
308        if (opline < next) {
309            opline = next;
310        }
311        else {
312            opline ++;
313        }
314    }
315}
316/* }}} */
317int xc_do_early_binding(zend_op_array *op_array, HashTable *class_table, int oplineno TSRMLS_DC) /* {{{ */
318{
319    zend_op *opline, *opcodes;
320
321#ifdef DEBUG
322    fprintf(stderr, "binding %d\n", oplineno);
323#endif
324    assert(oplineno >= 0);
325
326    /* do early binding */
327    opline = &(op_array->opcodes[oplineno]);
328
329    switch (opline->opcode) {
330    case ZEND_DECLARE_INHERITED_CLASS:
331#ifdef ZEND_ENGINE_2
332        {
333            zval *parent_name;
334            zend_class_entry **pce;
335            parent_name = &(opline - 1)->op2.u.constant;
336#ifdef DEBUG
337            fprintf(stderr, "binding with parent %s\n", Z_STRVAL_P(parent_name));
338#endif
339            if (zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &pce TSRMLS_CC) == FAILURE) {
340                return FAILURE;
341            }
342
343            if (do_bind_inherited_class(opline, class_table, *pce, 1 TSRMLS_CC) == NULL) {
344                return FAILURE;
345            }
346        }
347#else
348        if (do_bind_function_or_class(opline, NULL, class_table, 1) == FAILURE) {
349            return FAILURE;
350        }
351#endif
352
353#ifdef ZEND_FETCH_CLASS
354        /* clear unnecessary ZEND_FETCH_CLASS opcode */
355        if (opline > op_array->opcodes
356         && (opline - 1)->opcode == ZEND_FETCH_CLASS) {
357            zend_op *fetch_class_opline = opline - 1;
358
359#ifdef DEBUG
360            fprintf(stderr, "%s %p\n", Z_STRVAL(fetch_class_opline->op2.u.constant), Z_STRVAL(fetch_class_opline->op2.u.constant));
361#endif
362            OP_ZVAL_DTOR(fetch_class_opline->op2);
363            fetch_class_opline->opcode = ZEND_NOP;
364            ZEND_VM_SET_OPCODE_HANDLER(fetch_class_opline);
365            memset(&fetch_class_opline->op1, 0, sizeof(znode));
366            memset(&fetch_class_opline->op2, 0, sizeof(znode));
367            SET_UNUSED(fetch_class_opline->op1);
368            SET_UNUSED(fetch_class_opline->op2);
369            SET_UNUSED(fetch_class_opline->result);
370        }
371#endif
372        break;
373
374    default:
375        return FAILURE;
376    }
377
378    zend_hash_del(class_table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len);
379    OP_ZVAL_DTOR(opline->op1);
380    OP_ZVAL_DTOR(opline->op2);
381    opline->opcode = ZEND_NOP;
382    ZEND_VM_SET_OPCODE_HANDLER(opline);
383    memset(&opline->op1, 0, sizeof(znode));
384    memset(&opline->op2, 0, sizeof(znode));
385    SET_UNUSED(opline->op1);
386    SET_UNUSED(opline->op2);
387    return SUCCESS;
388}
389/* }}} */
390
391#ifdef HAVE_XCACHE_CONSTANT
392void xc_install_constant(char *filename, zend_constant *constant, zend_uchar type, zstr key, uint len TSRMLS_DC) /* {{{ */
393{
394    if (zend_u_hash_add(EG(zend_constants), type, key, len,
395                constant, sizeof(zend_constant),
396                NULL
397                ) == FAILURE) {
398        CG(in_compilation) = 1;
399        CG(compiled_filename) = filename;
400        CG(zend_lineno) = 0;
401#ifdef IS_UNICODE
402        zend_error(E_NOTICE, "Constant %R already defined", type, key);
403#else
404        zend_error(E_NOTICE, "Constant %s already defined", key);
405#endif
406        free(ZSTR_V(constant->name));
407        if (!(constant->flags & CONST_PERSISTENT)) {
408            zval_dtor(&constant->value);
409        }
410    }
411}
412/* }}} */
413#endif
414void xc_install_function(char *filename, zend_function *func, zend_uchar type, zstr key, uint len TSRMLS_DC) /* {{{ */
415{
416    zend_bool istmpkey;
417
418    if (func->type == ZEND_USER_FUNCTION) {
419#ifdef IS_UNICODE
420        istmpkey = (type == IS_STRING && ZSTR_S(key)[0] == 0) || ZSTR_U(key)[0] == 0;
421#else
422        istmpkey = ZSTR_S(key)[0] == 0;
423#endif
424        if (istmpkey) {
425            zend_u_hash_update(CG(function_table), type, key, len,
426                        func, sizeof(zend_op_array),
427                        NULL
428                        );
429        }
430        else if (zend_u_hash_add(CG(function_table), type, key, len,
431                    func, sizeof(zend_op_array),
432                    NULL
433                    ) == FAILURE) {
434            CG(in_compilation) = 1;
435            CG(compiled_filename) = filename;
436            CG(zend_lineno) = ZESW(func->op_array.opcodes[0].lineno, func->op_array.line_start);
437#ifdef IS_UNICODE
438            zend_error(E_ERROR, "Cannot redeclare %R()", type, key);
439#else
440            zend_error(E_ERROR, "Cannot redeclare %s()", key);
441#endif
442        }
443    }
444}
445/* }}} */
446ZESW(xc_cest_t *, void) xc_install_class(char *filename, xc_cest_t *cest, int oplineno, zend_uchar type, zstr key, uint len TSRMLS_DC) /* {{{ */
447{
448    zend_bool istmpkey;
449    zend_class_entry *cep = CestToCePtr(*cest);
450    ZESW(void *stored_ce_ptr, NOTHING);
451
452#ifdef IS_UNICODE
453    istmpkey = (type == IS_STRING && ZSTR_S(key)[0] == 0) || ZSTR_U(key)[0] == 0;
454#else
455    istmpkey = ZSTR_S(key)[0] == 0;
456#endif
457    if (istmpkey) {
458        zend_u_hash_update(CG(class_table), type, key, len,
459                    cest, sizeof(xc_cest_t),
460                    ZESW(&stored_ce_ptr, NULL)
461                    );
462        if (oplineno != -1) {
463            xc_do_early_binding(CG(active_op_array), CG(class_table), oplineno TSRMLS_CC);
464        }
465    }
466    else if (zend_u_hash_add(CG(class_table), type, key, len,
467                cest, sizeof(xc_cest_t),
468                ZESW(&stored_ce_ptr, NULL)
469                ) == FAILURE) {
470        CG(in_compilation) = 1;
471        CG(compiled_filename) = filename;
472        CG(zend_lineno) = ZESW(0, cep->line_start);
473#ifdef IS_UNICODE
474        zend_error(E_ERROR, "Cannot redeclare class %R", type, cep->name);
475#else
476        zend_error(E_ERROR, "Cannot redeclare class %s", cep->name);
477#endif
478        assert(oplineno == -1);
479    }
480    ZESW(return (xc_cest_t *) stored_ce_ptr, NOTHING);
481}
482/* }}} */
483
484/* sandbox {{{ */
485#undef TG
486#undef OG
487#define TG(x) (sandbox->tmp_##x)
488#define OG(x) (sandbox->orig_##x)
489/* }}} */
490xc_sandbox_t *xc_sandbox_init(xc_sandbox_t *sandbox, char *filename TSRMLS_DC) /* {{{ */
491{
492    if (sandbox) {
493        memset(sandbox, 0, sizeof(sandbox[0]));
494    }
495    else {
496        ECALLOC_ONE(sandbox);
497        sandbox->alloc = 1;
498    }
499
500    memcpy(&OG(included_files), &EG(included_files), sizeof(EG(included_files)));
501
502#ifdef HAVE_XCACHE_CONSTANT
503    OG(zend_constants) = EG(zend_constants);
504    EG(zend_constants) = &TG(zend_constants);
505#endif
506
507    OG(function_table) = CG(function_table);
508    CG(function_table) = &TG(function_table);
509
510    assert(EG(class_table) == CG(class_table));
511
512    OG(class_table) = CG(class_table);
513    CG(class_table) = &TG(class_table);
514    EG(class_table) = CG(class_table);
515
516    TG(included_files) = &EG(included_files);
517
518    zend_hash_init_ex(TG(included_files), 5, NULL, NULL, 0, 1);
519#ifdef HAVE_XCACHE_CONSTANT
520    zend_hash_init_ex(&TG(zend_constants), 20, NULL, OG(zend_constants)->pDestructor, 1, 0);
521#endif
522    zend_hash_init_ex(&TG(function_table), 128, NULL, OG(function_table)->pDestructor, 0, 0);
523    zend_hash_init_ex(&TG(class_table), 16, NULL, OG(class_table)->pDestructor, 0, 0);
524
525    sandbox->filename = filename;
526
527#ifdef E_STRICT
528    sandbox->orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
529    EG(user_error_handler_error_reporting) &= ~E_STRICT;
530#endif
531
532    return sandbox;
533}
534/* }}} */
535static void xc_early_binding_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
536{
537    xc_sandbox_t *sandbox = (xc_sandbox_t *) data;
538    xc_do_early_binding(CG(active_op_array), OG(class_table), oplineno TSRMLS_CC);
539}
540/* }}} */
541static void xc_sandbox_install(xc_sandbox_t *sandbox TSRMLS_DC) /* {{{ */
542{
543    int i;
544    Bucket *b;
545
546#ifdef HAVE_XCACHE_CONSTANT
547    b = TG(zend_constants).pListHead;
548    /* install constants */
549    while (b != NULL) {
550        zend_constant *c = (zend_constant*) b->pData;
551        xc_install_constant(sandbox->filename, c,
552                BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength TSRMLS_CC);
553        b = b->pListNext;
554    }
555#endif
556
557    b = TG(function_table).pListHead;
558    /* install function */
559    while (b != NULL) {
560        zend_function *func = (zend_function*) b->pData;
561        xc_install_function(sandbox->filename, func,
562                BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength TSRMLS_CC);
563        b = b->pListNext;
564    }
565
566    b = TG(class_table).pListHead;
567    /* install class */
568    while (b != NULL) {
569        xc_install_class(sandbox->filename, (xc_cest_t*) b->pData, -1,
570                BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength TSRMLS_CC);
571        b = b->pListNext;
572    }
573    xc_undo_pass_two(CG(active_op_array) TSRMLS_CC);
574    xc_foreach_early_binding_class(CG(active_op_array), xc_early_binding_cb, (void *) sandbox TSRMLS_CC);
575    xc_redo_pass_two(CG(active_op_array) TSRMLS_CC);
576
577    i = 1;
578    zend_hash_add(&OG(included_files), sandbox->filename, strlen(sandbox->filename) + 1, (void *)&i, sizeof(int), NULL);
579}
580/* }}} */
581void xc_sandbox_free(xc_sandbox_t *sandbox, int install TSRMLS_DC) /* {{{ */
582{
583    /* restore first first install function/class */
584#ifdef HAVE_XCACHE_CONSTANT
585    EG(zend_constants) = OG(zend_constants);
586#endif
587    CG(function_table) = OG(function_table);
588    CG(class_table)    = OG(class_table);
589    EG(class_table)    = CG(class_table);
590
591    if (install) {
592        xc_sandbox_install(sandbox TSRMLS_CC);
593
594        /* no free as it's installed */
595#ifdef HAVE_XCACHE_CONSTANT
596        TG(zend_constants).pDestructor = NULL;
597#endif
598        TG(function_table).pDestructor = NULL;
599        TG(class_table).pDestructor = NULL;
600    }
601
602    /* destroy all the tmp */
603#ifdef HAVE_XCACHE_CONSTANT
604    zend_hash_destroy(&TG(zend_constants));
605#endif
606    zend_hash_destroy(&TG(function_table));
607    zend_hash_destroy(&TG(class_table));
608    zend_hash_destroy(TG(included_files));
609
610    /* restore orig here, as EG/CG holded tmp before */
611    memcpy(&EG(included_files), &OG(included_files), sizeof(EG(included_files)));
612
613#ifdef E_STRICT
614    EG(user_error_handler_error_reporting) = sandbox->orig_user_error_handler_error_reporting;
615#endif
616
617    if (sandbox->alloc) {
618        efree(sandbox);
619    }
620}
621/* }}} */
Note: See TracBrowser for help on using the repository browser.