摘要:前言本文通過分析這個語句的編譯和執行來窺探解釋執行邏輯準備參考之前的系列文章,在環境下下載,編譯源代碼將代碼導入中編輯運行選項,增加運行參數設置斷點開始調試是一個測試腳本,放在目錄下,中只包含一條簡單的賦值語句調用堆棧參考之前的系列文章
前言
本文通過分析 "$a=1" 這個 PHP 語句的編譯和執行來窺探 php-cli 解釋執行邏輯
準備參考之前的系列文章,在 ubuntu 環境下下載,編譯 PHP 源代碼
將代碼導入 idea clion IDE 中
編輯運行選項,增加運行參數:-f test.php
設置斷點開始調試
test.php 是一個測試腳本,放在 sapi/cli/ 目錄下,test.php 中只包含一條簡單的賦值語句:
調用堆棧
參考之前的系列文章來了解 php-cli 啟動過程以及語法分析和字節碼生成的基本概念,這里直接給出調用堆棧:
我們嘗試從 zend_compile_expr 函數說起
zend_compile_expr賦值語句 is-a 表達式,zend_compile_expr 函數根據 ast 類型選擇調用 zend_compile_assign:
// zend_compile.c void zend_compile_expr(znode *result, zend_ast *ast) { ... switch (ast->kind) { ... case ZEND_AST_ASSIGN: zend_compile_assign(result, ast); break; } }zend_compile_assign
賦值語句的 ast 包含兩個 child ast,即 left hand side var(ast->child[0]) 和 right hand side expr(ast->child[1]),var_node 和 expr_node 兩個 znode 類型的變量是生成字節碼過程使用的中間變量
// zend_compile.c void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *var_ast = ast->child[0]; zend_ast *expr_ast = ast->child[1]; znode var_node, expr_node; zend_op *opline; uint32_t offset; if (is_this_fetch(var_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); } zend_ensure_writable_variable(var_ast);
然后我們來看看 switch case 語句
// zend_compile.c switch (var_ast->kind) { case ZEND_AST_VAR: case ZEND_AST_STATIC_PROP: offset = 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); return; }
剛看到這段代碼可能會覺得挺繞的:zend_delayed_xxx 函數是干啥的?最終生成的字節碼又保存在哪呢?
zend_emit_opemit 有 "發射,散播"的意思,所以 zend_emit_op 可能和字節碼保存相關:
// zend_compile.c 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; }
這里我們又遇到了全局變量 CG(compile globals),zend_emit_op 先調用 get_next_op 獲取可用的 zend_op(虛擬機指令),然后設置 op1, op2 為 opline 的兩個操作數
現在我們知道生成的字節碼保存在 CG 的 active_op_array 數組里
總結文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/22318.html
摘要:前言字節碼生成編譯的代碼主要集中在,文件中包含大量的函數,基本上一個函數對應語法規則文件一個非終結符,函數是所有函數的入口數據結構結構體是字節碼抽象結構體并沒有像名字那樣簡單,它包含了大量的字段供虛擬機在運行時使用一如既往的簡單,直觀,相比 前言 字節碼生成(編譯)的代碼主要集中在 zend_compile.c ,文件中包含大量的 zend_compile_xxx 函數,基本上一個函數...
摘要:前言本文簡要介紹虛擬機解釋執行字節碼的基本邏輯以及相關的數據結構,關于源代碼的下載,編譯,調試可以參考之前的系列文章我們來看看執行一個簡單的腳本的調用棧由于是執行腳本文件,所以調用了函數,最終調用函數和其它語言編寫的系統軟件類似,函數中 前言 本文簡要介紹 zend 虛擬機解釋執行字節碼的基本邏輯以及相關的數據結構,關于 PHP 源代碼的下載,編譯,調試可以參考之前的系列文章 exec...
摘要:在中央處理器的控制部件中,包含的寄存器有指令寄存器和程序計數器。這條指令的第一個操作數,代表累加寄存器在中央處理器中,累加器是一種寄存器,用來儲存計算產生的中間結果。第二個操作數則是進制的的表示。 showImg(https://ask.qcloudimg.com/http-save/1752328/57mlmnq3i5.png); CPU執行的也不只是一條指令,一般一個程序包含很多條...
摘要:但是有一個的指令,可以把字節碼翻譯成人類能看懂的東西。是文件分解器,可以反編譯即對編譯的文件進行反編譯,也可以查看編譯器生成的字節碼。現在有一個類,定義入下先用編譯成字節碼,再使用進行反編譯。 概要 Java工程師面試官偏愛的問題之一,就是abc和 new String(abc)的區別是什么?回答的比較好的會帶出Java堆,棧,常量池,引用等概念。但今天不止如此,我們從指令的角度,去看...
摘要:前言本文從函數定義的語法規則開始,簡要介紹解釋器如何編譯函數定義函數對應的節點為了看起來清楚一些,我們將語法規則定義與語法動作分開根據語法動作,這條函數定義規則會創建一個類型的結點,我們來看看方法是一個通用的方法,通 前言 本文從函數定義的語法規則開始,簡要介紹 PHP 解釋器如何 編譯 函數定義 函數對應的 AST 節點 為了看起來清楚一些,我們將 語法規則定義 與 語法動作分開: ...
閱讀 3712·2023-04-25 17:45
閱讀 3426·2021-09-04 16:40
閱讀 999·2019-08-30 13:54
閱讀 2126·2019-08-29 12:59
閱讀 1395·2019-08-26 12:11
閱讀 3273·2019-08-23 15:17
閱讀 1516·2019-08-23 12:07
閱讀 3878·2019-08-22 18:00