#include <R_ext/Rdynload.h>
#include <rlang.h>
#include "internal.h"

#include "globals.c"

#include "arg.c"
#include "attr.c"
#include "call.c"
#include "cnd.c"
#include "cnd-handlers.c"
#include "dots.c"
#include "dots-ellipsis.c"
#include "encoding.c"
#include "env.c"
#include "env-binding.c"
#include "eval.c"
#include "eval-tidy.c"
#include "exported.c"
#include "nse-inject.c"
#include "ast-rotate.c"
#include "file.c"
#include "fn.c"
#include "hash.c"
#include "names.c"
#include "nse-defuse.c"
#include "parse.c"
#include "quo.c"
#include "replace-na.c"
#include "squash.c"
#include "standalone-types-check.c"
#include "sym-unescape.c"
#include "tests.c"
#include "utils.c"
#include "vec.c"
#include "vec-raw.c"
#include "weakref.c"

struct rlang_globals_syms rlang_syms;

r_obj* rlang_zap = NULL;
r_obj* rlang_as_list_call = NULL;
r_obj* rlang_objs_keep = NULL;
r_obj* rlang_objs_trailing = NULL;
r_obj* fns_function = NULL;
r_obj* fns_quote = NULL;

void rlang_init_internal(r_obj* ns) {
    rlang_init_globals(ns);

    rlang_init_utils();
    rlang_init_arg(ns);
    rlang_init_attr(ns);
    rlang_init_call(ns);
    rlang_init_cnd(ns);
    rlang_init_cnd_handlers(ns);
    rlang_init_dots(ns);
    rlang_init_env();
    rlang_init_expr_interp();
    rlang_init_eval_tidy();
    rlang_init_fn();
    rlang_init_tests();

    rlang_syms.c_null = r_sym(".__C_NULL__.");
    rlang_syms.handlers = r_sym("handlers");
    rlang_syms.tryCatch = r_sym("tryCatch");
    rlang_syms.withCallingHandlers = r_sym("withCallingHandlers");

    rlang_zap = rlang_ns_get("zap!");

    rlang_as_list_call = r_parse("rlang_as_list(x)");
    r_preserve(rlang_as_list_call);

    rlang_objs_keep = r_chr("keep");
    r_preserve(rlang_objs_keep);

    rlang_objs_trailing = r_chr("trailing");
    r_preserve(rlang_objs_trailing);

    fns_function = r_eval(r_sym("function"), r_envs.base);
    fns_quote = r_eval(r_sym("quote"), r_envs.base);

    /* dots.c - enum dots_op */
    RLANG_ASSERT(DOTS_OP_MAX == DOTS_COLLECT_MAX * INJECTION_OP_MAX);
}

static r_obj* ffi_init_rlang(r_obj* ns) {
    rlang_init_internal(ns);
    return r_null;
}

static r_obj* ffi_fini_rlang(void) {
    return r_null;
}

// From version.c
extern r_obj* rlang_linked_version(void);

static const R_CallMethodDef r_callables[] = {
    {"ffi_alloc_data_frame", (DL_FUNC) &ffi_alloc_data_frame, 3},
    {"ffi_as_data_mask", (DL_FUNC) &ffi_as_data_mask, 1},
    {"ffi_as_data_pronoun", (DL_FUNC) &ffi_as_data_pronoun, 1},
    {"ffi_c_tests", (DL_FUNC) &ffi_c_tests, 0},
    {"ffi_call_has_precedence", (DL_FUNC) &ffi_call_has_precedence, 3},
    {"ffi_call_zap_inline", (DL_FUNC) &ffi_call_zap_inline, 1},
    {"ffi_chr_get", (DL_FUNC) &ffi_chr_get, 2},
    {"ffi_chr_has_curly", (DL_FUNC) &ffi_chr_has_curly, 1},
    {"ffi_cnd_signal", (DL_FUNC) &ffi_cnd_signal, 1},
    {"ffi_cnd_type", (DL_FUNC) &ffi_cnd_type, 1},
    {"ffi_compiled_by_gcc", (DL_FUNC) &ffi_compiled_by_gcc, 0},
    {"ffi_data_mask_clean", (DL_FUNC) &ffi_data_mask_clean, 1},
    {"ffi_data_pronoun_get", (DL_FUNC) &ffi_data_pronoun_get, 3},
    {"ffi_dict_as_df_list", (DL_FUNC) &ffi_dict_as_df_list, 1},
    {"ffi_dict_as_list", (DL_FUNC) &ffi_dict_as_list, 1},
    {"ffi_dict_del", (DL_FUNC) &ffi_dict_del, 2},
    {"ffi_dict_get", (DL_FUNC) &ffi_dict_get, 2},
    {"ffi_dict_has", (DL_FUNC) &ffi_dict_has, 2},
    {"ffi_dict_it_info", (DL_FUNC) &ffi_dict_it_info, 1},
    {"ffi_dict_next", (DL_FUNC) &ffi_dict_it_next, 1},
    {"ffi_dict_poke", (DL_FUNC) &ffi_dict_poke, 3},
    {"ffi_dict_put", (DL_FUNC) &ffi_dict_put, 3},
    {"ffi_dict_resize", (DL_FUNC) &ffi_dict_resize, 2},
    {"ffi_dots_flat_list", (DL_FUNC) &ffi_dots_flat_list, 7},
    {"ffi_dots_list", (DL_FUNC) &ffi_dots_list, 7},
    {"ffi_dots_pairlist", (DL_FUNC) &ffi_dots_pairlist, 7},
    {"ffi_duplicate", (DL_FUNC) &ffi_duplicate, 2},
    {"ffi_dyn_chr_get", (DL_FUNC) &ffi_dyn_chr_get, 2},
    {"ffi_dyn_chr_poke", (DL_FUNC) &ffi_dyn_chr_poke, 3},
    {"ffi_dyn_chr_push_back", (DL_FUNC) &ffi_dyn_chr_push_back, 2},
    {"ffi_dyn_cpl_get", (DL_FUNC) &ffi_dyn_cpl_get, 2},
    {"ffi_dyn_cpl_poke", (DL_FUNC) &ffi_dyn_cpl_poke, 3},
    {"ffi_dyn_cpl_push_back", (DL_FUNC) &ffi_dyn_cpl_push_back, 2},
    {"ffi_dyn_dbl_get", (DL_FUNC) &ffi_dyn_dbl_get, 2},
    {"ffi_dyn_dbl_poke", (DL_FUNC) &ffi_dyn_dbl_poke, 3},
    {"ffi_dyn_dbl_push_back", (DL_FUNC) &ffi_dyn_dbl_push_back, 2},
    {"ffi_dyn_info", (DL_FUNC) &ffi_dyn_info, 1},
    {"ffi_dyn_int_get", (DL_FUNC) &ffi_dyn_int_get, 2},
    {"ffi_dyn_int_poke", (DL_FUNC) &ffi_dyn_int_poke, 3},
    {"ffi_dyn_int_push_back", (DL_FUNC) &ffi_dyn_int_push_back, 2},
    {"ffi_dyn_lgl_get", (DL_FUNC) &ffi_dyn_lgl_get, 2},
    {"ffi_dyn_lgl_poke", (DL_FUNC) &ffi_dyn_lgl_poke, 3},
    {"ffi_dyn_lgl_push_back", (DL_FUNC) &ffi_dyn_lgl_push_back, 2},
    {"ffi_dyn_list_get", (DL_FUNC) &ffi_dyn_list_get, 2},
    {"ffi_dyn_list_poke", (DL_FUNC) &ffi_dyn_list_poke, 3},
    {"ffi_dyn_list_push_back", (DL_FUNC) &ffi_dyn_list_push_back, 2},
    {"ffi_dyn_pop_back", (DL_FUNC) &ffi_dyn_pop_back, 1},
    {"ffi_dyn_push_back", (DL_FUNC) &ffi_dyn_push_back, 2},
    {"ffi_dyn_push_back_bool", (DL_FUNC) &ffi_dyn_push_back_bool, 2},
    {"ffi_dyn_raw_get", (DL_FUNC) &ffi_dyn_raw_get, 2},
    {"ffi_dyn_raw_poke", (DL_FUNC) &ffi_dyn_raw_poke, 3},
    {"ffi_dyn_raw_push_back", (DL_FUNC) &ffi_dyn_raw_push_back, 2},
    {"ffi_dyn_resize", (DL_FUNC) &ffi_dyn_resize, 2},
    {"ffi_dyn_unwrap", (DL_FUNC) &ffi_dyn_unwrap, 1},
    {"ffi_env_dot_delayed_env", (DL_FUNC) &ffi_env_dot_delayed_env, 2},
    {"ffi_env_dot_delayed_expr", (DL_FUNC) &ffi_env_dot_delayed_expr, 2},
    {"ffi_env_dot_forced_expr", (DL_FUNC) &ffi_env_dot_forced_expr, 2},
    {"ffi_env_dot_type", (DL_FUNC) &ffi_env_dot_type, 2},
    {"ffi_env_dot_get", (DL_FUNC) &ffi_env_dot_get, 2},
    {"ffi_env_dots_exist", (DL_FUNC) &ffi_env_dots_exist, 1},
    {"ffi_env_dots_length", (DL_FUNC) &ffi_env_dots_length, 1},
    {"ffi_env_dots_names", (DL_FUNC) &ffi_env_dots_names, 1},
    {"ffi_ellipsis_dots_used", (DL_FUNC) &ffi_ellipsis_dots_used, 1},
    {"ffi_enexpr", (DL_FUNC) &ffi_enexpr, 2},
    {"ffi_enquo", (DL_FUNC) &ffi_enquo, 2},
    {"ffi_ensym", (DL_FUNC) &ffi_ensym, 2},
    {"ffi_env_bind", (DL_FUNC) &ffi_env_bind, 5},
    {"ffi_env_bind_list", (DL_FUNC) &ffi_env_bind_list, 3},
    {"ffi_env_binding_delayed_env", (DL_FUNC) &ffi_env_binding_delayed_env, 2},
    {"ffi_env_binding_delayed_expr",
     (DL_FUNC) &ffi_env_binding_delayed_expr,
     2},
    {"ffi_env_binding_forced_expr", (DL_FUNC) &ffi_env_binding_forced_expr, 2},
    {"ffi_env_binding_type", (DL_FUNC) &ffi_env_binding_type, 2},
    {"ffi_env_binding_types", (DL_FUNC) &r_env_binding_types, 2},
    {"ffi_env_clone", (DL_FUNC) &r_env_clone, 2},
    {"ffi_env_coalesce", (DL_FUNC) &ffi_env_coalesce, 2},
    {"ffi_env_get", (DL_FUNC) &ffi_env_get, 5},
    {"ffi_env_get_list", (DL_FUNC) &ffi_env_get_list, 5},
    {"ffi_env_has", (DL_FUNC) &ffi_env_has, 3},
    {"ffi_env_inherits", (DL_FUNC) &ffi_env_inherits, 2},
    {"ffi_env_poke", (DL_FUNC) &ffi_env_poke, 5},
    {"ffi_env_poke_parent", (DL_FUNC) &ffi_env_poke_parent, 2},
    {"ffi_env_syms", (DL_FUNC) &r_env_syms, 1},
    {"ffi_env_unbind", (DL_FUNC) &ffi_env_unbind, 3},
    {"ffi_eval_top", (DL_FUNC) &ffi_eval_top, 2},
    {"ffi_exprs_interp", (DL_FUNC) &ffi_exprs_interp, 6},
    {"ffi_f_lhs", (DL_FUNC) &r_f_lhs, 1},
    {"ffi_f_rhs", (DL_FUNC) &r_f_rhs, 1},
    {"ffi_fini_rlang", (DL_FUNC) &ffi_fini_rlang, 0},
    {"ffi_format_error_arg", (DL_FUNC) &ffi_format_error_arg, 1},
    {"ffi_get_expression", (DL_FUNC) &ffi_get_expression, 2},
    {"ffi_getppid", (DL_FUNC) &ffi_getppid, 0},
    {"ffi_glue_is_here", (DL_FUNC) &ffi_glue_is_here, 0},
    {"ffi_has_dots_unnamed", (DL_FUNC) &ffi_has_dots_unnamed, 1},
    {"ffi_has_local_precious_list", (DL_FUNC) &ffi_has_local_precious_list, 0},
    {"ffi_has_size_one_bool", (DL_FUNC) &ffi_has_size_one_bool, 0},
    {"ffi_hash", (DL_FUNC) &ffi_hash, 2},
    {"ffi_hash_file", (DL_FUNC) &ffi_hash_file, 1},
    {"ffi_hasher_init", (DL_FUNC) &ffi_hasher_init, 0},
    {"ffi_hasher_update", (DL_FUNC) &ffi_hasher_update, 2},
    {"ffi_hasher_value", (DL_FUNC) &ffi_hasher_value, 1},
    {"ffi_init_r_library", (DL_FUNC) &r_init_library, 1},
    {"ffi_init_rlang", (DL_FUNC) &ffi_init_rlang, 1},
    {"ffi_interp", (DL_FUNC) &ffi_interp, 2},
    {"ffi_interrupt", (DL_FUNC) &ffi_interrupt, 0},
    {"ffi_is_atomic", (DL_FUNC) &ffi_is_atomic, 2},
    {"ffi_is_call", (DL_FUNC) &ffi_is_call, 4},
    {"ffi_is_character", (DL_FUNC) &ffi_is_character, 4},
    {"ffi_is_closure", (DL_FUNC) &ffi_is_closure, 1},
    {"ffi_is_complex", (DL_FUNC) &ffi_is_complex, 3},
    {"ffi_is_data_mask", (DL_FUNC) &ffi_is_data_mask, 1},
    {"ffi_is_double", (DL_FUNC) &ffi_is_double, 3},
    {"ffi_is_finite", (DL_FUNC) &ffi_is_finite, 1},
    {"ffi_is_formula", (DL_FUNC) &ffi_is_formula, 3},
    {"ffi_is_function", (DL_FUNC) &ffi_is_function, 1},
    {"ffi_is_integer", (DL_FUNC) &ffi_is_integer, 2},
    {"ffi_is_integerish", (DL_FUNC) &ffi_is_integerish, 3},
    {"ffi_is_list", (DL_FUNC) &ffi_is_list, 2},
    {"ffi_is_logical", (DL_FUNC) &ffi_is_logical, 2},
    {"ffi_is_primitive", (DL_FUNC) &ffi_is_primitive, 1},
    {"ffi_is_primitive_eager", (DL_FUNC) &ffi_is_primitive_eager, 1},
    {"ffi_is_primitive_lazy", (DL_FUNC) &ffi_is_primitive_lazy, 1},
    {"ffi_is_raw", (DL_FUNC) &ffi_is_raw, 2},
    {"ffi_is_reference", (DL_FUNC) &ffi_is_reference, 2},
    {"ffi_is_splice_box", (DL_FUNC) &ffi_is_splice_box, 1},
    {"ffi_is_string", (DL_FUNC) &ffi_is_string, 3},
    {"ffi_is_vector", (DL_FUNC) &ffi_is_vector, 2},
    {"ffi_is_weakref", (DL_FUNC) &ffi_is_weakref, 1},
    {"ffi_length", (DL_FUNC) &ffi_length, 1},
    {"ffi_list_compact", (DL_FUNC) &r_list_compact, 1},
    {"ffi_list_poke", (DL_FUNC) &ffi_list_poke, 3},
    {"ffi_lof_arr_push_back", (DL_FUNC) &ffi_lof_arr_push_back, 3},
    {"ffi_lof_info", (DL_FUNC) &ffi_lof_info, 1},
    {"ffi_lof_push_back", (DL_FUNC) &ffi_lof_push_back, 1},
    {"ffi_lof_unwrap", (DL_FUNC) &ffi_lof_unwrap, 1},
    {"ffi_missing_arg", (DL_FUNC) &ffi_missing_arg, 0},
    {"ffi_names2", (DL_FUNC) &ffi_names2, 2},
    {"ffi_names_as_unique", (DL_FUNC) &ffi_names_as_unique, 2},
    {"ffi_new_call", (DL_FUNC) &ffi_new_call_node, 2},
    {"ffi_new_condition", (DL_FUNC) &ffi_new_condition, 3},
    {"ffi_new_data_mask", (DL_FUNC) &ffi_new_data_mask, 2},
    {"ffi_new_dict", (DL_FUNC) &ffi_new_dict, 2},
    {"ffi_new_dict_iterator", (DL_FUNC) &ffi_new_dict_iterator, 1},
    {"ffi_new_dyn_array", (DL_FUNC) &ffi_new_dyn_array, 2},
    {"ffi_new_dyn_list_of", (DL_FUNC) &ffi_new_dyn_list_of, 3},
    {"ffi_new_dyn_vector", (DL_FUNC) &ffi_new_dyn_vector, 2},
    {"ffi_new_formula", (DL_FUNC) &r_new_formula, 3},
    {"ffi_new_function", (DL_FUNC) &ffi_new_function, 3},
    {"ffi_new_node", (DL_FUNC) &r_new_node, 2},
    {"ffi_new_quosure", (DL_FUNC) &ffi_new_quosure, 2},
    {"ffi_new_splice_box", (DL_FUNC) &new_splice_box, 1},
    {"ffi_new_weakref", (DL_FUNC) &ffi_new_weakref, 4},
    {"ffi_nms_are_duplicated", (DL_FUNC) &ffi_test_nms_are_duplicated, 2},
    {"ffi_node_caar", (DL_FUNC) &ffi_node_caar, 1},
    {"ffi_node_cadr", (DL_FUNC) &ffi_node_cadr, 1},
    {"ffi_node_car", (DL_FUNC) &ffi_node_car, 1},
    {"ffi_node_cdar", (DL_FUNC) &ffi_node_cdar, 1},
    {"ffi_node_cddr", (DL_FUNC) &ffi_node_cddr, 1},
    {"ffi_node_cdr", (DL_FUNC) &ffi_node_cdr, 1},
    {"ffi_node_poke_caar", (DL_FUNC) &ffi_node_poke_caar, 2},
    {"ffi_node_poke_cadr", (DL_FUNC) &ffi_node_poke_cadr, 2},
    {"ffi_node_poke_car", (DL_FUNC) &ffi_node_poke_car, 2},
    {"ffi_node_poke_cdar", (DL_FUNC) &ffi_node_poke_cdar, 2},
    {"ffi_node_poke_cddr", (DL_FUNC) &ffi_node_poke_cddr, 2},
    {"ffi_node_poke_cdr", (DL_FUNC) &ffi_node_poke_cdr, 2},
    {"ffi_node_poke_tag", (DL_FUNC) &ffi_node_poke_tag, 2},
    {"ffi_node_tag", (DL_FUNC) &ffi_node_tag, 1},
    {"ffi_node_tree_clone", (DL_FUNC) &r_node_tree_clone, 1},
    {"ffi_ns_registry_env", (DL_FUNC) &ffi_ns_registry_env, 0},
    {"ffi_obj_address", (DL_FUNC) &ffi_obj_address, 1},
    {"ffi_pairlist_rev", (DL_FUNC) &r_pairlist_rev, 1},
    {"ffi_precious_dict", (DL_FUNC) &ffi_precious_dict, 0},
    {"ffi_preserve", (DL_FUNC) &ffi_preserve, 1},
    {"ffi_quo_get_env", (DL_FUNC) &ffi_quo_get_env, 1},
    {"ffi_quo_get_expr", (DL_FUNC) &ffi_quo_get_expr, 1},
    {"ffi_quo_is_call", (DL_FUNC) &ffi_quo_is_call, 1},
    {"ffi_quo_is_missing", (DL_FUNC) &ffi_quo_is_missing, 1},
    {"ffi_quo_is_null", (DL_FUNC) &ffi_quo_is_null, 1},
    {"ffi_quo_is_symbol", (DL_FUNC) &ffi_quo_is_symbol, 1},
    {"ffi_quo_is_symbolic", (DL_FUNC) &ffi_quo_is_symbolic, 1},
    {"ffi_quo_set_env", (DL_FUNC) &ffi_quo_set_env, 2},
    {"ffi_quo_set_expr", (DL_FUNC) &ffi_quo_set_expr, 2},
    {"ffi_quos_interp", (DL_FUNC) &ffi_quos_interp, 6},
    {"ffi_r_string", (DL_FUNC) &ffi_r_string, 1},
    {"ffi_raw_deparse_str", (DL_FUNC) &ffi_raw_deparse_str, 3},
    {"ffi_replace_na", (DL_FUNC) &ffi_replace_na, 2},
    {"ffi_run_c_test", (DL_FUNC) &ffi_run_c_test, 1},
    {"ffi_set_names", (DL_FUNC) &ffi_set_names, 4},
#ifdef RLANG_USE_PRIVATE_ACCESSORS
    {"ffi_sexp_iterate", (DL_FUNC) &ffi_sexp_iterate, 2},
#endif
    {"ffi_has_private_accessors", (DL_FUNC) &ffi_has_private_accessors, 0},
    {"ffi_squash", (DL_FUNC) &ffi_squash, 4},
    {"ffi_standalone_check_number_1.0.7",
     (DL_FUNC) &ffi_standalone_check_number,
     7},
    {"ffi_standalone_is_bool_1.0.7", (DL_FUNC) &ffi_standalone_is_bool, 3},
    {"ffi_sym_as_character", (DL_FUNC) &ffi_sym_as_character, 1},
    {"ffi_symbol", (DL_FUNC) &ffi_symbol, 1},
    {"ffi_test_Rf_error", (DL_FUNC) &ffi_test_Rf_error, 1},
    {"ffi_test_Rf_errorcall", (DL_FUNC) &ffi_test_Rf_errorcall, 2},
    {"ffi_test_Rf_warning", (DL_FUNC) &ffi_test_Rf_warning, 1},
    {"ffi_test_Rf_warningcall", (DL_FUNC) &ffi_test_Rf_warningcall, 2},

    {"ffi_test_base_ns_get", (DL_FUNC) &ffi_test_base_ns_get, 1},
    {"ffi_test_r_env_get", (DL_FUNC) &ffi_test_r_env_get, 2},
    {"ffi_test_chr_append", (DL_FUNC) &chr_append, 2},
    {"ffi_test_chr_prepend", (DL_FUNC) &chr_prepend, 2},
    {"ffi_test_current_frame", (DL_FUNC) &r_peek_frame, 0},
    {"ffi_test_lgl_sum", (DL_FUNC) &ffi_test_lgl_sum, 2},
    {"ffi_test_lgl_which", (DL_FUNC) &ffi_test_lgl_which, 2},

    {"ffi_test_obj_encode_utf8", (DL_FUNC) &obj_encode_utf8, 1},
    {"ffi_test_parse", (DL_FUNC) &ffi_test_parse, 1},
    {"ffi_test_parse_eval", (DL_FUNC) &ffi_test_parse_eval, 2},
    {"ffi_test_r_on_exit", (DL_FUNC) &r_on_exit, 2},
    {"ffi_test_r_warn", (DL_FUNC) &ffi_test_r_warn, 1},
    {"ffi_test_stop_internal", (DL_FUNC) &ffi_test_stop_internal, 1},
    {"ffi_test_sys_call", (DL_FUNC) &ffi_test_sys_call, 1},
    {"ffi_test_sys_frame", (DL_FUNC) &ffi_test_sys_frame, 1},
    {"ffi_unescape_character", (DL_FUNC) &ffi_unescape_character, 1},
    {"ffi_unpreserve", (DL_FUNC) &ffi_unpreserve, 1},
    {"ffi_use_local_precious_list", (DL_FUNC) &ffi_use_local_precious_list, 1},
    {"ffi_vec_alloc", (DL_FUNC) &ffi_vec_alloc, 2},
    {"ffi_vec_coerce", (DL_FUNC) &ffi_vec_coerce, 2},
    {"ffi_vec_poke_n", (DL_FUNC) &ffi_vec_poke_n, 5},
    {"ffi_vec_poke_range", (DL_FUNC) &ffi_vec_poke_range, 5},
    {"ffi_vec_resize", (DL_FUNC) &ffi_vec_resize, 2},
    {"ffi_which_operator", (DL_FUNC) &ffi_which_operator, 1},
    {"ffi_wref_key", (DL_FUNC) &ffi_wref_key, 1},
    {"ffi_wref_value", (DL_FUNC) &ffi_wref_value, 1},
    {"ffi_zap_srcref", (DL_FUNC) &zap_srcref, 1},
    {"rlang_linked_version", (DL_FUNC) &rlang_linked_version, 0},
    {NULL, NULL, 0}
};

static const R_ExternalMethodDef externals[] = {
    {"ffi_arg_match0", (DL_FUNC) &ffi_arg_match0, 3},
    {"ffi_call2", (DL_FUNC) &ffi_call2, 2},
    {"ffi_capturearginfo", (DL_FUNC) &ffi_capturearginfo, 2},
    {"ffi_capturedots", (DL_FUNC) &ffi_capturedots, 1},
    {"ffi_dots_values", (DL_FUNC) &ffi_dots_values, 7},
    {"ffi_eval", (DL_FUNC) &ffi_eval, 2},
    {"ffi_eval_tidy", (DL_FUNC) &ffi_eval_tidy, 3},
    {"ffi_exec", (DL_FUNC) &ffi_exec, 2},
    {"ffi_tilde_eval", (DL_FUNC) &ffi_tilde_eval, 3},
    {"ffi_try_fetch", (DL_FUNC) &ffi_try_fetch, 1},
    {"ffi_list2", (DL_FUNC) &ffi_list2, 0},
    {NULL, NULL, 0}
};

const struct r_test tests[] = {
    {"TRUE is TRUE", &test_that_true_is_true},
    {"FALSE is FALSE", &test_that_false_is_false},
    {NULL, NULL}
};

// From xxhash.h
extern uint64_t XXH3_64bits(const void*, size_t);

r_visible void R_init_rlang(DllInfo* dll) {
    R_RegisterCCallable(
        "rlang",
        "rlang_arg_match",
        (DL_FUNC) &arg_match_legacy
    );
    R_RegisterCCallable("rlang", "rlang_arg_match_2", (DL_FUNC) &cci_arg_match);
    R_RegisterCCallable(
        "rlang",
        "rlang_as_data_mask_3.0.0",
        (DL_FUNC) &ffi_as_data_mask
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_as_data_pronoun",
        (DL_FUNC) &ffi_as_data_pronoun
    );
    R_RegisterCCallable("rlang", "rlang_env_unbind", (DL_FUNC) &r_env_unbind);
    R_RegisterCCallable("rlang", "rlang_eval_tidy", (DL_FUNC) &rlang_eval_tidy);
    R_RegisterCCallable(
        "rlang",
        "rlang_format_error_arg",
        (DL_FUNC) &rlang_format_error_arg
    );
    R_RegisterCCallable("rlang", "rlang_is_quosure", (DL_FUNC) &is_quosure);
    R_RegisterCCallable(
        "rlang",
        "rlang_names_as_unique",
        (DL_FUNC) &names_as_unique
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_new_data_mask_3.0.0",
        (DL_FUNC) &ffi_new_data_mask
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_new_quosure",
        (DL_FUNC) &ffi_new_quosure
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_obj_type_friendly_full",
        (DL_FUNC) &rlang_obj_type_friendly_full
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_quo_get_env",
        (DL_FUNC) &ffi_quo_get_env
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_quo_get_expr",
        (DL_FUNC) &ffi_quo_get_expr
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_quo_set_env",
        (DL_FUNC) &ffi_quo_set_env
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_quo_set_expr",
        (DL_FUNC) &ffi_quo_set_expr
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_stop_internal",
        (DL_FUNC) &rlang_stop_internal
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_stop_internal2",
        (DL_FUNC) &rlang_stop_internal2
    );

    r_obj* r_as_function(r_obj * x, const char* arg);
    R_RegisterCCallable("rlang", "rlang_as_function", (DL_FUNC) &r_as_function);

    R_RegisterCCallable("rlang", "rlang_xxh3_64bits", (DL_FUNC) &XXH3_64bits);

    // Maturing
    R_RegisterCCallable(
        "rlang",
        "rlang_is_splice_box",
        (DL_FUNC) &is_splice_box
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_obj_encode_utf8",
        (DL_FUNC) &obj_encode_utf8
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_str_as_symbol",
        (DL_FUNC) &r_str_as_symbol
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_sym_as_character",
        (DL_FUNC) &ffi_sym_as_character
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_sym_as_string",
        (DL_FUNC) &ffi_sym_as_string
    );
    R_RegisterCCallable("rlang", "rlang_unbox", (DL_FUNC) &rlang_unbox);

    // Experimental
    R_RegisterCCallable("rlang", "rlang_squash_if", (DL_FUNC) &r_squash_if);

    // Deprecated
    R_RegisterCCallable(
        "rlang",
        "rlang_env_dots_list",
        (DL_FUNC) &rlang_env_dots_list
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_env_dots_values",
        (DL_FUNC) &rlang_env_dots_values
    );

    // Compatibility
    R_RegisterCCallable(
        "rlang",
        "rlang_as_data_mask",
        (DL_FUNC) &ffi_as_data_mask_compat
    );
    R_RegisterCCallable(
        "rlang",
        "rlang_new_data_mask",
        (DL_FUNC) &ffi_new_data_mask_compat
    );

    // Only for debugging - no stability guaranteed
    R_RegisterCCallable(
        "rlang",
        "rlang_print_backtrace",
        (DL_FUNC) &rlang_print_backtrace
    );
    R_RegisterCCallable("rlang", "rlang_env_print", (DL_FUNC) &rlang_env_print);

    R_registerRoutines(dll, NULL, r_callables, NULL, externals);
    R_useDynamicSymbols(dll, FALSE);
}
