前言

字节码生成(编译)的代码主要集中在 zend_compile.c ,文件中包含大量的 zend_compile_xxx 函数,基本上一个函数对应 语法规则文件 zend_language_parser.y 一个非终结符,zend_compile_top_stmt 函数是所有 zend_compile_xxx 函数的入口

数据结构

zend_op

zend_op 结构体是 PHP 字节码抽象

// zend_compile.h

struct _zend_op {
    const void *handler;
    znode_op op1;
    znode_op op2;
    znode_op result;
    uint32_t extended_value;
    uint32_t lineno;
    zend_uchar opcode;
    zend_uchar op1_type;
    zend_uchar op2_type;
    zend_uchar result_type;
}

zend_op_array

zend_op_array 结构体并没有像名字那样简单 zend op array,它包含了大量的字段供虚拟机在运行时使用

// zend_compile.h

struct _zend_op_array {
}

zend_compile_top_stmt

zend_compile_top_stmt 一如既往的简单,直观,相比之前看 ruby 源代码而言,感觉 欧美工程师 可能真的比 岛国工程师 牛叉一些

// zend_compile.c

void zend_compile_top_stmt(zend_ast *ast) {
    if (!ast) {
        return;
    }

    if (ast->kind == ZEND_AST_STMT_LIST) {
        zend_ast_list *list = zend_ast_get_list(ast);
        uint32_t i;
        for (i = 0; i < list->children; ++i) {
            zend_compile_top_stmt(list->child[i]);
        }
        return;
    }

    zend_compile_stmt(ast);

    if (ast->kind != ZEND_AST_NAMESPACE && ast->kind != ZEND_AST_HALT_COMPILER) {
        zend_verify_namespace();
    }
    if (ast->kind == ZEND_AST_FUNC_DECL || ast->kind == ZEND_AST_CLASS) {
        CG(zend_lineno) = ((zend_ast_decl *) ast)->end_lineno;
        zend_do_early_binding();
    }
}

ast 是抽象语法树(AST)的根节点(参考 PHP-7.1 源代码学习:语法分析 之 概述),函数首先对 ast 进行参数验证,针对 ZEND_AST_STMT_LIST 节点类型进行递归调用,然后调用 zend_compile_stmt 编译 各个 stmt,这个流程和语法规则也是精确对应的:

start: top_statement_list { CG(ast) = $1 }
top_statement_list:
        top_statement_list top_statement { $$ = zend_ast_list_add($1, $2) }
    |    /* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_STMT_LIST); }
top_statement:
    statement
    ...

zend_compile_stmt

zend_compile_stmt 函数基本上就是根据 zend_ast 类型(kind)进行 switch case

void zend_compile_stmt(zend_ast *ast) {
    if (!ast) {
        return;
    }

    CG(zend_lineno) = ast->lineno;
    ...
    switch (ast->kind) {
        case ZEND_AST_STMT_LIST:
            zend_compile_stmt_list(ast);
            break;
        case ZEND_AST_GLOBAL:
            zend_compile_global_var(ast);
            break;
        ...
        default:
        {
            znode result;
            zend_compile_expr(&result, ast);
            zend_do_free(&result);
        }
    }
    ...
}

总结


xingpingz
122 声望64 粉丝

博学,审问,慎思,明辨,力行