Rorschach
Rorschach

Reputation: 32426

Possible to decompile R bytecode?

Is it possible to go from compiled R code found in packages back to R source code? I would like to get the source code for various functions from packages installed from CRAN or other sources. I know I can download the full source via separate downloads.

Upvotes: 1

Views: 2030

Answers (2)

Joe
Joe

Reputation: 78

Andrie's answer above has been hugely helpful for me. I had to use a package that is not on CRAN or git, and was only distributed as a compiled package built for R 3.0.1. Obviously when I tried to use it with R 4 it didn't work, and I didn't want to keep switching back and forth between R versions, so I had to decompile the package, rebuild the source, and reinstall.

However, Andrie's code example has a few shortcomings, most importantly that it only decompiles exported functions, not internal ones. Obviously this post is pretty old, but I'm posting my updates here in case it's helpful for anyone else trying to do the same thing.

packagename <- "ggplot2" #input package name here

pkg <- asNamespace(packagename) # importing as namespace rather than package accesses internals

allfuns <- ls(name = pkg)

for(f in allfuns){
  # Andrie's [1] subset didn't work if the function arguments were more than one line long
  args <- head(capture.output(print(args(getFromNamespace(f, packagename)))), -1) 
  body <- paste(capture.output(print(body(getFromNamespace(f, packagename)))), 
                collapse = "\n")
  # This now writes directly to an R code file, rather than the console, to avoid copy/paste
  # You could tweak this to create a separate file for each function, if desired.
  cat(sprintf("%s <- %s\n%s\n\n", f, args, body), 
      file = paste(packagename, "functions.R"), 
      append = TRUE)
}

Upvotes: 1

Andrie
Andrie

Reputation: 179448

You can extract the text of functions in a package using args() and body(). To list all the objects in a package you can use ls() and specify the package environment.

Caveat: The approach below will give you the source code, but not the NAMESPACE or DESCRIPTION.

For example, to print the source code of everything in ggplot2, try this:

library(ggplot2)
pkg <- as.environment("package:ggplot2")
allfuns <- ls(envir = pkg)

for(f in allfuns[1:2]){
  args <- capture.output(print(args(f)))[1]
  body <- paste(capture.output(print(body(f))), collapse = "\n")
  cat(sprintf("%s <- %s\n%s\n\n", f, args, body))
}

This will give you:

%+% <- function (e1, e2) 
{
    e2name <- deparse(substitute(e2))
    if (is.theme(e1)) 
        add_theme(e1, e2, e2name)
    else if (is.ggplot(e1)) 
        add_ggplot(e1, e2, e2name)
}

%+replace% <- function (e1, e2) 
{
    if (!is.theme(e1) || !is.theme(e2)) {
        stop("%+replace% requires two theme objects", call. = FALSE)
    }
    e1[names(e2)] <- e2
    e1
}

Upvotes: 3

Related Questions