njs_generate_try_finally(njs_vm_t *vm, njs_generator_t *generator,
    njs_parser_node_t *node)
{
    njs_index_t                  exit_index;
    njs_jump_off_t               catch_end_offset;
    njs_vmcode_catch_t           *catch;
    njs_vmcode_try_end_t         *catch_end;
    njs_generator_block_t        *try_block, *catch_block;
    njs_generator_try_ctx_t      *ctx;
    njs_vmcode_try_trampoline_t  *try_break, *try_continue;

    ctx = generator->context;

    try_block = ctx->try_block;
    exit_index = try_block->index;
    catch_block = ctx->catch_block;

    njs_generate_code(generator, njs_vmcode_try_end_t, catch_end,
                      NJS_VMCODE_TRY_END, 0, node->left->right);
    catch_end_offset = njs_code_offset(generator, catch_end);

    if (catch_block->exit != NULL) {
        ctx->catch_exit_label = catch_block->exit->label;

        njs_generate_patch_block(vm, generator, catch_block,
                                 NJS_GENERATOR_EXIT);

        njs_generate_code(generator, njs_vmcode_try_trampoline_t,
                          try_break, NJS_VMCODE_TRY_BREAK, 1, NULL);

        try_break->exit_value = exit_index;

        try_break->offset = -sizeof(njs_vmcode_try_end_t);

    } else {
        try_break = NULL;
    }

    if (catch_block->continuation != NULL) {
        ctx->catch_cont_label = catch_block->continuation->label;

        njs_generate_patch_block(vm, generator, catch_block,
                                 NJS_GENERATOR_CONTINUATION);

        njs_generate_code(generator, njs_vmcode_try_trampoline_t,
                          try_continue, NJS_VMCODE_TRY_CONTINUE, 1,
                          NULL);

        try_continue->exit_value = exit_index;

        try_continue->offset = -sizeof(njs_vmcode_try_end_t);

        if (try_break != NULL) {
            try_continue->offset -= sizeof(njs_vmcode_try_trampoline_t);
        }
    }

    njs_debug_generator(vm, "EXIT  %s %p",
                        njs_block_type(generator->block->type),
                        generator->block);

    generator->block = catch_block->next;

    njs_code_set_jump_offset(generator, njs_vmcode_catch_t,
                             ctx->catch_offset);

    /* TODO: release exception variable index. */

    njs_generate_code_catch(generator, catch, ctx->exception_index, NULL);

    njs_code_set_jump_offset(generator, njs_vmcode_try_end_t,
                             catch_end_offset);

    njs_code_set_jump_offset(generator, njs_vmcode_try_end_t,
                             ctx->try_offset);

    njs_generator_next(generator, njs_generate, node->right);

    return njs_generator_after(vm, generator,
                               njs_queue_first(&generator->stack), node,
                               njs_generate_try_end, ctx, 0);
}
njs_generate_try_finally(njs_vm_t *vm, njs_generator_t *generator,
    njs_parser_node_t *node)
{
    njs_index_t                  exit_index;
    njs_jump_off_t               catch_end_offset;
    njs_vmcode_catch_t           *catch;
    njs_vmcode_try_end_t         *catch_end;
    njs_generator_block_t        *try_block, *catch_block;
    njs_generator_try_ctx_t      *ctx;
    njs_vmcode_try_trampoline_t  *try_break, *try_continue;

    ctx = generator->context;

    try_block = ctx->try_block;
    exit_index = try_block->index;
    catch_block = ctx->catch_block;

    njs_generate_code(generator, njs_vmcode_try_end_t, catch_end,
                      NJS_VMCODE_TRY_END, 0, node->left->right);
    catch_end_offset = njs_code_offset(generator, catch_end);

    if (catch_block->exit != NULL) {
        ctx->catch_exit_label = catch_block->exit->label;

        njs_generate_patch_block(vm, generator, catch_block,
                                 NJS_GENERATOR_EXIT);

        njs_generate_code(generator, njs_vmcode_try_trampoline_t,
                          try_break, NJS_VMCODE_TRY_BREAK, 1, NULL);

        try_break->exit_value = exit_index;

        try_break->offset = -sizeof(njs_vmcode_try_end_t);

    } else {
        try_break = NULL;
    }

    if (catch_block->continuation != NULL) {
        ctx->catch_cont_label = catch_block->continuation->label;

        njs_generate_patch_block(vm, generator, catch_block,
                                 NJS_GENERATOR_CONTINUATION);

        njs_generate_code(generator, njs_vmcode_try_trampoline_t,
                          try_continue, NJS_VMCODE_TRY_CONTINUE, 1,
                          NULL);

        try_continue->exit_value = exit_index;

        try_continue->offset = -sizeof(njs_vmcode_try_end_t);

        if (try_break != NULL) {
            try_continue->offset -= sizeof(njs_vmcode_try_trampoline_t);
        }
    }

    njs_debug_generator(vm, "EXIT  %s %p",
                        njs_block_type(generator->block->type),
                        generator->block);

    generator->block = catch_block->next;

    njs_code_set_jump_offset(generator, njs_vmcode_catch_t,
                             ctx->catch_offset);

     

    njs_generate_code_catch(generator, catch, ctx->exception_index, NULL);

    njs_code_set_jump_offset(generator, njs_vmcode_try_end_t,
                             catch_end_offset);

    njs_code_set_jump_offset(generator, njs_vmcode_try_end_t,
                             ctx->try_offset);

    njs_generator_next(generator, njs_generate, node->right);

    return njs_generator_after(vm, generator,
                               njs_queue_first(&generator->stack), node,
                               njs_generate_try_end, ctx, 0);
}