blob: 92e1b3a35bf35aa6f9815348f2b4355419605fdc [file] [log] [blame]
# File src/library/tools/R/sotools.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 2011-2020 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
if(.Platform$OS.type == "windows") {
read_symbols_from_dll_state <- new.env(hash = FALSE) # small
read_symbols_from_dll <- function(f, rarch)
{
DLL_nm <- read_symbols_from_dll_state$DLL_nm
if (is.null(DLL_nm)) {
## R CMD config will fail when 'sh' (from Rtools) is not on PATH
DLL_nm <- tryCatch(
Rcmd(c("config", "OBJDUMP"), stdout = TRUE, stderr = FALSE),
error = function(x) NULL,
warning = function(x) NULL)
if (is.null(DLL_nm) || !nzchar(DLL_nm) ||
!file.exists(paste0(DLL_nm, ".exe"))) {
## fall back to the old behavior: take OBJDUMP from PATH
DLL_nm <- "objdump.exe"
if(!nzchar(Sys.which(DLL_nm))) {
warning("this requires 'objdump.exe' to be on the PATH")
return()
}
}
read_symbols_from_dll_state$DLL_nm <- DLL_nm
}
f <- file_path_as_absolute(f)
s0 <- suppressWarnings(system2(DLL_nm, c("-x", shQuote(f)),
stdout = TRUE, stderr = TRUE))
status <- attr(s0, "status")
if (length(status) && status != 0) return()
l1 <- grep("^\tDLL Name:", s0)
l2 <- grep("^The Export Tables", s0)
if (!length(l1) || !length(l2)) return()
s1 <- s0[(l1[1L] + 2L):(l2 - 4L)]
s2 <- grep("\t[0-9a-f]+\t +[0-9]+", s1, value = TRUE)
sub(".* ([_A-Za-z0-9]+)$", "\\1", s2)
}
}
read_symbols_from_object_file <- function(f)
{
## reasonable to assume this on the path
if(!nzchar(nm <- Sys.which("nm"))) {
warning("this requires 'nm' to be on the PATH")
return()
}
f <- file_path_as_absolute(f)
if(!(file.size(f))) return()
s <- strsplit(system(sprintf("%s -Pg %s", shQuote(nm), shQuote(f)),
intern = TRUE),
" +")
## Cannot simply rbind() this because elements may have 2-4
## entries.
n <- length(s)
tab <- matrix("", nrow = n, ncol = 4L)
colnames(tab) <- c("name", "type", "value", "size")
## Compute desired i and j positions in tab.
i <- rep.int(seq_len(n), lengths(s))
j <- unlist(lapply(s, seq_along))
tab[n * (j - 1L) + i] <- unlist(s)
tab
}
get_system_ABI <- if(.Platform$OS.type == "windows") {
function() c(system = "windows", CC = "gcc", CXX = "g++",
F77 = "gfortran", FC = "gfortran")
} else {
function()
{
s <- Sys.getenv("R_SYSTEM_ABI")
if((s == "") || (substring(s, 1L, 1L) %in% c("@", "?")))
return(character())
s <- unlist(strsplit(s, ",", fixed = TRUE))
names(s) <- c("system", "CC", "CXX", "F77", "FC")
s
}
}
system_ABI <- get_system_ABI()
so_symbol_names_table <-
c("linux, C, gcc, abort, abort",
## http://refspecs.freestandards.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/baselib---assert-fail-1.html
"linux, C, gcc, assert, __assert_fail",
"linux, C, gcc, exit, exit",
"linux, C, gcc, _exit, _exit",
"linux, C, gcc, _Exit, _Exit",
"linux, C, gcc, printf, printf",
"linux, C, gcc, printf, __printf_chk",
"linux, C, gcc, printf, puts",
"linux, C, gcc, puts, puts",
"linux, C, gcc, putchar, putchar",
"linux, C, gcc, stderr, stderr",
"linux, C, gcc, stdout, stdout",
"linux, C, gcc, vprintf, vprintf",
"linux, C++, gxx, std::cout, _ZSt4cout",
"linux, C++, gxx, std::cerr, _ZSt4cerr",
"linux, C, gcc, rand, rand",
"linux, C, gcc, random, random",
"linux, C, gcc, rand_r, rand_r",
"linux, C, gcc, srand, srand",
"linux, C, gcc, srandom, srandom",
"linux, C, gcc, srand48, srand48",
## libcxx variants
"linux, C++, gxx, std::cout, _ZNSt3__14coutE",
"linux, C++, gxx, std::cerr, _ZNSt3__14cerrE",
"linux, Fortran, gfortran, open, _gfortran_st_open",
"linux, Fortran, gfortran, close, _gfortran_st_close",
"linux, Fortran, gfortran, rewind, _gfortran_st_rewind",
"linux, Fortran, gfortran, read, _gfortran_st_read",
"linux, Fortran, gfortran, write, _gfortran_st_write",
"linux, Fortran, gfortran, print, _gfortran_st_write",
"linux, Fortran, gfortran, stop, _gfortran_stop_numeric_f08",
"linux, Fortran, gfortran, stop, _gfortran_stop_string",
"linux, Fortran, gfortran, rand, _gfortran_rand",
"linux, Fortran, flang, open, f90io_open03",
"linux, Fortran, flang, open, f90io_open2003",
"linux, Fortran, flang, close, f90io_close",
"linux, Fortran, flang, rewind, f90io_rewind",
"linux, Fortran, flang, write, f90io_print_init",
"linux, Fortran, flang, print, f90io_print_init",
"linux, Fortran, flang, read, f90io_fmt_read",
"linux, Fortran, flang, write, f90io_fmt_write",
"linux, Fortran, flang, stop, f90_stop",
"linux, Fortran, flang, stop, f90_stop08",
"linux, Fortran, flang, rand, rand",
"osx, C, gcc, abort, _abort",
"osx, C, gcc, assert, ___assert_rtn",
"osx, C, gcc, exit, _exit",
"osx, C, gcc, _exit, __exit",
"osx, C, gcc, _Exit, __Exit",
"osx, C, gcc, printf, _printf",
"osx, C, gcc, printf, _puts",
"osx, C, gcc, puts, _puts",
"osx, C, gcc, putchar, _putchar",
"osx, C, gcc, stderr, ___stderrp",
"osx, C, gcc, stdout, ___stdoutp",
"osx, C, gcc, vprintf, _vprintf",
"osx, C++, gxx, std::cout, __ZSt4cout",
"osx, C++, gxx, std::cerr, __ZSt4cerr",
"osx, C, gcc, rand, _rand",
"osx, C, gcc, random, _random",
"osx, C, gcc, rand_r, _rand_r",
"osx, C, gcc, srand, _srand",
"osx, C, gcc, srandom, _srandom",
"osx, C, gcc, srand48, _srand48",
## libcxx variants
"osx, C++, gxx, std::cout, __ZNSt3__14coutE",
"osx, C++, gxx, std::cerr, __ZNSt3__14cerrE",
"osx, Fortran, gfortran, open, __gfortran_st_open",
"osx, Fortran, gfortran, close, __gfortran_st_close",
"osx, Fortran, gfortran, rewind, _gfortran_st_rewind",
"osx, Fortran, gfortran, read, __gfortran_st_read",
"osx, Fortran, gfortran, write, __gfortran_st_write",
"osx, Fortran, gfortran, print, __gfortran_st_write",
"osx, Fortran, gfortran, stop, __gfortran_stop_numeric",
"osx, Fortran, gfortran, stop, __gfortran_stop_string",
"osx, Fortran, gfortran, rand, __gfortran_rand",
"freebsd, C, gcc, abort, abort",
"freebsd, C, gcc, assert, __assert",
"freebsd, C, gcc, exit, exit",
"freebsd, C, gcc, _exit, _exit",
"freebsd, C, gcc, _Exit, _Exit",
"freebsd, C, gcc, printf, printf",
"freebsd, C, gcc, printf, puts",
"freebsd, C, gcc, puts, puts",
"freebsd, C, gcc, putchar, putchar",
"freebsd, C, gcc, stderr, __stderrp",
"freebsd, C, gcc, stdout, __stdoutp",
"freebsd, C, gcc, vprintf, vprintf",
"freebsd, C++, gxx, std::cout, _ZSt4cout",
"freebsd, C++, gxx, std::cerr, _ZSt4cerr",
"freebsd, C, gcc, rand, rand",
"freebsd, C, gcc, random, random",
"freebsd, C, gcc, srand, srand",
"freebsd, C, gcc, srandom, srandom",
"freebsd, C, gcc, srand48, srand48",
"freebsd, Fortran, gfortran, open, _gfortran_st_open",
"freebsd, Fortran, gfortran, close, _gfortran_st_close",
"freebsd, Fortran, gfortran, rewind, _gfortran_st_rewind",
"freebsd, Fortran, gfortran, read, _gfortran_st_read",
"freebsd, Fortran, gfortran, write, _gfortran_st_write",
"freebsd, Fortran, gfortran, print, _gfortran_st_write",
"freebsd, Fortran, gfortran, stop, _gfortran_stop_numeric_f08",
"freebsd, Fortran, gfortran, stop, _gfortran_stop_string",
"freebsd, Fortran, gfortran, rand, _gfortran_rand",
## stdout, stderr do not show up on Solaris
"solaris, C, solcc, abort, abort",
"solaris, C, solcc, assert, __assert_c99",
"solaris, C, solcc, exit, exit",
"solaris, C, solcc, _exit, _exit",
"solaris, C, solcc, _Exit, _Exit",
"solaris, C, solcc, printf, printf",
"solaris, C, solcc, putchar, putchar",
"solaris, C, solcc, puts, puts",
"solaris, C, solcc, vprintf, vprintf",
"solaris, C++, solCC, std::cout, __1cDstdEcout_",
"solaris, C++, solCC, std::cerr, __1cDstdEcerr_",
"solaris, C, solcc, random, random",
"solaris, C, solcc, rand, rand",
"solaris, C, solcc, rand_r, rand_r",
"solaris, C, solcc, srand, srand",
"solaris, C, solcc, srandom, srandom",
"solaris, C, solcc, srand48, srand48",
"solaris, Fortran, solf95, open, __f90_open",
"solaris, Fortran, solf95, close, __f90_close",
"solaris, Fortran, solf95, rewind, __f90_rewind",
"solaris, Fortran, solf95, read, __f90_eifr",
"solaris, Fortran, solf95, read, __f90_esfr",
"solaris, Fortran, solf95, print, __f90_eslw",
"solaris, Fortran, solf95, write, __f90_eslw",
"solaris, Fortran, solf95, print, __f90_esfw",
"solaris, Fortran, solf95, write, __f90_esfw",
"solaris, Fortran, solf95, write, __f90_esuw",
"solaris, Fortran, solf95, stop, __f90_stop",
"solaris, Fortran, solf95, stop, __f90_stop_int",
"solaris, Fortran, solf95, stop, __f90_stop_char",
"solaris, Fortran, solf95, runtime, abort",
"solaris, Fortran, solf95, rand, rand_",
"solaris, C, gcc, abort, abort",
"solaris, C, gcc, assert, __assert_c99",
"solaris, C, gcc, exit, exit",
"solaris, C, gcc, _exit, _exit",
"solaris, C, gcc, _Exit, _Exit",
"solaris, C, gcc, printf, printf",
"solaris, C, gcc, printf, puts",
"solaris, C, gcc, puts, puts",
"solaris, C, gcc, putchar, putchar",
"solaris, C, gcc, vprintf, vprintf",
"solaris, C, gcc, rand, rand",
"solaris, C, gcc, random, random",
"solaris, C, gcc, rand_r, rand_r",
"solaris, C, gcc, srand, srand",
"solaris, C, gcc, srandom, srandom",
"solaris, C, gcc, srand48, srand48",
"solaris, C++, gxx, std::cout, _ZSt4cout",
"solaris, C++, gxx, std::cerr, _ZSt4cerr",
"solaris, Fortran, gfortran, open, _gfortran_st_open",
"solaris, Fortran, gfortran, close, _gfortran_st_close",
"solaris, Fortran, gfortran, rewind, _gfortran_st_rewind",
"solaris, Fortran, gfortran, read, _gfortran_st_read",
"solaris, Fortran, gfortran, write, _gfortran_st_write",
"solaris, Fortran, gfortran, print, _gfortran_st_write",
"solaris, Fortran, gfortran, stop, _gfortran_stop_numeric_f08",
"solaris, Fortran, gfortran, stop, _gfortran_stop_string",
"solaris, Fortran, gfortran, rand, _gfortran_rand",
## Windows statically links libstdc++, libgfortran
## only in .o, positions hard-coded in check_so_symbols
"windows, C++, g++, std::cout, _ZSt4cout",
"windows, C++, g++, std::cerr, _ZSt4cerr",
"windows, Fortran, gfortran, open, _gfortran_st_open",
"windows, Fortran, gfortran, close, _gfortran_st_close",
"windows, Fortran, gfortran, rewind, _gfortran_st_rewind",
"windows, Fortran, gfortran, write, _gfortran_st_write",
"windows, Fortran, gfortran, print, _gfortran_st_write",
## in DLL
"windows, C, gcc, abort, abort",
"windows, C++, gxx, runtime, abort",
"windows, Fortran, gfortran, runtime, abort",
"windows, C, gcc, assert, _assert",
"windows, C, gcc, exit, exit",
"windows, C, gcc, _exit, _exit",
"windows, C, gcc, _Exit, _Exit",
"windows, C, gcc, printf, printf",
"windows, C, gcc, printf, puts",
"windows, C, gcc, puts, puts",
"windows, C, gcc, putchar, putchar",
"windows, C, gcc, vprintf, vprintf",
## Windows does not have (s)random
"windows, C, gcc, rand, rand",
"windows, C, gcc, rand_r, rand_r",
"windows, C, gcc, srand, srand",
"windows, C, gcc, srand48, srand48",
"windows, Fortran, gfortran, stop, exit",
## next will not show up with static libgfortran
"windows, Fortran, gfortran, rand, _gfortran_rand"
)
so_symbol_names_table <-
do.call(rbind,
strsplit(so_symbol_names_table,
split = ", ", fixed = TRUE))
colnames(so_symbol_names_table) <-
c("system", "language", "compiler", "ssname", "osname")
## Subscript according to system and compiler types here, rather than
## repeatedly doing this at run time.
so_symbol_names_table <-
so_symbol_names_table[(so_symbol_names_table[, "system"] ==
system_ABI["system"]) &
(so_symbol_names_table[, "compiler"] %in%
system_ABI[c("CC", "CXX", "F77", "FC")]),
c("language", "ssname", "osname"),
drop = FALSE]
so_symbol_names_handlers_db <- list()
## <NOTE>
## As we record the low-level (possibly mangled) symbol names for
## each system/compiler combination, there is no need for handlers to
## demangle into user-level names (e.g., using c++filt).
## </NOTE>
so_symbol_names_handlers_db$linux <-
function(x)
{
## Linux ELF symbol versioning, see
## http://lists.debian.org/lsb-spec/1999/12/msg00017.html:
## name@version for alternatives, name@@version for the default.
sub("@.*", "", x)
}
so_symbol_names_handlers_db$freebsd <-
function(x)
{
## same as Linux, most likely, lots of name@@VERSION
sub("@.*", "", x)
}
## Obsolete ones first,
nonAPI <- c("chol_", "chol2inv_", "cg_", "ch_", "rg_",
"fft_factor", "fft_work", "Brent_fmin", "optif0",
## then entry points which are not attribute-hidden
## and in a non-API header or no header at all.
"OutDec", "PRIMOFFSET", "RC_fopen", "R_CollectFromIndex",
"R_CompiledFileName", "R_FileExists",
"R_FreeStringBuffer", "R_FunTab", "R_GE_setVFontRoutines",
"R_GetVarLocMISSING",
"R_MethodsNamespace", "R_NewHashedEnv",
"R_OpenCompiledFile", "R_PV", "R_ParseContext",
"R_ParseContextLast", "R_ParseContextLine",
"R_ParseError", "R_ParseErrorMsg", "R_SrcfileSymbol",
"R_SrcrefSymbol", "R_Visible", "R_addTaskCallback",
"R_cairoCdynload", "R_data_class",
"R_deferred_default_method", "R_execMethod",
"R_findVarLocInFrame","R_fopen", "R_gc_torture",
"R_getTaskCallbackNames", "R_get_arith_function",
"R_gzclose", "R_gzgets", "R_gzopen", "R_ignore_SIGPIPE",
"R_isForkedChild", "R_isMethodsDispatchOn",
"R_moduleCdynload", "R_primitive_generic",
"R_primitive_methods", "R_print", "R_removeTaskCallback",
"R_running_as_main_program", "R_setInternetRoutines",
"R_setLapackRoutines", "R_setX11Routines",
"R_set_prim_method", "R_set_quick_method_check",
"R_set_standardGeneric_ptr", "R_strtod4",
"R_subassign3_dflt", "R_taskCallbackRoutine",
"Rconn_fgetc", "Rconn_printf", "Rdownload",
"Rf_EncodeComplex", "Rf_EncodeElement",
"Rf_EncodeEnvironment", "Rf_EncodeInteger",
"Rf_EncodeLogical", "Rf_EncodeReal", "Rf_GPretty",
"Rf_NewEnvironment", "Rf_PrintDefaults",
"Rf_ReplIteration", "Rf_Seql", "Rf_addTaskCallback",
"Rf_begincontext", "Rf_callToplevelHandlers",
"Rf_checkArityCall", "Rf_con_pushback",
"Rf_copyMostAttribNoTs", "Rf_deparse1", "Rf_deparse1line",
"Rf_dpptr", "Rf_endcontext", "Rf_envlength",
"Rf_formatComplex", "Rf_formatInteger",
"Rf_formatLogical", "Rf_formatReal", "Rf_init_con",
"Rf_isProtected", "Rf_mbrtowc", "Rf_mkFalse",
"Rf_printNamedVector", "Rf_printRealVector",
"Rf_printVector", "Rf_removeTaskCallbackByIndex",
"Rf_removeTaskCallbackByName", "Rf_set_iconv",
"Rf_sortVector", "Rf_strIsASCII", "Rf_strchr",
"Rf_strrchr", "Rf_ucstomb", "Rf_utf8towcs",
"Rf_wcstoutf8", "Rg_PolledEvents", "Rg_set_col_ptrs",
"Rg_wait_usec", "Ri18n_iswctype", "Ri18n_wcswidth",
"Ri18n_wctype", "Ri18n_wcwidth", "Rsockclose",
"Rsockconnect", "Rsocklisten", "Rsockopen", "Rsockread",
"Rsockwrite", "Runzip", "UNIMPLEMENTED_TYPE",
"baseRegisterIndex", "csduplicated", "currentTime",
"dcar", "dcdr", "do_Rprof", "do_Rprofmem", "do_X11",
"do_contourLines", "do_edit", "do_getGraphicsEventEnv",
"do_getSnapshot", "do_playSnapshot", "do_saveplot",
"do_set_prim_method", "dqrrsd_","dqrxb_", "dtype",
"dummy_fgetc", "dummy_ii", "dummy_vfprintf", "epslon_",
"extR_HTTPDCreate", "extR_HTTPDStop", "fdhess",
"getConnection", "getPRIMNAME", "known_to_be_latin1",
"locale2charset", "match5", "matherr",
"max_contour_segments", "mbcsToUcs2", "memtrace_report",
"parseError", "pythag_", "rs_", "rwarnc_",
"signrank_free", "tql2_", "tqlrat_", "tred1_", "tred2_",
"utf8locale", "wilcox_free", "yylloc",
## Rinterface.h, Rembedded.h, R_ext/{RStartup,eventloop}.h
"AllDevicesKilled", "R_CStackLimit", "R_CStackStart",
"R_ClearerrConsole", "R_CleanTempDir", "R_Consolefile",
"R_DefParams", "R_DirtyImage", "R_GUIType", "R_GlobalContext",
"R_HistoryFile", "R_HistorySize", "R_Home", "R_HomeDir",
"R_InputHandlers", "R_Interactive", "R_Outputfile",
"R_PolledEvents", "R_ReplDLLdo1", "R_ReplDLLinit",
"R_RestoreGlobalEnv", "R_RestoreGlobalEnvFromFile",
"R_RestoreHistory", "R_RunExitFinalizers", "R_SaveGlobalEnv",
"R_SaveGlobalEnvToFile", "R_SelectEx", "R_SetParams",
"R_SetWin32", "R_SignalHandlers", "R_SizeFromEnv", "R_Slave",
"R_Suicide", "R_TempDir", "R_checkActivity",
"R_checkActivityEx", "R_runHandlers",
"R_setStartTime", "R_set_command_line_arguments",
"R_setupHistory", "R_timeout_handler", "R_timeout_val",
"R_wait_usec", "RestoreAction", "Rf_CleanEd",
"Rf_KillAllDevices", "Rf_endEmbeddedR", "Rf_initEmbeddedR",
"Rf_initialize_R", "Rf_jump_to_toplevel", "Rf_mainloop",
"SaveAction", "addInputHandler", "editorcleanall", "fpu_setup",
"getDLLVersion", "getInputHandler", "getRUser", "get_R_HOME",
"getSelectedHandler", "initStdinHandler",
"process_site_Renviron", "process_system_Renviron",
"process_user_Renviron", "ptr_R_Busy", "ptr_R_ChooseFile",
"ptr_R_CleanUp", "ptr_R_ClearerrConsole", "ptr_R_EditFile",
"ptr_R_EditFiles", "ptr_R_FlushConsole", "ptr_R_ProcessEvents",
"ptr_R_ReadConsole", "ptr_R_ResetConsole", "ptr_R_ShowFiles",
"ptr_R_ShowMessage", "ptr_R_Suicide", "ptr_R_WriteConsole",
"ptr_R_WriteConsoleEx", "ptr_R_addhistory", "ptr_R_loadhistory",
"ptr_R_savehistory", "ptr_do_dataentry", "ptr_do_dataviewer",
"ptr_do_selectlist", "readconsolecfg", "removeInputHandler",
"run_Rmainloop", "setup_Rmainloop",
## in the non-API header R_ext/Connections.h
"R_new_custom_connection", "R_ReadConnection",
"R_WriteConnection", "R_GetConnection")
## non-API in Applic.h
## future <- c("dqrcf_", "dqrdc2_", "dqrls_", "dqrqty_", "dqrqy_", "optif9")
## d1mach_ and i1mach_ are mentioned (since R 2.15.3) in R-exts.
## grDevices uses R_Home R_InputHandlers R_TempDir R_Visible R_cairoCdynload R_fopen R_gzclose R_gzgets R_gzopen R_isForkedChild Rf_envlength Rf_strIsASCII Rf_utf8towcs Rg_set_col_ptrs Ri18n_wcwidth addInputHandler do_X11 do_contourLines do_getGraphicsEventEnv do_getSnapshot do_playSnapshot do_saveplot locale2charset mbcsToUcs2 ptr_R_ProcessEvents
## graphics uses OutDec R_print Rf_EncodeComplex Rf_EncodeInteger Rf_EncodeLogical Rf_EncodeReal Rf_GPretty Rf_PrintDefaults Rf_envlength Rf_formatComplex Rf_formatReal baseRegisterIndex known_to_be_latin1 max_contour_segments
## methods uses R_GetVarLocMISSING R_MakeExternalPtrFn R_MethodsNamespace R_data_class R_deferred_default_method R_execMethod R_findVarLocInFrame R_primitive_generic R_primitive_methods R_set_prim_method R_set_quick_method_check R_set_standardGeneric_ptr R_subassign3_dflt Rf_NewEnvironment Rf_envlength do_set_prim_method getPRIMNAME
## parallel uses R_isForkedChild
## stats uses Rf_PrintDefaults Rf_Seql Rf_copyMostAttribNoTs Rf_deparse1 Rf_deparse1line Rf_envlength Rf_mkFalse fdhess memtrace_report signrank_free wilcox_free
## tcltk uses R_Consolefile R_GUIType R_InputHandlers R_Outputfile R_PolledEvents R_checkActivity R_runHandlers R_timeout_handler R_timeout_val R_wait_usec ptr_R_ClearerrConsole ptr_R_FlushConsole ptr_R_ReadConsole ptr_R_ResetConsole ptr_R_WriteConsole
## tools uses RC_fopen R_FileExists R_NewHashedEnv R_ParseContext R_ParseContextLast R_ParseContextLine R_ParseError R_ParseErrorMsg R_SrcfileSymbol R_SrcrefSymbol Rconn_fgetc Rf_begincontext Rf_endcontext Rf_envlength Rf_mbrtowc Rf_strchr extR_HTTPDCreate extR_HTTPDStop getConnection parseError
## utils uses R_ClearerrConsole R_FreeStringBuffer R_GUIType R_moduleCdynload R_print R_strtod4 Rconn_fgetc Rconn_printf Rdownload Rf_EncodeElement Rf_PrintDefaults Rf_begincontext Rf_con_pushback Rf_endcontext Rf_envlength Rf_sortVector Rsockclose Rsockconnect Rsocklisten Rsockopen Rsockread Rsockwrite Runzip UNIMPLEMENTED_TYPE csduplicated do_Rprof do_Rprofmem do_edit getConnection known_to_be_latin1 ptr_R_addhistory ptr_R_loadhistory ptr_R_savehistory ptr_do_dataentry ptr_do_dataviewer ptr_do_selectlist
## modules use PRIMOFFSET R_GE_setVFontRoutines R_setInternetRoutines R_setLapackRoutines R_setX11Routines Rf_set_iconv currentTime dummy_fgetc dummy_vfprintf ucstomb utf8locale
check_so_symbols <- if(.Platform$OS.type == "windows") {
function(so, rarch, have_tables = FALSE)
{
if(!length(system_ABI)) return()
nms <- read_symbols_from_dll(so, rarch)
ind <- so_symbol_names_table[, "osname"] %in% nms
if(have_tables) ind[1:4] <- TRUE
tab <- so_symbol_names_table[ind, , drop = FALSE]
attr(tab, "file") <- so
tab2 <- intersect(sub("^_", "", nms), nonAPI)
if ("removeInputHandler" %in% tab2)
tab2 <- setdiff(tab2, c("R_InputHandlers", "addInputHandler",
"removeInputHandler"))
if(length(tab2)) attr(tab, "nonAPI") <- tab2
tab2b <- setdiff(c("R_registerRoutines", "R_useDynamicSymbols"),
sub("^_", "", nms))
if(length(tab2b)) attr(tab, "RegSym") <- tab2b
class(tab) <- "check_so_symbols"
tab
}
} else {
function(so)
{
if(!length(system_ABI)) return()
tab <- read_symbols_from_object_file(so)
tab2 <- tab[tab[, "type"] == "U", "name"]
nms <- tab[, "name"]
sys <- system_ABI["system"]
if(!is.null(snh <- so_symbol_names_handlers_db[[sys]]))
nms <- snh(nms)
ind <- so_symbol_names_table[, "osname"] %in% nms
tab <- so_symbol_names_table[ind, , drop = FALSE]
attr(tab, "file") <- so
tab2 <- sub("^_", "", tab2)
tab2a <- intersect(tab2, nonAPI)
if ("removeInputHandler" %in% tab2a)
tab2a <- setdiff(tab2a, c("R_InputHandlers", "addInputHandler",
"removeInputHandler"))
if(length(tab2a)) attr(tab, "nonAPI") <- tab2a
tab2b <- setdiff(c("R_registerRoutines", "R_useDynamicSymbols"), tab2)
if(length(tab2b)) attr(tab, "RegSym") <- tab2b
class(tab) <- "check_so_symbols"
tab
}
}
format.check_so_symbols <-
function(x, ...)
{
if(!length(x)) return(character())
## <FIXME split.matrix>
entries <- split.data.frame(x, x[, "osname"])
objects <- vector("list", length(entries))
names(objects) <- names(entries)
if(length(objs <- attr(x, "objects")))
objects[names(objs)] <- objs
c(gettextf("File %s:", sQuote(attr(x, "file"))),
unlist(Map(function(u, v, w)
c(strwrap(gettextf("Found %s, possibly from %s",
sQuote(v),
paste(sprintf("%s (%s)",
sQuote(u[, "ssname"]),
u[, "language"]),
collapse = ", ")),
indent = 2L, exdent = 4L),
if(length(w) > 1L) {
strwrap(sprintf("Objects: %s",
paste(sQuote(w), collapse =
", ")),
indent = 4L, exdent = 6L)
} else if(length(w)) {
strwrap(sprintf("Object: %s", sQuote(w)),
indent = 4L, exdent = 6L)
}),
entries, names(entries), objects)))
}
check_compiled_code <-
if(.Platform$OS.type == "windows") {
function(dir)
{
## Check compiled code in the DLL(s) of an installed package.
r_arch <- .Platform$r_arch
useST <- config_val_to_logical(Sys.getenv("_R_SHLIB_BUILD_OBJECTS_SYMBOL_TABLES_", "FALSE"))
useSR <- config_val_to_logical(Sys.getenv("_R_CHECK_NATIVE_ROUTINE_REGISTRATION_", "FALSE"))
compare <- function(x, strip_ = FALSE) {
## Compare symbols in the DLL and in objects:
symbols <-
Filter(length,
lapply(tables,
function(tab) {
nm <- tab[, "name"]
if (strip_) nm <- sub("^_", "", nm)
nm <- sub("_gfortran_stop.*", "exit", nm)
intersect(x[, "osname"], nm)
}))
## Drop the DLL symbols not in any object.
so <- attr(x, "file")
osnames_in_objects <- unique(as.character(unlist(symbols)))
x <- x[!is.na(match(x[, "osname"], osnames_in_objects)), , drop = FALSE]
attr(x, "file") <- .file_path_relative_to_dir(so, dir, TRUE)
attr(x, "objects") <-
split(rep.int(names(symbols), lengths(symbols)),
unlist(symbols))
class(x) <- "check_so_symbols"
x
}
so_files <-
Sys.glob(file.path(dir, "libs/i386",
sprintf("*%s", .Platform$dynlib.ext)))
bad <- if(length(so_files)) {
objects_symbol_tables_file <-
file.path(dir, "libs/i386", "symbols.rds")
if(file_test("-f", objects_symbol_tables_file)) {
bad <- Filter(length, lapply(so_files, check_so_symbols,
rarch = "i386", have_tables = TRUE))
tables <- readRDS(objects_symbol_tables_file)
Filter(length, lapply(bad, compare, strip_ = TRUE))
} else {
if(useST)
cat("Note: information on .o files for i386 is not available\n")
Filter(length, lapply(so_files, check_so_symbols, rarch="i386"))
}
} else NULL
nAPIs <- lapply(lapply(so_files, check_so_symbols, rarch = "i386"),
function(x) if(length(z <- attr(x, "nonAPI")))
structure(z,
file =
.file_path_relative_to_dir(attr(x, "file"),
dir, TRUE),
class = "check_nonAPI_calls"))
bad <- c(bad, Filter(length, nAPIs))
if (useSR) {
nRS <- lapply(lapply(so_files, check_so_symbols, rarch = "i386"),
function(x) if(length(z <- attr(x, "RegSym")))
structure(z,
file =
.file_path_relative_to_dir(attr(x, "file"),
dir, TRUE),
class = "check_RegSym_calls"))
bad <- c(bad, Filter(length, nRS))
}
so_files <-
Sys.glob(file.path(dir, "libs/x64",
sprintf("*%s", .Platform$dynlib.ext)))
bad2 <- if(length(so_files)) {
objects_symbol_tables_file <- file.path(dir, "libs/x64", "symbols.rds")
if(file_test("-f", objects_symbol_tables_file)) {
bad2 <- Filter(length, lapply(so_files, check_so_symbols,
rarch = "x64", have_tables = TRUE))
tables <- readRDS(objects_symbol_tables_file)
Filter(length, lapply(bad2, compare))
} else {
if(useST)
cat("Note: information on .o files for x64 is not available\n")
Filter(length, lapply(so_files, check_so_symbols, rarch="x64"))
}
} else NULL
nAPIs <- lapply(lapply(so_files, check_so_symbols, rarch = "x64"),
function(x) if(length(z <- attr(x, "nonAPI")))
structure(z,
file =
.file_path_relative_to_dir(attr(x, "file"),
dir, TRUE),
class = "check_nonAPI_calls"))
bad2 <- c(bad2, Filter(length, nAPIs))
if (useSR) {
nRS <- lapply(lapply(so_files, check_so_symbols, rarch = "x64"),
function(x) if(length(z <- attr(x, "RegSym")))
structure(z,
file =
.file_path_relative_to_dir(attr(x, "file"),
dir, TRUE),
class = "check_RegSym_calls"))
bad2 <- c(bad2, Filter(length, nRS))
}
if(!length(bad) && !length(bad2)) return(invisible(NULL))
bad <- if(length(bad) && length(bad2)) rbind(bad, bad2)
else if(length(bad2)) bad2 else bad
class(bad) <- "check_compiled_code"
bad
}
} else {
function(dir)
{
## Check compiled code in the shared objects of an installed package.
r_arch <- .Platform$r_arch
useST <- config_val_to_logical(Sys.getenv("_R_SHLIB_BUILD_OBJECTS_SYMBOL_TABLES_", "FALSE"))
useSR <- config_val_to_logical(Sys.getenv("_R_CHECK_NATIVE_ROUTINE_REGISTRATION_", "FALSE"))
compare <- function(x) {
## Compare symbols in the so and in objects:
symbols <-
Filter(length,
lapply(tables,
function(tab) {
nm <- tab[, "name"]
intersect(x[, "osname"], nm)
}))
## Drop the so symbols not in any object.
so <- attr(x, "file")
## (Alternatively, provide a subscript method
## for class "check_so_symbols".)
osnames_in_objects <- unique(as.character(unlist(symbols)))
x <- x[!is.na(match(x[, "osname"], osnames_in_objects)), , drop = FALSE]
attr(x, "file") <- .file_path_relative_to_dir(so, dir, TRUE)
attr(x, "objects") <-
split(rep.int(names(symbols), lengths(symbols)),
unlist(symbols))
class(x) <- "check_so_symbols"
x
}
so_files <- if(nzchar(r_arch))
Sys.glob(file.path(dir, "libs", r_arch,
sprintf("*%s", .Platform$dynlib.ext)))
else
Sys.glob(file.path(dir, "libs",
sprintf("*%s", .Platform$dynlib.ext)))
if(!length(so_files)) return(invisible(NULL)) # typically a fake install
bad <- Filter(length, lapply(so_files, check_so_symbols))
objects_symbol_tables_file <- if(nzchar(r_arch))
file.path(dir, "libs", r_arch, "symbols.rds")
else file.path(dir, "libs", "symbols.rds")
if(file_test("-f", objects_symbol_tables_file)) {
tables <- readRDS(objects_symbol_tables_file)
bad <- Filter(length, lapply(bad, compare))
} else if(useST)
cat("Note: information on .o files is not available\n")
nAPIs <- lapply(lapply(so_files, check_so_symbols),
function(x) if(length(z <- attr(x, "nonAPI")))
structure(z,
file =
.file_path_relative_to_dir(attr(x, "file"),
dir, TRUE),
class = "check_nonAPI_calls"))
bad <- c(bad, Filter(length, nAPIs))
if (useSR) {
nRS <- lapply(lapply(so_files, check_so_symbols),
function(x) if(length(z <- attr(x, "RegSym")))
structure(z,
file =
.file_path_relative_to_dir(attr(x, "file"),
dir, TRUE),
class = "check_RegSym_calls"))
bad <- c(bad, Filter(length, nRS))
}
class(bad) <- "check_compiled_code"
bad
}
}
format.check_compiled_code <-
function(x, ...)
{
if(!length(x)) return(character())
## sapply does not always simplify as one wants here if there is
## more than one DLL.
paste(unlist(lapply(x, format)), collapse = "\n")
}
format.check_nonAPI_calls <-
function(x, ...)
{
if(length(x))
c(gettextf("File %s:", sQuote(attr(x, "file"))),
if (length(x) > 1L) {
strwrap(paste("Found non-API calls to R:",
paste(sQuote(x), collapse = ", ")),
indent = 2L, exdent = 4L)
} else paste(" Found non-API call to R:", sQuote(x))
)
else character()
}
format.check_RegSym_calls <-
function(x, ...)
{
if(length(x))
c(gettextf("File %s:", sQuote(attr(x, "file"))),
if (length(x) > 1L) {
strwrap(paste("Found no calls to:",
paste(sQuote(x), collapse = ", ")),
indent = 2L, exdent = 4L)
} else paste(" Found no call to:", sQuote(x))
)
else character()
}
.shlib_objects_symbol_tables <-
function(file = "symbols.rds")
{
objects <- commandArgs(trailingOnly = TRUE)
tables <- lapply(objects, read_symbols_from_object_file)
names(tables) <- objects
saveRDS(tables, file = file, version = 2)
}
### --- Helpers for registering native routines added in R 3.4.0 ---
package_ff_call_db <-
function(dir)
{
## A few packages such as CDM use base::.Call
ff_call_names <- c(".C", ".Call", ".Fortran", ".External",
"base::.C", "base::.Call",
"base::.Fortran", "base::.External")
predicate <- function(e) {
(length(e) > 1L) &&
!is.na(match(deparse(e[[1L]]), ff_call_names))
}
calls <- .find_calls_in_package_code(dir,
predicate = predicate,
recursive = TRUE)
calls <- unlist(Filter(length, calls))
if(!length(calls)) return(NULL)
attr(calls, "dir") <- dir
calls
}
native_routine_registration_db_from_ff_call_db <-
function(calls, dir = NULL, character_only = TRUE)
{
if(!length(calls)) return(NULL)
ff_call_names <- c(".C", ".Call", ".Fortran", ".External")
ff_call_args <- lapply(ff_call_names,
function(e) args(get(e, baseenv())))
names(ff_call_args) <- ff_call_names
ff_call_args_names <-
lapply(lapply(ff_call_args,
function(e) names(formals(e))), setdiff,
"...")
if(is.null(dir))
dir <- attr(calls, "dir")
package <- # drop name
as.vector(.read_description(file.path(dir, "DESCRIPTION"))["Package"])
symbols <- character()
nrdb <-
lapply(calls,
function(e) {
if (startsWith(deparse(e[[1L]]), "base::"))
e[[1L]] <- e[[1L]][3L]
## First figure out whether ff calls had '...'.
pos <- which(unlist(Map(identical,
lapply(e, as.character),
"...")))
## Then match the call with '...' dropped.
## Note that only .NAME could be given by name or
## positionally (the other ff interface named
## arguments come after '...').
if(length(pos)) e <- e[-pos]
## drop calls with only ...
if(length(e) < 2L) return(NULL)
cname <- as.character(e[[1L]])
## The help says
##
## '.NAME' is always matched to the first argument
## supplied (which should not be named).
##
## But some people do (Geneland ...).
nm <- names(e); nm[2L] <- ""; names(e) <- nm
e <- match.call(ff_call_args[[cname]], e)
## Only keep ff calls where .NAME is character
## or (optionally) a name.
s <- e[[".NAME"]]
if(is.name(s)) {
s <- deparse(s)[1L]
if(character_only) {
symbols <<- c(symbols, s)
return(NULL)
}
} else if(is.character(s)) {
s <- s[1L]
} else { ## expressions
symbols <<- c(symbols, deparse(s))
return(NULL)
}
## Drop the ones where PACKAGE gives a different
## package. Ignore those which are not char strings.
if(!is.null(p <- e[["PACKAGE"]]) &&
is.character(p) && !identical(p, package))
return(NULL)
n <- if(length(pos)) {
## Cannot determine the number of args: use
## -1 which might be ok for .External().
-1L
} else {
sum(is.na(match(names(e),
ff_call_args_names[[cname]]))) - 1L
}
## Could perhaps also record whether 's' was a symbol
## or a character string ...
cbind(cname, s, n)
})
nrdb <- do.call(rbind, nrdb)
nrdb <- as.data.frame(unique(nrdb), stringsAsFactors = FALSE)
if(NROW(nrdb) == 0L || length(nrdb) != 3L) {
message("no native symbols were extracted")
return(NULL)
}
nrdb[, 3L] <- as.numeric(nrdb[, 3L])
nrdb <- nrdb[order(nrdb[, 1L], nrdb[, 2L], nrdb[, 3L]), ]
nms <- nrdb[, "s"]
dups <- unique(nms[duplicated(nms)])
## Now get the namespace info for the package.
info <- parseNamespaceFile(basename(dir), dirname(dir))
## Could have ff calls with symbols imported from other packages:
## try dropping these eventually.
imports <- info$imports
imports <- imports[lengths(imports) == 2L]
imports <- unlist(lapply(imports, `[[`, 2L))
info <- info$nativeRoutines[[package]]
## Adjust native routine names for explicit remapping or
## namespace .fixes.
if(length(symnames <- info$symbolNames)) {
ind <- match(nrdb[, 2L], names(symnames), nomatch = 0L)
nrdb[ind > 0L, 2L] <- symnames[ind]
} else if(!character_only &&
any((fixes <- info$registrationFixes) != "")) {
## There are packages which have not used the fixes, e.g. utf8latex
## fixes[1L] is a prefix, fixes[2L] is an undocumented suffix
nrdb[, 2L] <- sub(paste0("^", fixes[1L]), "", nrdb[, 2L])
if(nzchar(fixes[2L]))
nrdb[, 2L] <- sub(paste0(fixes[2L]), "$", "", nrdb[, 2L])
}
## See above.
if(any(ind <- !is.na(match(nrdb[, 2L], imports))))
nrdb <- nrdb[!ind, , drop = FALSE]
## Fortran entry points are mapped to l/case
dotF <- nrdb$cname == ".Fortran"
nrdb[dotF, "s"] <- tolower(nrdb[dotF, "s"])
attr(nrdb, "package") <- package
attr(nrdb, "duplicates") <- dups
attr(nrdb, "symbols") <- unique(symbols)
nrdb
}
format_native_routine_registration_db_for_skeleton <-
function(nrdb, align = TRUE, include_declarations = FALSE)
{
if(!length(nrdb))
return(character())
fmt1 <- function(x, n) {
c(if(align) {
paste(format(sprintf(" {\"%s\",", x[, 1L])),
format(sprintf(if(n == "Fortran")
"(DL_FUNC) &F77_NAME(%s),"
else
"(DL_FUNC) &%s,",
x[, 1L])),
format(sprintf("%d},", x[, 2L]),
justify = "right"))
} else {
sprintf(if(n == "Fortran")
" {\"%s\", (DL_FUNC) &F77_NAME(%s), %d},"
else
" {\"%s\", (DL_FUNC) &%s, %d},",
x[, 1L],
x[, 1L],
x[, 2L])
},
" {NULL, NULL, 0}")
}
package <- attr(nrdb, "package")
dups <- attr(nrdb, "duplicates")
symbols <- attr(nrdb, "symbols")
nrdb <- split(nrdb[, -1L, drop = FALSE],
factor(nrdb[, 1L],
levels =
c(".C", ".Call", ".Fortran", ".External")))
has <- vapply(nrdb, NROW, 0L) > 0L
nms <- names(nrdb)
entries <- substring(nms, 2L)
blocks <- Map(function(x, n) {
c(sprintf("static const R_%sMethodDef %sEntries[] = {",
n, n),
fmt1(x, n),
"};",
"")
},
nrdb[has],
entries[has])
decls <- c(
"/* FIXME: ",
" Add declarations for the native routines registered below.",
"*/")
if(include_declarations) {
decls <- c(
"/* FIXME: ",
" Check these declarations against the C/Fortran source code.",
"*/",
if(NROW(y <- nrdb$.C)) {
args <- sapply(y$n, function(n) if(n >= 0)
paste(rep.int("void *", n), collapse=", ")
else "/* FIXME */")
c("", "/* .C calls */",
paste0("extern void ", y$s, "(", args, ");"))
},
if(NROW(y <- nrdb$.Call)) {
args <- sapply(y$n, function(n) if(n >= 0)
paste(rep.int("SEXP", n), collapse=", ")
else "/* FIXME */")
c("", "/* .Call calls */",
paste0("extern SEXP ", y$s, "(", args, ");"))
},
if(NROW(y <- nrdb$.Fortran)) {
args <- sapply(y$n, function(n) if(n >= 0)
paste(rep.int("void *", n), collapse=", ")
else "/* FIXME */")
c("", "/* .Fortran calls */",
paste0("extern void F77_NAME(", y$s, ")(", args, ");"))
},
if(NROW(y <- nrdb$.External))
c("", "/* .External calls */",
paste0("extern SEXP ", y$s, "(SEXP);"))
)
}
headers <- if(NROW(nrdb$.Call) || NROW(nrdb$.External))
c("#include <R.h>", "#include <Rinternals.h>")
else if(NROW(nrdb$.Fortran)) "#include <R_ext/RS.h>"
else character()
c(headers,
"#include <stdlib.h> // for NULL",
"#include <R_ext/Rdynload.h>",
"",
if(length(symbols)) {
c("/*",
" The following symbols/expressions for .NAME have been omitted",
"", strwrap(symbols, indent = 4, exdent = 4), "",
" Most likely possible values need to be added below.",
"*/", "")
},
if(length(dups)) {
c("/*",
" The following name(s) appear with different usages",
" e.g., with different numbers of arguments:",
"", strwrap(dups, indent = 4, exdent = 4), "",
" This needs to be resolved in the tables and any declarations.",
"*/", "")
},
decls,
"",
unlist(blocks, use.names = FALSE),
## We cannot use names with '.' in: WRE mentions replacing with "_"
sprintf("void R_init_%s(DllInfo *dll)",
gsub(".", "_", package, fixed = TRUE)),
"{",
sprintf(" R_registerRoutines(dll, %s);",
paste0(ifelse(has,
paste0(entries, "Entries"),
"NULL"),
collapse = ", ")),
" R_useDynamicSymbols(dll, FALSE);",
"}")
}
package_native_routine_registration_db <-
function(dir, character_only = TRUE)
{
calls <- package_ff_call_db(dir)
native_routine_registration_db_from_ff_call_db(calls, dir, character_only)
}
package_native_routine_registration_skeleton <-
function(dir, con = stdout(), align = TRUE, character_only = TRUE,
include_declarations = TRUE)
{
nrdb <- package_native_routine_registration_db(dir, character_only)
writeLines(format_native_routine_registration_db_for_skeleton(nrdb,
align, include_declarations),
con)
}