内容简介:grape代码:我们来gdb一下整个过程,首先,在zend_compile_top_stmt入口处打断点:
关于$a=1整个过程的gdb过程与相关验证
grape
代码:
<?php
$a =1;
基本流程:
- zend_compile_top_stmt(CG(ast)) - zend_compile_stmt(ast) - zend_compile_expr(&result, ast); - zend_compile_assign(result, ast); - zend_delayed_compile_begin(); - zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W); - zend_compile_expr(&expr_node, expr_ast); - zend_delayed_compile_end(offset); - zend_emit_op(result, ZEND_ASSIGN, &var_node, &expr_node);
GDB过程
我们来gdb一下整个过程,首先,在zend_compile_top_stmt入口处打断点:
gdb下来我们进入
在zend_compile_stmt我们进入default的zend_compile_expr函数:
因为我们是赋值运算,我们此时走到了assign函数,接下来就是整个编译过程中的重点部分。
打印var_ast->kind
可以看出我们接下来要走的就是上图的几个函数,那么,这几个函数又是怎么走的呢?我们接着看,首先,进入zend_delayed_compile_begin():
这个函数的作用是取栈顶,然后在函数结束后赋值给offest,那我们看看这个offest是什么?
$a的处理
接下来进入$a的处理函数:
在这里记录下我们处理$a过程中调用的函数:
zend_delayed_compile_var (result=0x7fffffffa580, ast=0x7ffff5e7f060, type=1) zend_compile_simple_var (result=0x7fffffffa580, ast=0x7ffff5e7f060, type=1, delayed=1) zend_try_compile_cv (result=0x7fffffffa580, ast=0x7ffff5e7f060) lookup_cv (op_array=0x7ffff5e75460, name=0x7ffff5e5e4e0)
lookup_cv函数的作用是什么呢?首先我们先看lookcv的源代码:
static int lookup_cv(zend_op_array *op_array, zend_string* name) /* {{{ */{
int i = 0;
zend_ulong hash_value = zend_string_hash_val(name);
while (i < op_array->last_var) {
if (ZSTR_VAL(op_array->vars[i]) == ZSTR_VAL(name) ||
(ZSTR_H(op_array->vars[i]) == hash_value &&
ZSTR_LEN(op_array->vars[i]) == ZSTR_LEN(name) &&
memcmp(ZSTR_VAL(op_array->vars[i]), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)) {
zend_string_release(name);
return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
}
i++;
}
i = op_array->last_var;
op_array->last_var++;
if (op_array->last_var > CG(context).vars_size) {
CG(context).vars_size += 16; /* FIXME */
op_array->vars = erealloc(op_array->vars, CG(context).vars_size * sizeof(zend_string*));
}
op_array->vars[i] = zend_new_interned_string(name);
return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
}
我们发现,lookup_cv()函数它返回一个int类型的地址,是sizeof(zval)的整数倍,通过它可以得到每个变量的偏移量(80(后面会讲) + 16 * i),i是变量的编号。这样就规定了运行时在栈上相对于zend_execute_data的偏移量,从而在栈上方便地存储了$a这个变量(下一篇笔记会详细讲)。而$a在zend_op_array的vars数组上也冗余存了一份,这样如果后面又用到了$a的话,直接去zend_op_array的vars数组中查找找,如果存在,那么直接使用之前的编号i,如果不存在则按序分配一个编号,然后再插入zend_op_array的vars数组,节省了分配编号的时间。
另外,在zend_try_compile_cv这个函数中对于result进行赋值,那么我们打印下result的值:
我们发现,op_type=16(IS_CV),u.op.var=80
到此我们总结一下$a这个过程,核心函数lookupcv,在lookupcv中我们将变量存储在op_array->vars中,并且返回一个int型整数,代表着偏移量。随后在zend_try_compile_cv中将op_type和u.op.var赋值给znode *result,具体编译示例图如下图所示:
到此,$a即左子节点就结束了。
1的处理
接下来我们来进行右子树的处理,gdb如图:
右子树的处理比较简单,其调用函数为:
zend_compile_expr ZVAL_COPY(z, v)
重点函数在于ZVAL_COPY这个宏,首先我们看gdb中到达了这个宏
然后我们继续分析这个宏的作用,老规矩,先贴源码:
#define ZVAL_COPY(z, v) \
do { \
zval *_z1 = (z); \
const zval *_z2 = (v); \
zend_refcounted *_gc = Z_COUNTED_P(_z2); \
uint32_t _t = Z_TYPE_INFO_P(_z2); \
ZVAL_COPY_VALUE_EX(_z1, _z2, _gc, _t); \
if ((_t & (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) != 0) { \
GC_REFCOUNT(_gc)++; \
} \
} while (0)
它的功能是把一个zval(v)拷贝到另外一个zval(z)中,具体的一些分析请查看上一篇文章: https://segmentfault.com/a/11...
在进行复制完之后我们对于result进行打印
我们可以看到已经完成了赋值。
最后进行了 result->op_type = IS_CONST,op_type的赋值
重新打印result即最终的结果:
至此,$a和1都分别存在了两个znode中。下边开始生成指令。
我们进入到zend_emit_op函数中:
首先我们看这个函数所执行的所有指令:
其中,set_node出现的频次很高,我们来看一下它究竟有什么用:
#define SET_NODE(target, src) do { \
target ## _type = (src)->op_type; \
if ((src)->op_type == IS_CONST) { \
target.constant = zend_add_literal(CG(active_op_array), &(src)->u.constant); \
} else { \
target = (src)->u.op; \
} \
} while (0)
从代码中可以看出,对于操作数1,会将编译过程中临时的结构znode传递给zend_op中,对于操作数2,因为是常量(IS_CONST),会调用zend_add_literal将其插入到op_array->literals中。
接下来我们进行返回值的设置,此时会调用zend_make_var_result这个函数:
static inline void zend_make_var_result(znode *result, zend_op *opline) /* {{{ */
{
opline->result_type = IS_VAR; //返回值的类型设置为IS_VAR
opline->result.var = get_temporary_variable(CG(active_op_array)); //这个是返回值的编号,对应T位置
GET_NODE(result, opline->result);
}
static uint32_t get_temporary_variable(zend_op_array *op_array) /* {{{ */
{
return (uint32_t)op_array->T++;
}
返回值的类型为IS_VAR,result.var为T的值
最后打印opline看一下最终的结果:
,下面我们给出Assign操作对应的指令图,如图所示:
从图中可以看出,生成的opline中opcode等于38;op1的类型为IS_CV,op1.var对应的是vm_stack上的偏移量;op2的类型为IS_CONST,op2.constant对应的是op_array中literals数组的下标;result的类型为IS_VAR,result.var对应的是T的值;此时handler的值为空。
到此,编译阶段告一段落。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Big Java Late Objects
Horstmann, Cay S. / 2012-2 / 896.00元
The introductory programming course is difficult. Many students fail to succeed or have trouble in the course because they don't understand the material and do not practice programming sufficiently. ......一起来看看 《Big Java Late Objects》 这本书的介绍吧!