ResultCode insertAstForFunctionPreHook()

in agent/native/ext/AST_instrumentation.cpp [581:668]


ResultCode insertAstForFunctionPreHook( zend_ast_decl* funcAstDecl, ArgCaptureSpecArrayView argCaptureSpecs )
{
    // Before:
    //
    //    function add_filter( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) {
    //        ////////////////////////////
    //        // original function body //
    //        ////////////////////////////
    //    }
    //
    //    ZEND_AST_FUNC_DECL (name: add_filter, line: 7, flags: 0, attr: 0, childCount: 4)
    //        ZEND_AST_PARAM_LIST (line: 7, attr: 0, childCount: 4)
    //        NULL
    //        ZEND_AST_STMT_LIST (line: 7, attr: 0, childCount: 4)                                                                                                             <- original function body
    //        NULL
    //
    // After:
    //
    //    function add_filter( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) { /* fold-into-one-line-begin */
    //        \elastic_apm_ast_instrumentation_pre_hook( /* pre-hook args */ );
    //        { /* fold-into-one-line-end */
    //        ////////////////////////////
    //        // original function body //
    //        ////////////////////////////
    //    } }
    //
    //    ZEND_AST_FUNC_DECL (name: add_filter, line: 24, flags: 0, attr: 0, childCount: 4)
    //        ZEND_AST_PARAM_LIST (line: 24, attr: 0, childCount: 4)
    //        NULL
    //        ZEND_AST_STMT_LIST (line: 24, attr: 0, childCount: 2)                                                                                                                 <- new function body
    //            ZEND_AST_CALL (line: 24, attr: 0, childCount: 2)
    //                ZEND_AST_ZVAL (line: 24, attr: 0) [type: string, value: elastic_apm_ast_instrumentation_pre_hook]
    //                ZEND_AST_ARG_LIST (line: 24, attr: 0, childCount: 3)                                                                                                              <- pre-hook args
    //            ZEND_AST_STMT_LIST (line: 24, attr: 0, childCount: 4)                                                                                                        <- original function body
    //        NULL

    ELASTIC_APM_ASSERT_VALID_PTR( funcAstDecl );

    ResultCode resultCode;
    char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
    TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );
    String dbgCompiledFileName = stringIfNotNullElse( nullableZStringToStringView( CG(compiled_filename) ).begin, "<N/A>" );

    ELASTIC_APM_ASSERT( funcAstDecl->kind == ZEND_AST_FUNC_DECL || funcAstDecl->kind == ZEND_AST_METHOD, "funcAstDecl->kind: %s", streamZendAstKind( funcAstDecl->kind, &txtOutStream ) );
    textOutputStreamRewind( &txtOutStream );

    zend_ast* originalFuncBodyAst = nullptr;
    zend_ast* preHookCallAstArgList = nullptr;

    StringView dbgFuncName;
    if ( ! getAstDeclName( funcAstDecl, /* out */ &dbgFuncName ) )
    {
        ELASTIC_APM_LOG_ERROR( "Failed to get function name - returning failure" );
        ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
    }
    ELASTIC_APM_LOG_DEBUG_FUNCTION_ENTRY_MSG( "dbgFuncName: %s, compiled_filename: %s", dbgFuncName.begin, dbgCompiledFileName );
    debugDumpAstTreeToLog( (zend_ast*) funcAstDecl, logLevel_debug );

    originalFuncBodyAst = funcAstDecl->child[ g_funcDeclBodyChildIndex ];
    if ( originalFuncBodyAst == NULL )
    {
        ELASTIC_APM_LOG_TRACE( "originalFuncBodyAst == NULL" );
        ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
    }
    if ( originalFuncBodyAst->kind != ZEND_AST_STMT_LIST )
    {
        ELASTIC_APM_LOG_TRACE( "Expected originalFuncBodyAst->kind to be ZEND_AST_STMT_LIST but it is %s", streamZendAstKind( originalFuncBodyAst->kind, &txtOutStream ) );
        textOutputStreamRewind( &txtOutStream );
        ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
    }

    ELASTIC_APM_CALL_IF_FAILED_GOTO( createPreHookAstArgListByCaptureSpec( funcAstDecl, argCaptureSpecs, /* out */ &preHookCallAstArgList ) );

    funcAstDecl->child[ g_funcDeclBodyChildIndex ] = createAstListWithTwoChildren(
            ZEND_AST_STMT_LIST
            , createAstStandaloneFqFunctionCall( g_elastic_apm_ast_instrumentation_pre_hook_funcName, preHookCallAstArgList )
            , originalFuncBodyAst
    );

    resultCode = resultSuccess;
    finally:
    ELASTIC_APM_LOG_DEBUG_RESULT_CODE_FUNCTION_EXIT_MSG();
    debugDumpAstTreeToLog( (zend_ast*) funcAstDecl, logLevel_debug );
    return resultCode;

    failure:
    goto finally;
}