成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【PHP源碼學(xué)習(xí)】2019-03-27 pass_two函數(shù)詳解筆記

PumpkinDylan / 2971人閱讀

摘要:結(jié)果和我們?cè)O(shè)想的一致。另外一個(gè)非常重要的工作是通過(guò),設(shè)置對(duì)應(yīng)的,代碼如下其中和之前的對(duì)應(yīng)關(guān)系在中定義的。至此,整個(gè)抽象語(yǔ)法樹就編譯完成了,最終的結(jié)果為指令集,接下來(lái)就是在虛擬機(jī)上執(zhí)行這些指令。參考資料源碼分析源碼研究之淺談虛擬機(jī)

grape

全部視頻:https://segmentfault.com/a/11...

原視頻地址:http://replay.xesv5.com/ll/24...

流程回顧

上節(jié)課我們把$a=1這個(gè)過(guò)程編譯梳理了一遍,我們了解到op1,op2,result,opcode的生成過(guò)程,下面我們把整個(gè)過(guò)程來(lái)回顧一下。

static zend_op_array *zend_compile(int type)
{
    zend_op_array *op_array = NULL;
    zend_bool original_in_compilation = CG(in_compilation);

    CG(in_compilation) = 1;
    CG(ast) = NULL;
    CG(ast_arena) = zend_arena_create(1024 * 32); //首先會(huì)分配內(nèi)存

    if (!zendparse()) {  //zendparse(就是yyparse)(zend_language_parse.y) ==> 通過(guò)parser調(diào)用lexer,生成抽象語(yǔ)法樹ast_list,存到CG(ast);yyparse是通過(guò)bison編譯zend_language_parser.y生成
        int last_lineno = CG(zend_lineno);
        zend_file_context original_file_context;
        zend_oparray_context original_oparray_context;
        zend_op_array *original_active_op_array = CG(active_op_array);

        op_array = emalloc(sizeof(zend_op_array));
        init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); //初始化oparray
        CG(active_op_array) = op_array;

        if (zend_ast_process) {
            zend_ast_process(CG(ast));
        }

        zend_file_context_begin(&original_file_context);  
        zend_oparray_context_begin(&original_oparray_context);
        zend_compile_top_stmt(CG(ast));  //編譯ast生成oparray
        CG(zend_lineno) = last_lineno;
        zend_emit_final_return(type == ZEND_USER_FUNCTION); //PHP中會(huì)加return 1,在此進(jìn)行處理
        op_array->line_start = 1;
        op_array->line_end = last_lineno;
        pass_two(op_array);  //對(duì)于handler的處理
        zend_oparray_context_end(&original_oparray_context);
        zend_file_context_end(&original_file_context);

        CG(active_op_array) = original_active_op_array;
    }

    zend_ast_destroy(CG(ast));
    zend_arena_destroy(CG(ast_arena));

    CG(in_compilation) = original_in_compilation;

    return op_array;
}

大體流程為:詞法分析->語(yǔ)法分析->編譯ast生成op_array->處理return 1->對(duì)于handler做處理
以上處理return 1 環(huán)節(jié)之前的文章中我們都已經(jīng)提到過(guò),如果有不太理解的請(qǐng)翻閱之前的文章。接下來(lái)我們gdb程序到環(huán)節(jié)return 1。代碼:


我們來(lái)看一看到編譯ast生成op_array處的結(jié)果:


我們來(lái)看這個(gè)結(jié)果,vars是存我們的變量的,在這存的是a和b,并且last_Var=2只有兩個(gè);T是temporary,T=2說(shuō)明有兩個(gè)臨時(shí)變量。然后literals是存我們的字面量,再這里存的是2,3,last_literal=2表示現(xiàn)在有兩個(gè)字面量,接下來(lái)我們打印一下看是否和我們所解釋的一致。

結(jié)果和我們?cè)O(shè)想的一致。另外,對(duì)于opcode的值又是如何呢?


我們發(fā)現(xiàn),$a=2 op1是80,$b=3 op1為96,這是為什么呢?這之前我們說(shuō)過(guò)這個(gè)問題,因?yàn)樵跅V形覀兪欠峙湟粋€(gè)大小為16的內(nèi)存,所以需要增加16.第二個(gè),我們知道result.constant的0和1代表字面量偏移量分別為0和1.
到這里都是之前學(xué)習(xí)過(guò)的內(nèi)容,接下來(lái)繼續(xù)學(xué)習(xí)。

return 1的做了什么?

繼續(xù)執(zhí)行代碼:

我們發(fā)現(xiàn)在執(zhí)行完zend_emit_final_return這句之后我們的op_array發(fā)生了變化。那么為什么會(huì)發(fā)生這樣的變化呢?我們?cè)谖恼麻_頭有些到這個(gè)函數(shù)的作用是增加return 1結(jié)尾,那么具體其中是怎么來(lái)操作呢?我們來(lái)看代碼:

void zend_emit_final_return(int return_one) /* {{{ */
{
    znode zn;
    zend_op *ret;
    zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;

    if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE
            && !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR)) {
        zend_emit_return_type_check(NULL, CG(active_op_array)->arg_info - 1, 1);
    }

    zn.op_type = IS_CONST;
    if (return_one) {
        ZVAL_LONG(&zn.u.constant, 1); //在gdb過(guò)程中會(huì)走到這一步,把1賦值給zn.u.constant
    } else {
        ZVAL_NULL(&zn.u.constant);
    }

    ret = zend_emit_op(NULL, returns_reference ? ZEND_RETURN_BY_REF : ZEND_RETURN, &zn, NULL);//在此會(huì)像字面量中添加一個(gè)新的元素1
    ret->extended_value = -1;
}
static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */
{
    zend_op *opline = get_next_op(CG(active_op_array));
    opline->opcode = opcode;

    if (op1 == NULL) {
        SET_UNUSED(opline->op1);
    } else {
        SET_NODE(opline->op1, op1);
    }

    if (op2 == NULL) {
        SET_UNUSED(opline->op2);
    } else {
        SET_NODE(opline->op2, op2);
    }

    zend_check_live_ranges(opline);

    if (result) {
        zend_make_var_result(result, opline);
    }
    return opline;
}
#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)

我們發(fā)現(xiàn),gdb過(guò)程在這個(gè)函數(shù)中像literals里邊又新增1個(gè)元素,我們打印opcodes:


我們發(fā)現(xiàn),新增了一條指令,在代碼中就是return 1。
好的,到此,我們發(fā)現(xiàn),有三條指令,兩個(gè)變量,三個(gè)字面量。$a和$b的位置已經(jīng)有了,字面量也有了,我們發(fā)現(xiàn)handler還是個(gè)空指針,接下來(lái)我們看handler的生成。

pass_two設(shè)置handler

我們接著走,會(huì)走到pass_two這個(gè)函數(shù),這個(gè)函數(shù)中,對(duì)opline指令集做了進(jìn)一步的加工,最主要的工作是設(shè)置指令的handler,源碼如下:

ZEND_API int pass_two(zend_op_array *op_array)

{

     /**代碼省略**/

   while (opline < end) {//遍歷opline數(shù)組

      if (opline->op1_type == IS_CONST) {

               ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);

      } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {

       opline->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->op1.var);

          }

     

      if (opline->op2_type == IS_CONST) {

          ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);

      } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {

          opline->op2.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->op2.var);

               }

    if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {

       opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->result.var);

     }

     ZEND_VM_SET_OPCODE_HANDLER(opline);

     /**代碼省略**/

}

觀察代碼,該函數(shù)會(huì)對(duì)opline指令數(shù)組進(jìn)行遍歷,他會(huì)處理之前生成的每一條opline,我們拿IS_CONST來(lái)舉例,如果op1,op2的type為IS_CONST,那么將會(huì)調(diào)用ZEND_PASS_TWO_UPDATE_CONSTANT,代碼如下:

/* convert constant from compile-time to run-time */
# define ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, node) do { 
        (node).zv = CT_CONSTANT_EX(op_array, (node).constant); 
    } while (0)
# define CT_CONSTANT_EX(op_array, num) 
    ((op_array)->literals + (num))

我們知道,對(duì)于IS_CONST的變量的字面量是存在與literals里邊的,而constant是相對(duì)的下標(biāo),因此我們可以通過(guò)對(duì)于首地址偏移constant來(lái)進(jìn)行轉(zhuǎn)換為真實(shí)的偏移量。對(duì)于IS_VAR|IS_TMP_VAR類型的變量,會(huì)通過(guò)ZEND_CALL_VAR_NUM計(jì)算偏移量。

另外一個(gè)非常重要的工作是通過(guò)ZEND_VM_SET_OPCODE_HANDLER(opline),設(shè)置opline對(duì)應(yīng)的hanlder,代碼如下:

ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
{
    op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
}
static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op)
{
    return zend_vm_get_opcode_handler_ex(zend_spec_handlers[opcode], op);
}

其中opcode和handler之前的對(duì)應(yīng)關(guān)系在Zend/zend_vm_execute.h中定義的。opline數(shù)組經(jīng)過(guò)一次遍歷后,handler也就設(shè)置完畢,設(shè)置后的opline數(shù)組如圖所示:

結(jié)尾

最后我們打印下生成handler后的op_array:

我們發(fā)現(xiàn),handler已經(jīng)被賦值。
至此,整個(gè)抽象語(yǔ)法樹就編譯完成了,最終的結(jié)果為opline指令集,接下來(lái)就是在Zend虛擬機(jī)上執(zhí)行這些指令。

參考資料:
【PHP7源碼分析】PHP7源碼研究之淺談Zend虛擬機(jī)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31534.html

相關(guān)文章

  • 【LNMPR源碼學(xué)習(xí)筆記匯總

    摘要:此文用于匯總跟隨陳雷老師及團(tuán)隊(duì)的視頻,學(xué)習(xí)源碼過(guò)程中的思考整理與心得體會(huì),此文會(huì)不斷更新視頻傳送門每日學(xué)習(xí)記錄使用錄像設(shè)備記錄每天的學(xué)習(xí)源碼學(xué)習(xí)源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)基本變量筆記 此文用于匯總跟隨陳雷老師及團(tuán)隊(duì)的視頻,學(xué)習(xí)源碼過(guò)程中的思考、整理與心得體會(huì),此文會(huì)不斷更新 視頻傳送門:【每日學(xué)習(xí)記錄】使用錄像設(shè)備記錄每天的學(xué)習(xí) PHP7...

    Barrior 評(píng)論0 收藏0
  • 【每日學(xué)習(xí)記錄】使用錄像設(shè)備記錄每天的學(xué)習(xí)

    摘要:在這里使用學(xué)而思網(wǎng)校的錄像設(shè)備,記錄每天學(xué)習(xí)的內(nèi)容閆昌李樂階段李樂李樂李樂李樂李樂李樂馬運(yùn)運(yùn)李樂李樂李樂源碼集群閆昌源碼閆昌源碼主從復(fù)制李樂源碼施洪寶源碼施洪寶韓天 在這里使用學(xué)而思網(wǎng)校的錄像設(shè)備,記錄每天學(xué)習(xí)的內(nèi)容: 2019-06-24 ~ 2019-06-28 06-27 nginx by 閆昌 06-26 nginx module by 李樂 06-25 nginx http ...

    szysky 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<