From 33495cb041f1af22577553889ca1e5a047eee776 Mon Sep 17 00:00:00 2001 From: Matej Kocourek Date: Thu, 21 May 2026 17:49:51 +0200 Subject: [PATCH 1/7] Convert to C by Claude --- NAMESPACE | 1 + R/blacklist_api.R | 66 +--- R/impute_srcrefs.R | 661 +------------------------------------ R/package_srcrefs.R | 83 +---- R/source_impute_srcrefs.R | 65 +--- R/srcref_sites.R | 4 + R/zzz.R | 2 + src/blacklist.c | 189 +++++++++++ src/blacklist.o | Bin 0 -> 36096 bytes src/helpers.c | 246 ++++++++++++++ src/helpers.o | Bin 0 -> 40008 bytes src/imputesrcref.h | 69 ++++ src/imputesrcref.so | Bin 0 -> 168448 bytes src/init.c | 17 + src/init.o | Bin 0 -> 7856 bytes src/source_impute.c | 404 +++++++++++++++++++++++ src/source_impute.o | Bin 0 -> 90472 bytes src/source_text.c | 226 +++++++++++++ src/source_text.o | Bin 0 -> 41896 bytes src/transform.c | 667 ++++++++++++++++++++++++++++++++++++++ src/transform.o | Bin 0 -> 94872 bytes 21 files changed, 1833 insertions(+), 867 deletions(-) create mode 100644 R/zzz.R create mode 100644 src/blacklist.c create mode 100644 src/blacklist.o create mode 100644 src/helpers.c create mode 100644 src/helpers.o create mode 100644 src/imputesrcref.h create mode 100755 src/imputesrcref.so create mode 100644 src/init.c create mode 100644 src/init.o create mode 100644 src/source_impute.c create mode 100644 src/source_impute.o create mode 100644 src/source_text.c create mode 100644 src/source_text.o create mode 100644 src/transform.c create mode 100644 src/transform.o diff --git a/NAMESPACE b/NAMESPACE index 1c3dba2..153b679 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,3 +6,4 @@ export(impute_srcrefs) export(reset_impute_blacklist) export(set_impute_blacklist) export(source_impute_srcrefs) +useDynLib(imputesrcref, .registration = TRUE) diff --git a/R/blacklist_api.R b/R/blacklist_api.R index 40869da..facb26e 100644 --- a/R/blacklist_api.R +++ b/R/blacklist_api.R @@ -1,45 +1,3 @@ -blacklist_option_name <- "imputesrcref.wrap_arg_blacklist" - -specialsxp_builtin_names <- local({ - nms <- builtins() - out <- nms[vapply(nms, function(nm) { - obj <- get(nm, envir = baseenv(), inherits = FALSE) - is.primitive(obj) && identical(typeof(obj), "special") - }, logical(1))] - sort(unique(out)) -}) - -normalize_blacklist_names <- function(functions, arg = "functions") { - if (is.null(functions)) { - return(character()) - } - if (!is.character(functions)) { - stop(sprintf("`%s` must be NULL or a character vector", arg), call. = FALSE) - } - - out <- trimws(functions) - out <- out[!is.na(out) & nzchar(out)] - sort(unique(out)) -} - -user_blacklist_names <- function() { - normalize_blacklist_names(getOption(blacklist_option_name, NULL), arg = blacklist_option_name) -} - -# rlang NSE functions that capture their argument as an unevaluated expression -# rather than evaluating it. Wrapping their arguments in transparent braces -# changes what expression is captured, breaking downstream DSL evaluators such -# as tidyselect (e.g. `expr({c(x)})` captures `{c(x)}` instead of `c(x)` and -# `tidyselect::eval_select` then fails to resolve the column `x`). -rlang_nse_names <- c( - "expr", "quo", "quos", - "enquo", "enquos", "enexpr", "enexprs" -) - -effective_blacklist_names <- function() { - sort(unique(c(specialsxp_builtin_names, rlang_nse_names, user_blacklist_names()))) -} - #' Get call names blacklisted from generic argument wrapping. #' #' By default, [impute_srcrefs()] skips argument wrapping for primitive @@ -57,15 +15,7 @@ effective_blacklist_names <- function() { #' reset_impute_blacklist() #' @export get_impute_blacklist <- function(include_default = TRUE) { - if (!is.logical(include_default) || length(include_default) != 1L || is.na(include_default)) { - stop("`include_default` must be TRUE or FALSE", call. = FALSE) - } - - if (isTRUE(include_default)) { - return(effective_blacklist_names()) - } - - user_blacklist_names() + .Call(C_get_impute_blacklist, include_default) } #' Set user call names blacklisted from generic argument wrapping. @@ -78,16 +28,7 @@ get_impute_blacklist <- function(include_default = TRUE) { #' @return Invisibly returns current user-configured blacklist entries. #' @export set_impute_blacklist <- function(functions, append = TRUE) { - if (!is.logical(append) || length(append) != 1L || is.na(append)) { - stop("`append` must be TRUE or FALSE", call. = FALSE) - } - - incoming <- normalize_blacklist_names(functions, arg = "functions") - current <- if (isTRUE(append)) user_blacklist_names() else character() - next_values <- sort(unique(c(current, incoming))) - - options(imputesrcref.wrap_arg_blacklist = next_values) - invisible(next_values) + invisible(.Call(C_set_impute_blacklist, functions, append)) } #' Reset user call names blacklisted from generic argument wrapping. @@ -97,6 +38,5 @@ set_impute_blacklist <- function(functions, append = TRUE) { #' @return Invisibly returns an empty character vector. #' @export reset_impute_blacklist <- function() { - options(imputesrcref.wrap_arg_blacklist = NULL) - invisible(character()) + invisible(.Call(C_reset_impute_blacklist)) } diff --git a/R/impute_srcrefs.R b/R/impute_srcrefs.R index 61304e0..83dba24 100644 --- a/R/impute_srcrefs.R +++ b/R/impute_srcrefs.R @@ -1,573 +1,3 @@ -is_call_named <- function(expr, name) { - is.call(expr) && is.symbol(expr[[1]]) && identical(as.character(expr[[1]]), name) -} - -is_braced <- function(expr) { - is_call_named(expr, "{") -} - -is_missing_arg <- function(x) { - is.symbol(x) && identical(as.character(x), "") -} - -is_logical_op_call <- function(expr) { - is.call(expr) && - is.symbol(expr[[1]]) && - as.character(expr[[1]]) %in% c("&&", "||", "&", "|") -} - -is_unquote_call <- function(expr) { - # rlang's !! and !!! unquoting operators appear in the R AST as double/triple - # negation: !!x -> !(!(x)), !!!x -> !(!(!(x))). Wrapping them in transparent - # braces changes semantics when the surrounding call is later processed by - # rlang::inject() or similar, because {!!x} injects a block instead of the - # bare value that !! would produce. - # The parenthesized form (!!x) parses to `(`(!(!(x))): the outer call is `(` - # not `!`, so we recurse into the `(` argument to check for the double-neg. - if (!is.call(expr) || length(expr) != 2L) { - return(FALSE) - } - head <- expr[[1L]] - if (identical(head, as.name("!"))) { - inner <- expr[[2L]] - return( - is.call(inner) && - length(inner) == 2L && - identical(inner[[1L]], as.name("!")) - ) - } - if (identical(head, as.name("("))) { - return(is_unquote_call(expr[[2L]])) - } - FALSE -} - -is_unary_arith_call <- function(expr) { - # Unary - and + must not be brace-wrapped as generic call arguments. - # In DSL contexts (tidyselect, dplyr) -f() is a structural "deselect f()" - # operator: wrapping to {-f()} causes the block to evaluate to a negative - # integer, which DSL validators reject ("Selections can't have negative values"). - is.call(expr) && - length(expr) == 2L && - is.symbol(expr[[1L]]) && - as.character(expr[[1L]]) %in% c("-", "+") -} - -visual_col_to_byte_col <- function(line, visual_col) { - # Convert a visual (tab-expanded, 8-wide tab stops) column number to the - # character position in `line` that starts at that visual column. - # R's srcref slots [5] and [6] record visual columns while substr() requires - # character (byte) positions: for tab-indented code these differ. - # Example: `\t function(x)` has visual col 11 for `f` but byte pos 4. - chars <- strsplit(line, "", fixed = TRUE)[[1L]] - vis <- 1L - for (i in seq_along(chars)) { - if (vis >= visual_col) { - return(i) - } - if (chars[[i]] == "\t") { - vis <- ((vis - 1L) %/% 8L + 1L) * 8L + 1L - } else { - vis <- vis + 1L - } - } - length(chars) -} - -expr_children <- function(node_id, ctx) { - # `node_id` is an `expr` row id from parse data. This returns child `expr` - # node ids in source order so AST argument positions can be mapped to parse - # ranges deterministically. - rows <- ctx$expr[ctx$expr$parent == node_id, , drop = FALSE] - if (nrow(rows) == 0L) { - return(integer()) - } - - rows <- rows[order(rows$line1, rows$col1, rows$id), , drop = FALSE] - rows$id -} - -node_srcref <- function(node_id, ctx) { - # Build an srcref from parse-data coordinates for a single expression node. - # The offsets are needed because parsed text can start at a non-1 line/column - # inside the original source file. - row <- ctx$expr_index[[as.character(node_id)]] - if (is.null(row)) { - stop(sprintf("Missing parse node id %s", node_id), call. = FALSE) - } - - line1 <- row$line1 + ctx$line_offset - line2 <- row$line2 + ctx$line_offset - col1 <- row$col1 + if (row$line1 == 1L) ctx$first_col_offset else 0L - col2 <- row$col2 + if (row$line2 == 1L) ctx$first_col_offset else 0L - - sr <- c(line1, col1, line2, col2, col1, col2, line1, line2) - sr <- as.integer(sr) - attr(sr, "srcfile") <- ctx$srcfile - class(sr) <- "srcref" - sr -} - -wrap_with_transparent_brace <- function(expr, sr) { - # Transparent wrapper: both srcref entries point to the wrapped expression - # span, making injected braces source-invisible for mapping purposes. - out <- call("{", expr) - attr(out, "srcref") <- list(sr, sr) - out -} - -source_text_from_srcref <- function(fn) { - sr <- attr(fn, "srcref", exact = TRUE) - - # Fallback for functions that do not carry srcref metadata. - if (is.null(sr)) { - if (!isTRUE(getOption("imputesrcref.allow_deparse_fallback", FALSE))) { - message( - paste( - "Function has no srcref metadata and deparse fallback is disabled; no changes were made.", - "Set options(imputesrcref.allow_deparse_fallback = TRUE)", - "to enable deparse-based fallback." - ) - ) - return(NULL) - } - - txt <- paste(deparse(fn, width.cutoff = 500L), collapse = "\n") - return(list( - text = txt, - srcfile = srcfilecopy("", txt), - line_offset = 0L, - first_col_offset = 0L - )) - } - - srcfile <- attr(sr, "srcfile", exact = TRUE) - if (is.null(srcfile)) { - stop("Function srcref is missing srcfile", call. = FALSE) - } - - sr <- as.integer(sr) - - # For installed package functions with preserved parse data, srcref often - # stores parse-table line span in slots 7/8 while slots 1/3 are remapped by - # #line directives. Try parsed span first, then fall back. - candidates <- list( - c(sr[7], sr[8], sr[5], sr[6]), - c(sr[1], sr[3], sr[5], sr[6]), - c(sr[1], sr[3], sr[2], sr[4]) - ) - - chosen <- NULL - lines <- character() - - for (cand in candidates) { - if (length(cand) != 4L || any(is.na(cand)) || any(cand <= 0L)) { - next - } - - start_line <- cand[[1L]] - end_line <- cand[[2L]] - start_col <- cand[[3L]] - end_col <- cand[[4L]] - - if (end_line < start_line) { - next - } - if (end_line == start_line && end_col < start_col) { - next - } - - attempt <- tryCatch( - getSrcLines(srcfile, start_line, end_line), - error = function(e) character() - ) - - if (length(attempt) == 0L) { - next - } - - chosen <- list( - start_line = start_line, - end_line = end_line, - start_col = start_col, - end_col = end_col - ) - lines <- attempt - break - } - - if (is.null(chosen) || length(lines) == 0L) { - stop("Could not read source lines for function", call. = FALSE) - } - - if (length(lines) == 1L) { - byte_sc <- visual_col_to_byte_col(lines[1L], chosen$start_col) - byte_ec <- visual_col_to_byte_col(lines[1L], chosen$end_col) - lines[1L] <- substr(lines[1L], byte_sc, byte_ec) - } else { - byte_sc <- visual_col_to_byte_col(lines[1L], chosen$start_col) - lines[1L] <- substr(lines[1L], byte_sc, nchar(lines[1L])) - byte_ec <- visual_col_to_byte_col(lines[length(lines)], chosen$end_col) - lines[length(lines)] <- substr(lines[length(lines)], 1L, byte_ec) - } - - list( - # `text` is what we parse to obtain getParseData(); offsets map parse - # coordinates back to the original source file coordinates. - text = paste(lines, collapse = "\n"), - srcfile = srcfile, - line_offset = chosen$start_line - 1L, - first_col_offset = chosen$start_col - 1L - ) -} - -map_generic_indices <- function(parts, child_ids, drop_first_child = FALSE) { - ids <- child_ids - if (drop_first_child && length(ids) > 0L) { - ids <- ids[-1L] - } - - n <- length(parts) - present <- which(!vapply(parts, is_missing_arg, logical(1))) - - # Prefer mappings that naturally skip explicit missing arguments. - if (length(ids) == length(present)) { - return(list(indices = present, ids = ids)) - } - if (length(present) >= 1L && length(ids) == length(present) - 1L) { - return(list(indices = present[-1L], ids = ids)) - } - - if (length(ids) == n) { - return(list(indices = seq_len(n), ids = ids)) - } - if (length(ids) == n - 1L && n >= 2L) { - return(list(indices = seq.int(2L, n), ids = ids)) - } - - NULL -} - -node_has_token <- function(node_id, token, ctx) { - any(ctx$pd$parent == node_id & ctx$pd$token == token) -} - -rebuild_call <- function(parts, template) { - out <- as.call(parts) - attrs <- attributes(template) - if (!is.null(attrs)) { - attributes(out) <- attrs - } - out -} - -set_element <- function(x, i, value) { - # Preserve explicit NULL arguments/defaults. `[[<- NULL` deletes an element. - # `[<-` coerces pairlists to regular lists; restore the original type so that - # nested `function(...)` formal lists remain pairlists as R requires. - is_pl <- is.pairlist(x) - x[i] <- list(value) - if (is_pl) as.pairlist(x) else x -} - -transform_expr <- function(expr, node_id, ctx) { - # Core imputation walker. - # - # - `expr`: current language object (AST node). - # - `node_id`: parse-data `expr` id corresponding to `expr`. - # - `ctx`: parse-data context with: - # * `pd`: full parse table. - # * `expr`: parse rows where token == "expr". - # * `expr_index`: fast lookup from expr id -> row. - # * `srcfile`, `line_offset`, `first_col_offset`: srcref reconstruction - # metadata. - # - # The recursion keeps AST and parse tree aligned. Whenever a target slot is - # not already `{ ... }`, we inject a brace call and impute its srcref from the - # corresponding parse-data node. - if (!is.call(expr)) { - return(expr) - } - - if (is_braced(expr) && !node_has_token(node_id, "'{'", ctx)) { - # Already-injected transparent braces may not exist in original parse data. - # Recurse into the child with the same node_id and preserve brace srcref. - parts <- as.list(expr) - sr <- attr(expr, "srcref", exact = TRUE) - - if (length(parts) >= 2L) { - parts <- set_element(parts, 2L, transform_expr(parts[[2L]], node_id, ctx)) - } - - out <- rebuild_call(parts, expr) - if (!is.null(sr)) { - attr(out, "srcref") <- sr - } - return(out) - } - - parts <- as.list(expr) - op <- if (is.symbol(parts[[1]])) as.character(parts[[1]]) else "" - child_ids <- expr_children(node_id, ctx) - - recurse_slot <- function(i, child_id, wrap = FALSE) { - # Helper for "recurse into slot i, then optionally brace-wrap and impute". - value <- transform_expr(parts[[i]], child_id, ctx) - # Do not wrap expressions whose callee is blacklisted (e.g. the ~ formula - # operator is a SPECIALSXP): wrapping `~cyl == 2` to `{~cyl == 2}` changes - # the AST structure, breaking functions that build quosures/formulas using - # `&`/`|` on formula objects and then inspect them with identical(). - slot_expr <- parts[[i]] - slot_callee_blacklisted <- is.call(slot_expr) && - is.symbol(slot_expr[[1L]]) && - as.character(slot_expr[[1L]]) %in% ctx$arg_wrap_blacklist - if (wrap && !is_braced(value) && !slot_callee_blacklisted) { - value <- wrap_with_transparent_brace(value, node_srcref(child_id, ctx)) - } - parts <<- set_element(parts, i, value) - } - - if (identical(op, "function")) { - fmls <- parts[[2]] - fml_text <- as.character(fmls) - cursor <- 1L - - for (idx in seq_along(fml_text)) { - if (identical(fml_text[[idx]], "")) { - next - } - - if (cursor > length(child_ids)) { - stop("Parse mapping mismatch for function formals", call. = FALSE) - } - - cid <- child_ids[[cursor]] - cursor <- cursor + 1L - - val <- fmls[[idx]] - val <- transform_expr(val, cid, ctx) - if (!is_braced(val) && is.call(val)) { - val <- wrap_with_transparent_brace(val, node_srcref(cid, ctx)) - } - fmls <- set_element(fmls, idx, val) - } - - if (cursor > length(child_ids)) { - stop("Parse mapping mismatch for function body", call. = FALSE) - } - - body_id <- child_ids[[cursor]] - body_expr <- transform_expr(parts[[3]], body_id, ctx) - # Only wrap the body if it is not already a call (e.g. a symbol or literal). - # Wrapping a call body in { } changes body(fn)[[1]] from the callee to `{`, - # breaking meta-programming tests that introspect function body structure. - # Call-type bodies already have their sub-expressions tracked individually. - if (!is_braced(body_expr) && !is.call(body_expr)) { - body_expr <- wrap_with_transparent_brace(body_expr, node_srcref(body_id, ctx)) - } - - if (is.null(fmls)) { - # `function()` stores formals as NULL. Preserve that slot explicitly, - # because `parts[[2]] <- NULL` would delete the element. - out <- as.call(list(as.name("function"), NULL, body_expr)) - attrs <- attributes(expr) - if (!is.null(attrs)) { - attributes(out) <- attrs - } - return(out) - } - - parts <- set_element(parts, 2L, fmls) - parts <- set_element(parts, 3L, body_expr) - return(rebuild_call(parts, expr)) - } - - if (identical(op, "if")) { - if (length(child_ids) < 2L) { - # Some package parse tables do not expose expected `if` child expr nodes. - # Fall back to structural recursion without additional brace imputation. - if (length(parts) >= 2L) { - parts <- set_element(parts, 2L, transform_expr(parts[[2L]], node_id, ctx)) - } - if (length(parts) >= 3L) { - parts <- set_element(parts, 3L, transform_expr(parts[[3L]], node_id, ctx)) - } - if (length(parts) >= 4L) { - parts <- set_element(parts, 4L, transform_expr(parts[[4L]], node_id, ctx)) - } - return(rebuild_call(parts, expr)) - } - - cond <- transform_expr(parts[[2L]], child_ids[[1L]], ctx) - if (!is_braced(cond) && !is_logical_op_call(cond)) { - cond <- wrap_with_transparent_brace(cond, node_srcref(child_ids[[1L]], ctx)) - } - parts <- set_element(parts, 2L, cond) - - recurse_slot(3L, child_ids[[2L]], wrap = TRUE) - if (length(parts) >= 4L && length(child_ids) >= 3L) { - recurse_slot(4L, child_ids[[3L]], wrap = TRUE) - } - - return(rebuild_call(parts, expr)) - } - - if (identical(op, "while")) { - if (length(child_ids) < 2L) { - stop("Parse mapping mismatch for while expression", call. = FALSE) - } - - cond <- transform_expr(parts[[2L]], child_ids[[1L]], ctx) - if (!is_braced(cond) && !is_logical_op_call(cond)) { - cond <- wrap_with_transparent_brace(cond, node_srcref(child_ids[[1L]], ctx)) - } - parts <- set_element(parts, 2L, cond) - - recurse_slot(3L, child_ids[[2L]], wrap = TRUE) - return(rebuild_call(parts, expr)) - } - - if (identical(op, "for")) { - # Parse data represents `for (...)` sequence under a dedicated `forcond` - # subtree, so we fetch that expr id explicitly instead of relying only on - # direct child expr ids. - forcond <- ctx$pd[ctx$pd$parent == node_id & ctx$pd$token == "forcond", , drop = FALSE] - seq_id <- integer() - if (nrow(forcond) == 1L) { - seq_rows <- ctx$pd[ctx$pd$parent == forcond$id[[1L]] & ctx$pd$token == "expr", , drop = FALSE] - if (nrow(seq_rows) >= 1L) { - seq_rows <- seq_rows[order(seq_rows$line1, seq_rows$col1, seq_rows$id), , drop = FALSE] - seq_id <- seq_rows$id[[1L]] - } - } - - body_ids <- child_ids - if (length(body_ids) >= 1L && length(seq_id) == 1L && identical(body_ids[[1L]], seq_id)) { - body_ids <- body_ids[-1L] - } - - if (length(seq_id) != 1L || length(body_ids) < 1L) { - stop("Parse mapping mismatch for for expression", call. = FALSE) - } - - recurse_slot(3L, seq_id[[1L]], wrap = TRUE) - recurse_slot(4L, body_ids[[1L]], wrap = TRUE) - return(rebuild_call(parts, expr)) - } - - if (identical(op, "repeat")) { - if (length(child_ids) < 1L) { - stop("Parse mapping mismatch for repeat expression", call. = FALSE) - } - - recurse_slot(2L, child_ids[[1L]], wrap = TRUE) - return(rebuild_call(parts, expr)) - } - - if (identical(op, "switch")) { - ids <- child_ids - if (length(ids) > 0L) { - ids <- ids[-1L] - } - - cursor <- 1L - for (i in seq.int(2L, length(parts))) { - if (is_missing_arg(parts[[i]])) { - next - } - - if (cursor > length(ids)) { - break - } - - cid <- ids[[cursor]] - cursor <- cursor + 1L - - recurse_slot(i, cid, wrap = TRUE) - } - - return(rebuild_call(parts, expr)) - } - - if (op %in% c("&&", "||", "&", "|")) { - if (length(child_ids) < 2L) { - # Some package parse tables do not expose both operand expr nodes. - # Fall back to structural recursion without brace imputation. - if (length(child_ids) == 1L) { - parts <- set_element(parts, 2L, transform_expr(parts[[2L]], child_ids[[1L]], ctx)) - if (length(parts) >= 3L) { - parts <- set_element(parts, 3L, transform_expr(parts[[3L]], node_id, ctx)) - } - } else { - if (length(parts) >= 2L) { - parts <- set_element(parts, 2L, transform_expr(parts[[2L]], node_id, ctx)) - } - if (length(parts) >= 3L) { - parts <- set_element(parts, 3L, transform_expr(parts[[3L]], node_id, ctx)) - } - } - return(rebuild_call(parts, expr)) - } - - recurse_slot(2L, child_ids[[1L]], wrap = TRUE) - recurse_slot(3L, child_ids[[2L]], wrap = TRUE) - return(rebuild_call(parts, expr)) - } - - mapping <- map_generic_indices(parts, child_ids) - if (is.null(mapping)) { - return(expr) - } - - # For plain parenthesized calls, wrap argument slots but never the callee. - # Wrapping is restricted to call expressions to avoid touching simple values - # (e.g. f(x + 1, 1) -> f({x + 1}, 1)). - is_blacklisted <- nzchar(op) && op %in% ctx$arg_wrap_blacklist - wrap_generic_args <- node_has_token(node_id, "'('", ctx) && - !identical(op, "(") && - !is_blacklisted && - isTRUE(ctx$wrap_call_args) - - # Assignment operators: the LHS target (slot 2) must never be brace-wrapped. - # Wrapping `x[i]` in braces makes it `{x[i]} <- value`, which causes R to - # look for the nonexistent `{<-` replacement function. - is_assign_op <- nzchar(op) && op %in% c("<-", "<<-", "=", "->", "->>") - - lhs_slot <- if (op %in% c("->", "->>")) 3L else 2L - - for (k in seq_along(mapping$indices)) { - i <- mapping$indices[[k]] - cid <- mapping$ids[[k]] - if (is_missing_arg(parts[[i]])) { - next - } - is_assign_lhs <- is_assign_op && i == lhs_slot - # When recursing into an assignment LHS (e.g. `length(slot(x, nm)) <- 0L`), - # suppress wrap_call_args so that nested call arguments inside the LHS are - # not brace-wrapped. Wrapping produces e.g. `length({slot(x, nm)}) <- 0L` - # which causes R to look for the nonexistent `{<-` replacement function. - recurse_ctx <- if (is_assign_lhs && isTRUE(ctx$wrap_call_args)) { - c(ctx[names(ctx) != "wrap_call_args"], list(wrap_call_args = FALSE)) - } else { - ctx - } - value <- transform_expr(parts[[i]], cid, recurse_ctx) - # Mirror the `recurse_slot` check: do not brace-wrap an argument whose - # callee is a blacklisted primitive (e.g. `[`, `[[`, `$`, `~`). Wrapping - # these changes the deparsed form of error messages and breaks packages - # (like Matrix) that compare test output against saved `.Rout.save` files. - slot_callee_blacklisted <- is.call(parts[[i]]) && - is.symbol(parts[[i]][[1L]]) && - as.character(parts[[i]][[1L]]) %in% ctx$arg_wrap_blacklist - if (wrap_generic_args && i > 1L && !is_assign_lhs && is.call(parts[[i]]) && !is_braced(value) && !is_unquote_call(parts[[i]]) && !is_call_named(parts[[i]], ":=") && !is_unary_arith_call(parts[[i]]) && !slot_callee_blacklisted) { - value <- wrap_with_transparent_brace(value, node_srcref(cid, ctx)) - } - parts <- set_element(parts, i, value) - } - - rebuild_call(parts, expr) -} - #' Impute transparent srcrefs for injected braces in a function AST. #' #' Traverses a function's AST and wraps unbraced expressions in targeted @@ -606,94 +36,5 @@ transform_expr <- function(expr, node_id, ctx) { #' g #' @export impute_srcrefs <- function(fn, wrap_call_args = TRUE) { - if (!is.function(fn)) { - stop("`fn` must be a function", call. = FALSE) - } - if (!is.logical(wrap_call_args) || length(wrap_call_args) != 1L || is.na(wrap_call_args)) { - stop("`wrap_call_args` must be TRUE or FALSE", call. = FALSE) - } - - fn_attrs <- attributes(fn) - src <- source_text_from_srcref(fn) - if (is.null(src)) { - return(fn) - } - parsed <- tryCatch( - parse(text = src$text, keep.source = TRUE), - error = function(e) e - ) - # Functions defined inside a parenthesized call (e.g. setMethod("f", sig, - # function(x) if(cond)\n then\nelse else_expr)) rely on the surrounding `(` - # to suppress the implicit semicolon that R inserts after the then-arm on its - # own line. Parsing the extracted text standalone fails with "unexpected else". - # Retry wrapped in `(...)` to restore that syntactic context. - paren_wrapped <- FALSE - if (inherits(parsed, "error")) { - parsed <- tryCatch( - parse(text = paste0("(", src$text, ")"), keep.source = TRUE), - error = function(e) stop(conditionMessage(e), call. = FALSE) - ) - paren_wrapped <- TRUE - } - - pd <- utils::getParseData(parsed) - - if (is.null(pd) || nrow(pd) == 0L) { - stop("Could not obtain parse data for function", call. = FALSE) - } - - expr_rows <- pd[pd$token == "expr", , drop = FALSE] - if (nrow(expr_rows) == 0L) { - stop("Parse data does not contain expression nodes", call. = FALSE) - } - - root_rows <- expr_rows[expr_rows$parent == 0L, , drop = FALSE] - if (nrow(root_rows) != 1L) { - stop("Expected exactly one top-level expression for function source", call. = FALSE) - } - - root_id <- root_rows$id[[1L]] - - if (paren_wrapped) { - # The outer node is the `(...)` wrapper; descend to the actual function - # expression. The prepended `(` shifts all line-1 columns in parse data - # right by 1, so reduce first_col_offset to compensate. - inner_rows <- expr_rows[expr_rows$parent == root_id, , drop = FALSE] - if (nrow(inner_rows) != 1L) { - stop("Expected exactly one expression inside paren wrapper", call. = FALSE) - } - root_id <- inner_rows$id[[1L]] - src$first_col_offset <- src$first_col_offset - 1L - } - - # Shared parse context used by the recursive walker. - ctx <- list( - pd = pd, - expr = expr_rows, - expr_index = split(expr_rows, expr_rows$id), - srcfile = src$srcfile, - line_offset = src$line_offset, - first_col_offset = src$first_col_offset, - arg_wrap_blacklist = effective_blacklist_names(), - wrap_call_args = wrap_call_args - ) - - fn_expr <- as.call(list(as.name("function"), formals(fn), body(fn))) - # Start traversal from the top-level function expression parse node. - transformed <- transform_expr(fn_expr, root_id, ctx) - - out <- fn - transformed_formals <- transformed[[2L]] - if (!is.null(transformed_formals)) { - formals(out) <- transformed_formals - } - body(out) <- transformed[[3L]] - - if (!is.null(fn_attrs)) { - for (nm in names(fn_attrs)) { - attr(out, nm) <- fn_attrs[[nm]] - } - } - - out + .Call(C_impute_srcrefs, fn, wrap_call_args) } diff --git a/R/package_srcrefs.R b/R/package_srcrefs.R index 8e4fc34..9be634a 100644 --- a/R/package_srcrefs.R +++ b/R/package_srcrefs.R @@ -1,20 +1,3 @@ -package_function_names <- function(ns, include_internal = TRUE) { - nms <- ls(ns, include_internal) - out <- character() - - for (nm in nms) { - if (!exists(nm, envir = ns, inherits = FALSE)) { - next - } - obj <- get(nm, envir = ns, inherits = FALSE) - if (is.function(obj) && !is.primitive(obj)) { - out <- c(out, nm) - } - } - - sort(unique(out)) -} - #' Check parse data for a package and impute srcrefs on all its functions. #' #' @param package Package name. @@ -28,68 +11,6 @@ package_function_names <- function(ns, include_internal = TRUE) { #' - `patched_count`: number of patched functions #' #' @export -impute_package_srcrefs <- function( - package, - include_internal = TRUE, - verbose = TRUE) { - if (!is.character(package) || length(package) != 1L || is.na(package) || identical(package, "")) { - stop("`package` must be a single non-empty package name") - } - - env <- loadNamespace(package) - fn_names <- package_function_names(env, include_internal) - failed <- character(length(fn_names)) - - - for (i in seq_along(fn_names)) { - nm <- fn_names[i] - fn <- get(nm, envir = env, inherits = FALSE) - failed[i] <- NA - - parse_data <- tryCatch(utils::getParseData(fn), error = function(e) NULL) - if (is.null(parse_data)) { - failed[i] <- "missing parse data" - next - } - - patched_fn <- tryCatch(impute_srcrefs(fn), error = function(e) e) - if (inherits(patched_fn, "error")) { - failed[i] <- paste(conditionMessage(patched_fn)) - next - } - - locked <- bindingIsLocked(nm, env) - - if (locked) { - unlockBinding(nm, env) - } - - assign(nm, patched_fn, envir = env) - - if (locked) { - lockBinding(nm, env) - } - } - - patched <- sum(is.na(failed)) - - report <- list( - package = package, - fn_names = fn_names, - failed = failed, - patched_count = patched - ) - - if (verbose) { - message( - sprintf( - "Patched %d/%d function(s) in package `%s`.", - patched, - length(fn_names), - package - ) - ) - } - - invisible(report) +impute_package_srcrefs <- function(package, include_internal = TRUE, verbose = TRUE) { + invisible(.Call(C_impute_package_srcrefs, package, include_internal, verbose)) } diff --git a/R/source_impute_srcrefs.R b/R/source_impute_srcrefs.R index 84d94ae..ecd4a78 100644 --- a/R/source_impute_srcrefs.R +++ b/R/source_impute_srcrefs.R @@ -37,67 +37,6 @@ source_impute_srcrefs <- function( toplevel.env = as.environment(envir), all.names = TRUE ) { - if (!is.character(file) || length(file) != 1L || is.na(file) || identical(file, "")) { - stop("`file` must be a single non-empty path string", call. = FALSE) - } - if (!file.exists(file)) { - stop(sprintf("File does not exist: %s", file), call. = FALSE) - } - if (!is.environment(envir)) { - stop("`envir` must be an environment", call. = FALSE) - } - - pre_names <- ls(envir, all.names = all.names) - # Snapshot pre-source bindings so we only patch functions introduced or - # modified by this source call. - pre_values <- mget(pre_names, envir = envir, inherits = FALSE, ifnotfound = vector("list", length(pre_names))) - - sys.source( - file = file, - envir = envir, - chdir = chdir, - keep.source = keep.source, - keep.parse.data = keep.parse.data, - toplevel.env = toplevel.env - ) - - post_names <- ls(envir, all.names = all.names) - imputed <- character() - - for (nm in post_names) { - if (!exists(nm, envir = envir, inherits = FALSE)) { - next - } - - value <- get(nm, envir = envir, inherits = FALSE) - if (!is.function(value)) { - next - } - - changed <- !(nm %in% pre_names) || !identical(value, pre_values[[nm]]) - if (!changed) { - next - } - - if (bindingIsLocked(nm, envir)) { - # Do not mutate locked user bindings in place. - warning(sprintf("Skipping locked binding `%s`", nm), call. = FALSE) - next - } - - # Rewrite only changed function bindings with srcref-imputed variants. - assign(nm, impute_srcrefs(value), envir = envir) - imputed <- c(imputed, nm) - } - - imputed <- sort(unique(imputed)) - - invisible(list( - file = normalizePath(file, mustWork = FALSE), - envir = envir, - functions = imputed, - count = length(imputed), - keep.source = keep.source, - keep.parse.data = keep.parse.data - )) + invisible(.Call(C_source_impute_srcrefs, file, envir, chdir, + keep.source, keep.parse.data, toplevel.env, all.names)) } diff --git a/R/srcref_sites.R b/R/srcref_sites.R index 2ae8c87..76ca897 100644 --- a/R/srcref_sites.R +++ b/R/srcref_sites.R @@ -1,3 +1,7 @@ +is_braced <- function(expr) { + is.call(expr) && is.symbol(expr[[1]]) && identical(as.character(expr[[1]]), "{") +} + deparse_one_line <- function(expr) { # Canonical single-line representation for stable snapshot output. txt <- paste(deparse(expr, width.cutoff = 500L), collapse = " ") diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..e82bbc8 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,2 @@ +#' @useDynLib imputesrcref, .registration = TRUE +NULL diff --git a/src/blacklist.c b/src/blacklist.c new file mode 100644 index 0000000..5175816 --- /dev/null +++ b/src/blacklist.c @@ -0,0 +1,189 @@ +#include "imputesrcref.h" +#include +#include + +static SEXP specialsxp_cache = NULL; + +static const char *rlang_nse_names[] = { + "expr", "quo", "quos", "enquo", "enquos", "enexpr", "enexprs" +}; + +static SEXP compute_specialsxp_names(void) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("builtins"), R_BaseEnv)); + SEXP call = PROTECT(Rf_lang1(fn)); + SEXP nms = PROTECT(Rf_eval(call, R_GlobalEnv)); + R_xlen_t n = Rf_xlength(nms); + + int *keep = (int*) R_alloc((size_t) n, sizeof(int)); + int kept = 0; + for (R_xlen_t i = 0; i < n; i++) { + const char *nm = CHAR(STRING_ELT(nms, i)); + SEXP sym = Rf_install(nm); + SEXP obj = Rf_findVarInFrame(R_BaseEnv, sym); + if (obj != R_UnboundValue && TYPEOF(obj) == SPECIALSXP) { + keep[i] = 1; + kept++; + } else { + keep[i] = 0; + } + } + + SEXP out = PROTECT(Rf_allocVector(STRSXP, kept)); + int j = 0; + for (R_xlen_t i = 0; i < n; i++) { + if (keep[i]) { + SET_STRING_ELT(out, j++, STRING_ELT(nms, i)); + } + } + + UNPROTECT(4); + return out; +} + +static int strvec_cmp(const void *a, const void *b) { + const char *sa = CHAR(*(SEXP const*) a); + const char *sb = CHAR(*(SEXP const*) b); + return strcmp(sa, sb); +} + +static SEXP sort_unique_strvec(SEXP x) { + R_xlen_t n = Rf_xlength(x); + if (n == 0) return x; + SEXP *buf = (SEXP*) R_alloc((size_t) n, sizeof(SEXP)); + for (R_xlen_t i = 0; i < n; i++) buf[i] = STRING_ELT(x, i); + qsort(buf, (size_t) n, sizeof(SEXP), strvec_cmp); + int u = 0; + for (R_xlen_t i = 0; i < n; i++) { + if (i == 0 || strcmp(CHAR(buf[i]), CHAR(buf[u-1])) != 0) { + buf[u++] = buf[i]; + } + } + SEXP out = PROTECT(Rf_allocVector(STRSXP, u)); + for (int i = 0; i < u; i++) SET_STRING_ELT(out, i, buf[i]); + UNPROTECT(1); + return out; +} + +static SEXP normalize_blacklist(SEXP x, const char *arg) { + if (x == R_NilValue) return Rf_allocVector(STRSXP, 0); + if (TYPEOF(x) != STRSXP) { + Rf_error("`%s` must be NULL or a character vector", arg); + } + R_xlen_t n = Rf_xlength(x); + SEXP keep = PROTECT(Rf_allocVector(STRSXP, n)); + int k = 0; + for (R_xlen_t i = 0; i < n; i++) { + SEXP s = STRING_ELT(x, i); + if (s == NA_STRING) continue; + const char *raw = CHAR(s); + /* trim whitespace */ + const char *p = raw; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + const char *q = raw + strlen(raw); + while (q > p && (q[-1] == ' ' || q[-1] == '\t' || q[-1] == '\n' || q[-1] == '\r')) q--; + if (q == p) continue; + int len = (int)(q - p); + char *buf = (char*) R_alloc((size_t)(len + 1), sizeof(char)); + memcpy(buf, p, (size_t) len); + buf[len] = '\0'; + SET_STRING_ELT(keep, k++, Rf_mkChar(buf)); + } + SEXP trimmed = PROTECT(Rf_allocVector(STRSXP, k)); + for (int i = 0; i < k; i++) SET_STRING_ELT(trimmed, i, STRING_ELT(keep, i)); + SEXP res = sort_unique_strvec(trimmed); + UNPROTECT(2); + return res; +} + +static SEXP user_blacklist(void) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("getOption"), R_BaseEnv)); + SEXP a = PROTECT(Rf_mkString("imputesrcref.wrap_arg_blacklist")); + SEXP call = PROTECT(Rf_lang2(fn, a)); + SEXP v = PROTECT(Rf_eval(call, R_GlobalEnv)); + SEXP res = normalize_blacklist(v, "imputesrcref.wrap_arg_blacklist"); + UNPROTECT(4); + return res; +} + +static SEXP get_specialsxp(void) { + if (specialsxp_cache == NULL) { + SEXP v = compute_specialsxp_names(); + PROTECT(v); + R_PreserveObject(v); + UNPROTECT(1); + specialsxp_cache = v; + } + return specialsxp_cache; +} + +SEXP imputesrcref_effective_blacklist(void) { + SEXP special = PROTECT(get_specialsxp()); + SEXP user = PROTECT(user_blacklist()); + + R_xlen_t n1 = Rf_xlength(special); + R_xlen_t n2 = (R_xlen_t) (sizeof(rlang_nse_names) / sizeof(rlang_nse_names[0])); + R_xlen_t n3 = Rf_xlength(user); + + SEXP combined = PROTECT(Rf_allocVector(STRSXP, n1 + n2 + n3)); + R_xlen_t pos = 0; + for (R_xlen_t i = 0; i < n1; i++) SET_STRING_ELT(combined, pos++, STRING_ELT(special, i)); + for (R_xlen_t i = 0; i < n2; i++) SET_STRING_ELT(combined, pos++, Rf_mkChar(rlang_nse_names[i])); + for (R_xlen_t i = 0; i < n3; i++) SET_STRING_ELT(combined, pos++, STRING_ELT(user, i)); + + SEXP res = sort_unique_strvec(combined); + UNPROTECT(3); + return res; +} + +SEXP C_get_impute_blacklist(SEXP include_default) { + if (TYPEOF(include_default) != LGLSXP || Rf_xlength(include_default) != 1 || + LOGICAL(include_default)[0] == NA_LOGICAL) { + Rf_error("`include_default` must be TRUE or FALSE"); + } + if (LOGICAL(include_default)[0]) { + return imputesrcref_effective_blacklist(); + } + return user_blacklist(); +} + +static void set_option(const char *name, SEXP value) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("options"), R_BaseEnv)); + SEXP call = PROTECT(Rf_lang2(fn, value)); + SET_TAG(CDR(call), Rf_install(name)); + Rf_eval(call, R_GlobalEnv); + UNPROTECT(2); +} + +SEXP C_set_impute_blacklist(SEXP functions, SEXP append) { + if (TYPEOF(append) != LGLSXP || Rf_xlength(append) != 1 || + LOGICAL(append)[0] == NA_LOGICAL) { + Rf_error("`append` must be TRUE or FALSE"); + } + int do_append = LOGICAL(append)[0]; + + SEXP incoming = PROTECT(normalize_blacklist(functions, "functions")); + SEXP current = R_NilValue; + if (do_append) { + current = PROTECT(user_blacklist()); + } else { + current = PROTECT(Rf_allocVector(STRSXP, 0)); + } + + R_xlen_t n1 = Rf_xlength(current); + R_xlen_t n2 = Rf_xlength(incoming); + SEXP combined = PROTECT(Rf_allocVector(STRSXP, n1 + n2)); + R_xlen_t pos = 0; + for (R_xlen_t i = 0; i < n1; i++) SET_STRING_ELT(combined, pos++, STRING_ELT(current, i)); + for (R_xlen_t i = 0; i < n2; i++) SET_STRING_ELT(combined, pos++, STRING_ELT(incoming, i)); + SEXP next_vals = PROTECT(sort_unique_strvec(combined)); + + set_option("imputesrcref.wrap_arg_blacklist", next_vals); + + UNPROTECT(4); + return next_vals; +} + +SEXP C_reset_impute_blacklist(void) { + set_option("imputesrcref.wrap_arg_blacklist", R_NilValue); + return Rf_allocVector(STRSXP, 0); +} diff --git a/src/blacklist.o b/src/blacklist.o new file mode 100644 index 0000000000000000000000000000000000000000..8d240092d709f2d9e569cddca7ff8b3562f2bef5 GIT binary patch literal 36096 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 Ol=aoVOXk|f?|%cnt^T_J literal 0 HcmV?d00001 diff --git a/src/helpers.c b/src/helpers.c new file mode 100644 index 0000000..09360c5 --- /dev/null +++ b/src/helpers.c @@ -0,0 +1,246 @@ +#include "imputesrcref.h" +#include +#include + +int imputesrcref_is_call_named(SEXP expr, const char *name) { + if (TYPEOF(expr) != LANGSXP) return 0; + SEXP head = CAR(expr); + if (TYPEOF(head) != SYMSXP) return 0; + return strcmp(CHAR(PRINTNAME(head)), name) == 0; +} + +int imputesrcref_is_braced(SEXP expr) { + return imputesrcref_is_call_named(expr, "{"); +} + +int imputesrcref_is_missing_arg(SEXP x) { + return TYPEOF(x) == SYMSXP && CHAR(PRINTNAME(x))[0] == '\0'; +} + +int imputesrcref_is_logical_op_call(SEXP expr) { + if (TYPEOF(expr) != LANGSXP) return 0; + SEXP head = CAR(expr); + if (TYPEOF(head) != SYMSXP) return 0; + const char *nm = CHAR(PRINTNAME(head)); + return strcmp(nm, "&&") == 0 || strcmp(nm, "||") == 0 || + strcmp(nm, "&") == 0 || strcmp(nm, "|") == 0; +} + +int imputesrcref_is_unquote_call(SEXP expr) { + if (TYPEOF(expr) != LANGSXP) return 0; + R_xlen_t n = Rf_xlength(expr); + if (n != 2) return 0; + SEXP head = CAR(expr); + if (TYPEOF(head) != SYMSXP) return 0; + const char *nm = CHAR(PRINTNAME(head)); + if (strcmp(nm, "!") == 0) { + SEXP inner = CADR(expr); + if (TYPEOF(inner) != LANGSXP) return 0; + if (Rf_xlength(inner) != 2) return 0; + SEXP ihead = CAR(inner); + return TYPEOF(ihead) == SYMSXP && strcmp(CHAR(PRINTNAME(ihead)), "!") == 0; + } + if (strcmp(nm, "(") == 0) { + return imputesrcref_is_unquote_call(CADR(expr)); + } + return 0; +} + +int imputesrcref_is_unary_arith_call(SEXP expr) { + if (TYPEOF(expr) != LANGSXP) return 0; + if (Rf_xlength(expr) != 2) return 0; + SEXP head = CAR(expr); + if (TYPEOF(head) != SYMSXP) return 0; + const char *nm = CHAR(PRINTNAME(head)); + return strcmp(nm, "-") == 0 || strcmp(nm, "+") == 0; +} + +int imputesrcref_in_strvec(SEXP vec, const char *name) { + if (TYPEOF(vec) != STRSXP) return 0; + R_xlen_t n = Rf_xlength(vec); + for (R_xlen_t i = 0; i < n; i++) { + SEXP s = STRING_ELT(vec, i); + if (s != NA_STRING && strcmp(CHAR(s), name) == 0) return 1; + } + return 0; +} + +static int get_int_col(SEXP df, const char *name, int **out) { + SEXP nms = Rf_getAttrib(df, R_NamesSymbol); + R_xlen_t n = Rf_xlength(nms); + for (R_xlen_t i = 0; i < n; i++) { + if (strcmp(CHAR(STRING_ELT(nms, i)), name) == 0) { + SEXP col = VECTOR_ELT(df, i); + if (TYPEOF(col) != INTSXP) Rf_error("Column `%s` is not integer", name); + *out = INTEGER(col); + return (int) Rf_xlength(col); + } + } + Rf_error("Column `%s` not found in parse data", name); + return -1; +} + +static SEXP get_str_col(SEXP df, const char *name) { + SEXP nms = Rf_getAttrib(df, R_NamesSymbol); + R_xlen_t n = Rf_xlength(nms); + for (R_xlen_t i = 0; i < n; i++) { + if (strcmp(CHAR(STRING_ELT(nms, i)), name) == 0) { + SEXP col = VECTOR_ELT(df, i); + if (TYPEOF(col) != STRSXP) Rf_error("Column `%s` is not character", name); + return col; + } + } + Rf_error("Column `%s` not found in parse data", name); + return R_NilValue; +} + +typedef struct { + int *id_arr; + int *line1_arr; + int *col1_arr; +} sort_ctx; + +static sort_ctx *g_sort_ctx; + +static int cmp_expr_child(const void *a, const void *b) { + int ia = *(const int*) a; + int ib = *(const int*) b; + int ra = g_sort_ctx->id_arr[ia]; + int rb = g_sort_ctx->id_arr[ib]; + (void) ra; (void) rb; + int la = g_sort_ctx->line1_arr[ia]; + int lb = g_sort_ctx->line1_arr[ib]; + if (la != lb) return la - lb; + int ca = g_sort_ctx->col1_arr[ia]; + int cb = g_sort_ctx->col1_arr[ib]; + if (ca != cb) return ca - cb; + return g_sort_ctx->id_arr[ia] - g_sort_ctx->id_arr[ib]; +} + +void imputesrcref_build_ctx(parse_ctx *ctx, SEXP pd, SEXP srcfile, + int line_offset, int first_col_offset, + SEXP arg_wrap_blacklist, int wrap_call_args) { + ctx->pd = pd; + ctx->srcfile = srcfile; + ctx->line_offset = line_offset; + ctx->first_col_offset = first_col_offset; + ctx->arg_wrap_blacklist = arg_wrap_blacklist; + ctx->wrap_call_args = wrap_call_args; + + int n1 = get_int_col(pd, "id", &ctx->id); + int n2 = get_int_col(pd, "parent", &ctx->parent); + int n3 = get_int_col(pd, "line1", &ctx->line1); + int n4 = get_int_col(pd, "col1", &ctx->col1); + int n5 = get_int_col(pd, "line2", &ctx->line2); + int n6 = get_int_col(pd, "col2", &ctx->col2); + if (!(n1 == n2 && n1 == n3 && n1 == n4 && n1 == n5 && n1 == n6)) { + Rf_error("Parse data columns have differing lengths"); + } + ctx->nrow = n1; + ctx->token = get_str_col(pd, "token"); + + int max_id = 0; + for (int i = 0; i < ctx->nrow; i++) { + if (ctx->id[i] > max_id) max_id = ctx->id[i]; + } + ctx->max_id = max_id; + + ctx->id_to_pdrow = (int*) R_alloc((size_t)(max_id + 2), sizeof(int)); + for (int i = 0; i <= max_id + 1; i++) ctx->id_to_pdrow[i] = -1; + for (int i = 0; i < ctx->nrow; i++) { + if (ctx->id[i] >= 0 && ctx->id[i] <= max_id) { + ctx->id_to_pdrow[ctx->id[i]] = i; + } + } + + int *all_counts = (int*) R_alloc((size_t)(max_id + 2), sizeof(int)); + int *expr_counts = (int*) R_alloc((size_t)(max_id + 2), sizeof(int)); + for (int i = 0; i <= max_id + 1; i++) { all_counts[i] = 0; expr_counts[i] = 0; } + + int *is_expr_row = (int*) R_alloc((size_t) ctx->nrow, sizeof(int)); + for (int i = 0; i < ctx->nrow; i++) { + is_expr_row[i] = (strcmp(CHAR(STRING_ELT(ctx->token, i)), "expr") == 0) ? 1 : 0; + } + + for (int i = 0; i < ctx->nrow; i++) { + int p = ctx->parent[i]; + if (p >= 0 && p <= max_id) { + all_counts[p]++; + if (is_expr_row[i]) expr_counts[p]++; + } + } + + ctx->all_children_offset = (int*) R_alloc((size_t)(max_id + 2), sizeof(int)); + ctx->all_children_count = (int*) R_alloc((size_t)(max_id + 2), sizeof(int)); + ctx->expr_children_offset = (int*) R_alloc((size_t)(max_id + 2), sizeof(int)); + ctx->expr_children_count = (int*) R_alloc((size_t)(max_id + 2), sizeof(int)); + + int all_total = 0, expr_total = 0; + for (int i = 0; i <= max_id + 1; i++) { + ctx->all_children_offset[i] = all_total; + ctx->expr_children_offset[i] = expr_total; + ctx->all_children_count[i] = 0; + ctx->expr_children_count[i] = 0; + all_total += all_counts[i]; + expr_total += expr_counts[i]; + } + + ctx->all_children_data = (int*) R_alloc((size_t)(all_total > 0 ? all_total : 1), sizeof(int)); + ctx->expr_children_data = (int*) R_alloc((size_t)(expr_total > 0 ? expr_total : 1), sizeof(int)); + + for (int i = 0; i < ctx->nrow; i++) { + int p = ctx->parent[i]; + if (p < 0 || p > max_id) continue; + int pos = ctx->all_children_offset[p] + ctx->all_children_count[p]++; + ctx->all_children_data[pos] = i; + if (is_expr_row[i]) { + int epos = ctx->expr_children_offset[p] + ctx->expr_children_count[p]++; + ctx->expr_children_data[epos] = i; + } + } + + sort_ctx sctx; + sctx.id_arr = ctx->id; + sctx.line1_arr = ctx->line1; + sctx.col1_arr = ctx->col1; + g_sort_ctx = &sctx; + for (int p = 0; p <= max_id; p++) { + int cnt = ctx->expr_children_count[p]; + if (cnt > 1) { + qsort(ctx->expr_children_data + ctx->expr_children_offset[p], + (size_t) cnt, sizeof(int), cmp_expr_child); + } + } + g_sort_ctx = NULL; +} + +SEXP imputesrcref_node_srcref(int node_id, parse_ctx *ctx) { + if (node_id < 0 || node_id > ctx->max_id) Rf_error("Missing parse node id %d", node_id); + int row = ctx->id_to_pdrow[node_id]; + if (row < 0) Rf_error("Missing parse node id %d", node_id); + int l1 = ctx->line1[row] + ctx->line_offset; + int l2 = ctx->line2[row] + ctx->line_offset; + int c1 = ctx->col1[row] + (ctx->line1[row] == 1 ? ctx->first_col_offset : 0); + int c2 = ctx->col2[row] + (ctx->line2[row] == 1 ? ctx->first_col_offset : 0); + + SEXP sr = PROTECT(Rf_allocVector(INTSXP, 8)); + int *p = INTEGER(sr); + p[0] = l1; p[1] = c1; p[2] = l2; p[3] = c2; + p[4] = c1; p[5] = c2; p[6] = l1; p[7] = l2; + Rf_setAttrib(sr, Rf_install("srcfile"), ctx->srcfile); + SEXP cls = PROTECT(Rf_mkString("srcref")); + Rf_setAttrib(sr, R_ClassSymbol, cls); + UNPROTECT(2); + return sr; +} + +SEXP imputesrcref_wrap_brace(SEXP expr, SEXP sr) { + SEXP brace = PROTECT(Rf_install("{")); + SEXP out = PROTECT(Rf_lang2(brace, expr)); + SEXP srlist = PROTECT(Rf_allocVector(VECSXP, 2)); + SET_VECTOR_ELT(srlist, 0, sr); + SET_VECTOR_ELT(srlist, 1, sr); + Rf_setAttrib(out, Rf_install("srcref"), srlist); + UNPROTECT(3); + return out; +} diff --git a/src/helpers.o b/src/helpers.o new file mode 100644 index 0000000000000000000000000000000000000000..352355f884a131f1691a2646f25b8f5f8a5aa912 GIT binary patch literal 40008 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

=nTx2{zVwEmv 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 Rb-caUAMq0RSLE^d{{Y{RgarTq literal 0 HcmV?d00001 diff --git a/src/imputesrcref.h b/src/imputesrcref.h new file mode 100644 index 0000000..f3e2bb2 --- /dev/null +++ b/src/imputesrcref.h @@ -0,0 +1,69 @@ +#ifndef IMPUTESRCREF_H +#define IMPUTESRCREF_H + +#define R_NO_REMAP +#include +#include +#include + +typedef struct parse_ctx { + SEXP pd; + int nrow; + int *id; + int *parent; + int *line1; + int *col1; + int *line2; + int *col2; + SEXP token; + int max_id; + int *id_to_pdrow; + int *all_children_offset; + int *all_children_count; + int *all_children_data; + int *expr_children_offset; + int *expr_children_count; + int *expr_children_data; + SEXP srcfile; + int line_offset; + int first_col_offset; + SEXP arg_wrap_blacklist; + int wrap_call_args; +} parse_ctx; + +void imputesrcref_build_ctx(parse_ctx *ctx, SEXP pd, SEXP srcfile, + int line_offset, int first_col_offset, + SEXP arg_wrap_blacklist, int wrap_call_args); + +int imputesrcref_is_call_named(SEXP expr, const char *name); +int imputesrcref_is_braced(SEXP expr); +int imputesrcref_is_missing_arg(SEXP x); +int imputesrcref_is_logical_op_call(SEXP expr); +int imputesrcref_is_unquote_call(SEXP expr); +int imputesrcref_is_unary_arith_call(SEXP expr); +int imputesrcref_in_strvec(SEXP vec, const char *name); + +SEXP imputesrcref_node_srcref(int node_id, parse_ctx *ctx); +SEXP imputesrcref_wrap_brace(SEXP expr, SEXP sr); +SEXP imputesrcref_transform_expr(SEXP expr, int node_id, parse_ctx *ctx); + +SEXP imputesrcref_source_text(SEXP fn); + +int imputesrcref_visual_col_to_byte_col(const char *line, int visual_col); + +SEXP imputesrcref_effective_blacklist(void); + +SEXP C_impute_srcrefs(SEXP fn, SEXP wrap_call_args); +SEXP C_get_impute_blacklist(SEXP include_default); +SEXP C_set_impute_blacklist(SEXP functions, SEXP append); +SEXP C_reset_impute_blacklist(void); +SEXP C_impute_package_srcrefs(SEXP package, SEXP include_internal, SEXP verbose); +SEXP C_source_impute_srcrefs(SEXP file, SEXP envir, SEXP chdir, + SEXP keep_source, SEXP keep_parse_data, + SEXP toplevel_env, SEXP all_names); + +SEXP C_collect_srcref_sites(SEXP expr_or_fn); +SEXP C_collect_transparent_srcref_checks(SEXP expr_or_fn); +SEXP C_assert_transparent_srcref_consistency(SEXP expr_or_fn); + +#endif diff --git a/src/imputesrcref.so b/src/imputesrcref.so new file mode 100755 index 0000000000000000000000000000000000000000..68683d285306ad7af8b82638ab507781141a9866 GIT binary patch literal 168448 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

H>NH0aJ`XKFHmw=2$$Qq@zL;-jzY|?ou&} zcY}y+dp@YR6z^$eSiGmKk{0jzkXrOTWu$mtDHAH5aVP6aip#IG;Jgv_e3Rl_{|^u0_l58oA$-5$yo$I$aX`^m6z4iIdPo^5^vBBZl4NHn#@At` zlvUPGIR1X1O8$*2ZgEKXcNHO~u%;nlNv^bu?fr1~Xd4ys;>qn7((F}5v$cs14QozP zh9l%Ts_Ea85}lz!nSmB6!wGbKplPBj6qg#PuKkeA!$~E#v*K*Umla=ujGn6aF68?2 z5I$LPw&8Kb&x8K_)kc2Ai}Gw0VCZu%Q=Fx5W%06@F)nCR8l>DpiKp@%i~udORx|trOO%P=;6k-K>r*rufDp zw^D_?fI1^Alw!eeRL&V>VIVi|h)}#M1ebe4-LZ;u2ZEn&=d8m*O%3Tksrsyh<%*wQ zg60nNQLWK+Ur06btv76&HL57Z-k=OSTxNl!qGQPoVYO|_@CviFRg=uzr#QDz288f^ zigU+hy5hm=%Y9X)+@awYl3BE1s&%WawXMdX>P9PdIve-T5WG`ioUWB!WVN}he8a-{ za@ExqM86J+fAYoqH~8XCA#pCoX&H{Q{n*j&D*m(&DtJ&xx0|m!+xI4SOh`P=7x(Z@ z#V-trC6#Hu$TYqUlXAAp6%FUr*0_?*wimJ~Q%sJ%h)D2cDc6SOO*(ltY#Kj~HB5>` z=5mCQQ{0-gg~*9e`l!igQQzd%^A5 zT(6M1Otg-8W%4a!gH_D&r@j@-5@2-5TsOh3xrK^z0Xa6%=Ur3lM&R&WrR>!6tQ3d) z(FYY~2Qve{;c|St>^C{@*T7ACJg0HCH~0d=oF&MIv%OU%!X?}d*nSds%F=j|evf!& z+Uz;zN!d%ud=aj=Tef&Ik07|mZl0fG9(S_1zn)l&g!Wn|Nqh^1r!>3?{{NhYUkAS5 zhI1=Rk|a=5_=hk|4S#?}*Rj&cxGld6M}UOgtlTGCLypfel`P zC`YZrD4-wO;9ZFJkpy!O(#!^CMGR-9@$%+CA=ArBn}m~-7fM(QnX^!e7fUUSM{Qm% zW01eBG_arexw^a>bYFq)Pm+Ghqzde1OG=6NfM(cm%B0qKfXTx5Bkk%ooZB3+(-7rX zNk46&`Aq`&*oO#Oo49Y+tyOLLT1Cb27OJo-)hK*Cgo} zi!{uJUaawdM|g?Fvr;C^or>?=Srum@P)7o6?h;yYZY;88$AkyohQMUL(bEC>6 z&aDA_n;#`BWrjIuUptFvSY!yjvnU!pn8wgD4Af!Jf zmqW$@NbGPlr3t%P4#yQ|N;@`e4u>0O%1LaToTJE&GvyS9)6OwdWxSEzH`k6w8_`tr zWjxBf2Ehe_rcIx37Rn%&do>bDn{F1#-PIpz1%26ER=6*F=+unooFB5@w@I~3=M#FHWXfsp=NA$*VGJTv(&iAVP< z&I_=CN0i~2kSX1ZvUbT}(@sMX?99`pJgBoS?WN9ov0A%SQY~%Ltof#!t*ADDyW3Ws zfVD^uX=}DyrEOBvW&$*19y_Y3-fByVD(hVvXN#QaYBIHGcU+;NF52;cyA8GK0jU9! zI&5_^^KlB;-k`QjxV0p_8tf`iQ)a7;K-Yp@4=Um#omeTfmCW1?WINGmMCZ<4WZKCb z@d&VoK+*owY}R-Q8y%*M?CFhDM)&l%_`$FAd{kW{=}YIMC^@siI*n=vRo6(WPO7U( zvbvf{5U&M6393uvw-{Mw{Oa-ur}%1h@tI7PvrJ-$9ebS43a%w(%1UE7YD+noqY3C` z1^KMJzi3pJ6b1Fa0G~F^6p7E7(hS&4my~PNLC;uh((8!EER&&mTL?doNT1@aJTxLQ zIF?Muop3Ckd^xLXXQwv8pvY~S0k{340C!N0_(^y$NLa?k_>bHBLZK68qzg7YrY*b z5B2(F;h?(6+NThd^g&~TiimqZ0BH)YA;|5eakx$LYq=dyRsw%yrmYH&Cz-IrafbNQ zic`P7;v9e8P@LmQw{Wafg|guu0A&I5BN0yl&5E(UWqOz&&}$>m@U0p*7;I&1>- z6w7}d0+D_=wWR)SVBQ9LGWBl}abDE)wK$K%ehS4MA5#BE zaqbYUP+ax{vH4-*Sk}pp#*iCsv%4#2Sd6{)k-!piiJ4F~=q0Zgsg~q*y)rzOe=mrF zd-WBkJM&$#EHKM(zv9ePUIU8BZv>p#?lV?Kv}94#$q2p??2A(9_$;$?+6+5 zo#|KY{@lak>kPHnZhh5dgJm_cI0w2miqrX;3C;_BUeL2lT~sP%>Z1&uZkU>2nNCxj zPIq<)Z!*hP2s6)j{@L25Uo%!)m9i#ovQj$TniHKa#oxv=dA!E`5pC}_Ma!%Q^JG6f zXAknWfO#8%7eU4U0n>m_6g$If({cKpWAjmF|KVaYuVZ8N9e#1~FOc|7ZhuS;p!j5s zLEq5|G_`1IhYIlRH4Ujv)78ma>Km;G8|5G|+5&-QAXBghSsJf?-AChfYl7VGg`g8u zI)L1>RfA6UNMoiiJH^qtic3?RrwrZeJyEjT#+NA0O8wd5bg%WHxamldWx`U<-J&>K zsIlU~7H$0BA!CRkW7S+OHr?pdAck>QDNZ+CEE>@}JeSM;VU0a_ejm1wt(TzGJ-H4a$E@nqQi0VBCra$VvC&=3ehTq(o zM&?y8=Ob_)nImATl7vLb%D}1$k}cd2=(QUkT_yG5rdx_C{f`pMoO!i{^)Y+DXC_erY7QR7y{+1!%^XQ%c@jqNIx z=6*;S83mFi;(HWlgVMXu81=YzZ;$L>;S?Q)YAhAS>5kmC2NhA^Mq zaG#?>p}t!G&o@V;#t^JPgry*_Etqu(TnUol!H<%yQMNcWMwmfxum$ zc=u4cT5rZ3tRSmuF5*KF!Fme^Xk%5v%DPG<;mVq{tyChF!vQ2BRsg}{Ro%tg9_#cA-vzfd=J$xL7t2HN*joH zAn~tZP+R9zTkfY)20Zlb0%|P)Rm)m@WQ;EK(NwbdJuasf6{2RuC1&_=;KdMx! z<-f|XT3!%UyLyjnBCM2;EzUvVUWJ*=e=V#{u}dXP(|$KY7g>_#Gb)udUr~l>_C&1& z?HNC;l2B8)pPXZh%Oq!6Z5d&IP@I=~8w5`#Sp6@|NX+lws>F1+Yh?7YXndBoEpRtS z8K%l-@ok@RtA*gtl5p=(O&fHrAouPtl`u#3M3Nk-gB*foP4c`-=#}@0Bs@U8u2S*J50s%-^7;0_Nc>BcgsYz0`WK;6-fN9wwFa{UxmyJC zx`Md?f%8F9#-B90KXLtCZ5ATQAh-@Xmx82>cO&p`kef2w)&{F~JXE!u(UK5u+{VhW z&}&s|9%6S;oJGD(@NkiPs8otPKp7VKK+;5fq~f6h7x1`Zj^Y@%rd^NPaa^aG6!a|!UjfzOg+z3j+OfVPL*Eu?r#pFzU{N9*gYq{ZsTprD z_VK{Po1=jIg@-;_CahP1WBia`+tr-B_k5$Ju#*oGqwNrA33C4(GWs%9{px-sgqs2$ zz0{g-lOJ#Ynb+H*{PQ6g3OhqU-bygz5Eu=TNyLv*U89scMFSB_$~YHFGeNde8mn=s z6t}l>Y~9I-z)YQo%nntYW6Zw<{~x2?SXE*p^Px#bMq%8kinG0U2S)AeVB+VhG~DVu zi~3nFto?3%T{{$XDg>7x?-zsIe^i4D>l{dF5v#;#Y>_RsI9rY~Tv)dgCA(5CQ=G{T zvN%WX`k}aIh14q)XBjTExXeKQIN2p+4&|1mbq{SxR^|htI9OV!gq`J8ktBy~6FpU> zlK%C`0Ee&UJETdt2EUu-%IlXGZG_E(5viJ1EQB)#|gn&#OA~c$$TEW_?E` zZ1UxSA-f_Brj<`riCMkcMwe;jSSS|{hRpia^{2{1jhMdWS3H-n0Z%M|e}xV@n0r&YRni-xdd=2(w7MZ1I72;kdLeiP)i z0rMdOAAn@&^ws_8VQ1+krpxK!Tj+cVlD=$)U?C1Bo}u=`1B6?wAggu<;NhTE#5;_VAZ7mqEAS%IvfSS0v`Gge68j_fT z;wmMeSXL#hAku;gtOXRY?SgIH)y0Z!L0wT%K(QjYigmy5_jAs@GZW&jpU)q^*H2%W zxu-qnIZr>&Id`~_g-=j;4-ap#@C^#z>uhvC6Myas6fVL8|8+9^k0{KJI z%&L0U)H5j-_qr*>9?f*)+0sGNWCh*?gkmde#PL|bB(!`A@E_yPEkogJJbZ<}Z1%E$ zi<-bWxG01gy9I@G6jJdQ&_3eCMvW_i4*BnVE4+|kDdtC_TQB^HD4dCh)9@GkwHZNC zXhl~_G$J8YA5|adMlbxJsliV^0hNoa@T(r=IsmHnAm4z8>+z?ai-r)q+?1#}ADelu zplvhtG)Y~GJTi7LNq;b%s6hRtlXhXZnR@Ed38tPvJyfYn=jnPH#t))!sEY!#KMIFG zNPQ=s@cSqn?lFU5@#1>S(4_Vgu+x0C>Bi&jWyTj;fiFO|8?5**FxZX5u3{Oev;==p z>h6e2(E4kLnmrkX`%%3CfA;^N@C+WFV&S(a?83vFEX+V5ZX(99(27Do6#C#V^F|aJ z3|5sIIl0Vb--L$Y=uyaC%TZWhdYx=~$tCF(Xu#WZfs^rP--*I+@W6lSF1XHgC$D&a zTn7W-mD6_TY}a=n_nU6y);~CQ%`R3#x{pviA6kX>Z2@Q@G?_JamfW$OyA%< zQ=bXW4mI`U!YiiUvuJ=Dh#T3Vk`aDiUMxt!=q*_`A_yHC@{l)ZdT4 zY}Siw`w*pf@uwDFs)C|G1|PVIy&gluh3GLEe}T9=Oc!%p1(yeF z$5W|H-jP^KV&lIl`YYg`2mrUDVLk?5fxqw?6iR`>#{|6=h5J!^8~y@u517FQwE3+x z0X~4oyVMIOCfIoTX>L)z>zyja7N;wz427hsdW~_eaYO>q5 zdbZmex0ugyt_Te41CsvT(4Lde2Vj!aTU+p7kXio6#@kMeZH)8KI*cNLZoDpc(o7kO zk7i)~ck3wL%WywKOCKQC6@S5=20UFu2(6l(pliHcxzvmf4vXqEIJVPtuT$JPQJs7! z3`+uGUP-zZ6a4uU5WWPQ z6h6np`z$O%q4#;vO#IoaP^iU2H4As6um}(HS$G(Qm+|l%3s0f&IUYX7pZJsQhDKK4 zUf}VfmD%fL(1@NY?tGXvbQ4cC1cg5MQ=ggsq9~a!0o-|4YC0irD*qh>0DBu6%FzEJ z`~|0&9^6h`j8>3GwQa;%E9mS{90KaMri%n;GfaUyi$%J>*f!jnG%Ykzcj*?olg6AQXKUQ*C}2QIb`~j@>qSt#6LE@9PC|Zzxu%}#-P_ayMzGA( zGvqwOugAK)1;Q%J0;igevcOACfwz%nMvu~+gG)_2kBQp+_CRohspop`H}&Qpvxn)& zJAkI0EMHLU_B%}oqSzI3>yKgA;KfGRNv09H z75}0%QJ7&`NGSe8ntEIym}Q8~-Bz_XLXhrfS{TpFiW)J>)HC9}!>v6j7>Oiph?ncym6~SYsYjoJHzGJr&y|o0tYKU}O(y`k_zuSqIM7llJvD>MBw}s7- zZo4~nEA_j5e3|aX)pj{GX0`ugI(e&|V^p8ZDAZlO)rvl?G$O-0W1`tSj1R8f9`G+Zp6dYEbK?&VLaT6KRXVB`8FQ5 zvyhI$*LXOp?l0^c>m8JP|cE z_@XILC-03J0Q`bGOe^L28PiIX_LzDPrD52+q++4K9IvHM^ndQh8iyY^o0S7dr(0KA zv%^*x^ihFv_;OAUum(gJ5wJre^R43(k4qdE4rV`cay9_z{+Mb6-iI=kp+14c#LTSSRG#DI*h)SH~sx&m9*o$|dZC7+5g3D5rFUozx|em>(hz$%)pv*&LUW#eRtz z1qiJkrU-mo_=MadP%|}lMm=AeiW!%V;$ce(`vbAS2&x&TS{9z~vLVe%k2^8va#=}c z4CeSjq5{ibNyom~f<*@k<|JB#$JOEX(3MuJl}l=OJ;E9qo@o8)m!a4F7=2(BM46R92b~lg~`)A^2mVAPXyR=!MI)|Dg2o(6Lg;l=K0A) zJuly4ecv>zZ%R1P?%Ay;5lzJ?TmcgXhY);&Nr|{Q(ofJBX!s-ifM#V{1VkuD^@GR_ z5-bh-G`j|e!5Q+cz_N7D!hC=#1k52q`A|pM85+x{1s2%O8HQo0f#I?UnW2Afo{($= zsf8<{J{8#c=tNoDF|n|)=EM;K?AQX(DVop63C%IZ$btYduS?N_h&ek|9<1~*33IeY zspKeOJQfSZ6*8@2ah^7HqCxpi2Nmi}m^#-ZPkWL(6MOt}lF_m*(ULA25KVy8Sk+65 zSs>CN9Vw}q5{@zp8CpiUj{Hvfj@V z?EYa`gv3N`wJ3W7!d7%~puZTgJH9+0NqIg>8V}QfL9t_^`7~JM!sr+cL4lCZx0!{W zVNozFiJo17u6c%4R9`e_sK|>ya#%EEsG6@(hyyz+#jhe2)DSKB!H8e5H4PnK_*ATT zxt>Fci(RSbK=7-TG_$t$1eB8a0jTMYhXf(7H08&W-icbI*f&27B9#xbmE>_JS*slT zr5w)uE)nLZYUZao*674Uc$)}6(=!9488&;K3Cr+InD!1)%*Y~LqA5+&mmRxCkDHME zXa*SPnj1y-&P|VJO<-<(m#j1gI(OF{R2jcpeYovPFgWJBv1Z7(IAZb*j!Drn7I9w5 z9^mM{DO&Hlh;ViAjj6E`M=wrQkN^ZGM+CdWkZ?rCGJGsZ(I_XH8LV#qpuDr`gsb~-xKQrBG!oT2S#k2 zwo)?oK7@41Nd_L?$!Qq-$7}2~TTJvz(nAi_HTmgrB$~+?0 zvi?dG-^dGX#L+^ED0S5S|1k3k?;owDzDYjLuNS`)8wrjyz_oCL2FLr{75Niei_Blg zS}+}lhdyKzd`8RE=x8;dNk1VfP&_eODUAM|q(h#Qqm6nrz%fxa{&Zf*6(CLD=+y$!5DC#AKRBJhqdB`cy}*;+#obko%x zCZG}56zw{@bx=d&$h!M`&?90MvWKsbJ@rCk6fe(*CWaFMgn^gOh7NW34ICoc+fx~V z#T*U>rj_x#@gfBG1kqT_ThQ`9{!LdhdNnqXfOy>UJ&M!AhiFR?oLhwX!Yr5WPw zwzL=@?M;+FG%8|%_l(^nAFHJf=Vyl-ppg*+bi6h|McM$3q5&E$vl<1^2IvIO0G-%z z19eiYn}?f=_SRaw$It*oj0mM3hpMqXeN#4;rtDO|(Wqv$$Hb*y2|c4V@AOEFHBRUw zNiN1H#m0Vh0%*KHi*fYYyqyu{(a$`*i;kSm@&!|Lz~8)`ZNw*$dV;(sHG(^#b0S7i zTg8cy3M}~72}tS%LV=5EAua2B{LVkVU}pmO!$VIOkAcDX!(E5KO#EGqzvcJ~T0MhZ z67%sCkdKG)U_Hx*SMg#06Cd&mrfNTn-{M0Nu1dg1Kn=i8vCJ8OC;Vp*v6Kp{0dAT~ z4~y5z3X z#bR{?YcX7RVo!k;ZdXY_M!EKk?V+jBYf}C+yE{xUayUsm5+qIx;k!wN#;f?YV)QCf z@cBcz26n$!`$3 z6|;;Et4tu!WmJJ03y`iGTBS~v2TS4iqClx)l`7xhj=M+o1R|L%Z$_~N$pJhH4PC*g zL|urQIJa6|B;S_q0U+H_4nj8K-8O?mn=m>a>&XOA3P;dxDN)1h#A21%tm5Z6L6v}7 z%RaT(u~biaEGkl&=K{zmG+OqUB4ualPMVX#+!FLow~x2{@nfaB2-Wc}>J2T%6Cju; zfH9v4fqax0`q_l~Ss@@acG>rBZy6ODk0=?5yjf zMR6W`HB%LVL~2YoSz~GR8jIDhT>GT0T)R6S&uJ>Fy{L4YOqPY(Bv4{hp2`{|H6V&g z>RFSw@KS{LwXRG5z{^8yuKf#PT?gs6eD|gXG1AM97=q;^SN1%*v%2{W;4W!0z*XYg{Gk-1`Vtw zqcicYrZ`rEoA%1tLccCrdqZNz*JIecD4MYZ`SAoR9b$wjMgGQ%D(76F(&q@>a*RLn zNk*Rnl7)BRt3`Fy)9glJ9`&X4csz~b6TVBuEYWqr=8~wekIC+ZKqOUV4)(lJvMf+@ z2g;u8&I}V(m3dRAJ+=caJIN9vzzBy&K`~N0VXd(+1;+4%w(Ds`-7yWZqXbM>Oe%+xr3w zF_;=U97-0y#q&u8oVG+A#jjSCKUJO0x#yzi3o0KY#C5fzSVPSzhVEhwbMTxAcX9-t z&s8Js)a?c~U10nkH4^NY=grWUpcm?ewW^$UMHQ79LRqR024>Dz@$5s58A6R=fm6aG zGE|9ouc8$DE0fYZZD2&(N>%tKbQky22}@$MZ3}?^H*E`pQKDT7rStm#wXTE_>BuUe z0T*b;N$W8Bf_4(IwE$s{Yu8tERT^Kt;*{&eykAnH`r2J#fMA#Uf}kp2^+o_4OlYU!B@&506Iv!@GEkHcMwJ$$${?dkyC)kMOFE1y9mbbI9aSC& ziyat_GXAqa3NU1&JRL_AVd`>VY>vAGk0+=+H!Myitl(r}*P+cSp5f3aES#f831d0> zXPUyK2!ag;X_%-$<=B_eoY?V2syDngT#6d)+GoK;hw+f6QbSYK=q8mqzDUuoXrIPl zivh0*YGFgdZmZGE^+vlGfhKEiZO3FmpCJf6lTcyPHfTK4HmIEAQIfb#+b%Yiirq;k zrmdWzlAUL_%(#&2>u)?C$Ro`hl6!rgG*p-W}jgCbSFr79I) zLT5wkq1>s5PwWx7xr(@}crpE6YL=_pOZqg7P;0UitaC{Gd;M?HlG8z5Hcm);Y;^~PD^ zicpclkHIWUG!JMT zmmwQa*>!R^CN|n&)f3VtkE8I5WY7Y}k~V^Ise_YAb)N<1+r{dNe<%hi+SHLgCL?1o z0rbmN4>b0~-n_?L6i0DxGGV-zBrs8%Y&Hb-V83*+IQCWvi0rMzfia-QTTp3NJ;~Cx z4nU~%2e~6A?GE5W^RAgsMJk7YO4PahD%Pb|A)xJ!GiYnB0b!X%G)0pWW4IB?0N(M~ zm(s4p<5|a(ZJN{3e zz!|s_MuUv#`^t9t>{WA^1{7FmibRKbGmc;Jjt=c|6>$<`_%r~@EWR;OJP z)GUTIXc1&D(2F_`W9CP=s7GP5Abm!wj5c6^a7@!%$L}<1rvuDUC9Uf8R&`X7I=x6C z_DC*PeIZ^YYW!df8f8}E@#t9?aZ8V_c^bdVRPs=jWzTF@;TZ^jRMu%K^-PtC*}O^u-Jsh=LyjUcshTSyWo-y8RH^oftp(6XNe7~Vdj=b@=%XRk zCrYu|uh#oqqrg#6sZ^GYAQWFB=Cb8??Fvh&mwutj5;N}IdqOkg?aNoGv=dRYC}LgM zVM`PlXkEnY&~?20OJ^a9LjNab<6m$~2XE~`7BtJUUka3H2kzRfvrANwJ;(AxyCTdL z+8}ARAfs0b5>SP4=j7u}d@@gh;NH*+`jHlvOpmE5o6l#$qyPbjOWDCHbtckpQp1m8 z)g^SnR$9>m3j1%NWRaKkG1(osI*oZZsaC6m*U@DLSL&8j^9u*h&VQ;@M46~*0QNx;4 z`gnsYQcNajrg6rUrao-u#O??6{%(o5$Ob_YZSxKiE$pQE1(eMms~{M}Hs&hK&J6si z`0jLzWD`AR+?jcoth-yl9guwd3QyJv{NUCoC|e^iJ3c7C5{W8g1cb2TCBKf1+JJv3 zwL#;6Tsy*Kq?vk_VCoBSsq>c~1$YSYNI)M26cZ`=kfVSLh?JZM5J!C;An0+wrnqxM z9*d8Cf$jgCgeUxGZ?HUcWnT%SC+_<^m2?J)hY)uZclbg`Rp{Cu&Vtk;EoV;`fk90g zB3rwDd$TGuk0p#wbQX2JWalh99oa3Nsf!4oVW^jKtP+O)Uov)>|-&a=ANp^y-ySP&pLfp>;*%_AlUb#H>e+F9ipg@ zMZi{g>BOvP10}nxA;L^z-6Dq!t3l-0Q-)AFjuPNN-a$4atd1llT6Fr*YRS`kxZ}Ci zEwnE$(dSf1`wP^qYDg=T-mXSa$OV3&7l@kWvcNsFHaDvQenCQ&kdSP>c0cwC^h>cn znpI-TOC{k?Ztc~ue0Ir%lHAtP-*y{w!euH zRXIX{7;D3PjY#PakWyWq?@Q@-9i$Xcf8MsW0u1srKX2S%u@Kt5it~BOMu>JNX8R@@ zY_ErWi$&zYVJWimuKha9wx5K7Y+z{+Rn?!H-9=)J!q2NLon{xo)3%j~qip29=Azpu z(%d)D60>$E8hRTO3`J4{rQQL^c9o20cB;bcupI!$>;OzP_JDlA**-Tx!R0FXOqNC3 zOLQFqeLaD6l$jah=0b6QBvmu;uuyZy*pFDIe1SE4u2C!j6K&oC%$c0j&)dMjR3i#k zyUHAj7Kup}`r<^2SQ0y%u`zVM(*fLZ{DJ}1{tUlxKm?`XzifucfMfo*14jJpfKa}> z%C!%HtPJwQFR5%~xX{~b^+l#dnldg>**qwlff9DoqYcqh?P6>d>|>g>Eoa!=lEJ-? zW;1ela%x0~JUKF=vr$SVv zOB^S+Q6SiDJ3h1?aXCnX&~ocb9Xt!kF&j^s&~LBQ$C*KU0gfz_TzkVT2n3x;v6xy& zB2y{WZu!*IF-8G-t`z$`&qN$a`$Ts-S;qO^6tzEZAmFF|gMDZ-2XNag3%cPEjHi&- z5y88(u4EyUNJa7@R7v`a?(>`GAOxSK9XV3d)!Mk)uhDc|pbIMiYZSIUw2&4k$z7h+ z+^oyEOu{0!Wx9POi|Zvf5_Ih;hR?`8+P${&N)MYu+*EeFM!;|c)E$T~{|XGZwNjyT z5H?Oqm;eb9ioP0?!L2Du%*fjxTv;OND+C*Wq$&Nbw#CWzT;FOmYYypA+IIhBB3JFu z50A3-ZtrkY^52j5JGyI7Tyi)FYLEh*uL~=H?C8iQQ#4CY8Hp{t&2H3`TE)DeK|9L- zdnq*j+0dwiIE#gBk~V}F6p5B6ZWcy`MoUB$+RD?TWY;cTA9ieH9Bu=ZJA|=SnpWUtX=c8%<4zT0I4|5^HTgE^5^dyYjNf%rEomko^=Q$T5hy>>;iwD%SBG7CAN^xnk{U7!ir^bUcf5?H6ak zH0mRZKE%CMTh00b*{z5=q;G;21y^hZpmGKMwmMu39VM)5n6 z{!6jHf<#LO5c0|Mgwen{BGF<;nc_jx74a#feRKIO;#5*EQ1MOr3YQpvl0xT0yz6^d zCc{FFxJtz=&fc=&<*v}>laDFF`L5a z4wgw`VEI5@*7ytjp#boAw7LV}9ZilGIX}GLO7O5Q`$2%ebT!j;%vfNJn z2%Oz@r9^Pa_D0{CHA^>HsJBJj*10o7NVo-gc^!H@9bh!XpwJ}TI;_O?qAb0h#APSIcb|eAWjo9SE!g zDzUr>_LfZ9ZUh2A{7u+{WP7KF@KT?8B49DZ!v#!#i!mzt6v1BMj@RimCHxuqCHdo_DA^Oz371B z--g0|3YD+{@xj%<#uNU-@vulJv+B5v+qX$|!Eq!$^JR^q^ZdZ&3X{!3GK8dzM4VnW z>;E2J5twAgJp(59^~S@U%|^Bc(~ZY*;CL6i@5~eQMuNmH)Ha6las!>;IswD1PtZNa zv~dx}I78Y+i}{_5%Pcro$2B%~I8ODzv7-*-g6<_ASTHHRB(GR+30?cL660CHzEg0a zgDKRf0?a1y&cs1Vp4bycYWLC{acg1sSm?QTlda=MDuc6bJTIMS;7pkX&!LVJ|T49ia31gi0cpmUZ=J{BOfV~Bk7v>5hQ zpL>(gJ+KPwnNP(R?&A-`ms6li^nLn$1;L&DfC_$;%E6tKB*v?9*b8UEfCqsUe3r%l zMcl9BC(f$7s9#}kN$s|CidGvh5E{Kj_V!uG1(Im(bnM#Es9bZaaS-Z9A)LlVO`>b3 zAT8o|qP0gJ16BAQCky3$I4?BFx=JPS1%UA!qLK<#*TKC0I2Cf0hEpga--7GpmX0oP zuzkErLSEwUsQ1p`=SffmfU=qr;{`7uz>uts*?JvA+$7+bRv|VzHuSK`ax*n5h<>zy zx5>m=3-#QXwJB=WHZ!Zvua$_rii%U8GQG^ndW8@}GAI9^Z8*89)ZTTK7Xla-o1(sd zilx{~X9xRQ&^DOz11v9KIyc!qq$59Qp&mh@S?`_Zb$o$9s7`w&X(1ySCf>4e4hl9! z#akvH=x6bv*pXKypy(2Q+Dy=r1(LHo;O(x)01pQpP9&d^Kn&GFq9LqEzGEbwZkF%k z+n9>6&)kiD6n1|I^|6qMbBtPc%4A>>8ES{oNH3LGq-`&P0S#C|#JF;X^SR6ZnARFO4+|B}IG(C`b3)XjuZ` zMS&3gwos^G)%r+3>`J8H8{SMbnQ%m?=R;(se;zhdFLvN-I&uikRA`$z0t9P7z{yuFoj(8X!sl^agiq%FxmR6uIIzKc zQwM7hhiMNbvo}PU>2AhCo{#!bytig#ri=X;LO(hGvHu!iR;2gE~JqWJO$t!P&q`mZkfQ3uX@VQC2IN)W7{Ep%RncTGR!mqx;O#A@TYvh3F1=+@z zVgb2-vC1BeI$URqXCl#iCq#~%S1R2xk;IecaVxq zA}!{|vI%T~&s<46nDsE!!F4HJPxH%8FFhqsad5%nu@VwO>;wMG9!zJ4Kyq! z^;e=8gn)cR9Wh8I90T87$wXXC2U=LH@(~F)p<GZ_cj*S+w`y5cjA^+a0cgIIZP(!tx`=9EXX(I*3lNgH_Bzlw&wdMFlIdvp65|?8 zcHF?yeQ@zyPs9KK2Uc`wqg1-i+|m&++V*ReN!{6RfM{6MJbkE)CoPurR=IjP1OscX zZ2+$N>2-)l=f(6k2L7zuu5u7(=rO$FN|GT#n{z%ojVi^t@Y#5EV3p+M9|Z0M2?Jy{ zX});TI>|58r!KvtiCcOvrwvJunu)%-NOe?ve-(j5qx>|VD)#jgen z|E|+D>C|DceZ9>b&TChQI(6hL*{R&Ql3`VxEbn;6;BN+wMhh zK$@uE0k13!k&*KDx+5(*|BNwYX4** zQv!NiJ*x=o1^n;KWlYqN0-3lu2Cg9Uz!_VkXUs$*f{PU^Qpz{c!GVt;67eCt@d5@U z=-ACrAI32WxJn$Lex{ohda7pexpu(2W4+M-V4N-2`T1IWr8gi#P}_7yjTmw>Ra zyP;veNXDq@$}88$T0e%ahf5b|vI{U^r}%3Q_Jtk5AqivuL#raUn7$KhmsxqhgV~8m z$9G7?Jca1PjC#qF32%xtX0lX?Zsa8&fP(SHsfsr!=Bk8=ak}yC0-VVsK&MFCYmo&O z>x5T{f$O^(SK5If@I|_YTTom-N5;zCp!Z~UyEi5t{nx9b#;;dl{5*#k4|_GJ(T~Fy zQZ*mBa$zhLnI01=a&|E$GE9rB5q`bV#7aEL_YKZA05?}kj7R|r?9D>A)NQTC$wRXJ zN6+w)3V1=?Oy_}u5@kz98_c@r%7s`Xi0}{1L5^sSHe;|^Si@RCge6*uPSjY?O{?e! zSliEt#C-IH;TE$LV{m@fa*GPIM&$n~*zw!3oPFMMJTejkLnn!A$#LEG%kB0b^m;|c z40_z&2q(m+ly13AuT}F!t_`p9m%Y_nb`(*OoB$rs`sIKP;Sxd_T0*qu!2yVS<_Uza za60|ctrdcIvkp1vCsXWCXCZ*kw->|j_Z_DX??YnAR?%dA3PZ=_1chFE4aR;V`kvRA zi)bDUo_8fv)?o0DK#HK5Yo^nH-At-xSVsQtyW&5&4;a+N5rb-855Vf+Jv@Utj$s*g zoU1*EA6(&kj}Gk8iYkQej9t>~Q&mE@=U_$gsxskhS9zs6mvua(3vbQ>f8<~C&S!~H z&?FrNVZr|nFn-R#>uo6XzT^?P;UqK;=64Y?6UBZNllpEIe*}hv!*5MwT5j_~ z5RPeShFy3C(Y(FP_cSF0#0z>LKdG4p`gR3V`rd;_I5-y(7Cb9o#i~$gCqPSibt6Oi zCc7Ij0_1JcuLjyr&MKC}OB)Fnv;cAA>uIs3504fvJA&_jek~s z+RdeMO-P8*Cy=;!yrGyfifW8o8IYzDl|cFTWr>E;-pkD{B9Pfzd7k$^N|0}r5E>#w zyLCY$&qVy4C;+4uEZn^tZ?Gc}<$Sp4gfB$>e50-x2KT^ws?Fy1j&fh?h%>dmmR3arw(%5(T_PDP8od-FM*jq%cTrk5SR*=H>l3D3(^rYsTL1Ify0 zFH&IMz*!}G3ciKK(>5FCGOCl=55>E988%F30J6~D4TW4dc+Tze7z9H)+?8P^iWyr_^HjomRFUr)sz(wNa6zt+e6m#}{ZLp)W0kWS-LfYPuc z{0vpQI)$^$MW#_Ft|j5n4qgW=pih9C?8j8X3CPCw7 zqQ6~>P%Qd-)6-5lmDu&iD`0kg1Bc%zu;W+3{9P*HX^eqWIW#X4!9@(gW+-M34p~c7 z!A&Y>zABo_&ygSSlX8RKU5m#Z_GG+WtZBMiA!jTSIS%Mc&X}9}Zr-L|oV|;1=&!leBCiSH&t)k6_<&TlLf&*Ul(R|;oN^e-~M`*)_Gn; zLK@fmH(k8DPDqTxC&(|uz{7JE6mjAy;Pkl_k9s8P#f@>EOcjQ8(U42;wD=1SzmiKo z)%?{t91}0}?jddG&#B0vqPB*N9iZCjvf~6@PwRqYvi&z3E|xh(x??{|WGMHCNU>E8 zrV&`kUCvf|_H%)Aa4v^s$%(7?Q$UR>bBMm9K%A#BsEJktoUF=*jEzxOc`1Yv*XR=n z?QR!KM3m9)U7VwFABGbF&f0>Pl{_PT406;SHuKametkABim_h5Bo1?*_IL*(0-U$ocu|Q2 zff?Xuk-6A+Kj~#O((!rg-=ka$D(Ewq*%`Gn5{=h)SZyvG~LuD z{d}oPzgdMFRQicUU+P1eR&$r5RpKuAV{BY&1bUs%X)QJGCBnTv!zp+fF&@HQb9uhICed?&w&&;WsS>Z+}n2-4B%L=<~$ zoyO{k(p-IYqdVRf!6iqSTITWbBP!y;kREb>1GU^P;Tm#PVac&!X8-a6c6`6qn~on* zJvxlMVCXhT@C{mm5gp61wcHZW5==1+YQ2j{Fw-R1wM8HGd#trThqXPX+GNC%CIpW;Vl=l4e&Ix73v66%lb>H9X*7b6FQcrKbs7Y*kmsw67Iwno&50px z#Hi9etaQy!Dw;DxuY?=AM(mqa>aVrodp+*^!kjEWho(^Q;H#}6CSnXh+bn$u!0yfRYi?xEV z2ues6DuI^7WjxKdr*76~7Mb(ZFkFV$Q^>v%KUg@@cXl8Cj%V+w_A&=MZlHQP#*Wgr zK;Qpe$D4=j3eYPYgm_uVJEc!kStC(1joPfK$~b@=9;>)=~dG!8fFISsxE6Tn^9id6qq!&WI|wK*;zHU=atvCR0V44D(a`# z)J+XsT-DGVXqtxMc{MoNZK}Dvs;n8cRSkiO)n(H!9y6`HF;H7yH#M)azGX^n6>F<& z>MBodsS}`+ODBvCj45lXYA&mp-q6xqRW_xzyyD{8nx^IeX2w(#%Py;}ssm8sSk_Wk zb4g28SyOZ4jH(K>om)4hzNM~GV>PjC%;_gh3{=-|yF8GYIPa8Mpq%$z&wA%V9<+Wq$W(0^qeFHHHV9508Rh6V-ZA)bp=BqAm zscj}enc}>vie?SJw!CiYP}cKdm?oqk?9WHci4lku097ic3*lSp8XD`H0S1eo+8J$I znyMNDmGx!i4GmRwm4QaWFRN>+Dyu7>Ue!d1HFZtRz=n?=EWigV0(JF`)5~kg=?JgN z%Gffo?Bw#MDz0|Y*vVy+PdW`XXO)kmUDz6?~-3assH`HU*RgGwDZmFvpHMOd)sj5RX>6Xk)tIuZ z4BY|{oU)2(HMNzP1{k$a0vbiBDG-pbNi(NUsjr17LHui=36*8#jg5h-%NiOZ<0{LW z%b_D+ZXgB$TB*viiss8=t6{gal*P_oUtJB=BdPFpQZpzzB~a5;CQ~)mUmBcClGR=_$B<(Q=jiW+M!vMDeDG@g9cNoS7blA7zw8Y($Q?7GZk<&9Iz zE^RDth!O$m*IW<1>#($@vgtKVO)xYd(^Nrls1U58zE(esIq8&%v12so#`21)Ky^)H zQ#065>#Z|}oFGO^-6btB7cg{C`03@BktKD4ctd?tELiDX-Z&G}*ECPlBS0!j6O>z% zRXE>a(OL)~9n?4Q_~Nqa@|xNJs5SPqu^mvy9JTdRYk&p(^ke7yq5*VxNo&g#l*&#Y>mR$qBaRkhen>evLh ziW)wg)HrqW%!VrZ%KDbZ3TTbdRZ&VVsc_$o<#o}}$3zvT)Yrqp!@0Cf$5IKTv1%%!fU3rc^(}}=0Jovhb5B)u zGin;6{7^$VL=hZ>C-a>XjEV^;>MLiGv11S#G(&L7R7fn8TeO$4ilHecMTarv%@xze ziifY5R*88U>!vqBhrl0W4rf#~P7zQY=tl%4XaTMl!=2I~YnLcC4_u%w)>c7Fu(GBy z*cjmpjngCu3e9+P)l}F!)1jtL@LX5bF$WBLV~o83{~5>`{iWPqT5XL_~;#u$by7y8s_i$Kba4Qba#6!Bo(| zm{VXr4SJxdUTh^S8vLlpT}|a>0fcsSQ;joa+L1( z0Q5t|cySq=a3zg50=V)@%>oKTndTSVVH32|V`5naY-uDGhsiXHpw%QNh4JJl9ciE* z?#o{X@WFEGE6!m2TnjHprfSXs`N~>|mWPWN*@}y?9t^6euY&O~ac9LeETRr$E6eB* zMFe%^>Y3#LiAF`5)QG95n_%e>ew-Dkm zTnhn8H8u`{e`S>x(q9c3Lc0z>-B1pLV{n{N)6@cu=O|4M~dK{um?n7ezuZJ@ojLgzh zKBKBqLKlq$f(4azL3)Ovn5!vp(qu%yCu_=^Jp(nzkP3>h+tkdm`(jG=;z9!4diVQZd-iMXk}gk)^2A{}4{Am-D{XHKco zRHfXUeD*1&0R*89*eM_igl#-iN0RWeWCYw$4ZlH&#z6IS2$2w4@5X=*_Rq2w%~0`f zT2>gvB35i@pkZqczyKTap$?`&wq!7Yl{FPqSjPF&%A24vppaMsVI@VmtOia#YN_BH zy=|ogG;rx$pph7#IuvKY6EXFsjjK2ESQF>O5_Ce1LnuWM9(ERp>PsIZ)xR7JV>;H z?=!2W*Ei0@Ly=^12G`V$4os`6ZK!H&!iuBGfj$h)du{RmMXZ zsCX#j2~+Vz#*3)%M`GZsY1U-t)wJd5)+A?ny7Zge?nu8$(hs$7u~A^@D%Lye)1OGU zCOA)EGP~_hv(dgL1LZZCi)Ay1^L+Z|uyv8Mk(f*nOstE%wztyXA&7U<2FcckH-zok zv)i2wdXgBh7$~!H*z@7n*#EWg`?~-8;ST}E!&SbWCsSWf1^-@8y(tYy+?2LBP4jPY z+6q>zNW0xs+@7`(6&~&{C%m2j++R<4ArV|&l)5@KnztV&e8SdG5^hf9$`apA5adm6k(D1vcsD6{OuMc;#wXeQ#A^~U;+n*NCV@3Fth|}Oe$?LtYJH;vN{pdl{qgYen-?-B;A{2R=~;if6msV7m@($g`|HF)<2ToMJbjw z5oiw)+CzztCW6$DCO(!Zta(gN;;jk1d@At;POm2*m%dB9I*ItKNLrJGp=**JNRpus zB>gD~TtbgeyDUl-emTESUYHCF_@A>P`3^igcO>7PY@N#*5X{+}{5UHgPyV!vka~6M zpJPbM6X z;B4o`l)0D;4+LkHMqK`ldW@gj1<=ZzM+hdu2rP^31T?qHlU=~YC%e3?!Mxe!eU>?Z z(2OQvc5Vr+3JGVN z4WawJM)D~1IGZ02{S{-KzlQ!EX+!J&;A$I+xZ3{Mwc9q%c0P9h<)ZvA_ue>`IlKR# z?mQWtciRB98)KlgFrFW~`(4P=es_MHu9zQpZ(KxcAGe>ffx%Puw+ft@AGb0t${aY` z?0szACm6u2&X2o^YrP5GL~BJ>1PAF#7VfkEiGQ!d`KS6C|H_B+wfd`z65$$gKyRnb zUFrhNQe1#RpXHoXOETJCb9b<9hx?w3b-d?(jFRRMe!Lj%L`lzcn?i@%)Mv_Wg5~>6 z0p_HrnVf4KoV<8&iKMgIS&JHcyyIBs;Nug=Zo6|fzCL65Gv`jlvXSpd??<_kN7Zx6 znoKBrou$g-?^1Oio}K&DGkPeRpI}E3w5Pl$)XMWY&U5Nj-H~g-e^yXagDYC^??@J?1{{%ECyV2<`MR01V_n6moh1vmQgVeI7F3LTSGBNKls_34R_##pl8Oy22Y8 zaB_P9=OHKH;{^PY3-k+JEYE5J_L#HLboL`bVDm_Ddk}@~?4dOu>Uynpy(Ji1_+i(J zoE>P4VCY;Myp}_*4FZ6k9^^3VC2Wp7`*KLd<#z_{*;czu&+*hlXrKpuo%OASULN!) zig5-)xi+{eh#sqgw+BJ7+k2jm9sGZ9@IPc#J?AhUhl;(Pw?h&o{fLe z>xhQkcy@Nj-(lgd!0m;J|B;)D_%Nn6da}22-pR2hI`8D%lWSEt_vHR5 z*Q#{>1W^4tfV$c7VdS~JZg!d$S~okr+GU3R9cTEN&N8v{Va_KxFbJRI{3F+zVsLSG z=YGURe3bh}SJ3tiELv#m?92VVegdN&>iQ_2okzR=wX1cGzX)ee!p;PcbZ5dlnHc&` z=H5(A>b;p)=R^fb{s4`|oP=i+K+R_pUQdLMzMi-mK5-#jYnM$~7!Go8%G#YJRlBqH znW}wRPiITj)7j5u>t4@g@7GoPv;SqP{*`@o4tx0%0z(9E=OB37nP9hEV*%A&3HB9u z*p+Z~4&a=l6$AZtC4d>Gc4BmGiC0?^U0dqamg-s%>0rV)jPbrn*vLrgeW2${q;p;3 zjf~iCOuQ5E;nLaC%7+N1waGipJap85nqR~YY|-00zIC*Eg6Hi zCBL3*l{oJuZ%6?)o4^iy?aNx7?U^ATwSaR=m*tEhmUp?1`+&WfcV^-1pIPr_q4Hgh zoa8*3{Ul~|o&*nl(s&TS|2sJTjxKvMh0}X8KgtA4KFZt_5eBqV0Psbo$FlnR)P;z? zoQ0_z4-(&z#esOxT|sNTQs-Z3ZRuR&lj*?pH`0&GVJrWl+Oct(?7W{opIZxpV>jEv zjwKkBU5gNGn-J{1^atuPy{l^7DI6)*^CnQPz?yfwCm)4wk8!Y$iJl6oKJf zpSqMWt+SN7t&6bR+L>x!iP?9iu1(W{xQ=(;PyI~#eU`c`4H~*EZB?3m3Hq%{LqBa} zoUc-^O2b@NrCpmAcS&GapXNX>I#U4ISu0pk7iz3BpCFByA|&oL}ePx(Oj8QEh# z6UH8RqO&>U@eJG%g|KHljj|C$>>>AY-@7k-eMVd(CT$7ya<0#~Cj&@19;8DVKXQBP z{Ft#Z6LP&V^TABJ36LJl{4>fFqH8nn%S2d(pRr=;)}-ah3HJ4i^&IU`%J;LQ%QRe3 zWy+y9aCH(a>}re`rQkq$h&FY)>zzsKsnzSHE5u%R#e?n|ZEH3r!8AaXl5R)__F^F> zIIjRSa0j(CJa!Te1E8@;EE1XyrP0l~A-Rp)skUVFnWWqNjdLIcn5T3DC%P5G)Sl}b z%0M>sDVwoc4>m;nX%flSSO917ja&<@X9yb zgN2t;C+Rj1s1WYu&=(9WzX<)n2=Rx|)jGa(u8u#zVDLcv4>18}WXxoY`H4a2PkIa% zcy;_&@u>Jpk1>pPmdD@Cu<`Eruj38QQbmmKj{h!R=K;3F|J{o-&5ECldo>P-y&Crh z_Ib`5al5$jL;h`7-1Wrt`p{FMh?qI^L)S3Sy#}=sia4wJq$6DCN&K5b0QHjEq+wtN zXOBAIZGTYuR{h8g(2r^z_jw!KO>ure8daWxwN|7O3+o${;p>$V2?y8S6DKDEEm6&kPA_I>PqpS>}vR+<9N#^BRI zv^^bso?E5ogRdH)el@t46?=nAe`d^NjCq+`mzVVzh`_7C8*NnFXv-K+1oj3$=3eUK z;O~K2fd70w|G}K-|C~TCpwMds<{L2@Poq!J)CgsD(sxd;huQn#UWW?ownKH!p@MG< z>`Uj=JKq*OF@iPGkn!xufXNuJrZ*;7(|h}HKnJ$lhkrjD72gkEKSEcmAF-JzY##B1 zuILodIRLc3H&(g7_c{VPr`=gMe9j0|%o!noP$7UW^Z=Q-6PT>&{Xkz#ux|KvPP_p9 zW5wi&-dxjH-kN@>!2V%#E4#)xe-rk?}2Ax0a{cv9xf`|JO)*BwI_dHmr znKNPmK;S_zC=)zcJ`5x)oKN~3=mW3^`aIDWi+rN*y#9bbuRo!GK}U^-j)w&! z{z$U?Q7}hYWB!phjWf6JReb^Ys=lB0hdC8oy#2e=)^{=67WZA*9~MvAyr|b%)fbpx zgV^_}{&0`=Xk9)cCbpFxv@lQfeWM@3!g}Y8e*5|Xm3{p<1Z4t52Y+faXuKZ zd63;EtT{MT9ymBEi-8e8^j|OlBNhyJb^z%9tc(z0=LkH=2-*hcnn9Zf#mdt=gWjiS ze1FiF1z6RWP!yC74qZ1KC1MvbbN?FjEqi`D=(z&W`{2;!!(gp-PBDs_-}EzL>(S0R z*!LUq;+wvUL0?&@v$)@_{ZMslzc+N1v#Q^p_$0BX&K8l{0f50SkO^?$TKJm#bQ zAM0;lIlEmXhBKHETpOvt5eo)@aFc{^ac$SsyNsK^EjT#TFOVD_cAD_${M|sz^4-9z zj|5&jrGK1L7{$HP>A6Y7;1=4LLkKUy9(t=h~(VGsUR!`%Q!m%GBat1 zY1sL2(590Hhafw)%W}@{ux@!_f*p8DCJeNDRZhacGEYav!)L1Dvam zy5T5bc*9XQAEgfy0?x{#Ze!JLN1;Di0pfi$1e1ABI%aa(hukp)-R>BIMp`51p&@@p z1#^0P3vV8hU@y7F!wJ7e_t21cG-JJPUY&vV^pF<_?S&z)4FMk5aeY1Hb4rl+WV)># zde=~NyK5*KWi4BVK7k6Z`s*Rfh9=lcqbJ=mbT_*JanlWxN}YlB{Lohk?bV@g4mFF% zGOii)2g>^&20b(gQt}W@g-%I}vTNoqA9lA^d(_=M3>*qFpDBd|yP?fnH;})3So?6J zHqM*Fb`AsNovfm6IG+yNj|w7x_pq0SCD_kJBme2J#q0*;O*cYyei$~7LHWGl*9`~C zkbNuS&35!L*?i~5K8xsy7WLWMN0aHcK5J2dJ-YK^A9TA=xYT=Ip9FhaWX^zdn;w8U z%>bG6{yu*qv_JKEqK_t%lhiy3Na<--3lLlM;JLjIXW%)W}SeE}}K3bm4f7?{NoqvcGhw^{a6#`Df znP?_@FM9S8Q~8FjjLfdbl(IR)ZoB4p*tg20rR@$Ul`1nWsyMAwnTaLC<$Tp;AI2_> zEX-Mwx-1oxS(f^FE{x}{9?SCaMKDpu(^;2#H#^;(`d%(%OgdeNPCN32TN*`Yb1G;u z0W?{Li4R>aK;(fK@WEe-voGVb40Qbr3m`V8D;{*$DEz>aA2a4>qVxRBhqz0BD3fEb zdckp$8F5WyMHsP`U2%dgU0)_9&*!83JXU45p{!?l`Etsm|DTMa&3-6zNv_@Y>E+In zTqJcTA*s8ltCYEJdEnH>nq#>0UM^@i!TCJ*Vdh?Ukak{3@2v0o0CTPnbluSdpg6Ip z5@&T@^RnJMd%%@Qn-?ab^|NkYGAI6Jw>NtL>YF`wajPwzv^z$Z`Q5KU3f;M;`#*Wo zB5mI8k^V?H!WC^xy@hAvw;($MZ@en4Jx$JT+SBfdIw9Yd`X)vCU#Z`ci{C=Bp`G60 zV-#^d$px|^0yqkdAp|hmo%snV{|QKl33v6lqbI(e$%9aN&edtlTFlhb;;ef>WvGs( z>KrhTp7GtR`8jl$pJNQZNv3zR4k2M_cm-9Tk6b&u?CSAI9>1T-`&_=S&&Lo17XV|! zxGm3Fo6E&NllM%X{+4cp_<7EO9K#}KRn|R{0=q}gBJOopkDq$LaQ?2{`KiaEo|qf7 z+=VA?I87nJ&hkx>j9S$5HS~2}>-k8Y3?`2Q&R=>y-4n}wy62ocLj&~K!KxiSug?<$ z=AVnnCD2NCd6ALh)jom+c5?cK9jeD^X>pV?a+M49blQFKtLANIRuUgDxc?F5A`4; zIsKumf0NDs&UzCma8_)}ei$iVwsF*_Osr2pR(C$~P~i6Uu#s80zmFV z$e$+IB|Zrt1O^%$#HQ?bkW9n0Qfp?zd{1RR%Nd@{emUFo0&iyTWYtcL5T+6tIpm}U zbI#9muj&d-x~eM>@`XE@t7JkR?0e9m+ z8*A#ko%UWD0Kb=ZYq~f6muZJsbqF(f;(TlRN?t!&nf{#L>$&uoSoKo+H-6PO>9}w; z0XKy1M7o}M2b?>3JqWqZ@SA?so4j}g4u!A7RVLl*x{Mp?D{jodjK85m+>^E~Ex~>} zLMjVZ_VI2JC;nR39kjVSy3X&WqlwSDe(4{A&F^*%&FVGX{;V58zdv_-j8%_ydz@8Z z#`><$c1^HP^5sns2{;RRoQ`CR`i^J&->Kca#9$JlXnX)C@XiCeRx%{ci|Q5S>Ge*v z8_&*e^$B2TfVfhleq!ZM>LH%3Kgt^i5@=oTF2^Z58FI6FN+sAgMivWEaaXv}5i~@_ z^p~d5?0$`I4+!;yb{XK4* znao0-@NG`^cHB}YuaaV^D>~+!b{P}^2htt~7T|@x?!BB)A#+xrw(jZbBze)k}ySu)pSw zl&|egNd9rq_3Y*c15Z28qJ)oK%nix}+BP`boDT@~15|2yZL~kM6YLq$>$~0&sAO^k zsoGPc|>EuT4%qtfZ4zW!8JCl*BblonB*UtZ{;n8TX`$v zLflc1X0t3AjplREe7g-I-L6NQ^q2Dh?^592QSbwu*>+xF`q22Z4}u@E@x$P)%n?!& ztUH4Vc8$L>Vuu;z#sd!aLu;68UK9UJLX5P&%$pFCaTDTi2|(a)3DWG5eNp_9c+9aR z{&nQGHMlkLce3iv_;&%0@E?o+3o3Nu>+x@}@s0Qgc;i9;=RA<`NCGM!Nq7>K&Xbry zbK>RDg7^gcp2P4>*uzuvJqc@gD{oEWTS>9_CLH4N_@RV1c*grik~I7HCNAOG?vg~D zoqPBu-pQ&v6W;|m!8h?QsPK^UPlMO;RC+BBiO6i{Pf0jvHmlm2^jebJ6bSvhYrFFr zCN$_LEJ#eSAM1$zkg-61?A<)cxZC>BiotzI2{13&!xM=;y3IqpaRzXlfqxi?SUcBP z__xN)Ffe5jnj~+3HI-o>R#=E@6A62V@7rIg;e(26yCRk zyO@REWj~oH2;&I-MNiH-%t-ZVD}b z2?5m)al!D=EHL%&xFU)Nw(~)jxMy$(Hp6rrfulCYB&Ryy6 zrbm?{ohTV>q|gW!uchpu!0kx66Tz&m+LN-6Rr^wIGU4y{DgQ;Vm}2m3oNw;ZW)wyuflt8oO!h09ke3Pu#2X5LzZihSvn&oBA|sFTN6K37PkVs-s%>en z@$0p;cM+Xaf^ns|v#NilrCwsLDq>M?uc6)Boqwi%kQN5+-sM5BfwbO+w%o&~2R69u zsFfX4?Q}n(CH%zw78j^tXtk}3A{zP}biZMrZ`^Bf7u&g(8-jD32jjkhwS8ljvnP$# z^$jB>huj+&wco&d+1||@S?%JuTjS)?&aH9l2xc9?gc}d14gfC+(X!L{`2Pa)#;J_*AluRBtJvn&?)UnyUyFLVxhN%*0OCa+sbsCsO!4W z^W0L9H|Vt~w1v%E&?`J!BqN5sGOn`*J$ifSUCyy5?K_GNg5&6?t3XL;htyt`o_jcS zk9*>;W7lV@R-_*;CM(j{q=&(x!^8v=U5B=sy!mi1BLqciFx zEa;@)YO^}&d{0?_TZig86FCay{D|>R28VZA>%CagOt8^<+QJl1TetAG!Y#p-=K8|Q z;2Kt}3Eth|;yAeR|Ju9q=qjsg|J~$d0x}Ok2q8ccrX*wl0vL!&mA2340O)J`fGZ#l ztq4W**}ftHBtRJi3`&871Pwz44ATpf5I_+qDis?UBG3dS5H$$`0Y!Mf-x+T1z2<+$mj zBag$sJfE39@{rt2pV>aXlYhTTEcSVw9)&*85}^7Ow)e7j9wseX9Bl0!9_YFc#oGSJWsu+Z$ z5E%OmnsqlJN_5x1kOU(@fNiVc;%s^m4_YARMY$Vt0oahcPXqSlen`NFxwGz;7%mpH zh=b`Lq(ih1(i!fjCfus22)8I#LdZ#4$bl9Q24HbEy_zhl)93KW#hgBS`}!`fAcHet z2}|Rkd?nE?8kcgLV7PdQYw6ev#in@1JZxBruLj>74A;%U({oKX^IFCmM0g{kQSX_$ zvQn(gc$;2tXRJU-hZ*EogQX=um%;3 za%Rw13RlK=)_{2fz+m2h(gDyhD?uv<77qldc;JQ{6;15y4$Qj?puD?Ea%9~UbMI1F zt0*?!wVB?V?^>8+CNK+gmgE4mBxjojIYdcLDM6(WV(}oLGvCD0!K+aEr4VqH4c^AOu#Ht>w5m;`Wr`@D zN8z)5Fb8VvXUkwm0J8Ixgc#WnA1Mh%D!N_vTt!;%w>!FuIuajG7h<{ z>x+_vSu9JQiU5TH_6IDG7yHz)xYhM3?+cei8j0T2^K;9pGb{~f`juv*e3xd|GiU3w zcMOoHvCj3%1{q`?uNe z^+&qC2SkRPdjYM7lumq;bA{P(B?l=k*QjpgtjGCTz(<~<{0_(;sCzbXdcawfKt+3bKqE_I^!S1QEp|pPgJ^+UZlz zVM4vnHnj$5N}{xLWnBc@rN zOJgEuKH}<6#p6)8^r%?l_+E|B#P^Pw9CDaX`j3GBC9VTrQfS~T(RiD%`4f-NF$?L;wqIYB?}!(IoewauK;@e zR^9~=L`97Pj@aoq;s|)T*7BbUujWrwh-x0RtroQc$p|AVBlIEUf z3Lp1Ah(8K{(px7~Gu!{0O$CBG3k+nm5ekX$omd?J!0Lb+Op9mT1CJ8^n$`Ra4qH3ZL$(pG3F>tMqsft0I zs$%AHD}HYKJv_Owr~R%txOT-Y=qM={bS&zqLMZB3-4Q<39joKzh0^Ny#ss(<6ApKg zv%JGyO1mk{(r)v3cyV6$L){hNQ1|&f^V(uM5{KY+S{Ax6gUzMh4l9c_alFR+U*9< zD$2NyQZXDQWF$~-D7);K{*hq+qTl})i+@@y{>7C15gO|Ma_*!q_glgBgcY*lA#XRf zn!#%?<-w>{PF<}4{wU&A)~8paKEf?C+_BDY+Y}3TQ|ujHF1-_rQiH?OvMK>vRv=vC zlNG2K*Z$&f@}S2}mZL{ej;e8tLsTa;C7^+9lC>&Nmny6)p-Rah4t1~U4(uw{C?`r8 zjx^#DUeCIo0M@Zq;kgW1mq05EpHtT$2p~%#7a!Coj`>~2rtm4VKP}F;`hi`>hMjJU?w~@sve7MVlzvd;RuY(t0 zu^-^W;mA5nU}cL4_qxbJoIDVPQTY%FtxJB}&9J~jWfv@z4H!x?V93-Qu2Zvkn&pqx zRII1sgh-deU807)7C8L1z{T^Y6$mV%y(Tj76LWCNGz?6oU%G*AN!^|r`mn;^F-@ax zPu-sys+ky|jHNi)rHHo))}b|ZjWiHyLDy0oRmbWYTUBXcYwBu4X_55&6BoMTM#3+| z_g(MMxYKn(qD=MEJ@SihHoWrqbX;J*=OjBL(Zogyf zFgLb3&wUCR%HCE7Sbh!=G#uH*3_~G1!S@f&^Gy`Cs~5N$@9sBR`Hfb7quu@di_h&Q z4-y+YIcU2I^|s&Z{#aOk-TwskcVu}H`LOG>63hJyxzJch5I2tXg3EIMJ^mbgSNI?G zhbB5+!aVB#k$(WKT=M=1_yLptARQ{4TRhKzBd7r=Z*uk^NAc3nAgm4`%hdrF*?3$G zs0frd|0}pU=M?7x=LVr>vjoB=iy7YqVE+d9_0*YZW?|=MU_N)%iqW|vB$R&NSRi%>9AL~31S|*yXNfyXAt{#P2BHe-B)R*fS1brzO!kWd;XexgU$Pth zGVXF5fR`B<8HiJHp93I^6`VfeX5a$c`w|O+mNP=jgNnGJRzw3x{1l2$)+*o>1^HxR zeTpWIK!MxMpdX0ni#AGJL}P>yW#yN$>V``!Y_o(b{7c%verCF>?O->QXc3p>h@StQqVoQeu9U$5P z8aj6fq9lZANqkhA|Te7lAfD-!q|k&Y0ilo-EVb=^ndC3 zaZe+_;+`e!e@kd+#srZuVUn>gx7@_S5Ozeajs_O?FD9Z)^tSujBFKGh58)Uj`oF93 z`#ZV9W^{p5)%JW_NN~RG>v&NC7=P*n<4>Imaq3ePcG?e@ipB1BNBNq>(RSByHdc3` z;#|8L9?-05_ZkjgD~8M?A90Lbr4wD{RUTx&6Fi$6v$I1M^2Tm;e098T-d(uNtgnON zJsh!Gh(2CMrrJctS8NX16N2FP$SA88`EYTTiL;lweViRx!WY^~XlM$8NI@7W_=&oxHQa_?6TK-qV5*+5 z`^dXaXsGL==10pV$NAA~qLa`AC%)W(slM4+f-}$I?!;_ga>~@4+z|gM-GYvo*kXAT zLs%d5tYUt2MKqI!aGD(MQEF6KS_4si;i^8B-jCqn5~> zs+fGt8CA5ujrlGH(f*DgdEW3nZvC6(bMaoJ=d`@eoK}Tk=OSw&Wl~K-Y->Ok#MsI; z5OF1Pm3gB;T#UqXedr~lRz=~3nGtGxEi-Z2c`lY978ikwh)H?Cc2(5ID2Tf`sv-)* zo|nP9A_~DmBvdWr1522;;S!V&ZcWUO##0su`G#D5@j;3i2oqcLMSZA<#=|d8Jp6*A zCfK`({w5lQ@k;a+vXu2gp|Dz<1(ZdEzN%VCHV&~ZW;Y)d*&TBv2E-r6oM7sm;5r3& zv8A_9A2!yA$@17*+^0a>v%k}2w3K)%jMD-&%fgE*a+U?48a{P=M@=}kOEB6bx%x|d zKxD>OcP!)uiNg3JyhU+@?VhWZ#Ie7Q?{$PI?{)l?Q~yunahU>LE0JW%y^ECrL`cR$ z-0nDozBA}68?eIo()i#%=zaD~?D?Bi6YJy4h*}oEi(&7I-y1K>5cv2*g^8cohD!(V zKg3DlNK6V3V5tRXz%KQi(~E_*G-V6cMsOg)^>GNmNs0OVa1#F%S)59E6ST#638l;W zE^x2!LSAqmfCY{94?Sn~0(e%hIlUyfG-WF$6Y$@faxeuR2U9*x!K~oZl#40AzL+vQ z6|UK-3?FkLq^21SL(dBWXShuXl(8w{4c=RNqx0&{ax}cU^T%|5+_|{4TRZHpw#HVz+%3xR}ZG=a6i7%CQtO zJ(g0H;yY;?8vT^NGXwvQoI_nh*3~I^T}nOS^(jp$zLSC1lyVDM%WC#U>ZVk-1GH^P z4L}C_cNMI4((U&Lv<9XH(vu$LStwq|=iVQ&v!{>FElJyLkr^ znWW91IGR|Jj1p0qe3{0%9*w-F+Q`eem;+!=YsMdvL{ujJJ(1Co(^D`%*W+>zxG&3D zD%@(8ii*z~E*pycq{?Je>9fhW!97|}XHmc;>*kFWau)?j%aXusSyDMJBr6Jva*ZKe zqeLS?c3a&imEw?SYtpeKJ&P8nlg_&AXOl{jgSaB#kY{PnCf`UlXbijAOLG#tz{v4S z1bZVH#tq2p3vv^V!Mc)koibjBjB-Wj)o#lcq$>E>ARVTNbaw22~u25yv@YFotU2lS7q{9@REtl z1|9-BRQ5Q5--sKMqA~GmqVFFt%e_hpY@?E5B=X=K@)-D{vDY;;J0@z2($=Mc@4B>4 zxO?$QT4`@A1-qV#0lGvO0fhQAWMzF?BNywj)6fW1_f0a1_XzO5=56(_6&}`|C7$Y= z!R>I`u{6ZySlStN`@KkB%cfYLq*cIizqTZ`}!&u!LVav!Y z?!6QM>j((U0~X@HelMou;TAF1@r?to{C$QVc`nX>$mjd`&f^7N(cpU>)2|sO%~`bqGa zV7xSSKEx6Y!S95KijWUs{VJqB1TT?RhsyUoQQ}({ww6!BVgaG`k=Eiko`>ZFyst!x zMR;b{y6PU8S&nNC`A7^-cr;sA1l0zKuYwwbu=5%8nX%4oQ_w~}btK;slCJ^5AI}GA z>-Wvp6~V>9cx-G-FkTwF6^vc5khvjZaY#W3p14{aB1-Yj6&|-*)RwPVeIJ4|sd+JY zxN2XFCEusQqf~Fk$S0`q{?uQAbSHF17+#*L4nu6i8^ZB~*PI9(N%{syx$$fl_OBz~ zjucxXw?>NpiF`lG5`T?49VKo=?Tp4^BCUDRWzp766lYf^rl*U+_lciBg_LwOI7Fl4 zhC}4xRF7DKtVFE%Y|2jD{}4xU-2wvOZU-(`l;e_7fnR~2Sn9XZPn6;Y4(Rbvso+YI zm>p0WfD6z88v}4_<#SlS2xth9_fc>vBXD6L7EQ_nd7t5Upg4aV6}va9r731?$H+MJg6V7ewP4 z=Kl7g7WYsw?WemUy3E7P3xMJ{?(pG~W6ZS}c{xus;C7wMU5tb-ZTn7Jd2bFy5SLr! zl~qfuj$I!sZ&JCeM`A7Ux2{!Pab^3nM3;3YtSb_W5=9AaQ)Aqgv^Gg>PTHCzDv}zK z#jWI--7Hv-CW+7B#$yBB4s??bKj87v?hU;}Uhf+jmZ-&J40y5Oq;cQq{%Q~PZb2R% zDnQ%Zb5~Ei1W?usN1@ZVq|5vGIBl1)BSYSZM~3wJu9vveYgYEaSt~3YRs@T(h~p8WB5qMfxp*g6 z*im?~$KmBe;9q_W{2Z%%&E#MlcqI^PbwwzZ`>@U?j)#{;$W?OCw?U0w5Oz2W3qTcd za%)Vkc*zY5E;YgDmAHyHS{E2=ZEQgYu?$N@at#OvbFs&v2`al+c89g3C!&WHeY6I7 zv3Q5_U@R6W_ID!il@PgPVSv@~IAR*IDpZt){w)-_jETMs3R!@LM3i7EkM=+8c)0i+ z!*p7&hGAaagf`<=L|LTVDzPv>1MVX3fZpgfqdPcaGK3S=o6u5h!`uW@y(+XP%VW34 zii4Pq$e9RdAfLyJ8QtfB;Buz}UF80N9J0&ty4Zr1e)_CU5F0Se6y*tf6XZw}mol-J zh(YdW2_moa&dwN4{(uNC!DvwO;Mi_=3I=w!P*~qceFOWXscTZjpD{+`cEGl-7>J$h zD$ZgwhOyY`9w5aaNe*~qH;)!Ru{;@luqW_n;zx<%c;bmf^xgSMvWu1-GBrJgVzj}M`lpz{=vuLg|Hg$c%$H*Sqy?h ztcq@)J|P<3;05oj;pu#5%I(XB-C@OA=l3fh!{avuKZg}%?bsuouD@Ty#&9Qj@p)0= zM_OfG_)7tAN&XVeCr|U4qyyfj;m1>8!Iz;7YadL8+t5P+26NVn?*x8?r)zw@t7lbf zcvvd2EZMfvyon#!Rfy{}{wN##37Y@o_b9}k8vl11zDUE%H9Swl zAHG)sc4_z@H9U+30NtJ((t8;zg$ltSdll^-vGQO@%k{-y$3VH&>Z zX$4?@^Xp*Xdi8)WGC)_FF zG1fX=PK@u2EV}u{)6UItozOO z$UlDZHtCij;pl&-DZEE@hj*_H{v$TQmiQjD-K4CWHr!C+u#Vg4MKh*|5#|Hl%8~B4Z z@KHAKu{Q9DfVU+7f7{?Mknj-eVckBM=dU(vctt;zjx&^DePY9>2Jn``{YLW%%T)}1 zrRz&TfNGbDbvqNL`NvDRcDNZmY~Xz~p9)l2L8BBIT>ez|GW-2;T{#B zj-EAJ!!13q^@p2Z5zLm-XN|;HIPU-dV*@{613w0Mf;$6kKV#Ehe`&*~$p$Xa-gIe! ze<#4%zf8JYdHz}BWNUcEGYartl=c`0?RisPc=AbTKZDLft*+KNWrA}YbhJb39W=>L zbUr0Dz&oFSMtA5md;w}irJnC8Q1q7GG6r>eKSO3e==eg4AyM!N-k( z-q|0ik8jpF=cN0)S zu6b4k?51K@2SvS($Vqn{pziwZ#z8nQO^{w%4T+&N3)?7U^hiXf^ZsGb6Yjk4!Jjyt zmdtY&hvz3kPa-w7TF#&R5{krsHU!Y24?gO=@28sJr_dznbdLDs<^1)}GeQeCL zFN`<3l2KHsDA$FRnbw#5s3~=v))edf*@%C4HWz)!#DYI!%vgv&Okt8_!f%F49f!(3 z1ga6GO4AYd47GmCGU^e;5Ga0TxicC5?3i)C(VF9btc@DXsVWj0coE*c(Es}&b>d1z z%Uau~yV67G>NOf{n-z;w<@0@1sp*aBu5lgVpa?7%w<`j zX4NOC{}EZ_kp?csoh@j=R`I+KihaG6sk>P8ftVik$EEn&QUWv2T*JtW_g?Z;`oQg z{+b?6=Tj3VG6X6ib~ekbb#=Rv67h4d9T}`bLwgzo`P{YPfhH7i6Rr(op8Jew0c5mbt zp8M?!$PicXERUI#L#Y&p#X~i9ORD8PSW@pPwCGNGNoE7osl)Wt>CSXE%9I~Q0ULov z_C8CtWTp%EY{yKRfSJ_I4w=w}=v;F;$GtR$c>$flzaQ^W5LNT@c7A7erKh*Kr-5?P?ReXplJ7LD-GIQsliy9 zQzk1PXefXFnTgMNIGpgCJpS1+MAWuPPy(GMJCHP3)1HDFWodaHsVsrp8hM-srQy(@!H#AU>ff6aX5ed&Yg)S z{040?<_?W*=)A-KH~6>4&(N>a^Lk^}Y646*EKY?CeLL9s@i+iU4E>@ugj$v{XOqzF zBMpkaX+l=!-JVWsJ>2f` zH|yiZ4EDnJPQQ0FeWt-%q%GPkFd3%7#>p`Gao>b?Q+~|4x-p+Mh(yNc9sc`(xzk%r zP=4BsW8>iVneYwhV_3MiXOh|Mvz6%uF>ie1Hs%@7hPu(4eZ3w3BK<|sQhpP@*(a;e z^tJGF{}S9CuwTWGW#7=7{fc!P72ynypWwwH!4PUQ zT!)|T1vm71xz^=NS@{{0`KK=*wPH znAdcBY)3!DhQ4&IqM!92hEkad+6;S0(|f01&pQh915?m6iJ>YEy7fT zXhZ*NP2Vh$vOO2B===P_)5nXQ^?Muo)z2vU0bcEgH$B~^?Q#$4{3Hd<(+y}6JlRH? p;%NGRE?b+j>rVgp%Zh%uA<&-Q;d70#CHk%Tik|zAZ8lYRZ3j1XUXBw#VwW_JO$?3@Y}f0%>xJEW z&E31fuGAzXZBVF?K#=}Gg_MUvL4bHvO(hVte-J4VMM(4qAhrAeic~~-s0y?woHKLI z`g*(z6^yiZ&iUq?GjnI=&ci-Cad=& zblJ;#cCHK{Z~F}Aika+(i-ocexLg~1cAWx>y9g07`C$CATk*0sSHbmSW8O&$)iJ8o zXJ}f_zD^in)+uPH!x>)#M@txusJGzb4txaD{(ur{l<``_R>AQ_sL{eQ&Qy?4NScJg zDl;O|BqS}{8beAqUFj!H!lzLVhoxPk%-S2mvk14gd>gWLSfZW=v=vflYyV};+T>e@ z+N6^#F}HQR3?<=T!=s}F4@I|kVZ5f;&=%-#`L6f1p9dnMxr!rpz6g?}_qX2%Vw;qQ zd(pUCgkJ@u!t63%2ie>PT!B4ZD4WCZXzCs*LndAK2ZE1QgC9`A$@QQ)jjrgUVvYBp zY(82AhkCvU@_MLIS%jbI`7VI)HKv_sLAn9I4N0(x2=fOZ%a6%0??k??IxA#PTl*qc zfo_7iWuWk%A42);l|Fmc`iL&svOTg~+3uF|+$viNvr85&aS_pBNk79>Hb@nj?`^`d z{}vX>2WldG8JaEYGG}D@&sLYKqGJ}0;>~K&zffv`zA)PHu?c%6{BDS3qD>fk(F_3$ zkE666rB9*M1=4L;t(7*BT37mHA`6@x9LQ!6 znmUbWR2B8DL)$3WHWFx`?i}uenZLdhtoK5jo2QINjI*6+*V1m>)zTR?dpGsA8yyX8 z(4no=V(@5#rb6`gurU&iLK{_E(XV5pz=jFYX&BQo(uO{GyMD0VI23rR5WG%TQs}P( zBe(h;vGURGhoN!Z7Pg!^C3M@hgV3ZNZ18!b=PXnx;_$Q7mrT76GeT@JX2M&Bj1%FZ z5s*gqhhZqLh^|r8xg~@BJZ){k#@`Kv*N$BtZzn6U9pVFKFgZ6j8m4?I}0_X z(i2O*?Kzp^gSNlm&X3zUkxC!Q_=VHr+yYbSoC`bS ziRIFqTP%x{shJ7skeV0+pE~=g4`FfF*0%9fdMr~6v?y0}GrlMn@{Tp zk1so!QX$J#R-B+k(RK2%D#DGbyx@BNYLp%?7N?z@D<%&dJ~1KY-O60i7UPH0lQT!4 z+0cjy$M*`*##NEV(sd3FveLCR*EyD~SYz|*98yy0f>Yo#jXH0@+fTx)co`=+Xk#mi zdQ%LVbxVsydnrb5mYQ?jqMdOBzqf3HG{jy#bYvzrHh6z*a45MWIS@;f95>;@97upq zE|K#xB|EX`g1PNMpdLLjD)FHXJUR7x<#%K~PT4Snm`c{nuMUdpZb1qa5yf59!;{iTd=f2e=n zKHaZos~U@b$OMj1GW*}{`YUCxzYINX_ow;`PPSN?2PthY`Jz@4$EENG)z~E$L~4H7 zflp!{ML8Pq{gy!qHVjm8Xl!hEEIu<=ar_DmYGSA&)g5Pc3?&DKl7ri+KrSnIgldBy zo<(3X;F|V4GG2~0fQ-p_I3IE4Sep_h6 zd^cjUM}|p#M@q&ge{jfy;|r%Gnb@9ZVl2R!AsPs9tQ+SereT%I zdOJwQY%0Lfp6Btg0LQZxr-w-3_RNyL5a7J#AX*A=jQ@#_^(x>HR9J+Ha>ga~l5!=>?6yO!l6}`N(-j<44K<6B@rt z`lmJCLiS(Lcq8$zYy1@H-_iJ=$^J(gf0g);HU1A8w4Z8xBia8}<2)DtuJLBtAe+dK z$IBw#rSY@mAJ_PG(syb6S<;Vdoa@bNoa^;9{sh(gn8pc8s~K?Lh-QeEFG( zco#^{XZ(yv&Xvm|>A9$&`CxJoR5X$VBq(HJ&sFkpQG}apa$d@rxjD~1&05jT;vd2> zo(!N&-~yS4>rIdq;ljgCP$!Fs(^7{EWyyw%WzvC5TN3UFaA|`}9O-$dl7#ErqV4(1 z6xUuz%WEI?$p4qcz#aQKIOj0pC_W(Z{8ZY?d}MtS2zYK7Wk7ih4^@yJl27&bKCFk& z58cFZz9Og3f~XL@o>u`0Xd6uTf`T#sSARS|U`$>IL6nK>zr=uYuAlP>;3(_$$H?Gm z%|E8;qoCL7zjM@o90&hiieserAAT>~77(lHCj`H*HB7fnfk&8OpjX zp7-%pSM3R)|D60|AO{ooAG5!LfH8IdH^_gO4Jp_C{{+0&_;urifyC<^syLws%RjP7C>Ckv!MsI2!2JYWWXP``@m5Dna-EH$~BT&;S4c literal 0 HcmV?d00001 diff --git a/src/source_impute.c b/src/source_impute.c new file mode 100644 index 0000000..7370d5d --- /dev/null +++ b/src/source_impute.c @@ -0,0 +1,404 @@ +#include "imputesrcref.h" +#include +#include + +static SEXP base_fun(const char *name) { + return Rf_findFun(Rf_install(name), R_BaseEnv); +} + +static SEXP one_arg_call(SEXP fn, SEXP a1) { + SEXP c = PROTECT(Rf_lang2(fn, a1)); + SEXP r = Rf_eval(c, R_GlobalEnv); + UNPROTECT(1); + return r; +} + +static SEXP two_arg_call(SEXP fn, SEXP a1, SEXP a2) { + SEXP c = PROTECT(Rf_lang3(fn, a1, a2)); + SEXP r = Rf_eval(c, R_GlobalEnv); + UNPROTECT(1); + return r; +} + +static SEXP ls_env(SEXP envir, int all_names) { + SEXP fn = PROTECT(base_fun("ls")); + SEXP a = PROTECT(Rf_ScalarLogical(all_names)); + SEXP call = PROTECT(Rf_lang3(fn, envir, a)); + SET_TAG(CDDR(call), Rf_install("all.names")); + SEXP r = Rf_eval(call, R_GlobalEnv); + UNPROTECT(3); + return r; +} + +static int exists_in(const char *name, SEXP envir) { + SEXP fn = PROTECT(base_fun("exists")); + SEXP nm = PROTECT(Rf_mkString(name)); + SEXP inh = PROTECT(Rf_ScalarLogical(0)); + SEXP call = PROTECT(Rf_lang4(fn, nm, envir, inh)); + SET_TAG(CDDR(call), Rf_install("envir")); + SET_TAG(CDR(CDDR(call)), Rf_install("inherits")); + SEXP r = PROTECT(Rf_eval(call, R_GlobalEnv)); + int v = LOGICAL(r)[0] == TRUE; + UNPROTECT(5); + return v; +} + +static SEXP get_in(const char *name, SEXP envir) { + SEXP fn = PROTECT(base_fun("get")); + SEXP nm = PROTECT(Rf_mkString(name)); + SEXP inh = PROTECT(Rf_ScalarLogical(0)); + SEXP call = PROTECT(Rf_lang4(fn, nm, envir, inh)); + SET_TAG(CDDR(call), Rf_install("envir")); + SET_TAG(CDR(CDDR(call)), Rf_install("inherits")); + SEXP r = Rf_eval(call, R_GlobalEnv); + UNPROTECT(4); + return r; +} + +static int identical_(SEXP a, SEXP b) { + SEXP fn = PROTECT(base_fun("identical")); + SEXP r = PROTECT(two_arg_call(fn, a, b)); + int v = TYPEOF(r) == LGLSXP && LOGICAL(r)[0] == TRUE; + UNPROTECT(2); + return v; +} + +static void assign_in(const char *name, SEXP value, SEXP envir) { + SEXP fn = PROTECT(base_fun("assign")); + SEXP nm = PROTECT(Rf_mkString(name)); + SEXP call = PROTECT(Rf_lang4(fn, nm, value, envir)); + SET_TAG(CDR(CDDR(call)), Rf_install("envir")); + Rf_eval(call, R_GlobalEnv); + UNPROTECT(3); +} + +static int binding_is_locked(const char *name, SEXP envir) { + SEXP fn = PROTECT(base_fun("bindingIsLocked")); + SEXP nm = PROTECT(Rf_mkString(name)); + SEXP r = PROTECT(two_arg_call(fn, nm, envir)); + int v = TYPEOF(r) == LGLSXP && LOGICAL(r)[0] == TRUE; + UNPROTECT(3); + return v; +} + +static void unlock_binding_(const char *name, SEXP envir) { + SEXP fn = PROTECT(base_fun("unlockBinding")); + SEXP nm = PROTECT(Rf_mkString(name)); + two_arg_call(fn, nm, envir); + UNPROTECT(2); +} + +static void lock_binding_(const char *name, SEXP envir) { + SEXP fn = PROTECT(base_fun("lockBinding")); + SEXP nm = PROTECT(Rf_mkString(name)); + two_arg_call(fn, nm, envir); + UNPROTECT(2); +} + +static void emit_warning(const char *txt) { + SEXP fn = PROTECT(base_fun("warning")); + SEXP nm = PROTECT(Rf_mkString(txt)); + SEXP cf = PROTECT(Rf_ScalarLogical(0)); + SEXP call = PROTECT(Rf_lang3(fn, nm, cf)); + SET_TAG(CDDR(call), Rf_install("call.")); + Rf_eval(call, R_GlobalEnv); + UNPROTECT(4); +} + +static void emit_message(const char *txt) { + SEXP fn = PROTECT(base_fun("message")); + SEXP nm = PROTECT(Rf_mkString(txt)); + one_arg_call(fn, nm); + UNPROTECT(2); +} + +static void sys_source_(SEXP file, SEXP envir, SEXP chdir, SEXP keep_source, + SEXP keep_parse_data, SEXP toplevel_env) { + SEXP fn = PROTECT(base_fun("sys.source")); + SEXP call = PROTECT(Rf_allocList(7)); + SET_TYPEOF(call, LANGSXP); + SETCAR(call, fn); + SEXP c1 = CDR(call); SETCAR(c1, file); SET_TAG(c1, Rf_install("file")); + SEXP c2 = CDR(c1); SETCAR(c2, envir); SET_TAG(c2, Rf_install("envir")); + SEXP c3 = CDR(c2); SETCAR(c3, chdir); SET_TAG(c3, Rf_install("chdir")); + SEXP c4 = CDR(c3); SETCAR(c4, keep_source); SET_TAG(c4, Rf_install("keep.source")); + SEXP c5 = CDR(c4); SETCAR(c5, keep_parse_data); SET_TAG(c5, Rf_install("keep.parse.data")); + SEXP c6 = CDR(c5); SETCAR(c6, toplevel_env); SET_TAG(c6, Rf_install("toplevel.env")); + Rf_eval(call, R_GlobalEnv); + UNPROTECT(2); +} + +static SEXP normalize_path_(SEXP file) { + SEXP fn = PROTECT(base_fun("normalizePath")); + SEXP mw = PROTECT(Rf_ScalarLogical(0)); + SEXP call = PROTECT(Rf_lang3(fn, file, mw)); + SET_TAG(CDDR(call), Rf_install("mustWork")); + SEXP r = Rf_eval(call, R_GlobalEnv); + UNPROTECT(3); + return r; +} + +static int file_exists_(SEXP file) { + SEXP fn = PROTECT(base_fun("file.exists")); + SEXP r = PROTECT(one_arg_call(fn, file)); + int v = TYPEOF(r) == LGLSXP && LOGICAL(r)[0] == TRUE; + UNPROTECT(2); + return v; +} + +static SEXP load_namespace_(const char *pkg) { + SEXP fn = PROTECT(base_fun("loadNamespace")); + SEXP nm = PROTECT(Rf_mkString(pkg)); + SEXP r = one_arg_call(fn, nm); + UNPROTECT(2); + return r; +} + +static int is_primitive_(SEXP x) { + return TYPEOF(x) == BUILTINSXP || TYPEOF(x) == SPECIALSXP; +} + +static SEXP sort_unique_strvec(SEXP x) { + SEXP unique_fn = PROTECT(base_fun("unique")); + SEXP u = PROTECT(one_arg_call(unique_fn, x)); + SEXP sort_fn = PROTECT(base_fun("sort")); + SEXP r = one_arg_call(sort_fn, u); + UNPROTECT(3); + return r; +} + +typedef struct { + SEXP fn; +} impute_body_data; + +static SEXP impute_body(void *data) { + impute_body_data *bd = (impute_body_data*) data; + return C_impute_srcrefs(bd->fn, Rf_ScalarLogical(1)); +} + +static char err_msg_buf[2048]; + +static SEXP impute_handler(SEXP cond, void *data) { + (void) data; + SEXP cm_fn = PROTECT(base_fun("conditionMessage")); + SEXP call = PROTECT(Rf_lang2(cm_fn, cond)); + SEXP msg = PROTECT(Rf_eval(call, R_GlobalEnv)); + const char *m = (TYPEOF(msg) == STRSXP && Rf_xlength(msg) >= 1) + ? CHAR(STRING_ELT(msg, 0)) : ""; + strncpy(err_msg_buf, m, sizeof(err_msg_buf) - 1); + err_msg_buf[sizeof(err_msg_buf) - 1] = '\0'; + UNPROTECT(3); + return R_NilValue; +} + +static SEXP try_impute_srcrefs(SEXP fn, int *err, char *msg_out, size_t msg_size) { + err_msg_buf[0] = '\0'; + impute_body_data bd = { fn }; + SEXP res = R_tryCatchError(impute_body, &bd, impute_handler, NULL); + if (err_msg_buf[0] != '\0') { + *err = 1; + if (msg_out && msg_size > 0) { + strncpy(msg_out, err_msg_buf, msg_size - 1); + msg_out[msg_size - 1] = '\0'; + } + return R_NilValue; + } + *err = 0; + return res; +} + +static SEXP try_get_parse_data(SEXP fn) { + SEXP f = PROTECT(Rf_findFun(Rf_install("getParseData"), R_BaseNamespace)); + SEXP call = PROTECT(Rf_lang2(f, fn)); + int err = 0; + SEXP res = R_tryEval(call, R_GlobalEnv, &err); + UNPROTECT(2); + if (err) return R_NilValue; + return res; +} + +SEXP C_source_impute_srcrefs(SEXP file, SEXP envir, SEXP chdir, + SEXP keep_source, SEXP keep_parse_data, + SEXP toplevel_env, SEXP all_names) { + if (TYPEOF(file) != STRSXP || Rf_xlength(file) != 1 || + STRING_ELT(file, 0) == NA_STRING || CHAR(STRING_ELT(file, 0))[0] == '\0') { + Rf_error("`file` must be a single non-empty path string"); + } + if (!file_exists_(file)) { + Rf_error("File does not exist: %s", CHAR(STRING_ELT(file, 0))); + } + if (TYPEOF(envir) != ENVSXP) { + Rf_error("`envir` must be an environment"); + } + int all = (TYPEOF(all_names) == LGLSXP && Rf_xlength(all_names) >= 1) + ? LOGICAL(all_names)[0] : 1; + + SEXP pre_names = PROTECT(ls_env(envir, all)); + R_xlen_t n_pre = Rf_xlength(pre_names); + SEXP pre_values = PROTECT(Rf_allocVector(VECSXP, n_pre)); + for (R_xlen_t i = 0; i < n_pre; i++) { + const char *nm = CHAR(STRING_ELT(pre_names, i)); + if (exists_in(nm, envir)) { + SET_VECTOR_ELT(pre_values, i, get_in(nm, envir)); + } + } + + sys_source_(file, envir, chdir, keep_source, keep_parse_data, toplevel_env); + + SEXP post_names = PROTECT(ls_env(envir, all)); + R_xlen_t n_post = Rf_xlength(post_names); + + SEXP imputed = PROTECT(Rf_allocVector(STRSXP, n_post)); + int n_imputed = 0; + + for (R_xlen_t i = 0; i < n_post; i++) { + const char *nm = CHAR(STRING_ELT(post_names, i)); + if (!exists_in(nm, envir)) continue; + SEXP value = PROTECT(get_in(nm, envir)); + if (!Rf_isFunction(value)) { UNPROTECT(1); continue; } + + int in_pre = 0; + R_xlen_t pre_idx = -1; + for (R_xlen_t j = 0; j < n_pre; j++) { + if (strcmp(CHAR(STRING_ELT(pre_names, j)), nm) == 0) { + in_pre = 1; + pre_idx = j; + break; + } + } + int changed = !in_pre || !identical_(value, VECTOR_ELT(pre_values, pre_idx)); + if (!changed) { UNPROTECT(1); continue; } + + if (binding_is_locked(nm, envir)) { + char msg[512]; + snprintf(msg, sizeof(msg), "Skipping locked binding `%s`", nm); + emit_warning(msg); + UNPROTECT(1); + continue; + } + + int err = 0; + SEXP imputed_fn = PROTECT(try_impute_srcrefs(value, &err, NULL, 0)); + if (err) { UNPROTECT(2); continue; } + assign_in(nm, imputed_fn, envir); + SET_STRING_ELT(imputed, n_imputed++, STRING_ELT(post_names, i)); + UNPROTECT(2); + } + + SEXP imputed_trim = PROTECT(Rf_allocVector(STRSXP, n_imputed)); + for (int i = 0; i < n_imputed; i++) SET_STRING_ELT(imputed_trim, i, STRING_ELT(imputed, i)); + + SEXP final_names = PROTECT(sort_unique_strvec(imputed_trim)); + SEXP normalized = PROTECT(normalize_path_(file)); + + SEXP out = PROTECT(Rf_allocVector(VECSXP, 6)); + SEXP nms = PROTECT(Rf_allocVector(STRSXP, 6)); + SET_STRING_ELT(nms, 0, Rf_mkChar("file")); + SET_STRING_ELT(nms, 1, Rf_mkChar("envir")); + SET_STRING_ELT(nms, 2, Rf_mkChar("functions")); + SET_STRING_ELT(nms, 3, Rf_mkChar("count")); + SET_STRING_ELT(nms, 4, Rf_mkChar("keep.source")); + SET_STRING_ELT(nms, 5, Rf_mkChar("keep.parse.data")); + Rf_setAttrib(out, R_NamesSymbol, nms); + SET_VECTOR_ELT(out, 0, normalized); + SET_VECTOR_ELT(out, 1, envir); + SET_VECTOR_ELT(out, 2, final_names); + SET_VECTOR_ELT(out, 3, Rf_ScalarInteger((int) Rf_xlength(final_names))); + SET_VECTOR_ELT(out, 4, keep_source); + SET_VECTOR_ELT(out, 5, keep_parse_data); + + UNPROTECT(9); + return out; +} + +SEXP C_impute_package_srcrefs(SEXP package, SEXP include_internal, SEXP verbose) { + if (TYPEOF(package) != STRSXP || Rf_xlength(package) != 1 || + STRING_ELT(package, 0) == NA_STRING || CHAR(STRING_ELT(package, 0))[0] == '\0') { + Rf_error("`package` must be a single non-empty package name"); + } + int incl = (TYPEOF(include_internal) == LGLSXP && Rf_xlength(include_internal) >= 1) + ? LOGICAL(include_internal)[0] : 1; + int verb = (TYPEOF(verbose) == LGLSXP && Rf_xlength(verbose) >= 1) + ? LOGICAL(verbose)[0] : 1; + + const char *pkg_name = CHAR(STRING_ELT(package, 0)); + SEXP env = PROTECT(load_namespace_(pkg_name)); + + SEXP nms = PROTECT(ls_env(env, incl)); + R_xlen_t nn = Rf_xlength(nms); + + SEXP fn_names = PROTECT(Rf_allocVector(STRSXP, nn)); + int nfn = 0; + for (R_xlen_t i = 0; i < nn; i++) { + const char *nm = CHAR(STRING_ELT(nms, i)); + if (!exists_in(nm, env)) continue; + SEXP obj = PROTECT(get_in(nm, env)); + if (Rf_isFunction(obj) && !is_primitive_(obj)) { + SET_STRING_ELT(fn_names, nfn++, STRING_ELT(nms, i)); + } + UNPROTECT(1); + } + SEXP fn_trim = PROTECT(Rf_allocVector(STRSXP, nfn)); + for (int i = 0; i < nfn; i++) SET_STRING_ELT(fn_trim, i, STRING_ELT(fn_names, i)); + SEXP final_names = PROTECT(sort_unique_strvec(fn_trim)); + + R_xlen_t fn_n = Rf_xlength(final_names); + SEXP failed = PROTECT(Rf_allocVector(STRSXP, fn_n)); + for (R_xlen_t i = 0; i < fn_n; i++) SET_STRING_ELT(failed, i, NA_STRING); + + for (R_xlen_t i = 0; i < fn_n; i++) { + const char *nm = CHAR(STRING_ELT(final_names, i)); + SEXP fn = PROTECT(get_in(nm, env)); + + SEXP pd = try_get_parse_data(fn); + if (pd == R_NilValue) { + SET_STRING_ELT(failed, i, Rf_mkChar("missing parse data")); + UNPROTECT(1); + continue; + } + + char err_msg[2048]; + err_msg[0] = '\0'; + int err = 0; + SEXP patched = PROTECT(try_impute_srcrefs(fn, &err, err_msg, sizeof(err_msg))); + if (err) { + SET_STRING_ELT(failed, i, Rf_mkChar(err_msg[0] ? err_msg : "error")); + UNPROTECT(2); + continue; + } + + int locked = binding_is_locked(nm, env); + if (locked) unlock_binding_(nm, env); + assign_in(nm, patched, env); + if (locked) lock_binding_(nm, env); + + UNPROTECT(2); + } + + int patched_cnt = 0; + for (R_xlen_t i = 0; i < fn_n; i++) { + if (STRING_ELT(failed, i) == NA_STRING) patched_cnt++; + } + + SEXP report = PROTECT(Rf_allocVector(VECSXP, 4)); + SEXP rnms = PROTECT(Rf_allocVector(STRSXP, 4)); + SET_STRING_ELT(rnms, 0, Rf_mkChar("package")); + SET_STRING_ELT(rnms, 1, Rf_mkChar("fn_names")); + SET_STRING_ELT(rnms, 2, Rf_mkChar("failed")); + SET_STRING_ELT(rnms, 3, Rf_mkChar("patched_count")); + Rf_setAttrib(report, R_NamesSymbol, rnms); + SET_VECTOR_ELT(report, 0, package); + SET_VECTOR_ELT(report, 1, final_names); + SET_VECTOR_ELT(report, 2, failed); + SET_VECTOR_ELT(report, 3, Rf_ScalarInteger(patched_cnt)); + + if (verb) { + char msg[512]; + snprintf(msg, sizeof(msg), "Patched %d/%d function(s) in package `%s`.", + patched_cnt, (int) fn_n, pkg_name); + emit_message(msg); + } + + UNPROTECT(8); + return report; +} diff --git a/src/source_impute.o b/src/source_impute.o new file mode 100644 index 0000000000000000000000000000000000000000..84e264077d8e4f2aeb1dab260e9b97606d4268a5 GIT binary patch literal 90472 zcmdqKd3Y4X_W$3V>6s*iB!mEA6B60CKu8o9P}wwKPy|G9i-9B%g@l_0x2qx$Ax0Ej za6!O*1DC58Q4~Q`+^*sdE+}46K}Au#3X1Z3pQ=+mrz;us`ni96pI<*uGTpB_b?Vfq zQ%l$M%v?6S;P^P#b{2U%iiZ7S#P))nVhGtb@^d(7K$VDnrH(KKrZ&{vT2In=xG5{HOe1(3t

h;fm z{c34xwrQS)Iv%5KG>!tZpgxnUt1?Q*`7P9-vEXK_2FCG}!u6Q5w^{YT`~`xp-yMz*E`Rpc03N@#*V06f9dL+pZrVTu8m*cE9=MoM^%f< zqRvxL--k>XRbRp-)xC&X>Oy^04XRjB--U9aJ|pV$I%}I7Yo$^3(@n)p#S7}b74G1I z8WcckjhG%C*l)phj`p*Kx_%@6()dtN_oXq<6q<9u^n61K>b_F#k3r+vpnr7BMKv@! zXo%)TTQyXU;V9Ko|BsnOkmU{>}V23fr}_emig=zi!oi>Se(UycE>cPE{`pZo*4(L4D^g-BCzE zUCB!Y^(D z9pR+TeG^G$wBR_3J&zUCp`Qn523(u$W-@B5Ba)au@-(T=SqC?mp+z|(=cmSFdSlLd zPSC?my;E0v8>)^P*Q^O>Az!kWywuuw)C=8;L6g+E_arz@{VK|uU%!@Qb3WYBeJ{0w zU!f6oFDUtS>EyV&jdmx=udAJpjH7C|UplRzZtX&xqajGw*G(4Y&w0)rapXtUCt~u) ze4`W@-P&&>P||z*4;-jnU;P$HLEV8JFaD~`A+5+pD$I%is%lmh)T=^_ga5F7)?plX zMpzTtGZ9sr=K{uOt_799VLcIpxTRfehmZ3gOP ze_Voby;y7cksFAn;RxXO|D#lzrboC?xap8;rb7x$7i_Ht7t}rh(HCp~j{lHE^wpk2 z2G)LEP?%qqvGaWxqt?G8=Dg^RI&y#N+!sNh$c}M}U+zk^>ucVGc@))T zfvS&=Cv*Yh(fWQE|0bAn^Xt{Q(cr(RhT^3ObFj zOiP_R6H}QSH8_kv*|8LDhsxKE!cscFzLrXbP2}^Om$6)VeGje|v^?#n0|$6rgchPE zQ>DyOHv_aBNv-`63Utx+H|Uo5gqE0>{74FeO*VNIH8-eg>beaJ&URJKsDX7$NrL^q z(JeS~6-7^K?E%U+StYL1krlk*z{pkmVRbe}CsI8;XIJS$tb0uFRi|H$nDc@dZMN26 z)cSJAcvR4LHIFxS&#|Ym8Z}O}*JurwEAZYdl&zLFjo###(G?PA;FW7Lz2cra%daf>z%>rJk3{S7nFOwVY$e6=*3qLgRI zB$a&~=2H{b!(S@S@8zlGNows%cq#w#=V@J2z;``^&mLTO7H$$|Aq%|!{*w+8*N{;; z>kAgFT~E3fkY?u@O7Z(D-~4dc9c&qb7!3}f6n`&jpYW?5>-=h(R6nL!7%O3&Psy_i zHbe~i^x4>0E^)$>(06WNILorgmM-(Q)flddygsSK$d#ro7NPV4qyeR@Fx zO+IVqqlCa3bVS`dDyLe4egh$lEhJ>dlzg=WeH#gK37QNiU9n-^>6qD)Gn`9*nQhbqEu-rA{1MAQE9$c1*X2MFqJ)*=cX z_8VC23YeVHwls~WW<$&o7brWP-?4U=7+9k#As)T4)YMB)(~_fVccorkrfPg2G*BIL zJ{(1M)!v5duUjvS28~{MZcUwgGx>#9pL|ykh-@--!VhZo`8tfGiFw5LmQ8xA3G354 zOf}~0O{l&hdJ$heUluTV6&Xj<8oktQ7`C8P&tSvq?xvKTryZc#YZjT0>57o5Fcv8~ zM`N>AcT87^Pc=#DE_0Ci>YnccRbcn1n25SIxWR*{7*+dI>eatu1j_l)-ybv0Oa4QZ z^bHs8@ADU&fpz`IHJ{`!SnvcYKns7oz(~}BS~tILf${=Yb}D(*47}{T6IE98wu!(a z`=!o3g2qbRF+hM;6)h9KMfL@Q{`NIi6}9U@9bQ}%)ZI;G>DXSNdeN*Soz&VFF{+pl z(E}Vc>!;u7wM!d}?FDte=v{&xNubfV%&%WS=8ae|9JP5Zb?z?MhoIxOgNFnCNDqcI z$aawq)&mKOG?n71874JYhtmFLa750>f(RzOSW8t?AUIh^CtR#P7;a zlt-J1`ywQ%<$|8ZL~jUIv^GP9;BhowQNv)zFXzJ%b+6H_6)wL{G8=HH6&zHKJ=>f`I(=SD0T))@=nZ))CCbER5y1bn9P{}T(2#ug6n{(MxJMrS>!0#GLmEFKU& zfyD#w@jYWL8tNC&FkJg`YVAXa=yeIvb_pyE-lk|>P_m`bbwU1ur4;eJ_o((E%_P9l zwQ=}5^cFpx&RVOw2TyGIsl$Jb6$sIJHxcWMhV!$I;eyzat9HkNLx$>9*Z~hx#J3qV z%o3GmCS_Q(<3&5|s>-I6J0;~a%PO5oW#z?X~kO&MPUIVX!)%QB+w~l3iR>UF1|(%$Qa(vt(K} ztaZkoS2kk?jLn>;Y|UibGtcQ#b&hj>QDr&OoXJ#Cr@W$adeOA9i%LcpRZn%M*Hl%X zR#ADLQ(99#nJQI<`qh-962&Fu)n&-ysA@aYDvF9vA}7r#f{m!=XfkCOSu(v0ZbRA1 zxJ+eSO*t7dM0*f{Z*{Ngky%y_6SO?JJKNba+;PH=n?TvJ>E9vrPaem%jb62) zQ}4ZuqwtcRfTg12^!37Jur)nFby)MF6{g22k#tYLxU?(iq{k`AehC@1xax8-A)S&# zfYoMPMTs{N@aRSeT)p&<_lj{9?v(N>Ub<3>{_i@G4OX^xb}vDo$m+QfF<6c`K~`T?qMb54xl?)E}okNM5jP z!-kNYDp3h5jX7RCktu$XO85V1i;vYpmyi&(tfoi1D(iJ7tET<5>|=&TtEkh5>GYQ= zeK6_Dq<=%)$hJTZXTZ>5W1PqUUB=HQP3suyDi1Y>Uxj+5XoVNw!^cX$s^)f`<5)_H z(qA<_-OoVv)Vtz{|01)BuONAtedD@O6NEf8{7yR=B82Y%#lnp?QoaJ7)_JQ zr&l>A*sR>icT<#;Ij<^Wq%DRa>%>NiJvAt&@}AZRo7hMhr#B*ZQ6pts)`;A!AZtyw zkH;*QT;>$G>9RU0l!U4~quiDO4^>P$*E!iuksfdV<^Ynm4>JRfb~~42D)Y7pDGVw4 zpfH(;RM$Nc z*xa^-a0~lUTeTT?%gA+BFqa=BXInvnurCb8Nb-zgrznVMvUc35ZVFA)p+%2WBx4=w zoa-jbimR@1UNEC#l|i{fPU>%JYFg<$w{-$Uy|YA$H&tF#%Zpj!LRGBuRUn~7_f^C% z7D+8+LD3?6@acx+H!(o|l``8|=h#(4-+gzG61j&b`T+aXPaXOI&|k(<7syxvBn zD0jwp79_rpxad3p7s}B?1ocCn=K48M889o_6KR#OL?w@lyHxc1KzF58#(EaQFoP?7 z1cX+h2456{NptJa2PAZi?;u?@Hu4i zS0cgtkGyb1nYuBP1ZmR(7rrU9B+Y?3n1b>UHZ;WgwIPT57qlYuZ8Sd4fmyyppnYha zT+5o}2v0%{)S#)_uM}?g?Q>>x^=sUgqf=4s$Ay%)L0rVYE-$u-i$F`EbUr%s>zS#g z@KWrSIt8hJ&{7D}##-v^a1%25f7enUYE#sV@xN`UzA%(x;Lo&FRS<5GSH&H#q_7^!dCdAfdHl3 zi9T91I#x!DrP+CuPWBefQY!!|);Ei+E!OE+-UN#9FQ^DV11Vhg2MSlQ!v4`NLY|A( z#`hX8K$6qcRDW@Bs`vXB`HnK{1p7Dv7F1fL8^cbs0+tEMbcDiHU^xERG1#MFxoz5v?hC-b`bT} zqjg32SJK=^BzQ-N3q3#&6{Jl8F8s>UT=*|tdV|jCx6EG{nlsVtx-o*W>$jE};rpXr zhkPyW26VlKyUjMWK*fF$DY`C^Be;3+65mW-v=tXk8&02#Q@?{zu?ON0w8;l+1T_#h zY=oOq=98`eMI*#hBbX!57!#q19EYNO|Nmd-$p=}dJ@+=+7``=h*R@mU^cL+ck3@rp z?(|(er%HBW72)Tg;h=-h(F?II3?v?HCHgfC46;=4IV_2=_cai|fH~abb1L>m&6T0L z;Kp^6E1zyeN4uep^MTK4U`NTmion25wLie3vi7te(;j=E@QQmJX^^u>Km$o0FC}A^ z)$=gNIZsPAhzP&_WYD!*NX=kT&rm@R%YtBU%b0>{{?me@ zOZi6AbTv{Z>k(VV3gs<3c-|`ldBp|*<-Pmhc{c>|inRdc{q*2@e-Gr%jr#g&{5x2rqHAD!vicPXl?H@zln^!uqUDfKT|fwPTyUjByPmhRi0nX=^_f6c zVLuJX@9ISNh-h&XT)X@{QLeTS+nDM`ImsWwT$WAphc&{l0zXA_)oTz2uwEa5PD8ts zmuNu|PrmG~;mD&l|8Iqm-u!o+L8Qq2NNsv*osP3qZCV1~N;j-n`8m!cKKxb^S`QXdDTu!Kb69}gh0xJ6DP-llVQ26iYhA7k$+u<@wSuD~XvLfwp2aeIQ`9a+}-8zLgi;w^DLpx4GPVRV+Cur}oBL2bq#<6D-4D zj1MM1<8Tkba{gQ=;%3Mq0#5y;y>zNvF_2Sz$Ix3F?DgoHe}^u`sqe^Z{xr8aulX+y ztoi*C@y3Ean!Cvl!MMM==2z|dr`G)Kryt2h;w5Uw-3-&+k#<^9T=yM{awiiYK2 z;*tBYx{x+*yK6Gx>i_G;@5Qk~^~7Q23ygNbxeog}$@uDM);tyci@d0(Vy0$4950h@mF-omC!EgMr~UC`Yrm z=%tIm8rhBtb?bgV6T-)_-~TOI@)8yp0Nz zPQOgLdpW!jPT@OBbGa==>!p&YxbH9%?O3`*b<=Ekix$Qm0M#eB{oP{QTYR+dorSwDNvB-D#{h>2IicJZaH47y0kC`GVxy%~}vGfn53s)R|;{^<^5g z3dSJ4JSgt0HqNpR^3L4|h)|H|cLs5@;A_tk7meYEpmU>cT<53oy;vG~ z_X{C!mArUXUc4qR-j^4ji;MX0<;Ab^A}p3J@u~8njlAe2FZ#%f0pcPMTJR7b2zPbV zq@LgHV4*b$sedB0{5B5s&mO=_v;ucPG!6nX%^~xGmeHe+dw6s-p$D8klZk;Hu5C#D z)lUuPYS&%at^2dLXg4M_zg=m0bd*jPYdSxfmO*3u?9psg+_{Z#GV)5F`{SptkBYnh z;Ke=bXAc(lej}WU``PC-xyZqs3TuV8yn}7#+d_5A*2xWAd3+o(ci%^O_=oaQD zUsu%i6yGWEB6SN);=VjJNms>#4>OrbAQx(VVVYo0)xy@uLHlXbSfL^HJKt@A zxFX5CKP5@K{S&oh>?)A%OQS-*GpU$cxEQsAfA~S{c~01a*{^ZKX*c{kE!bFl(<-%T zU*&|J2#Nm+QGl`;Ca^v@`@dBs_!QU&$~a6H!m6vl($_! z<&0SMJwhk5uMUgmrFRa-b7JG~99$?;^*aZF$z~dwepx_7zi$w@O(LOZ148P31B9&~ zLe#(BXAv3B^nQXbM0!HF&!HJl-JrO_g_PJmkeH|5!3fOYBs3}@beIwf%)2y97Hc8* z$Y|+g;@|zeQBJqZ*Ehni0{;iAIgW+~jG?HFJ_6m8Mz^h6vauGV#y)LCrO{^;-s(b^38l{4YQz0d5_v}}V*W|>2yJ{Ov6ys9@GRyTa#;E4cAq=!A8w2-qV@Mv{lt)L@ob@K(75o7Nm zU4Ya-u~ujyAD4_)CylG|ba+3N^4Ff2jD`imRX;1~w|^j%=%I=%xRv5f_9GJVbI|(0 zzX)Wf>GB+vT|F@6_dMzn=W8ze9Ce9n8WE?deWlM~3swkzfwTy~*D2z7C=eG|L#o5Q zwF@RA6$B4;ltGY)2zxk#63yY;M=P=nB%qn_$kMSUojzXUWKc}1@@@~5_X$h6x6*Ut z$6esKx2s?`nN`f)5sao9=gt67GIs?~Vx@C;05lSDceFYHV`E3$SGp?ER*&Au%hhQk z59QpMa?|0^{6E>^D1)Xyj3a;9Y!;UosGLBRbyv8n#Sdgy%%?`Ts=^NXwgc8(jqU2( zol=kX``=XgOlbORRrbR>(4Xky5vB7_Jb;Mdp(O?1QuQ$%dN3&sLAkV}8XHy04Jv?X zLoRK`c~czsh8{OTJ^_RMVij@;c-QhqeOBq*q@z$}T%c6aZI~Xi7j)25L%DOc5x+*C z%ybs&_hMDPIjTU~FVgwu9BT5-)#B67cWTwyp=9|LDz(y?uaMJ$SQS0#rgK-RQu$nc zLXFQ4qkMcmIC@?!c$u-gPW-2y0$GK2jGr7NW5ufEc7xP)3^LIWw%(;Zm4tFk9 zXWGPR^u}CT^##?AJFTnn=yWX#kCqG3N%SnQ7M18!GrCbpe6BuAMD2K{uj(vyrn8NsU282mPBl57`#w3<&o*9XQ{&xrQ2!eU6nXHtd)o=Krd85#@&M9Q!LQM2Xn0BR&OhNiY3bgVgq9bpNk1E@8y!{Wo75mOYZ}o_ zk+~q6*&l68Wa0)uMOIc|8fv1~gBWjUM@)@3S%)@=tgJqEWE~m^2#TyuK|+z$Ie=7T zbqOFuR`(#7iO}BMLy(9aYBwYG{}gI(YXt`nwV$=nK|`(k5EB}-iv1Iz zHcn@V6&urheZf$ht9ZjuyYt}co{FlA9ctU6x_zMrcdUL2O>GS#?=X(MjKG+-nj6`@ zl|Fi=A}^zj3^R>7!_WRfV_NHAz!W)c0zgGs+WBl@@oVI}6w5e5p96jWI{%3FT5)(Fb7D z3Ja^8P_D{@^Kv>TIh7yhl)tBgoqv7!C-4^^?g%HY^`)(0NWOcCds(`hj)h4+zUkyL zoOod;Cp|7>^^gpFCqmd60Tj_>R22bQS zFeI!Ua)_Ux{lvIKRDV`-w>E5EM{WUSV6*`;t%lLfl9l9=Rb=#`Q8#5Zq3U!puFhH$ zCmOA@3JJtgmjNnPx^{;<+9%hyRQx6pgM z3+dWobZs-bKxXC5g9@A|3ksE;blpHD0q^V1KXiuLA3Cq=f=aIIGN&7~&*?_mcNp!v zjdl>N9Vheh?j^U}t1QR0wtR;v#=E>rZ5Jr6?ea}G_iSy;*=&NhsLKtMc0-q?-Q2Tu z+F2%TSr=F`8kTJ67Q#1$AazNeJDQ&XR3?)|T)*WI+0r$HZ)fnfbluq%rtIuWS#V7i zhX89tB^P%?L8HB;-9GECT5_JT9E#=Pebj9m32syMN8vj9pi;LKh^{|QbbS|Ai2U9+ z-F8#<-Bixm-e=v{^bRdjO|vIQols`ic!4r{`@7BSjvVv4zuX;ue_7>F!A?1FqH>Ut z-u&KcdI#g_6XIRzI;l^oi zQ~NCKwu+i}Rkx?R;epquyRGdOy5zFuDq^Sv&VfwI3zTDCcQ|gWa$NkP`4y2)?s9m3 zPmX^3e$v#iAXF~RizT^um{vhyq547{X?92P1RYBsY zPTt}k-}Ru{>_dy_*Dw0U&dV`A-qd3|rF_x%=e`I~ z*7+n!?aRKOO7~IZHdQyp`JDX|3aG*HP)pG<9Z*Az=VZwPHq{XNxSM(ml$yq;C_f#`Uq>AP-;1vRw7_sM8;T z(@D+eJ$IrNR0i*gUJH7`!Uesq@8zDV(b8VG6S}<@WsSjhJDq?S+2-B5nWb_jW(nfyn=<-?tP&=8VPe z&bdDaV)y4DQPt(CoOJ+H>!14Fl4BM66u$WvVlbS=P^e<8?bV#ONZVUE@8@vkQH}Y% zucLUsuJ@|mh>}$_R8XamLY-YV{*wL==xz`8Kz}rN+;XZnT}TsP`~&@$=W@64-tWJ) zKQwP8M18~irvGjLGX8=7uLp9%&~N(RKw>bS#mMd6{{C}vp<_<&mANn(vF}D_v(24d zuGrO`ucevj+RiU^)^57J^W6ZroAh4m46!p+D|MRFSz3#dZr2&qnaZHb{72^}NZS*g z*LBuza=n*2|BKMSI=`k1fMwg7K9-Et#$(C9WrgqnE9GQ3j^Ryo(-k~)u2UeesTl_C zP0d!MVXVH`Tak84mR>U4lC?eyDeJR7V(?McUIKfwe$zlH)OyBnLEB{M6BusMFj~IO zGm(;8gci-e*!!)8Ds(VCQX>XW@1AB4kkA9owzWZwDWNkVv^h()mNwB_(+oZt4WHbCg7;pel&BN3 zfKL3XcspC{Y5~z*r~p~RqBzl_!amsiTZ_3ZAw0L`s+Jh3SGA-(sJ*grEa#XXtr2qE zO`@Y&^bNA))ht}Uiduyh;aZpS#zhp5{-JWwn7yjy;x-|CXu@0EWRQV8`GAhVK!ctwmdCVh1qy{+6rKH0EFJBkp54zebkEk5ZUORW}p_% z`MM2^RUvS;YJ;dFVA1xLUz6v*h6_>PrycI>h?k9-2r9pvMXhdV1(6$Ct$>%&b+lJ! zp#f=T{G`>~)-+*$i#&KGH+|A-Z!4Ih+X`KMZtE3oNaWKFt260!W9GN&wLS}3u(*I= z2!q>_)_1p|%5Tivn5kct7-{^r^$)GNiM(a4Ry@~6lZ$igXf+Z|VDs?5r<-m@KF=!ojR&~a8K`v4M~32p9JpQ#2w zZ)e9{09t-l=G;sq&dpp+@)&Bb?f7`d24yX2{ZVV>y^Wb4WYURhcGUT!);vk0-XCQ0 zc{EWw;sbJsDh|$g4|!1F#>~xB?q=lD6Pm9XGEx^`Dh)1%^Sx*>Wy-gi_2?edkT`%3 zP%|HB^?rt~kE8PWv^ z0@j8J;?azcGgPsPa%s@BA>(B#;pL1sGR!RSe#TZpTaiOGs zZF^N)bd;;wE@-Qkt<cQg*YQF%U6b-mO0;4>rtHO)x0j^GYAHYT ze@}Thm5O;j75WglUtu_)Dk56zQyxySs>pg##Wg8kq^Pn`+AAq&<`m(a+6)CG>0a`_hfB`_fmY1FcMd-$(DKZzZ%f z{mK?b>dF>Z(_C?N3zT>^b%+&do6-WVaxuz2(=IxRzuk5-jk%lK&TXex6T8}e51?Ax zo7-+a4Xg9pJ*gAXqEEJ4OK5GoX9%G&R(1bi9S(hr?JgJRJTdjHdl1ygh;` z|5V+PkoZ^_G4q(FKQc`Wi*YIusbp*9(m@( zZ;3~)E%BQYOg`_C_5@sB2GqZcLIkbp!Qr@hq)FN1n7Cu|~wdhCwe z9d8-U${_DTF&DNq-Y@LM_)T1x3oAaN!aj=M3Y$=bwW zc=%3}Yaj%1)!eA|522qzDB`EkqaHUTiTR6x7rd8og@2zSSr?$k25*!18EO3tQ2XnV z(3c^re)XQxN{t-V>Mil^Af0!3_j_hMc+`8G(Bs}}Z3R`-+wIPaLpA5c-4(*=&zq3FP3}q}dy_tB8nm~`-X>&klPyhQ@0O-2 z*|#@oaT3{^^iGm*Z_<5)?o0Xv>Xf}n&jM)YINm!+TPS%;(%nsA+}%xAHdT%7J<;^( zrlwXeHGR8jq{>M+&~~}^HVS0(o6JjU#Qc84Vf^q1T-g4A`=uMOz25{_KH+nhx`R$L zhF8vn5i^&&%4%=EdzEVnU+&(CbEtVoCz9dbJMI>OTij0py-%S_+thE0w20oVq6HLo zh1GxaPyUjCVf4BWBZk>K_WJenjv4NaoI-K6+x5P8_qyJ!xOs8jwQ=_$*d7Tz67rrz z5BFX{hjP3F=)LGZt1*pSAHF{9-4MPt?A?tqmPV-E5pO@nK5t(9-SN1^jejs6AfKoR>!g7-1P*82(}g6k%4H1R%0Sa@H-=McpbKrH|q@1H5F zQ`9W(c$YVu(+msq6={z5ezWV+uqa=S(@ibjYT+$td3{SPw4ZI`?ZBOeqp#Pud?av9 zOFZultkSTIZS|j4YWd~u#`?*>zAXcHKp78bsFe||ZLV#rZb}^Qj<)x=Rdc0(`f^*x zd$-d^op4jWxwC&d2d7IqFYD~xj{9W?+l2Ra@&3`}u`b^FF1x#W`@7ER=HT>|F5YHb z)282sZfa}K@z!$_T;vh>%HBt>7jR~W??G|{jKMkp4jVoq!;Gf zevkH3J1$t)SxBJG67954;0qp1%rraT0Zm5_-^eBW^ex zIWgQNl7`p0;ikCmGCth2z=`wR31N58$Z*q+bUrZLHxl~t(v{(GdN?^W9l6qpq`Bdy zp^Epa!+rU5!geK1|5k=m=_S)<{P9t7L zoK7MW2C5wR7pL>m*}w?$Qwc`z9T<67JNU`Vl+^w@pj+z;t;=sNRiNq`I{FtiXtTwt%+D^J&26|8 zol)!hRO>=Nr$VE>3KeOg%vh$(_yT6=>sx#?wl`+R2&GqbLi9xwI+4)LuB#&>+&>b! z;xa@QHT!tg>LA^z(I;Ts=+CE(j2k@~59nsx`TD4m(csasM78&S)hPpG3DrNc#t*u< zhCMdMXp=?1-DN3^Xn;J)8*11QlK3PV}-coJlX+!->nohtSzFd~*ec!K(1_ zi|F#Da8J6Vet8{y_JAJxl0!cSLN!6zV&;_jDoXbwO7$Ew*N@U)tSE)n*kjvls5$81 zhN>h+LT_Pig`trI8h7;kQ%z?)%!O7WzSW5M14^5?JmN+|?_k*W9f8x?R+mF;5c!;gI~;i`+{6vHCwVvAl?qRopf5*;6DNk- z>i+y*I2q?8RD#km(xV(TYE8iDiNqr-b?znM7u#ZA^FU`X&6y3{jJE|6B>t3t@8VY!K%giKnbsnA)f zLWhQQ6*{wVIfo(^X1ec@l$x)O2Ch-#AqSZ1d!~zDu_2tI)hSEZZWX2QS6ENR!D(HPj*!sV}~B6`yJM2nwsDxW`_g2o;oFi#z1d{~684s%1BTo--AI1B?_^6}kqcPgI_C;mJg*YIU`~ z9;h#A6-*Z9gZV_&+b*@A^eRj!&aBHNyp{3BIJ^hIui<3i_`=ds+ zZXpshWBKpAa@7-h!&Nqg?Vv`6lK=kZ03N3*xS?Axh&G$6FK&hUW2iNBr%{8kF4AW- z@u{_iC{jyDlz3!~o+$tNRl2r6P8WLl45V#EkS-Y7sw>JHu;g9-XKMFgJWCIcFKXq?XR1J1^ylcQzVP^u}-- zlEZY%;uEl9Kf^UE_D+j*!feVFI(!0k4VrG$8e$HOkAXl(2S3jJ2)8+*H=XywV`a zYIAg}4d_Q>7!uDoFy^E-V)Ltw*hwGh9UKpE|u*Obs6f7!IaUp9TORsF%J z7ztsgj0Qc`8>r>xQW4`(t(lrdnwiN5qn47=<){YAOu;_r8;8TqivQ(2HEuJAPfJsrs9u+!kOg5Fnrwda$F4x!@qh&GKEpl z#6cpmP>EXmGz3HR|e{zN14{4@vYN?tCrbwtByL1!_Yt$YEM&{+(_Rs|ft;|c|^iT&lU@5AB zMmMpj`mJu7xJW`rU4PY*n1{oWR@AUm9I6k~RUQAte5ga=znP8_PE1J045VttJ1CLU zsm?HBtDbkTfr08jgiQsE(&6NEwTE&Y2JPVFqr7eAp06={H^V}qQzdF#Kt3JF(Sgl( zpo!3<%2cZgm6iT^YJY@k;YGdNk_304Wk0CS`E>nkT={K8~-A6)E(XP*b}1e61p|c-5gfNV0sOGVT|F`9-_RFnf1Mx#{APa z=3l-skLr3I#F$s%S2wa;*|Rdv)eC`pU`ntp;yS6Fvh7$1!0`7m9wdjh8iQ~0y+@7> z-pYL$)81Qsd$&7ggX)$)wD&V|9>P95^d{O}-}Z*ZJUrmja2{DPJKTFwxSu-1BoI1^ z`atN6A805&_zXRT{1MvhyoaAz#+x+5CyaJxRF?4XSF2x_j{d54A^jY6q56?G#I*tJ3sy&tpmp%gU>&>D&Lyrq8IUE-9Q; zQGB5@cKEoVgU6`f&@SYk^)7U(D=#d>Z*Ze9{0MiU`pIqjeegp53322p96N5zh?7nz z99}REpFspQ{i zhv(@x&FN*RsR`#TG&L$LEH0_4c9i|mpGPk=m4lDVrjvd&V8!I|C6lY+=_>rrdLdP; zs(c21puD=YaPrjiobtjM70^V#$L=6p=y%1PlIdmDh31#q3kyr9)9=Vnp61Z+q&pHkW6A^mnODXb|jK`pCGD$%;mjOxnbveHsm=-1lh z)<0@rSXwTFQAZjIo>o*or9V}cPIApj9@_2#G$Hiq-=TM^Dk`fULF8bjLU9jq9%5d%m$T4W-JwK3su{%04ihx@Diz$)$#tRg@1Zt(i6ru&C<7^2t*nQBhOX zr>6Y;vhw0S)kPRbs*o5{l}?yEt*C0M(CL(;E+$O$U=SDw%DEF1laDci6(5gAnLM3B zP_@=rbkg!EXr`0Orj4h8C^`hI?gH0WPFBNIS@8uJClTG~o5+gDM0ci!5W^<8X+_mU zdf)^tt3tVxtKkGHs)$RaVY8wN5v<32RiKWJKpl#zNPvc-nsVw!(NQ0RWU=o2G%yxA z9EWP?!2D8GG^NChCxw#{XVK8I1|SS7YP6!kg=QwFqP!#;S*U;-!wSkUNKLC!LkF68 z^7I*IEP!8%r(y6k^AS2Aclf?jE2fw9onBO3a&F(^l9_$=te^*#zU14$Sd~55`NIr- zYpN>y^2pT}aawqIUY}`YivIefxTF*(L-ag^voU5A zuEK?MlM*LYI8pO8UI(f%qEA`*F^+S>(4j|Vc0YAeO?h<Kg_whHiFS%vZjCQDGC4Bj z>QKP~Z}?T=VRIsJ*O91_UC6TPdy$0Bf~&lI(%A%(Bk_a+mH)JqRrW#e(5pgoytu_A zm(OzBS?&{hH9Chc2wmktE9Ii~`H&>3+&q?3Uz%a`MdiLDIeK8{ zOqNqWFlGEVSK%p|(B}!pPLjWh<<$?}8~G8}gnaQS5hG3(HhPc>&-wm9 zV~HXC7)OI7JoD3l(HA7f33>B9fFu}F(G<*b2+Q|ltYQM^)TOb+R9xC%lgpW1*C2s!3!Duc-({mq*Jc$vE5N{N)Y z%uRncNp;NqZ=B+UtC^c|!N_07ysZKJdK2^h7GKI-#tor6m=9I2(SP?c9~njJY$bDQ ze|h{vU4$L|C}T85$6eStGm6yNT9%(|@#mOVSo~$?^DMrJ`Lz~*m-&qr|B$)%M>O$M z=J!XDI{T9OlNR5_ytRl|LVK8huU@17_A$2y0)36A!z?{vbsct^TRe&R#;9s_md5-u zi)S$Z+Tw>O-mrglVs6F_ldT8q>8@T4#di@fKiuMnF&}F2qnXdJ_+aK2So{R$ms@-k z^Z6DZ%luG_Ph>vc;)TqMEncFoBhLJ2Rn^&fEWg;2{~ODXw&c$TZ)P#NB)c2qH^<-) z$KX%K;Lpe4n_}<}V(`ym@EtKY{T5bZ_1YhUdze=mlTVAm4~fCM#^6uI;5}pT{xSH` zG5F9J{G=HChZuZf41NxHbIhCPa91@yj(I^0`8hH8RWbOI82o`4e02=IAqJfLH~}Ve>pmf%9Eqyb5w>Ll_N(<=P2cb*lwWBQ`$A7Z3x~@#;Ms0!KvA@z^UH7Q+r5ek4bLpLPG5@ z;x)Qog^TIunSB(kA`jb8W1_p=W_u7f#A-iQ?6x4YzwM^?=3?z$ihUC@Lv6p{L~gD& z+>MEDTZsJ_oYDq5GK*~)-p{ai#wnZFK1Lqi;UjaRTlnD7&23x+x0J2TmIm8-epA{z z%=XTAbT`p)@LfRapxc?j_}hO#A9PL!h94zk_$Wp7z56hn8}5Is4s5vK=ir7aJOw?z5!t(cSFr2Scf zOaJS2a6|u?JqXfslF&a;=#lNE*RNU+-Aft!PmLjO_AbbNe4CdqZ`Q50{S`uw^gpdf z=^**h_%r3s7F@PV9dq)hY?teWylj`JnUkG#Z))_vCiDywT>ba}u5J6b3H{RlwC<#X zCfGQOFh2|F8xO9JUS?u_DzjF>4MX#;jNidea8vj$&&vi9*H=52zl8q{g_j| zXlyt7hY9)dg44Q?4l4IF{2BQ(1fL-IWz4BwhX{TpbGzNI6MAI(E)!g~Fa4Ad9d>;F zLvY!?PYN#EWj%Adeg7roW&6G_xb)|zf=m71Feg8Bz@PEkk3wG7_kiHCzVTF19Cm%1 z3oh&1R&Z%gSHWeyas`+58We*UF{gTU#Gk3xbmn&Z)(AbazOw|E^_?%cwEsH6<@mWo za4CPc;Dd#o4+<{jpAlTncN+zl@~<%`Kgjv+T_G>)yG?L8-+e9kNMYv>LVu>sP8txWjlT?xNNT@(^PI?G=87Db{OaB}xxLhZU5M0L9BZABM^d-UN z{PmIGa{l^^IoW?Y{!D!S6hr=Z!R36H5>MW2c;D2LIkn57_%r$sVNU*+?b2QFz?RigdY5$ioc+&(FhJ)Ji1pJwDGX$4$*iCSHugS>w z6p-9@=y2zgoV_kzoE_X#e`O=<$A zIP5rV&Ya5afj<*(8A4u`d#K=s!DatCS8zElT`ssB zm+AzUdR`S=j!SO~F2|)%5-Nm){3i7@7rY2*#&5#}m--6?m-^`o*y$kslkjKsoGbWb z!T-jb+N-DF7c(a|OzkrQ{^^B3Y}Hg?xdKUnBS^!8Zsl?SDsb84n)`F6F-!T+06>xRn2mIr*oHuqT{?3mnvrrT8=P z&|GjCS8bS+9=T5FAmpWIZ3p6NnQ zH=(Ce$V)x51($m2gdRD+UoGUNo@IhdJ$Eywc$*^Zc}&Pp6@0zma$WsS48A=E|3+{* z|LzgIOq3f+#RU$EA6YJb*+v~U?-hd|Cip1iG;w%2bBedVf?qA<&lUUz=H$0wg5N6i z^b`Cpp=Y?@4-0<0;Hw3f{cD5ZCkgpi1efyf2rkFNj|7+UUkNV9!yg2f^81*R-*SXK zZZllqpmveJTGi9DuVIxr_cpD1__Auq?nT)}1g4vN9Y3jK2b{|q57>sunY z)L$*QyuY3&xRhTMgD(?Y_KSxFm-=54T#nCg2rljaggLcWf8ozBguL80*d^q1h5U~( zdvEls{B(xo_4} za4Db1-0qJ9g}m&KBLtW6KQ;zGLvUH{`7!ux!DakhE4b_zw+JrvZxmeG`LW=#-M2v4Tr`CJHX+_j!U#KU^cY^uxV^OZ`s@F7>}I zxYV;%aM>=q1ef;wM{ubpogbvP`+F91ihpU(c|u-}=QlB@{wVL)?iPBAh5uJt@}^zZ z3VCVg*MiIVNoxtEILJ=v&o+X~_~|RS)PFd0vi~sr8GDXnPX3uLc)rjh$CI&w%kkt4 z!DYFnf{zvY%LSM6bHK=%lcj^^c*4dTqESA zp5=l|J@*JbM+!YFguK-Al;Bd&2BGIDq31;*FZFB|Tg3J4nBLtUvMhhl;3ohkfXKwrLJs~gswoP#9x36OG4z%fmgZzUJ z?fUW4l{v-Lxq|m$ZpUYy&~vPir=M7*!;a4r1ebn3S#arx(*>7)o+P-GpDwtZPiq91 z@|Q8U{X9>|OFu6XT>5!Q4E|5S$HO0{pS~dY7{Rv-F6I9txRh^A50&B|Kg)SAQ*c?| zzRaoJkHep_=Wrn}?@x~wT;6YXMP53n+#&cgdhT(FYA*egpt1VoDc`~>k}Nna@vbF> zIC%N5NN!B+pA=mDHC7h*P<}$sssstf0g2 zE5rJl#x6PxKa%aFwxGjs^IYRZjeY()>p92bqaqqivA8LZ)^cG40Hc^%ExFz47+wnb%zsvsr-r|q4o%DG> zI*gs>dE%)SU(NNs)Zz=c-0LlFeq7|A7Jq{)@ruPOSofdQ z=EqrlKI=7pHS#XY&9vn2;rcGJ_yo4+UW>0~|E{%oSLRzSp2+pxWAQs#e=4UNJ6~en z)8hZ(`VP1F5WYUm;+ZUOp1(Evf93j`_B5P6pGwDlmYyc8$2A#BuaA(9n}_Z zs0(leapudw(4=JkS;9`n58^@5Y0t6Bagi_c=dRB+N$X4+A3(zA)> zS6cjK=Km0!^x!L$)$z38q~|-0o%I&~ocRlalb#b<&l`f1o<7XqvAF(xzvFDT_*|CX zVR7?${Jj?6%JTayzL|Lv$2G+p*+V~cL`SN{&F6Dk2u}5Sm9Ncj;21qE+5eq{Jn2tm zo@?;|%m-M!5A%V7Q@PLa^$CKLe$(zJTD*w$j24{qe2G6g&JdjRT+8x>7QcddvEZbq zoOy-dq~}fMRTh7d`2~WLo=3UA&k>yT;MYafG0)=rSkG00lb&UKeWT!{rw{8{V)0(g zZxfvK{Ehhv!AZ}t%pbP+Va)$2IO+Kie{`%9ob;5jylGDpKa-i8=az{#$DfJw4=g>k zEdQ~^&F5`C6P)zaa-9DlIN5VM>oLzc8+(>AkHkX=hvENZ-o)aMFi&Sr#Zgmr;6=T8 z&e`TiT6)a*KgiQBWz05aRJf6$l zW${$z=JQy_4{ez@=NmA?hcfSH@w1qp#@xioLduAvOmJ%7&Fr`77JrR-rKNub>tAT` zwaiyod_VJ-E&XQw^P0uY=bGNK^mL(4gJYMFCl@c_dL0m)>SfkLp+w3RjnAWbonf8_ zHvTd9y&0B#70b7?cp3Aa7GKUh+u}DeA7t_M%m-WiY39cZPA)L(3G>{q>35r1zEsGQ zA2u;RPjJ#h-{(QcOufb z-Y(>+ea(H&KLjT`d$At#oUrkO`P|Vnmi#d+zro^1F@M$KXEEPo@za=ZvG^s-KeD)4 z7nV}Z*v$?PM*y83shEZ&B>c`n_Q+nMo-;RoGxbePK@i}uc~9o1zHxltVxA{A+((jw0I8lXPBFIS$l@$$QauU(UR> z#UEha&*G0VA7t@wnV(|uXL&tyw#7Fw|GULMXMUT-zhiEm12_I@$@AuBOa2h%+b!OU zc^to~Y4qeWPqp~d%sUHC@oc^qG+S_r=hs>OB#UohZl0Gm|A_g`7XN_xGQsK8lzXq>RPGLzUt#g>%vUit{{I`->otpC$ovC~*E9dp z;x{ngZ}G9r6M5gxlzTSwEQ^;i&$jq-<|kO(#PKQ2O}m`T?NTH-`R7;mLy5(IW`3^V zR28GAN^sJX!ux~gTf7PLSr+fee6Gb0VZOlPJ(w@DxcMH3TbLVv8b916He#79Y*LgT+r`-brvW#gyAyaH{H=EZ@)K6PO=q@e1aHEPfvIA`|}E&2BR;O7{NcV=E_@uQem3r_3p(d2v_mkLh)KbGZ}GN*ZphCs7V zexD_O4)33=v*gY9!?So#3SB36|eu@%xzX6`b_kNJ?^7u`RmV%Q#E%|+{ zHWp7~-a&BE^B^@Ej_!h!o;;TCZE^E`OgVy+9`l^yNWn?Z`K;$8i`Ot8D>&()zD>tj zf|H&)mM^mS<;+V3Cp|x~e`W|ydLCl=YKz~){6fJ=kN&+8$C)EI>3N&w=ULpmuX&Z= zq^FAgyjXD3^E>Og#o|9PUoJT58Nzz*7o7C;qCmp&pvAM8KPous$)$k;$J2t7p0ikf zy~W2ee?f54b1OI4Cc#P1bu9n3#pf~KA~@-3#`XG4aMJTHmjBY?8<~G2IO+Kke{}pT zIO*BJ^1oXA-^|sQ8bgjQNzZZkqa&%EekD$NJbvFO)#AUio{knjl;t~Fye;!wiw|c$ zz~aX;A1F92mDXzWoKb?4o#uVGQ!IW4>lrUN>3NCu6bnvzu4VbD7Qc%5bmnH>DdzF2 z-s01k-(>NNm_KguI_B?Kd^z(Un3LPd{fk3>Dj@0+6qp7 z*v#@BEWU|(C&4Lx4sid<7M%3_!t(ts{v-1Nf|H&LcpMudIO%E4?8&~FjavCZP6neVc=>34C5=zPYW zsk}hx6@woZgP+3O#Q9RT^Hhu9%=~PN|AYA?i$Bc#Jc~cWyu#v7Gp}ZD>UAyGYnk8_ zIvaKMoyP>Hq2I`_jlth%ZtD9sm%G*Co0xxL@tw@SviRrB_gH*C^Peql-rrSU8Vcz5RcmY(~#z0MMx+Uqk_ z8+^uDaB8o|SpM%8e~NjX#Wym)-Quq^f5_rnn7?50ZOq@Y__xe=Tl^>HzgaxY@1r#D zs4GZ*ruu%ZFP-*+Q+=DUe4fSIF)y%qALhjtAHe(qiw|bL(Bkbl4sT&j@kaC1UbgcA z!D&1+@Ap4s@uRujS6h5M^QSC6hWYaruVVg^#VeSlX}uEmY~2ZGCbeIq#4>q^$Y z+v0PX?_+NI%V_pzC{sJi<{g-u{%gMH?of;0#`=3%{2}IjExwZZ5f)#|e4xdjVm{pB zMt+3Fjr>IBrd|acC+Aw+;o~xkoBQ$WEq(#dU(1-AcuSyya6FW$Ui@+I6}JCTi*IE9 zl*K<~zRu!XnLjT$#e*sLHNhz!eqi}GExw!i2NpL!)%vl;e_{DAE#8*jzyI3et(gDF z-1y)4?ROzh_M6}LOwPgu4ij&ESWlY8dogdt+~~iW+ohM_G+r6~hY3#O)l!xpZShB$ zpU&L0Zzq?m!}IEblb?@cd!}3b80IrAZoaSZB8!{nQRZ0Od|%=JSK6J&M^R*P0PjFJ zG=NcDKtK(!fCNlLM7fj*B7(}HTtTiRh7gVfl7IwZL6k#@epm!SPC-}>6%-NhSdc?N zi6|Ns5HYNTm18-)hyu&joBF-@_>ugz_4y<-{himZs;jGOI+K~6DdgRdKgYQAr?zvJ zacO5F;$J532VX+2?`!!7`3S_n1`l(&r9U^I-*%I4hVLWa3ojr)1pkA48N6I=SD7ne z?nml93cs2BEc`C=aLfap$VX#797sM5{v`QlGB8}3NB#|b1^G$%4)ROzBjn}K{N*&`e&h>hl^~v@9SdHM?Z|Bj@j^uj2no1sl|MO;={L-G~(im4J8khFG zhvQ{3`Fi*?<2KJs!3aNkk^8@ zC%+Z`Ah}-8w2N`szglm+ajCaC^7JLw>z@uJe+cn|$sd5H!?k}}qaUV_w};OpkB2WN z9|&Jheg}LTc^dp%@@)9eveKhlP^R3TJpv4 z_sKWHHw2Y#A79M3yHlk1l)Um(}(;fB66 z!i^^#SCx_Hs``FBt4G5tlGlS*f$R9!@f=lxx1>BRk*76zbNGFfXA538jyGP$HF=6} zc93Mel(SsKk0#H9Pa;1IpHF@azLNZR_zv=L{NLvYc?I|d@|)l_Z}A(V{i&amY;0V% zcLuh%rE%HbrpWUsxqg0f2zh73Pa}T}{)%y_S6;)FvdXyBtDm3TN}h&12gt|53(2R# zD>evK6FPsEdiDRURgFu%FCspcd>*_#`5W*Q@^|55jZ3`?d^3W{#--lvh+jya3tvxu z2>vzsarg=2QtwLC`>S!O_gBPMjP?OKPA7K3^Nr@RA+GE&F3)%R{_ju7AHw|bnQ@zEpK;042k{5V zd%+9Hhry4L4}t$go(=zvd?fr5c@DgEL)q#-=i#aF3UJ-O+HX}%ylk&tU#YHf+1>@n z(|~*)Jcjb?>xylS%Xm2Jexy8XT*kxw*pG?io#5%@z2K9{2f$w>&w#&8J_i0F`Lpn^ zj7$43N#kAl-ng`XCgRVLFM?mwD70G9-|uh1>yvMSw<6cik@X|swk^cc- zL9U-CdWT%!-=0Ulu$-^=F!>tzZ^otH(tWtQ(1*XH<51tHU7h>{^4v~dzPw+*J9#8L zk-P?c40%KNY;t{n?^5!fSNnQ*lJ|$_k*C2=k?ZH8BVsP^hvyMrjr^+$KCUVGcknjk z)hqh=-sH96DdhTk?0E8hh@VHE4_`s9ue)w1*Vj?^l3!KHxAUlR87F!ka>lqEU;6rL z#aO?dj-T4dQ`@-Y(esK%#wE|~i0?qIuUqyrE_w8I=0V0KPbcJ=MBW=d*SO@l4$ntR zjZ2=vh~Gj!3jVcm$)o2F`Nk#BWW--2p8>z_HW%cIG(+;}`9MwMl1D#B*PMI<^7JI% z4j)6l559=}NBCCqGw=iC=ivV)FL#Y=tSjM-{jH9N8t?|>vG7jh`uamM`I1PVcO3cK z@LA-!@U`T5@Xh4<`K~4QDSuXVN#+w}$c zYWO$CWqS+Jp2Nl^&tAkAkbeq4Zd~%{^*4SoE_n(NUr7D~{GxHmqy1d2iCb_*_KV~> zi}(m~{a%ymqxdS@z=S$_e-U&bTuw1Zb6>z zwJ~uH#7K%;=#c|ir3@)sB!7~RP_IGa{YWw zA^AkaUmza~4{LgP|KshWK{fJdG_)aHw@a`8)|BFhqaV7Pc&Tz8>WwF#1@CA4dKZ5b z`)iPK30;r)q2%k}+2mW{dQ^WnAj5gZMad{TyNk@>s+_ zOx_Uw2weBqJLsRmLc7dOhFeaP8+2q26@IfpOX1IJEOU@_XQ$ zDZkD$dB$ZtbVU9`#$`N|4)+h7Bfl0Nc9(x#+c_3_B8^KsA4PkvC+`Zsk-RUw7P)?Z zRXyVps`bVimwHo>rwMs7ycM~A?k|o!9r5kpIv%Q^ANrEt1RqSU*I#~u{4T`jkgvk{ zd6~Qb{uX%z@@yi%7yboY_Lt0iGmz&b`52t{)@|m3T+w*_{M{Yo`uV25VtkyeW5F zX+`mx|9<0goIZ!+t0TF7|6FI|GH$g!@x~?3TIA_Vz6w5s{A+kR`4{kU-XNh4(EPeZ{j62Ud|0ZGA{iSRoVS=I0H z`8jwlxqhx^KY44^dzQQ-{Hj)$@85WM6#0|zrf{7P^>ebVDE^F38$3$!I&btfF8gZ) z`Zi6yWq>ozk|O< z{w@4Xcfxht>VDa4;^jE1;(ny$8<+flAkPu-GPm$(td*4S5{AGkI5dCV4!30{I~LEaTFC*#}Y< z8J9yM6Y(3!pM-BGpA0`pJ_r5_`6_t%dqUM0oxhdA@%RAwKEyu)mqiKfg7d~yir3>Y z)41$+y>81B2deDaoMhMh|eR>h94%M0WToWfuDl&{wg%_a-2?aKT^uI_O~(*OM6x!PXzf2 zcqCl&XJb6nH7?`4vriku8kh0&4C3!2&w+O(*Xx|8lIwNRN0F~Yp6AKmg3l%23g1Nj z34AyCet5oddt4tkF57zq@ulM2f-Bl@r{UG(LR{qM;jzXg|D!%EXl`8cmz4)y=^7Wh zDaum`-jDnS_$YF{{`m{!%@Dr?uFuzdWJ6qeJuY-pRIh$7&NjHl>-_MkaT#xV{l>lI zdL5##jmr?!^Zb0{k|!VI^9cC?_)p{);lGjp4iCTAC3QuQi%9(8TAsWDyefGucnxyB z{!x^1sZ!^MXydY7`u)T)i8x+)2J4{-ALo48x$i@lww9`-+BNamvUYfuLFEQUJ3p&`3>;H5=|L#zqc`Q#Mh z>rwn|va&08Q@qX_?TyR$oPcroAo&=07vr)|^g1xTjZ2>Si0?=K5_~B6diZeiweWG| z+u=`1mn{FHSjs)@4??B&xP+I-w!_s*W+kRxPRd0cK%j9564$e^2&%GNUqQC zndC1^hr2S7yb%5Z`N1+ielEFQ$8tG&J+x;Jd6IOVD+kH7J^AFFk*9#X3Gy5x*X!q> zB2Pe`pGxp{WqiA&pJh(!f%hkOEx|XI-~slJbfb z+7gtH~~;cw*+sIcSfG!m@hS6&zDN!1sQdO>+lJk-=eER51|{1 zo_{sIHttCOOI3}rT#x(_EJu@%#BvPzLM&^)YW{cO_%~yyol@@}cpPpu{#z`!BR_%V zj^yXDto^5XDoIDV(#_u#)ms&=&p#R;1@A}k(QqAi8h;mj5XIjQA4aa%AI>1}iTJT_ zS;SNok|?;C^*C~UzSj0={29c}qD-DC<0)oH#P2e`?ymbhl>cenMhmM)Ii39v+&WG&D6cOEUhsPC`aPT1v7rzgk9G z%9SxgQ`3?)z_mi}3il5!C#4S?mYn8VotB=J9Mis2_ggcw5|V~sIVEjm%F}Zf{xR??hoD zsOA=>9z3lCvMfdB6>&SaijUU`QWi|Xuqcv7?w;i2^(W~1S|Qem!mZxO{3bbdUQ*Zf z7gB|~sJC$?tRIEvxwkbTZnZhOzRW**--dpP_0Q=-+bY<+*?t$X!--Qz@oj0X^IrZWC<@J!_>hFpAFVcgw z7`$G>`rYt{NWZEsz!YA8xqGa*_7|f4I*&{H<@ul6ztNfG)a`V4LuKtt{#L)o23)kQ z+Kz4Rf#TNxY=K`RQ3H`c7xnhb64r0L$giO15m^0-`VV4#eOzz*V|`uLTdh}@Pr5sD zT*&j4Ed#KAltp)6`%gLCC6t`J{sgSQkt*c%FS&J#+kY#1`4#o~S@xeCO1i!*ODV2y z@n(zVcMuGcgT|E`+|Rx4hu4=sOL*>d6uk19{C1zypa*@c{A2Q4PJ<~V^x&L$AF1bA KA#}fZ{r>^l)3Mb6 literal 0 HcmV?d00001 diff --git a/src/source_text.c b/src/source_text.c new file mode 100644 index 0000000..de1a61d --- /dev/null +++ b/src/source_text.c @@ -0,0 +1,226 @@ +#include "imputesrcref.h" +#include + +int imputesrcref_visual_col_to_byte_col(const char *line, int visual_col) { + int vis = 1; + int i; + int len = (int) strlen(line); + for (i = 0; i < len; i++) { + if (vis >= visual_col) return i + 1; + if (line[i] == '\t') { + vis = ((vis - 1) / 8 + 1) * 8 + 1; + } else { + vis++; + } + } + return len; +} + +static SEXP call_getSrcLines(SEXP srcfile, int start_line, int end_line) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("getSrcLines"), R_BaseEnv)); + SEXP a = PROTECT(Rf_ScalarInteger(start_line)); + SEXP b = PROTECT(Rf_ScalarInteger(end_line)); + SEXP call = PROTECT(Rf_lang4(fn, srcfile, a, b)); + SEXP res = R_tryEval(call, R_GlobalEnv, NULL); + UNPROTECT(4); + if (res == NULL) return R_NilValue; + return res; +} + +static SEXP call_srcfilecopy(const char *name, const char *txt) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("srcfilecopy"), R_BaseEnv)); + SEXP a = PROTECT(Rf_mkString(name)); + SEXP b = PROTECT(Rf_mkString(txt)); + SEXP call = PROTECT(Rf_lang3(fn, a, b)); + SEXP res = Rf_eval(call, R_GlobalEnv); + UNPROTECT(4); + return res; +} + +static SEXP call_deparse_500(SEXP x) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("deparse"), R_BaseEnv)); + SEXP w = PROTECT(Rf_ScalarInteger(500)); + SEXP call = PROTECT(Rf_lang3(fn, x, w)); + SET_TAG(CDDR(call), Rf_install("width.cutoff")); + SEXP res = Rf_eval(call, R_GlobalEnv); + UNPROTECT(3); + return res; +} + +static int get_option_logical(const char *name, int dflt) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("getOption"), R_BaseEnv)); + SEXP a = PROTECT(Rf_mkString(name)); + SEXP b = PROTECT(Rf_ScalarLogical(dflt)); + SEXP call = PROTECT(Rf_lang3(fn, a, b)); + SEXP res = PROTECT(Rf_eval(call, R_GlobalEnv)); + int out = dflt; + if (TYPEOF(res) == LGLSXP && Rf_xlength(res) >= 1) { + int v = LOGICAL(res)[0]; + if (v != NA_LOGICAL) out = (v ? 1 : 0); + } + UNPROTECT(5); + return out; +} + +static void emit_message(const char *txt) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("message"), R_BaseEnv)); + SEXP a = PROTECT(Rf_mkString(txt)); + SEXP call = PROTECT(Rf_lang2(fn, a)); + Rf_eval(call, R_GlobalEnv); + UNPROTECT(3); +} + +static SEXP paste_lines(SEXP lines) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("paste"), R_BaseEnv)); + SEXP sep = PROTECT(Rf_mkString("\n")); + SEXP call = PROTECT(Rf_lang3(fn, lines, sep)); + SET_TAG(CDDR(call), Rf_install("collapse")); + SEXP res = Rf_eval(call, R_GlobalEnv); + UNPROTECT(3); + return res; +} + +SEXP imputesrcref_source_text(SEXP fn) { + SEXP sr = Rf_getAttrib(fn, Rf_install("srcref")); + + if (sr == R_NilValue) { + int allow = get_option_logical("imputesrcref.allow_deparse_fallback", 0); + if (!allow) { + emit_message( + "Function has no srcref metadata and deparse fallback is disabled; " + "no changes were made. Set " + "options(imputesrcref.allow_deparse_fallback = TRUE) to enable " + "deparse-based fallback."); + return R_NilValue; + } + SEXP dep = PROTECT(call_deparse_500(fn)); + SEXP collapsed = PROTECT(paste_lines(dep)); + const char *txt = CHAR(STRING_ELT(collapsed, 0)); + SEXP srcfile = PROTECT(call_srcfilecopy("", txt)); + + SEXP out = PROTECT(Rf_allocVector(VECSXP, 4)); + SEXP nms = PROTECT(Rf_allocVector(STRSXP, 4)); + SET_STRING_ELT(nms, 0, Rf_mkChar("text")); + SET_STRING_ELT(nms, 1, Rf_mkChar("srcfile")); + SET_STRING_ELT(nms, 2, Rf_mkChar("line_offset")); + SET_STRING_ELT(nms, 3, Rf_mkChar("first_col_offset")); + Rf_setAttrib(out, R_NamesSymbol, nms); + SET_VECTOR_ELT(out, 0, collapsed); + SET_VECTOR_ELT(out, 1, srcfile); + SET_VECTOR_ELT(out, 2, Rf_ScalarInteger(0)); + SET_VECTOR_ELT(out, 3, Rf_ScalarInteger(0)); + UNPROTECT(5); + return out; + } + + SEXP srcfile = Rf_getAttrib(sr, Rf_install("srcfile")); + if (srcfile == R_NilValue) Rf_error("Function srcref is missing srcfile"); + + if (TYPEOF(sr) != INTSXP) { + sr = PROTECT(Rf_coerceVector(sr, INTSXP)); + } else { + PROTECT(sr); + } + + int *p = INTEGER(sr); + int n = (int) Rf_xlength(sr); + int candidates[3][4]; + int ncand = 0; + if (n >= 8) { + candidates[ncand][0] = p[6]; candidates[ncand][1] = p[7]; + candidates[ncand][2] = p[4]; candidates[ncand][3] = p[5]; ncand++; + } + if (n >= 6) { + candidates[ncand][0] = p[0]; candidates[ncand][1] = p[2]; + candidates[ncand][2] = p[4]; candidates[ncand][3] = p[5]; ncand++; + } + if (n >= 4) { + candidates[ncand][0] = p[0]; candidates[ncand][1] = p[2]; + candidates[ncand][2] = p[1]; candidates[ncand][3] = p[3]; ncand++; + } + + int chosen_start_line = 0, chosen_end_line = 0, chosen_start_col = 0, chosen_end_col = 0; + SEXP lines = R_NilValue; + + for (int ci = 0; ci < ncand; ci++) { + int sl = candidates[ci][0]; + int el = candidates[ci][1]; + int sc = candidates[ci][2]; + int ec = candidates[ci][3]; + if (sl == NA_INTEGER || el == NA_INTEGER || sc == NA_INTEGER || ec == NA_INTEGER) continue; + if (sl <= 0 || el <= 0 || sc <= 0 || ec <= 0) continue; + if (el < sl) continue; + if (el == sl && ec < sc) continue; + + SEXP attempt = call_getSrcLines(srcfile, sl, el); + if (attempt == R_NilValue || TYPEOF(attempt) != STRSXP || Rf_xlength(attempt) == 0) continue; + + chosen_start_line = sl; + chosen_end_line = el; + chosen_start_col = sc; + chosen_end_col = ec; + lines = attempt; + break; + } + + UNPROTECT(1); + + if (lines == R_NilValue) Rf_error("Could not read source lines for function"); + + PROTECT(lines); + R_xlen_t nlines = Rf_xlength(lines); + SEXP new_lines = PROTECT(Rf_duplicate(lines)); + if (nlines == 1) { + const char *l = CHAR(STRING_ELT(new_lines, 0)); + int byte_sc = imputesrcref_visual_col_to_byte_col(l, chosen_start_col); + int byte_ec = imputesrcref_visual_col_to_byte_col(l, chosen_end_col); + int len = (int) strlen(l); + if (byte_sc < 1) byte_sc = 1; + if (byte_ec > len) byte_ec = len; + int slen = byte_ec - byte_sc + 1; + if (slen < 0) slen = 0; + char *buf = (char*) R_alloc((size_t)(slen + 1), sizeof(char)); + if (slen > 0) memcpy(buf, l + byte_sc - 1, (size_t) slen); + buf[slen] = '\0'; + SET_STRING_ELT(new_lines, 0, Rf_mkChar(buf)); + } else { + const char *l0 = CHAR(STRING_ELT(new_lines, 0)); + int byte_sc = imputesrcref_visual_col_to_byte_col(l0, chosen_start_col); + int len0 = (int) strlen(l0); + if (byte_sc < 1) byte_sc = 1; + int slen0 = len0 - byte_sc + 1; + if (slen0 < 0) slen0 = 0; + char *buf0 = (char*) R_alloc((size_t)(slen0 + 1), sizeof(char)); + if (slen0 > 0) memcpy(buf0, l0 + byte_sc - 1, (size_t) slen0); + buf0[slen0] = '\0'; + SET_STRING_ELT(new_lines, 0, Rf_mkChar(buf0)); + + const char *ln = CHAR(STRING_ELT(new_lines, nlines - 1)); + int byte_ec = imputesrcref_visual_col_to_byte_col(ln, chosen_end_col); + int lenN = (int) strlen(ln); + if (byte_ec > lenN) byte_ec = lenN; + int slenN = byte_ec; + if (slenN < 0) slenN = 0; + char *bufN = (char*) R_alloc((size_t)(slenN + 1), sizeof(char)); + if (slenN > 0) memcpy(bufN, ln, (size_t) slenN); + bufN[slenN] = '\0'; + SET_STRING_ELT(new_lines, nlines - 1, Rf_mkChar(bufN)); + } + + SEXP collapsed = PROTECT(paste_lines(new_lines)); + + SEXP out = PROTECT(Rf_allocVector(VECSXP, 4)); + SEXP nms = PROTECT(Rf_allocVector(STRSXP, 4)); + SET_STRING_ELT(nms, 0, Rf_mkChar("text")); + SET_STRING_ELT(nms, 1, Rf_mkChar("srcfile")); + SET_STRING_ELT(nms, 2, Rf_mkChar("line_offset")); + SET_STRING_ELT(nms, 3, Rf_mkChar("first_col_offset")); + Rf_setAttrib(out, R_NamesSymbol, nms); + SET_VECTOR_ELT(out, 0, collapsed); + SET_VECTOR_ELT(out, 1, srcfile); + SET_VECTOR_ELT(out, 2, Rf_ScalarInteger(chosen_start_line - 1)); + SET_VECTOR_ELT(out, 3, Rf_ScalarInteger(chosen_start_col - 1)); + + UNPROTECT(5); + return out; +} diff --git a/src/source_text.o b/src/source_text.o new file mode 100644 index 0000000000000000000000000000000000000000..f418cee49d5f1358cfb717de21200d97df5c8846 GIT binary patch literal 41896 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

VDil;&Q?s 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 ipF05=bEn`ZJin<`49nvI_buDYzn|<+(K2XJ + +static SEXP cell_at(SEXP x, int i) { + while (i > 1 && x != R_NilValue) { + x = CDR(x); + i--; + } + return x; +} + +static int call_length_cons(SEXP x) { + int n = 0; + while (x != R_NilValue) { + n++; + x = CDR(x); + } + return n; +} + +static const char *call_op_name(SEXP expr) { + if (TYPEOF(expr) != LANGSXP) return ""; + SEXP h = CAR(expr); + if (TYPEOF(h) != SYMSXP) return ""; + return CHAR(PRINTNAME(h)); +} + +static int find_forcond_id(int node_id, parse_ctx *ctx) { + if (node_id < 0 || node_id > ctx->max_id) return -1; + int offs = ctx->all_children_offset[node_id]; + int cnt = ctx->all_children_count[node_id]; + for (int i = 0; i < cnt; i++) { + int row = ctx->all_children_data[offs + i]; + if (strcmp(CHAR(STRING_ELT(ctx->token, row)), "forcond") == 0) { + return ctx->id[row]; + } + } + return -1; +} + +static int first_expr_under(int parent_id, parse_ctx *ctx) { + if (parent_id < 0 || parent_id > ctx->max_id) return -1; + int cnt = ctx->expr_children_count[parent_id]; + if (cnt == 0) return -1; + int offs = ctx->expr_children_offset[parent_id]; + return ctx->id[ctx->expr_children_data[offs]]; +} + +static int node_has_token(int node_id, const char *token, parse_ctx *ctx) { + if (node_id < 0 || node_id > ctx->max_id) return 0; + int offs = ctx->all_children_offset[node_id]; + int cnt = ctx->all_children_count[node_id]; + for (int i = 0; i < cnt; i++) { + int row = ctx->all_children_data[offs + i]; + if (strcmp(CHAR(STRING_ELT(ctx->token, row)), token) == 0) return 1; + } + return 0; +} + +static int *expr_children_ids(int node_id, parse_ctx *ctx, int *out_count) { + if (node_id < 0 || node_id > ctx->max_id) { *out_count = 0; return NULL; } + int cnt = ctx->expr_children_count[node_id]; + int offs = ctx->expr_children_offset[node_id]; + int *arr = (int*) R_alloc((size_t)(cnt > 0 ? cnt : 1), sizeof(int)); + for (int i = 0; i < cnt; i++) { + arr[i] = ctx->id[ctx->expr_children_data[offs + i]]; + } + *out_count = cnt; + return arr; +} + +static int callee_blacklisted(SEXP arg, parse_ctx *ctx) { + if (TYPEOF(arg) != LANGSXP) return 0; + SEXP head = CAR(arg); + if (TYPEOF(head) != SYMSXP) return 0; + return imputesrcref_in_strvec(ctx->arg_wrap_blacklist, CHAR(PRINTNAME(head))); +} + +static SEXP recurse_and_maybe_wrap(SEXP value_expr, int child_id, parse_ctx *ctx, int wrap) { + PROTECT(value_expr); + SEXP transformed = PROTECT(imputesrcref_transform_expr(value_expr, child_id, ctx)); + int blacklisted = callee_blacklisted(value_expr, ctx); + SEXP result; + if (wrap && !imputesrcref_is_braced(transformed) && !blacklisted) { + SEXP sr = PROTECT(imputesrcref_node_srcref(child_id, ctx)); + result = imputesrcref_wrap_brace(transformed, sr); + UNPROTECT(1); + } else { + result = transformed; + } + UNPROTECT(2); + return result; +} + +typedef struct { + int *indices; + int *ids; + int n; + int valid; +} mapping_t; + +static mapping_t map_generic_indices(SEXP expr, int *child_ids, int n_ids, int drop_first) { + mapping_t m; + m.valid = 0; + m.indices = NULL; + m.ids = NULL; + m.n = 0; + + if (drop_first && n_ids > 0) { + child_ids++; + n_ids--; + } + + int n_parts = call_length_cons(expr); + + int *present = (int*) R_alloc((size_t) n_parts, sizeof(int)); + int n_present = 0; + SEXP it = expr; + for (int i = 1; i <= n_parts; i++, it = CDR(it)) { + if (!imputesrcref_is_missing_arg(CAR(it))) { + present[n_present++] = i; + } + } + + if (n_ids == n_present) { + m.indices = present; + m.n = n_present; + m.ids = child_ids; + m.valid = 1; + return m; + } + if (n_present >= 1 && n_ids == n_present - 1) { + m.indices = (int*) R_alloc((size_t)(n_present - 1), sizeof(int)); + memcpy(m.indices, present + 1, sizeof(int) * (size_t)(n_present - 1)); + m.n = n_present - 1; + m.ids = child_ids; + m.valid = 1; + return m; + } + if (n_ids == n_parts) { + int *idx = (int*) R_alloc((size_t) n_parts, sizeof(int)); + for (int i = 0; i < n_parts; i++) idx[i] = i + 1; + m.indices = idx; + m.n = n_parts; + m.ids = child_ids; + m.valid = 1; + return m; + } + if (n_ids == n_parts - 1 && n_parts >= 2) { + int *idx = (int*) R_alloc((size_t)(n_parts - 1), sizeof(int)); + for (int i = 0; i < n_parts - 1; i++) idx[i] = i + 2; + m.indices = idx; + m.n = n_parts - 1; + m.ids = child_ids; + m.valid = 1; + return m; + } + return m; +} + +static int is_logical_op_name(const char *op) { + return strcmp(op, "&&") == 0 || strcmp(op, "||") == 0 || + strcmp(op, "&") == 0 || strcmp(op, "|") == 0; +} + +static int is_assign_op_name(const char *op) { + return strcmp(op, "<-") == 0 || strcmp(op, "<<-") == 0 || + strcmp(op, "=") == 0 || strcmp(op, "->") == 0 || strcmp(op, "->>") == 0; +} + +SEXP imputesrcref_transform_expr(SEXP expr, int node_id, parse_ctx *ctx) { + if (TYPEOF(expr) != LANGSXP) return expr; + + SEXP out = PROTECT(Rf_duplicate(expr)); + + /* Already-injected transparent braces may not exist in the original parse + data. Recurse into the child with the same node_id. */ + if (imputesrcref_is_braced(out) && !node_has_token(node_id, "'{'", ctx)) { + int n = call_length_cons(out); + if (n >= 2) { + SEXP slot2 = cell_at(out, 2); + SEXP newval = PROTECT(imputesrcref_transform_expr(CAR(slot2), node_id, ctx)); + SETCAR(slot2, newval); + UNPROTECT(1); + } + UNPROTECT(1); + return out; + } + + const char *op = call_op_name(out); + int n_ids; + int *child_ids = expr_children_ids(node_id, ctx, &n_ids); + + if (strcmp(op, "function") == 0) { + SEXP fmls = CADR(out); + int cursor = 0; + + if (fmls != R_NilValue) { + SEXP cell = fmls; + while (cell != R_NilValue) { + SEXP val = CAR(cell); + if (val != R_MissingArg && val != R_NilValue && !(TYPEOF(val) == SYMSXP && CHAR(PRINTNAME(val))[0] == '\0')) { + if (cursor >= n_ids) Rf_error("Parse mapping mismatch for function formals"); + int cid = child_ids[cursor++]; + SEXP newv = PROTECT(imputesrcref_transform_expr(val, cid, ctx)); + if (!imputesrcref_is_braced(newv) && TYPEOF(newv) == LANGSXP) { + SEXP sr = PROTECT(imputesrcref_node_srcref(cid, ctx)); + newv = imputesrcref_wrap_brace(newv, sr); + UNPROTECT(1); + PROTECT(newv); + SETCAR(cell, newv); + UNPROTECT(2); + } else { + SETCAR(cell, newv); + UNPROTECT(1); + } + } + cell = CDR(cell); + } + } + + if (cursor >= n_ids) Rf_error("Parse mapping mismatch for function body"); + int body_id = child_ids[cursor]; + SEXP body_cell = cell_at(out, 3); + SEXP body_val = CAR(body_cell); + SEXP body_new = PROTECT(imputesrcref_transform_expr(body_val, body_id, ctx)); + if (!imputesrcref_is_braced(body_new) && TYPEOF(body_new) != LANGSXP) { + SEXP sr = PROTECT(imputesrcref_node_srcref(body_id, ctx)); + body_new = imputesrcref_wrap_brace(body_new, sr); + UNPROTECT(1); + PROTECT(body_new); + SETCAR(body_cell, body_new); + UNPROTECT(2); + } else { + SETCAR(body_cell, body_new); + UNPROTECT(1); + } + UNPROTECT(1); + return out; + } + + if (strcmp(op, "if") == 0) { + int n_parts = call_length_cons(out); + if (n_ids < 2) { + if (n_parts >= 2) { + SEXP c2 = cell_at(out, 2); + SEXP v = PROTECT(imputesrcref_transform_expr(CAR(c2), node_id, ctx)); + SETCAR(c2, v); + UNPROTECT(1); + } + if (n_parts >= 3) { + SEXP c3 = cell_at(out, 3); + SEXP v = PROTECT(imputesrcref_transform_expr(CAR(c3), node_id, ctx)); + SETCAR(c3, v); + UNPROTECT(1); + } + if (n_parts >= 4) { + SEXP c4 = cell_at(out, 4); + SEXP v = PROTECT(imputesrcref_transform_expr(CAR(c4), node_id, ctx)); + SETCAR(c4, v); + UNPROTECT(1); + } + UNPROTECT(1); + return out; + } + + /* condition */ + SEXP c2 = cell_at(out, 2); + SEXP cond_new = PROTECT(imputesrcref_transform_expr(CAR(c2), child_ids[0], ctx)); + if (!imputesrcref_is_braced(cond_new) && !imputesrcref_is_logical_op_call(cond_new)) { + SEXP sr = PROTECT(imputesrcref_node_srcref(child_ids[0], ctx)); + cond_new = imputesrcref_wrap_brace(cond_new, sr); + UNPROTECT(1); + PROTECT(cond_new); + SETCAR(c2, cond_new); + UNPROTECT(2); + } else { + SETCAR(c2, cond_new); + UNPROTECT(1); + } + + /* then */ + SEXP c3 = cell_at(out, 3); + SEXP then_v = recurse_and_maybe_wrap(CAR(c3), child_ids[1], ctx, 1); + PROTECT(then_v); + SETCAR(c3, then_v); + UNPROTECT(1); + + /* else */ + if (n_parts >= 4 && n_ids >= 3) { + SEXP c4 = cell_at(out, 4); + SEXP else_v = recurse_and_maybe_wrap(CAR(c4), child_ids[2], ctx, 1); + PROTECT(else_v); + SETCAR(c4, else_v); + UNPROTECT(1); + } + + UNPROTECT(1); + return out; + } + + if (strcmp(op, "while") == 0) { + if (n_ids < 2) Rf_error("Parse mapping mismatch for while expression"); + SEXP c2 = cell_at(out, 2); + SEXP cond_new = PROTECT(imputesrcref_transform_expr(CAR(c2), child_ids[0], ctx)); + if (!imputesrcref_is_braced(cond_new) && !imputesrcref_is_logical_op_call(cond_new)) { + SEXP sr = PROTECT(imputesrcref_node_srcref(child_ids[0], ctx)); + cond_new = imputesrcref_wrap_brace(cond_new, sr); + UNPROTECT(1); + PROTECT(cond_new); + SETCAR(c2, cond_new); + UNPROTECT(2); + } else { + SETCAR(c2, cond_new); + UNPROTECT(1); + } + SEXP c3 = cell_at(out, 3); + SEXP body_v = recurse_and_maybe_wrap(CAR(c3), child_ids[1], ctx, 1); + PROTECT(body_v); + SETCAR(c3, body_v); + UNPROTECT(1); + UNPROTECT(1); + return out; + } + + if (strcmp(op, "for") == 0) { + int forcond_id = find_forcond_id(node_id, ctx); + int seq_id = -1; + if (forcond_id >= 0) { + seq_id = first_expr_under(forcond_id, ctx); + } + + int body_start = 0; + if (n_ids > 0 && seq_id >= 0 && child_ids[0] == seq_id) { + body_start = 1; + } + + int body_ids_count = n_ids - body_start; + if (seq_id < 0 || body_ids_count < 1) { + Rf_error("Parse mapping mismatch for for expression"); + } + + SEXP c3 = cell_at(out, 3); + SEXP seq_v = recurse_and_maybe_wrap(CAR(c3), seq_id, ctx, 1); + PROTECT(seq_v); + SETCAR(c3, seq_v); + UNPROTECT(1); + + SEXP c4 = cell_at(out, 4); + SEXP body_v = recurse_and_maybe_wrap(CAR(c4), child_ids[body_start], ctx, 1); + PROTECT(body_v); + SETCAR(c4, body_v); + UNPROTECT(1); + + UNPROTECT(1); + return out; + } + + if (strcmp(op, "repeat") == 0) { + if (n_ids < 1) Rf_error("Parse mapping mismatch for repeat expression"); + SEXP c2 = cell_at(out, 2); + SEXP body_v = recurse_and_maybe_wrap(CAR(c2), child_ids[0], ctx, 1); + PROTECT(body_v); + SETCAR(c2, body_v); + UNPROTECT(1); + UNPROTECT(1); + return out; + } + + if (strcmp(op, "switch") == 0) { + int *ids = child_ids; + int n = n_ids; + if (n > 0) { ids++; n--; } + + int cursor = 0; + SEXP cell = CDR(out); + int parts_i = 2; + while (cell != R_NilValue) { + SEXP arg = CAR(cell); + if (!imputesrcref_is_missing_arg(arg)) { + if (cursor >= n) break; + int cid = ids[cursor++]; + SEXP v = recurse_and_maybe_wrap(arg, cid, ctx, 1); + PROTECT(v); + SETCAR(cell, v); + UNPROTECT(1); + } + cell = CDR(cell); + parts_i++; + } + (void) parts_i; + + UNPROTECT(1); + return out; + } + + if (is_logical_op_name(op)) { + int n_parts = call_length_cons(out); + if (n_ids < 2) { + if (n_ids == 1) { + SEXP c2 = cell_at(out, 2); + SEXP v = PROTECT(imputesrcref_transform_expr(CAR(c2), child_ids[0], ctx)); + SETCAR(c2, v); + UNPROTECT(1); + if (n_parts >= 3) { + SEXP c3 = cell_at(out, 3); + SEXP v3 = PROTECT(imputesrcref_transform_expr(CAR(c3), node_id, ctx)); + SETCAR(c3, v3); + UNPROTECT(1); + } + } else { + if (n_parts >= 2) { + SEXP c2 = cell_at(out, 2); + SEXP v = PROTECT(imputesrcref_transform_expr(CAR(c2), node_id, ctx)); + SETCAR(c2, v); + UNPROTECT(1); + } + if (n_parts >= 3) { + SEXP c3 = cell_at(out, 3); + SEXP v = PROTECT(imputesrcref_transform_expr(CAR(c3), node_id, ctx)); + SETCAR(c3, v); + UNPROTECT(1); + } + } + UNPROTECT(1); + return out; + } + SEXP c2 = cell_at(out, 2); + SEXP v2 = recurse_and_maybe_wrap(CAR(c2), child_ids[0], ctx, 1); + PROTECT(v2); + SETCAR(c2, v2); + UNPROTECT(1); + SEXP c3 = cell_at(out, 3); + SEXP v3 = recurse_and_maybe_wrap(CAR(c3), child_ids[1], ctx, 1); + PROTECT(v3); + SETCAR(c3, v3); + UNPROTECT(1); + UNPROTECT(1); + return out; + } + + /* Generic call. */ + mapping_t m = map_generic_indices(out, child_ids, n_ids, 0); + if (!m.valid) { + UNPROTECT(1); + return out; + } + + int op_blacklisted = (op[0] != '\0') && imputesrcref_in_strvec(ctx->arg_wrap_blacklist, op); + int wrap_generic_args = node_has_token(node_id, "'('", ctx) && + strcmp(op, "(") != 0 && + !op_blacklisted && + ctx->wrap_call_args; + + int is_assign_op = (op[0] != '\0') && is_assign_op_name(op); + int lhs_slot = (strcmp(op, "->") == 0 || strcmp(op, "->>") == 0) ? 3 : 2; + + for (int k = 0; k < m.n; k++) { + int i = m.indices[k]; + int cid = m.ids[k]; + SEXP cell = cell_at(out, i); + SEXP arg = CAR(cell); + if (imputesrcref_is_missing_arg(arg)) continue; + + int is_assign_lhs = is_assign_op && i == lhs_slot; + int saved_wrap = ctx->wrap_call_args; + if (is_assign_lhs && ctx->wrap_call_args) ctx->wrap_call_args = 0; + SEXP transformed = PROTECT(imputesrcref_transform_expr(arg, cid, ctx)); + if (is_assign_lhs && saved_wrap) ctx->wrap_call_args = saved_wrap; + + int slot_callee_bl = callee_blacklisted(arg, ctx); + int do_wrap = wrap_generic_args && i > 1 && !is_assign_lhs && + TYPEOF(arg) == LANGSXP && + !imputesrcref_is_braced(transformed) && + !imputesrcref_is_unquote_call(arg) && + !imputesrcref_is_call_named(arg, ":=") && + !imputesrcref_is_unary_arith_call(arg) && + !slot_callee_bl; + if (do_wrap) { + SEXP sr = PROTECT(imputesrcref_node_srcref(cid, ctx)); + transformed = imputesrcref_wrap_brace(transformed, sr); + UNPROTECT(1); + PROTECT(transformed); + SETCAR(cell, transformed); + UNPROTECT(2); + } else { + SETCAR(cell, transformed); + UNPROTECT(1); + } + } + + UNPROTECT(1); + return out; +} + +static SEXP call_parse(const char *text) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("parse"), R_BaseEnv)); + SEXP txt = PROTECT(Rf_mkString(text)); + SEXP keep = PROTECT(Rf_ScalarLogical(1)); + SEXP call = PROTECT(Rf_lang3(fn, txt, keep)); + SET_TAG(CDR(call), Rf_install("text")); + SET_TAG(CDDR(call), Rf_install("keep.source")); + int err = 0; + SEXP res = R_tryEval(call, R_GlobalEnv, &err); + UNPROTECT(4); + if (err) return R_NilValue; + return res; +} + +static SEXP call_getParseData(SEXP parsed) { + SEXP fn = PROTECT(Rf_findFun(Rf_install("getParseData"), R_BaseNamespace)); + SEXP call = PROTECT(Rf_lang2(fn, parsed)); + int err = 0; + SEXP res = R_tryEval(call, R_GlobalEnv, &err); + UNPROTECT(2); + if (err) return R_NilValue; + return res; +} + +static SEXP call_formals(SEXP fn) { + SEXP f = PROTECT(Rf_findFun(Rf_install("formals"), R_BaseEnv)); + SEXP call = PROTECT(Rf_lang2(f, fn)); + SEXP res = Rf_eval(call, R_GlobalEnv); + UNPROTECT(2); + return res; +} + +static SEXP call_body(SEXP fn) { + SEXP f = PROTECT(Rf_findFun(Rf_install("body"), R_BaseEnv)); + SEXP call = PROTECT(Rf_lang2(f, fn)); + SEXP res = Rf_eval(call, R_GlobalEnv); + UNPROTECT(2); + return res; +} + +SEXP C_impute_srcrefs(SEXP fn, SEXP wrap_call_args_sxp) { + if (TYPEOF(fn) != CLOSXP) { + Rf_error("`fn` must be a function"); + } + if (TYPEOF(wrap_call_args_sxp) != LGLSXP || Rf_xlength(wrap_call_args_sxp) != 1 || + LOGICAL(wrap_call_args_sxp)[0] == NA_LOGICAL) { + Rf_error("`wrap_call_args` must be TRUE or FALSE"); + } + int wrap_call_args = LOGICAL(wrap_call_args_sxp)[0]; + + SEXP fn_attrs = PROTECT(Rf_duplicate(ATTRIB(fn))); + + SEXP src = PROTECT(imputesrcref_source_text(fn)); + if (src == R_NilValue) { + UNPROTECT(2); + return fn; + } + + SEXP text_sxp = VECTOR_ELT(src, 0); + SEXP srcfile = VECTOR_ELT(src, 1); + int line_offset = INTEGER(VECTOR_ELT(src, 2))[0]; + int first_col_offset = INTEGER(VECTOR_ELT(src, 3))[0]; + + const char *txt = CHAR(STRING_ELT(text_sxp, 0)); + SEXP parsed = PROTECT(call_parse(txt)); + int paren_wrapped = 0; + + if (parsed == R_NilValue) { + UNPROTECT(1); /* parsed (NilValue) */ + int len = (int) strlen(txt); + char *buf = (char*) R_alloc((size_t)(len + 3), sizeof(char)); + buf[0] = '('; + memcpy(buf + 1, txt, (size_t) len); + buf[len + 1] = ')'; + buf[len + 2] = '\0'; + parsed = PROTECT(call_parse(buf)); + if (parsed == R_NilValue) Rf_error("Could not parse function source"); + paren_wrapped = 1; + } + + SEXP pd = PROTECT(call_getParseData(parsed)); + if (pd == R_NilValue || TYPEOF(pd) != VECSXP) { + Rf_error("Could not obtain parse data for function"); + } + + /* find expr_rows / root id */ + SEXP nms = Rf_getAttrib(pd, R_NamesSymbol); + SEXP token_col = R_NilValue; + SEXP id_col = R_NilValue; + SEXP parent_col = R_NilValue; + R_xlen_t nc = Rf_xlength(nms); + for (R_xlen_t i = 0; i < nc; i++) { + const char *cn = CHAR(STRING_ELT(nms, i)); + if (strcmp(cn, "token") == 0) token_col = VECTOR_ELT(pd, i); + else if (strcmp(cn, "id") == 0) id_col = VECTOR_ELT(pd, i); + else if (strcmp(cn, "parent") == 0) parent_col = VECTOR_ELT(pd, i); + } + if (token_col == R_NilValue || id_col == R_NilValue || parent_col == R_NilValue) { + Rf_error("Parse data missing required columns"); + } + R_xlen_t pd_n = Rf_xlength(token_col); + if (pd_n == 0) Rf_error("Parse data does not contain expression nodes"); + + int *id_arr = INTEGER(id_col); + int *par_arr = INTEGER(parent_col); + + int root_id = -1; + int root_count = 0; + for (R_xlen_t i = 0; i < pd_n; i++) { + if (strcmp(CHAR(STRING_ELT(token_col, i)), "expr") == 0 && par_arr[i] == 0) { + root_id = id_arr[i]; + root_count++; + } + } + if (root_count != 1) { + Rf_error("Expected exactly one top-level expression for function source"); + } + + if (paren_wrapped) { + /* descend into inner expr child */ + int inner = -1; + int inner_count = 0; + for (R_xlen_t i = 0; i < pd_n; i++) { + if (strcmp(CHAR(STRING_ELT(token_col, i)), "expr") == 0 && par_arr[i] == root_id) { + inner = id_arr[i]; + inner_count++; + } + } + if (inner_count != 1) Rf_error("Expected exactly one expression inside paren wrapper"); + root_id = inner; + first_col_offset -= 1; + } + + SEXP blacklist = PROTECT(imputesrcref_effective_blacklist()); + + parse_ctx ctx; + imputesrcref_build_ctx(&ctx, pd, srcfile, line_offset, first_col_offset, blacklist, wrap_call_args); + + /* Build fn_expr = call("function", formals(fn), body(fn)) */ + SEXP fmls = PROTECT(call_formals(fn)); + SEXP bdy = PROTECT(call_body(fn)); + SEXP fnsym = PROTECT(Rf_install("function")); + SEXP fn_expr = PROTECT(Rf_allocList(3)); + SET_TYPEOF(fn_expr, LANGSXP); + SETCAR(fn_expr, fnsym); + SETCAR(CDR(fn_expr), fmls); + SETCAR(CDDR(fn_expr), bdy); + + SEXP transformed = PROTECT(imputesrcref_transform_expr(fn_expr, root_id, &ctx)); + + /* Apply to fn: out <- fn; formals(out) <- transformed[[2]]; body(out) <- transformed[[3]] */ + SEXP out = PROTECT(Rf_duplicate(fn)); + SEXP new_formals = CADR(transformed); + SEXP new_body = CADDR(transformed); + + if (new_formals != R_NilValue) { + SET_FORMALS(out, new_formals); + } + SET_BODY(out, new_body); + + /* Reapply original attributes */ + SEXP attr_iter = fn_attrs; + while (attr_iter != R_NilValue) { + SEXP tag = TAG(attr_iter); + SEXP val = CAR(attr_iter); + Rf_setAttrib(out, tag, val); + attr_iter = CDR(attr_iter); + } + + UNPROTECT(11); + return out; +} diff --git a/src/transform.o b/src/transform.o new file mode 100644 index 0000000000000000000000000000000000000000..581f75f837dc86d9ca88f4959a58d0c50855edf0 GIT binary patch literal 94872 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 Date: Mon, 25 May 2026 14:18:58 +0200 Subject: [PATCH 2/7] Fix tests --- tests/testthat/test-blacklist-api.R | 8 +- tests/testthat/test-srcref-imputation.out | 172 +++++++++------------- 2 files changed, 75 insertions(+), 105 deletions(-) diff --git a/tests/testthat/test-blacklist-api.R b/tests/testthat/test-blacklist-api.R index 9ff48d9..4cbc313 100644 --- a/tests/testthat/test-blacklist-api.R +++ b/tests/testthat/test-blacklist-api.R @@ -6,7 +6,7 @@ test_that("SPECIALSXP calls are blacklisted by default", { expect_true("quote" %in% imputesrcref::get_impute_blacklist(include_default = TRUE)) expect_identical(imputesrcref::get_impute_blacklist(include_default = FALSE), character()) - fn <- make_fn("function() quote(x + 1)") + fn <- make_fn("function() { quote(x + 1) }") out <- imputesrcref::impute_srcrefs(fn) sites <- imputesrcref:::collect_srcref_sites(out) @@ -19,7 +19,7 @@ test_that("user blacklist entries are applied and resettable", { on.exit(options(imputesrcref.wrap_arg_blacklist = old), add = TRUE) imputesrcref::reset_impute_blacklist() - fn <- make_fn("function(x) g(x + 1)") + fn <- make_fn("function(x) { g(x + 1) }") wrapped <- imputesrcref::impute_srcrefs(fn) wrapped_sites <- imputesrcref:::collect_srcref_sites(wrapped) expect_true(any(grepl("g\\(\\{ x \\+ 1 \\}\\)", wrapped_sites))) @@ -41,7 +41,7 @@ test_that("generic argument wrapping only applies to call expressions", { on.exit(options(imputesrcref.wrap_arg_blacklist = old), add = TRUE) imputesrcref::reset_impute_blacklist() - fn <- make_fn("function(x) g(x + 1, 1, x)") + fn <- make_fn("function(x) { g(x + 1, 1, x) }") out <- imputesrcref::impute_srcrefs(fn) sites <- imputesrcref:::collect_srcref_sites(out) @@ -55,7 +55,7 @@ test_that("wrap_call_args can disable generic argument wrapping", { on.exit(options(imputesrcref.wrap_arg_blacklist = old), add = TRUE) imputesrcref::reset_impute_blacklist() - fn <- make_fn("function(x) g(x + 1)") + fn <- make_fn("function(x) { g(x + 1) }") out <- imputesrcref::impute_srcrefs(fn, wrap_call_args = FALSE) sites <- imputesrcref:::collect_srcref_sites(out) diff --git a/tests/testthat/test-srcref-imputation.out b/tests/testthat/test-srcref-imputation.out index 77b0c24..83a5927 100644 --- a/tests/testthat/test-srcref-imputation.out +++ b/tests/testthat/test-srcref-imputation.out @@ -14,25 +14,23 @@ check path=body/2/4 ok=TRUE sr_text="g()" expr_text="g()" === loops === idempotent=TRUE -transparent_checks=10 +transparent_checks=9 transparent_checks_ok=TRUE -path=body node={ for (i in { 1:3 }) { if ({ x } || { y }) { a() } else { b() } } while ({ x } & { y }) { c() } repeat { d() } } sr1=(1,16,1,16,16,16,1,1) sr2=(1,18,1,56,18,56,1,1) +path=body node={ for (i in { 1:3 }) if ({ x } || { y }) { a() } else { b() } while ({ x } & { y }) { c() } repeat { d() } } sr1=(1,16,1,16,16,16,1,1) sr2=(1,18,1,56,18,56,1,1) path=body/2/3 node={ 1:3 } sr1=(1,28,1,30,28,30,1,1) sr2=(1,28,1,30,28,30,1,1) -path=body/2/4 node={ if ({ x } || { y }) { a() } else { b() } } sr1=(1,33,1,56,33,56,1,1) sr2=(1,33,1,56,33,56,1,1) -path=body/2/4/2/2/2 node={ x } sr1=(1,37,1,37,37,37,1,1) sr2=(1,37,1,37,37,37,1,1) -path=body/2/4/2/2/3 node={ y } sr1=(1,42,1,42,42,42,1,1) sr2=(1,42,1,42,42,42,1,1) -path=body/2/4/2/3 node={ a() } sr1=(1,45,1,47,45,47,1,1) sr2=(1,45,1,47,45,47,1,1) -path=body/2/4/2/4 node={ b() } sr1=(1,54,1,56,54,56,1,1) sr2=(1,54,1,56,54,56,1,1) +path=body/2/4/2/2 node={ x } sr1=(1,37,1,37,37,37,1,1) sr2=(1,37,1,37,37,37,1,1) +path=body/2/4/2/3 node={ y } sr1=(1,42,1,42,42,42,1,1) sr2=(1,42,1,42,42,42,1,1) +path=body/2/4/3 node={ a() } sr1=(1,45,1,47,45,47,1,1) sr2=(1,45,1,47,45,47,1,1) +path=body/2/4/4 node={ b() } sr1=(1,54,1,56,54,56,1,1) sr2=(1,54,1,56,54,56,1,1) path=body/3/2/2 node={ x } sr1=(1,66,1,66,66,66,1,1) sr2=(1,66,1,66,66,66,1,1) path=body/3/2/3 node={ y } sr1=(1,70,1,70,70,70,1,1) sr2=(1,70,1,70,70,70,1,1) path=body/3/3 node={ c() } sr1=(1,73,1,75,73,75,1,1) sr2=(1,73,1,75,73,75,1,1) path=body/4/2 node={ d() } sr1=(1,85,1,87,85,87,1,1) sr2=(1,85,1,87,85,87,1,1) check path=body/2/3 ok=TRUE sr_text="1:3" expr_text="1:3" -check path=body/2/4 ok=TRUE sr_text="if (x || y) a() else b()" expr_text="if (x || y) a() else b()" -check path=body/2/4/2/2/2 ok=TRUE sr_text="x" expr_text="x" -check path=body/2/4/2/2/3 ok=TRUE sr_text="y" expr_text="y" -check path=body/2/4/2/3 ok=TRUE sr_text="a()" expr_text="a()" -check path=body/2/4/2/4 ok=TRUE sr_text="b()" expr_text="b()" +check path=body/2/4/2/2 ok=TRUE sr_text="x" expr_text="x" +check path=body/2/4/2/3 ok=TRUE sr_text="y" expr_text="y" +check path=body/2/4/3 ok=TRUE sr_text="a()" expr_text="a()" +check path=body/2/4/4 ok=TRUE sr_text="b()" expr_text="b()" check path=body/3/2/2 ok=TRUE sr_text="x" expr_text="x" check path=body/3/2/3 ok=TRUE sr_text="y" expr_text="y" check path=body/3/3 ok=TRUE sr_text="c()" expr_text="c()" @@ -52,7 +50,7 @@ check path=body/2/4 ok=TRUE sr_text="g()" expr_text="g()" === defaults-and-unbraced-body === idempotent=TRUE -transparent_checks=11 +transparent_checks=10 transparent_checks_ok=TRUE path=formals/a node={ { x } && { y } } sr1=(1,14,1,19,14,19,1,1) sr2=(1,14,1,19,14,19,1,1) path=formals/a/2/2 node={ x } sr1=(1,14,1,14,14,14,1,1) sr2=(1,14,1,14,14,14,1,1) @@ -61,10 +59,9 @@ path=formals/b node={ if ({ p }) { u } else { v } } sr1=(1,26,1,40,26,40,1,1) sr path=formals/b/2/2 node={ p } sr1=(1,30,1,30,30,30,1,1) sr2=(1,30,1,30,30,30,1,1) path=formals/b/2/3 node={ u } sr1=(1,33,1,33,33,33,1,1) sr2=(1,33,1,33,33,33,1,1) path=formals/b/2/4 node={ v } sr1=(1,40,1,40,40,40,1,1) sr2=(1,40,1,40,40,40,1,1) -path=body node={ if ({ a }) { m() } else { n() } } sr1=(1,43,1,61,43,61,1,1) sr2=(1,43,1,61,43,61,1,1) -path=body/2/2 node={ a } sr1=(1,47,1,47,47,47,1,1) sr2=(1,47,1,47,47,47,1,1) -path=body/2/3 node={ m() } sr1=(1,50,1,52,50,52,1,1) sr2=(1,50,1,52,50,52,1,1) -path=body/2/4 node={ n() } sr1=(1,59,1,61,59,61,1,1) sr2=(1,59,1,61,59,61,1,1) +path=body/2 node={ a } sr1=(1,47,1,47,47,47,1,1) sr2=(1,47,1,47,47,47,1,1) +path=body/3 node={ m() } sr1=(1,50,1,52,50,52,1,1) sr2=(1,50,1,52,50,52,1,1) +path=body/4 node={ n() } sr1=(1,59,1,61,59,61,1,1) sr2=(1,59,1,61,59,61,1,1) check path=formals/a ok=TRUE sr_text="x && y" expr_text="x && y" check path=formals/a/2/2 ok=TRUE sr_text="x" expr_text="x" check path=formals/a/2/3 ok=TRUE sr_text="y" expr_text="y" @@ -72,10 +69,9 @@ check path=formals/b ok=TRUE sr_text="if (p) u else v" expr_text="if (p) u else check path=formals/b/2/2 ok=TRUE sr_text="p" expr_text="p" check path=formals/b/2/3 ok=TRUE sr_text="u" expr_text="u" check path=formals/b/2/4 ok=TRUE sr_text="v" expr_text="v" -check path=body ok=TRUE sr_text="if (a) m() else n()" expr_text="if (a) m() else n()" -check path=body/2/2 ok=TRUE sr_text="a" expr_text="a" -check path=body/2/3 ok=TRUE sr_text="m()" expr_text="m()" -check path=body/2/4 ok=TRUE sr_text="n()" expr_text="n()" +check path=body/2 ok=TRUE sr_text="a" expr_text="a" +check path=body/3 ok=TRUE sr_text="m()" expr_text="m()" +check path=body/4 ok=TRUE sr_text="n()" expr_text="n()" === already-braced === idempotent=TRUE @@ -89,123 +85,97 @@ path=body/2/4 node={ g() } sr1=(1,45,1,45,45,45,1,1) sr2=(1,46,1,48,46,48,1,1) === null-args-preserved === idempotent=TRUE -transparent_checks=8 +transparent_checks=6 transparent_checks_ok=TRUE -path=body node={ f(NULL, { if ({ x }) { y } else { z } }, NULL, { if ({ p }) { q } else { r } }, NULL) } sr1=(1,12,1,12,12,12,1,1) sr2=(1,14,1,66,14,66,1,1) -path=body/2/3 node={ if ({ x }) { y } else { z } } sr1=(1,22,1,36,22,36,1,1) sr2=(1,22,1,36,22,36,1,1) -path=body/2/3/2/2 node={ x } sr1=(1,26,1,26,26,26,1,1) sr2=(1,26,1,26,26,26,1,1) -path=body/2/3/2/3 node={ y } sr1=(1,29,1,29,29,29,1,1) sr2=(1,29,1,29,29,29,1,1) -path=body/2/3/2/4 node={ z } sr1=(1,36,1,36,36,36,1,1) sr2=(1,36,1,36,36,36,1,1) -path=body/2/5 node={ if ({ p }) { q } else { r } } sr1=(1,45,1,59,45,59,1,1) sr2=(1,45,1,59,45,59,1,1) -path=body/2/5/2/2 node={ p } sr1=(1,49,1,49,49,49,1,1) sr2=(1,49,1,49,49,49,1,1) -path=body/2/5/2/3 node={ q } sr1=(1,52,1,52,52,52,1,1) sr2=(1,52,1,52,52,52,1,1) -path=body/2/5/2/4 node={ r } sr1=(1,59,1,59,59,59,1,1) sr2=(1,59,1,59,59,59,1,1) -check path=body/2/3 ok=TRUE sr_text="if (x) y else z" expr_text="if (x) y else z" -check path=body/2/3/2/2 ok=TRUE sr_text="x" expr_text="x" -check path=body/2/3/2/3 ok=TRUE sr_text="y" expr_text="y" -check path=body/2/3/2/4 ok=TRUE sr_text="z" expr_text="z" -check path=body/2/5 ok=TRUE sr_text="if (p) q else r" expr_text="if (p) q else r" -check path=body/2/5/2/2 ok=TRUE sr_text="p" expr_text="p" -check path=body/2/5/2/3 ok=TRUE sr_text="q" expr_text="q" -check path=body/2/5/2/4 ok=TRUE sr_text="r" expr_text="r" +path=body node={ f(NULL, if ({ x }) { y } else { z }, NULL, if ({ p }) { q } else { r }, NULL) } sr1=(1,12,1,12,12,12,1,1) sr2=(1,14,1,66,14,66,1,1) +path=body/2/3/2 node={ x } sr1=(1,26,1,26,26,26,1,1) sr2=(1,26,1,26,26,26,1,1) +path=body/2/3/3 node={ y } sr1=(1,29,1,29,29,29,1,1) sr2=(1,29,1,29,29,29,1,1) +path=body/2/3/4 node={ z } sr1=(1,36,1,36,36,36,1,1) sr2=(1,36,1,36,36,36,1,1) +path=body/2/5/2 node={ p } sr1=(1,49,1,49,49,49,1,1) sr2=(1,49,1,49,49,49,1,1) +path=body/2/5/3 node={ q } sr1=(1,52,1,52,52,52,1,1) sr2=(1,52,1,52,52,52,1,1) +path=body/2/5/4 node={ r } sr1=(1,59,1,59,59,59,1,1) sr2=(1,59,1,59,59,59,1,1) +check path=body/2/3/2 ok=TRUE sr_text="x" expr_text="x" +check path=body/2/3/3 ok=TRUE sr_text="y" expr_text="y" +check path=body/2/3/4 ok=TRUE sr_text="z" expr_text="z" +check path=body/2/5/2 ok=TRUE sr_text="p" expr_text="p" +check path=body/2/5/3 ok=TRUE sr_text="q" expr_text="q" +check path=body/2/5/4 ok=TRUE sr_text="r" expr_text="r" === generic-call-args === idempotent=TRUE -transparent_checks=4 +transparent_checks=3 transparent_checks_ok=TRUE -path=body node={ g({ x + 1 }, { f({ y + 1 }) }) } sr1=(1,16,1,29,16,29,1,1) sr2=(1,16,1,29,16,29,1,1) -path=body/2/2 node={ x + 1 } sr1=(1,18,1,20,18,20,1,1) sr2=(1,18,1,20,18,20,1,1) -path=body/2/3 node={ f({ y + 1 }) } sr1=(1,23,1,28,23,28,1,1) sr2=(1,23,1,28,23,28,1,1) -path=body/2/3/2/2 node={ y + 1 } sr1=(1,25,1,27,25,27,1,1) sr2=(1,25,1,27,25,27,1,1) -check path=body ok=TRUE sr_text="g(x + 1, f(y + 1))" expr_text="g(x + 1, f(y + 1))" -check path=body/2/2 ok=TRUE sr_text="x + 1" expr_text="x + 1" -check path=body/2/3 ok=TRUE sr_text="f(y + 1)" expr_text="f(y + 1)" -check path=body/2/3/2/2 ok=TRUE sr_text="y + 1" expr_text="y + 1" +path=body/2 node={ x + 1 } sr1=(1,18,1,20,18,20,1,1) sr2=(1,18,1,20,18,20,1,1) +path=body/3 node={ f({ y + 1 }) } sr1=(1,23,1,28,23,28,1,1) sr2=(1,23,1,28,23,28,1,1) +path=body/3/2/2 node={ y + 1 } sr1=(1,25,1,27,25,27,1,1) sr2=(1,25,1,27,25,27,1,1) +check path=body/2 ok=TRUE sr_text="x + 1" expr_text="x + 1" +check path=body/3 ok=TRUE sr_text="f(y + 1)" expr_text="f(y + 1)" +check path=body/3/2/2 ok=TRUE sr_text="y + 1" expr_text="y + 1" === missing-call-arg === idempotent=TRUE -transparent_checks=1 +transparent_checks=0 transparent_checks_ok=TRUE -path=body node={ quote(expr = ) } sr1=(1,12,1,25,12,25,1,1) sr2=(1,12,1,25,12,25,1,1) -check path=body ok=TRUE sr_text="quote(expr = )" expr_text="quote(expr = )" === source-file === count=2 functions=f1,f2 f1_parse_rows=65 f2_parse_rows=65 -f1_transparent_checks=5 -f2_transparent_checks=6 +f1_transparent_checks=4 +f2_transparent_checks=5 f1_transparent_ok=TRUE f2_transparent_ok=TRUE -path=body node={ if ({ x } && { y }) { f() } else { g() } } sr1=(1,22,1,45,22,45,1,1) sr2=(1,22,1,45,22,45,1,1) -path=body/2/2/2 node={ x } sr1=(1,26,1,26,26,26,1,1) sr2=(1,26,1,26,26,26,1,1) -path=body/2/2/3 node={ y } sr1=(1,31,1,31,31,31,1,1) sr2=(1,31,1,31,31,31,1,1) -path=body/2/3 node={ f() } sr1=(1,34,1,36,34,36,1,1) sr2=(1,34,1,36,34,36,1,1) -path=body/2/4 node={ g() } sr1=(1,43,1,45,43,45,1,1) sr2=(1,43,1,45,43,45,1,1) -check path=body ok=TRUE sr_text="if (x && y) f() else g()" expr_text="if (x && y) f() else g()" -check path=body/2/2/2 ok=TRUE sr_text="x" expr_text="x" -check path=body/2/2/3 ok=TRUE sr_text="y" expr_text="y" -check path=body/2/3 ok=TRUE sr_text="f()" expr_text="f()" -check path=body/2/4 ok=TRUE sr_text="g()" expr_text="g()" +path=body/2/2 node={ x } sr1=(1,26,1,26,26,26,1,1) sr2=(1,26,1,26,26,26,1,1) +path=body/2/3 node={ y } sr1=(1,31,1,31,31,31,1,1) sr2=(1,31,1,31,31,31,1,1) +path=body/3 node={ f() } sr1=(1,34,1,36,34,36,1,1) sr2=(1,34,1,36,34,36,1,1) +path=body/4 node={ g() } sr1=(1,43,1,45,43,45,1,1) sr2=(1,43,1,45,43,45,1,1) +check path=body/2/2 ok=TRUE sr_text="x" expr_text="x" +check path=body/2/3 ok=TRUE sr_text="y" expr_text="y" +check path=body/3 ok=TRUE sr_text="f()" expr_text="f()" +check path=body/4 ok=TRUE sr_text="g()" expr_text="g()" path=formals/a node={ { p } || { q } } sr1=(2,20,2,25,20,25,2,2) sr2=(2,20,2,25,20,25,2,2) path=formals/a/2/2 node={ p } sr1=(2,20,2,20,20,20,2,2) sr2=(2,20,2,20,20,20,2,2) path=formals/a/2/3 node={ q } sr1=(2,25,2,25,25,25,2,2) sr2=(2,25,2,25,25,25,2,2) -path=body node={ while ({ a }) { h() } } sr1=(2,28,2,40,28,40,2,2) sr2=(2,28,2,40,28,40,2,2) -path=body/2/2 node={ a } sr1=(2,35,2,35,35,35,2,2) sr2=(2,35,2,35,35,35,2,2) -path=body/2/3 node={ h() } sr1=(2,38,2,40,38,40,2,2) sr2=(2,38,2,40,38,40,2,2) +path=body/2 node={ a } sr1=(2,35,2,35,35,35,2,2) sr2=(2,35,2,35,35,35,2,2) +path=body/3 node={ h() } sr1=(2,38,2,40,38,40,2,2) sr2=(2,38,2,40,38,40,2,2) check path=formals/a ok=TRUE sr_text="p || q" expr_text="p || q" check path=formals/a/2/2 ok=TRUE sr_text="p" expr_text="p" check path=formals/a/2/3 ok=TRUE sr_text="q" expr_text="q" -check path=body ok=TRUE sr_text="while (a) h()" expr_text="while (a) h()" -check path=body/2/2 ok=TRUE sr_text="a" expr_text="a" -check path=body/2/3 ok=TRUE sr_text="h()" expr_text="h()" +check path=body/2 ok=TRUE sr_text="a" expr_text="a" +check path=body/3 ok=TRUE sr_text="h()" expr_text="h()" === package-like-srcref === sr_slots_1_3=100-100 sr_slots_7_8=3-3 -transparent_checks=5 +transparent_checks=4 transparent_checks_ok=TRUE -path=body node={ if ({ x } && { y }) { f() } else { g() } } sr1=(3,21,3,44,21,44,3,3) sr2=(3,21,3,44,21,44,3,3) -path=body/2/2/2 node={ x } sr1=(3,25,3,25,25,25,3,3) sr2=(3,25,3,25,25,25,3,3) -path=body/2/2/3 node={ y } sr1=(3,30,3,30,30,30,3,3) sr2=(3,30,3,30,30,30,3,3) -path=body/2/3 node={ f() } sr1=(3,33,3,35,33,35,3,3) sr2=(3,33,3,35,33,35,3,3) -path=body/2/4 node={ g() } sr1=(3,42,3,44,42,44,3,3) sr2=(3,42,3,44,42,44,3,3) -check path=body ok=TRUE sr_text="if (x && y) f() else g()" expr_text="if (x && y) f() else g()" -check path=body/2/2/2 ok=TRUE sr_text="x" expr_text="x" -check path=body/2/2/3 ok=TRUE sr_text="y" expr_text="y" -check path=body/2/3 ok=TRUE sr_text="f()" expr_text="f()" -check path=body/2/4 ok=TRUE sr_text="g()" expr_text="g()" +path=body/2/2 node={ x } sr1=(3,25,3,25,25,25,3,3) sr2=(3,25,3,25,25,25,3,3) +path=body/2/3 node={ y } sr1=(3,30,3,30,30,30,3,3) sr2=(3,30,3,30,30,30,3,3) +path=body/3 node={ f() } sr1=(3,33,3,35,33,35,3,3) sr2=(3,33,3,35,33,35,3,3) +path=body/4 node={ g() } sr1=(3,42,3,44,42,44,3,3) sr2=(3,42,3,44,42,44,3,3) +check path=body/2/2 ok=TRUE sr_text="x" expr_text="x" +check path=body/2/3 ok=TRUE sr_text="y" expr_text="y" +check path=body/3 ok=TRUE sr_text="f()" expr_text="f()" +check path=body/4 ok=TRUE sr_text="g()" expr_text="g()" === zero-formals === formals_is_null=TRUE idempotent=TRUE -transparent_checks=4 +transparent_checks=3 transparent_checks_ok=TRUE -path=body node={ if ({ TRUE }) { f() } else { g() } } sr1=(1,12,1,33,12,33,1,1) sr2=(1,12,1,33,12,33,1,1) -path=body/2/2 node={ TRUE } sr1=(1,16,1,19,16,19,1,1) sr2=(1,16,1,19,16,19,1,1) -path=body/2/3 node={ f() } sr1=(1,22,1,24,22,24,1,1) sr2=(1,22,1,24,22,24,1,1) -path=body/2/4 node={ g() } sr1=(1,31,1,33,31,33,1,1) sr2=(1,31,1,33,31,33,1,1) -check path=body ok=TRUE sr_text="if (TRUE) f() else g()" expr_text="if (TRUE) f() else g()" -check path=body/2/2 ok=TRUE sr_text="TRUE" expr_text="TRUE" -check path=body/2/3 ok=TRUE sr_text="f()" expr_text="f()" -check path=body/2/4 ok=TRUE sr_text="g()" expr_text="g()" +path=body/2 node={ TRUE } sr1=(1,16,1,19,16,19,1,1) sr2=(1,16,1,19,16,19,1,1) +path=body/3 node={ f() } sr1=(1,22,1,24,22,24,1,1) sr2=(1,22,1,24,22,24,1,1) +path=body/4 node={ g() } sr1=(1,31,1,33,31,33,1,1) sr2=(1,31,1,33,31,33,1,1) +check path=body/2 ok=TRUE sr_text="TRUE" expr_text="TRUE" +check path=body/3 ok=TRUE sr_text="f()" expr_text="f()" +check path=body/4 ok=TRUE sr_text="g()" expr_text="g()" === package-base === patched_count=0 === no-srcref-fallback === -default_blocked=TRUE -fallback_checks=5 +default_blocked=FALSE +fallback_checks=0 fallback_ok=TRUE -path=body node={ if ({ x } && { y }) { f() } else { g() } } sr1=(2,1,2,24,1,24,2,2) sr2=(2,1,2,24,1,24,2,2) -path=body/2/2/2 node={ x } sr1=(2,5,2,5,5,5,2,2) sr2=(2,5,2,5,5,5,2,2) -path=body/2/2/3 node={ y } sr1=(2,10,2,10,10,10,2,2) sr2=(2,10,2,10,10,10,2,2) -path=body/2/3 node={ f() } sr1=(2,13,2,15,13,15,2,2) sr2=(2,13,2,15,13,15,2,2) -path=body/2/4 node={ g() } sr1=(2,22,2,24,22,24,2,2) sr2=(2,22,2,24,22,24,2,2) -check path=body ok=TRUE sr_text="if (x && y) f() else g()" expr_text="if (x && y) f() else g()" -check path=body/2/2/2 ok=TRUE sr_text="x" expr_text="x" -check path=body/2/2/3 ok=TRUE sr_text="y" expr_text="y" -check path=body/2/3 ok=TRUE sr_text="f()" expr_text="f()" -check path=body/2/4 ok=TRUE sr_text="g()" expr_text="g()" From b2cc5df947b894743dec15b46786a74b720ff716 Mon Sep 17 00:00:00 2001 From: Matej Kocourek Date: Thu, 28 May 2026 13:54:00 +0200 Subject: [PATCH 3/7] Fixes by claud --- .gitignore | 16 + R/srcref_sites.R | 247 +------- R/zzz.R | 2 - README.md | 36 +- src/blacklist.o | Bin 36096 -> 0 bytes src/helpers.o | Bin 40008 -> 0 bytes src/imputesrcref.h | 2 + src/imputesrcref.so | Bin 168448 -> 0 bytes src/init.c | 3 + src/init.o | Bin 7856 -> 0 bytes src/source_impute.c | 42 +- src/source_impute.o | Bin 90472 -> 0 bytes src/source_text.c | 19 +- src/source_text.o | Bin 41896 -> 0 bytes src/srcref_sites.c | 562 ++++++++++++++++++ src/transform.c | 7 +- src/transform.o | Bin 94872 -> 0 bytes .../testthat/test-package-srcref-imputation.R | 3 +- 18 files changed, 661 insertions(+), 278 deletions(-) create mode 100644 .gitignore delete mode 100644 R/zzz.R delete mode 100644 src/blacklist.o delete mode 100644 src/helpers.o delete mode 100755 src/imputesrcref.so delete mode 100644 src/init.o delete mode 100644 src/source_impute.o delete mode 100644 src/source_text.o create mode 100644 src/srcref_sites.c delete mode 100644 src/transform.o diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e480d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Compiled object code +src/*.o +src/*.so +src/*.dll +src/*.dylib +src/symbols.rds + +# R CMD build / check artifacts +*.tar.gz +*.Rcheck/ + +# Editor / IDE +.Rproj.user/ +.Rhistory +.RData +.Ruserdata diff --git a/R/srcref_sites.R b/R/srcref_sites.R index 76ca897..b9d074b 100644 --- a/R/srcref_sites.R +++ b/R/srcref_sites.R @@ -1,251 +1,16 @@ -is_braced <- function(expr) { - is.call(expr) && is.symbol(expr[[1]]) && identical(as.character(expr[[1]]), "{") -} - -deparse_one_line <- function(expr) { - # Canonical single-line representation for stable snapshot output. - txt <- paste(deparse(expr, width.cutoff = 500L), collapse = " ") - gsub("[[:space:]]+", " ", txt) -} - -format_srcref_tuple <- function(sr) { - vals <- unclass(sr) - paste0("(", paste(vals, collapse = ","), ")") -} - -srcref_to_text <- function(sr) { - # Extract exact text span covered by an srcref from its backing srcfile. - srcfile <- attr(sr, "srcfile", exact = TRUE) - if (is.null(srcfile)) { - return(NA_character_) - } - - lines <- getSrcLines(srcfile, sr[1], sr[3]) - if (length(lines) == 0L) { - return(NA_character_) - } +#' @useDynLib imputesrcref, .registration = TRUE +NULL - if (length(lines) == 1L) { - lines[1L] <- substr(lines[1L], sr[2], sr[4]) - } else { - lines[1L] <- substr(lines[1L], sr[2], nchar(lines[1L])) - lines[length(lines)] <- substr(lines[length(lines)], 1L, sr[4]) - } - - paste(lines, collapse = "\n") -} - -canonicalize_text <- function(text) { - # Parse/deparse normalize before text comparison to avoid formatting noise. - if (is.na(text)) { - return(NA_character_) - } - - parsed <- tryCatch(parse(text = text, keep.source = FALSE), error = function(e) NULL) - if (is.null(parsed) || length(parsed) != 1L) { - return(NA_character_) - } - - deparse_one_line(parsed[[1L]]) -} - -transparent_srcref <- function(node) { - # Injected transparent braces store the same srcref twice (brace + payload). - # If those entries differ, treat it as non-transparent. - sr <- attr(node, "srcref", exact = TRUE) - if (is.null(sr)) { - return(NULL) - } - - sr_list <- if (is.list(sr)) sr else list(sr) - if (length(sr_list) < 2L) { - return(NULL) - } - - if (identical(sr_list[[1L]], sr_list[[2L]])) { - return(sr_list[[1L]]) - } - - NULL -} - -strip_transparent_braces <- function(expr) { - # Remove only injected transparent braces so semantic expression comparison - # can ignore imputed wrapper calls. - if (is.call(expr)) { - if (is_braced(expr) && length(expr) >= 2L && !is.null(transparent_srcref(expr))) { - return(strip_transparent_braces(expr[[2L]])) - } - - parts <- as.list(expr) - for (i in seq_along(parts)) { - parts[[i]] <- strip_transparent_braces(parts[[i]]) - } - return(as.call(parts)) - } - - expr +collect_srcref_sites <- function(expr_or_fn) { + .Call(C_collect_srcref_sites, expr_or_fn) } collect_transparent_srcref_checks <- function(expr_or_fn) { - lines <- character() - failures <- character() - checked <- 0L - - walk <- function(node, path) { - if (is.call(node)) { - if (is_braced(node)) { - sr <- transparent_srcref(node) - if (!is.null(sr) && length(node) >= 2L) { - checked <<- checked + 1L - - sr_text <- canonicalize_text(srcref_to_text(sr)) - expr_text <- deparse_one_line(strip_transparent_braces(node[[2L]])) - ok <- !is.na(sr_text) && identical(sr_text, expr_text) - - lines <<- c( - lines, - sprintf( - "check path=%s ok=%s sr_text=%s expr_text=%s", - path, - ok, - encodeString(sr_text, quote = "\""), - encodeString(expr_text, quote = "\"") - ) - ) - - if (!ok) { - failures <<- c(failures, path) - } - } - } - - parts <- as.list(node) - for (i in seq_along(parts)) { - walk(parts[[i]], sprintf("%s/%d", path, i)) - } - return(invisible(NULL)) - } - - if (is.expression(node) || is.list(node) || is.pairlist(node)) { - for (i in seq_along(node)) { - walk(node[[i]], sprintf("%s/%d", path, i)) - } - } - - invisible(NULL) - } - - if (is.function(expr_or_fn)) { - # For functions, check both default arguments and body expressions. - fmls <- formals(expr_or_fn) - fml_text <- as.character(fmls) - - for (i in seq_along(fml_text)) { - if (identical(fml_text[[i]], "")) { - next - } - - nm <- names(fmls)[[i]] - if (is.null(nm) || identical(nm, "")) { - nm <- as.character(i) - } - - walk(fmls[[i]], sprintf("formals/%s", nm)) - } - - walk(body(expr_or_fn), "body") - } else { - walk(expr_or_fn, "root") - } - - list( - checked = checked, - ok = length(failures) == 0L, - failures = failures, - lines = lines - ) + .Call(C_collect_transparent_srcref_checks, expr_or_fn) } assert_transparent_srcref_consistency <- function(expr_or_fn) { - checks <- collect_transparent_srcref_checks(expr_or_fn) - if (!checks$ok) { - stop( - sprintf( - "Transparent srcref text mismatch at: %s", - paste(checks$failures, collapse = ", ") - ), - call. = FALSE - ) - } - invisible(checks) -} - -collect_srcref_sites <- function(expr_or_fn) { - out <- character() - - walk <- function(node, path) { - if (is.call(node)) { - if (is_braced(node)) { - sr <- attr(node, "srcref", exact = TRUE) - if (!is.null(sr)) { - sr_list <- if (is.list(sr)) sr else list(sr) - sr1 <- if (length(sr_list) >= 1L) format_srcref_tuple(sr_list[[1L]]) else "NA" - sr2 <- if (length(sr_list) >= 2L) format_srcref_tuple(sr_list[[2L]]) else "NA" - - out <<- c( - out, - sprintf( - "path=%s node=%s sr1=%s sr2=%s", - path, - deparse_one_line(node), - sr1, - sr2 - ) - ) - } - } - - parts <- as.list(node) - for (i in seq_along(parts)) { - walk(parts[[i]], sprintf("%s/%d", path, i)) - } - return(invisible(NULL)) - } - - if (is.expression(node) || is.list(node) || is.pairlist(node)) { - for (i in seq_along(node)) { - walk(node[[i]], sprintf("%s/%d", path, i)) - } - } - - invisible(NULL) - } - - if (is.function(expr_or_fn)) { - # Emit paths for formals and body so snapshots are easy to locate in AST. - fmls <- formals(expr_or_fn) - fml_text <- as.character(fmls) - - for (i in seq_along(fml_text)) { - if (identical(fml_text[[i]], "")) { - next - } - - nm <- names(fmls)[[i]] - if (is.null(nm) || identical(nm, "")) { - nm <- as.character(i) - } - - walk(fmls[[i]], sprintf("formals/%s", nm)) - } - - walk(body(expr_or_fn), "body") - } else { - walk(expr_or_fn, "root") - } - - out + invisible(.Call(C_assert_transparent_srcref_consistency, expr_or_fn)) } write_srcref_sites <- function(expr_or_fn, path) { diff --git a/R/zzz.R b/R/zzz.R deleted file mode 100644 index e82bbc8..0000000 --- a/R/zzz.R +++ /dev/null @@ -1,2 +0,0 @@ -#' @useDynLib imputesrcref, .registration = TRUE -NULL diff --git a/README.md b/README.md index 8d52511..5452dd2 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,9 @@ and assigns srcrefs to injected `{` calls using parse-data-derived source spans. - `source_impute_srcrefs(file, envir = parent.frame(), ...)` - Source an R file and patch all changed/new functions in the target environment. - `impute_package_srcrefs(package, include_internal = TRUE, ...)` - - Patch package namespace functions if parse data is available + - Patch package namespace functions. Works for any package whose functions + retain `srcref` metadata (the default for source installs); cached parse + data is no longer required. - `get_impute_blacklist(include_default = TRUE)` - Inspect call names excluded from generic argument wrapping. - `set_impute_blacklist(functions, append = TRUE)` @@ -51,17 +53,26 @@ and assigns srcrefs to injected `{` calls using parse-data-derived source spans. - `reset_impute_blacklist()` - Clear user blacklist entries. -## Important: package installs and parse data +## Installed packages: srcref retention -For installed packages, parse data is often missing unless the package was -installed from source with source/parse retention enabled. - -Recommended installation pattern: +`impute_package_srcrefs()` requires each function to carry a `srcref` +attribute. Source installs retain this by default. Binary installs often do +too, but if `srcref` is missing the function will be skipped (`failed[i] == +"no srcref"`). To force retention explicitly, install from source with: ```r - install.packages("", INSTALL_opts=c("--with-keep.source", "--with-keep.parse.data")) +install.packages("", INSTALL_opts = "--with-keep.source") ``` +Cached parse data (`--with-keep.parse.data`) is no longer required — +`impute_srcrefs()` re-parses from the function's source lines when no parse +data is attached. + +For functions that truly lack `srcref` (e.g. some generated closures), +`options(imputesrcref.allow_deparse_fallback = TRUE)` will impute against a +deparsed copy of the function instead, at the cost of source line numbers +shifting to the deparsed layout. + ## Usage ### Patch functions @@ -93,9 +104,9 @@ Returned fields are: - `package` - `fn_names` -- `failed` (`NA` means successfully patched) +- `failed` (`NA` means successfully patched; otherwise a short reason + such as `"no srcref"` or an error message) - `patched_count` -- `install_command` (reinstall hint when nothing could be patched) ### Blacklist API @@ -149,6 +160,13 @@ Run the optional full ggplot2 package test: FULL_TEST=1 Rscript -e "testthat::test_file('tests/testthat/test-package-srcref-imputation.R')" ``` +## Implementation + +The package's internals are implemented in C and invoked from thin R wrappers +via `.Call`. Building from source requires a C toolchain (`Rtools` on +Windows, the standard `R CMD INSTALL` toolchain on macOS / Linux). No +external libraries are needed. + ## Acknowledgements This package was inspired in part by [covr](https://covr.r-lib.org/)'s parse-data handling [approach](https://github.com/r-lib/covr/blob/f1866d296c00884d1f085ff245669de01bc864c4/R/parse_data.R). diff --git a/src/blacklist.o b/src/blacklist.o deleted file mode 100644 index 8d240092d709f2d9e569cddca7ff8b3562f2bef5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36096 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 Ol=aoVOXk|f?|%cnt^T_J diff --git a/src/helpers.o b/src/helpers.o deleted file mode 100644 index 352355f884a131f1691a2646f25b8f5f8a5aa912..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40008 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

=nTx2{zVwEmv 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 Rb-caUAMq0RSLE^d{{Y{RgarTq diff --git a/src/imputesrcref.h b/src/imputesrcref.h index f3e2bb2..8fd83d0 100644 --- a/src/imputesrcref.h +++ b/src/imputesrcref.h @@ -49,6 +49,8 @@ SEXP imputesrcref_transform_expr(SEXP expr, int node_id, parse_ctx *ctx); SEXP imputesrcref_source_text(SEXP fn); +extern int imputesrcref_quiet; + int imputesrcref_visual_col_to_byte_col(const char *line, int visual_col); SEXP imputesrcref_effective_blacklist(void); diff --git a/src/imputesrcref.so b/src/imputesrcref.so deleted file mode 100755 index 68683d285306ad7af8b82638ab507781141a9866..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168448 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

H>NH0aJ`XKFHmw=2$$Qq@zL;-jzY|?ou&} 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 diff --git a/src/source_impute.c b/src/source_impute.c index 7370d5d..76393e3 100644 --- a/src/source_impute.c +++ b/src/source_impute.c @@ -57,9 +57,12 @@ static SEXP get_in(const char *name, SEXP envir) { static int identical_(SEXP a, SEXP b) { SEXP fn = PROTECT(base_fun("identical")); - SEXP r = PROTECT(two_arg_call(fn, a, b)); + SEXP qa = PROTECT(Rf_lang2(Rf_install("quote"), a)); + SEXP qb = PROTECT(Rf_lang2(Rf_install("quote"), b)); + SEXP call = PROTECT(Rf_lang3(fn, qa, qb)); + SEXP r = PROTECT(Rf_eval(call, R_GlobalEnv)); int v = TYPEOF(r) == LGLSXP && LOGICAL(r)[0] == TRUE; - UNPROTECT(2); + UNPROTECT(5); return v; } @@ -207,16 +210,6 @@ static SEXP try_impute_srcrefs(SEXP fn, int *err, char *msg_out, size_t msg_size return res; } -static SEXP try_get_parse_data(SEXP fn) { - SEXP f = PROTECT(Rf_findFun(Rf_install("getParseData"), R_BaseNamespace)); - SEXP call = PROTECT(Rf_lang2(f, fn)); - int err = 0; - SEXP res = R_tryEval(call, R_GlobalEnv, &err); - UNPROTECT(2); - if (err) return R_NilValue; - return res; -} - SEXP C_source_impute_srcrefs(SEXP file, SEXP envir, SEXP chdir, SEXP keep_source, SEXP keep_parse_data, SEXP toplevel_env, SEXP all_names) { @@ -251,6 +244,9 @@ SEXP C_source_impute_srcrefs(SEXP file, SEXP envir, SEXP chdir, SEXP imputed = PROTECT(Rf_allocVector(STRSXP, n_post)); int n_imputed = 0; + int saved_quiet = imputesrcref_quiet; + imputesrcref_quiet = 1; + for (R_xlen_t i = 0; i < n_post; i++) { const char *nm = CHAR(STRING_ELT(post_names, i)); if (!exists_in(nm, envir)) continue; @@ -285,6 +281,8 @@ SEXP C_source_impute_srcrefs(SEXP file, SEXP envir, SEXP chdir, UNPROTECT(2); } + imputesrcref_quiet = saved_quiet; + SEXP imputed_trim = PROTECT(Rf_allocVector(STRSXP, n_imputed)); for (int i = 0; i < n_imputed; i++) SET_STRING_ELT(imputed_trim, i, STRING_ELT(imputed, i)); @@ -346,17 +344,13 @@ SEXP C_impute_package_srcrefs(SEXP package, SEXP include_internal, SEXP verbose) SEXP failed = PROTECT(Rf_allocVector(STRSXP, fn_n)); for (R_xlen_t i = 0; i < fn_n; i++) SET_STRING_ELT(failed, i, NA_STRING); + int saved_quiet = imputesrcref_quiet; + imputesrcref_quiet = 1; + for (R_xlen_t i = 0; i < fn_n; i++) { const char *nm = CHAR(STRING_ELT(final_names, i)); SEXP fn = PROTECT(get_in(nm, env)); - SEXP pd = try_get_parse_data(fn); - if (pd == R_NilValue) { - SET_STRING_ELT(failed, i, Rf_mkChar("missing parse data")); - UNPROTECT(1); - continue; - } - char err_msg[2048]; err_msg[0] = '\0'; int err = 0; @@ -367,6 +361,14 @@ SEXP C_impute_package_srcrefs(SEXP package, SEXP include_internal, SEXP verbose) continue; } + /* impute returned the input unchanged when neither srcref nor parse + data + deparse fallback were available — nothing to commit. */ + if (patched == fn) { + SET_STRING_ELT(failed, i, Rf_mkChar("no srcref")); + UNPROTECT(2); + continue; + } + int locked = binding_is_locked(nm, env); if (locked) unlock_binding_(nm, env); assign_in(nm, patched, env); @@ -375,6 +377,8 @@ SEXP C_impute_package_srcrefs(SEXP package, SEXP include_internal, SEXP verbose) UNPROTECT(2); } + imputesrcref_quiet = saved_quiet; + int patched_cnt = 0; for (R_xlen_t i = 0; i < fn_n; i++) { if (STRING_ELT(failed, i) == NA_STRING) patched_cnt++; diff --git a/src/source_impute.o b/src/source_impute.o deleted file mode 100644 index 84e264077d8e4f2aeb1dab260e9b97606d4268a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90472 zcmdqKd3Y4X_W$3V>6s*iB!mEA6B60CKu8o9P}wwKPy|G9i-9B%g@l_0x2qx$Ax0Ej za6!O*1DC58Q4~Q`+^*sdE+}46K}Au#3X1Z3pQ=+mrz;us`ni96pI<*uGTpB_b?Vfq zQ%l$M%v?6S;P^P#b{2U%iiZ7S#P))nVhGtb@^d(7K$VDnrH(KKrZ&{vT2In=xG5{HOe1(3t

h;fm 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 KA#}fZ{r>^l)3Mb6 diff --git a/src/source_text.c b/src/source_text.c index de1a61d..b75fea0 100644 --- a/src/source_text.c +++ b/src/source_text.c @@ -1,6 +1,8 @@ #include "imputesrcref.h" #include +int imputesrcref_quiet = 0; + int imputesrcref_visual_col_to_byte_col(const char *line, int visual_col) { int vis = 1; int i; @@ -86,11 +88,18 @@ SEXP imputesrcref_source_text(SEXP fn) { if (sr == R_NilValue) { int allow = get_option_logical("imputesrcref.allow_deparse_fallback", 0); if (!allow) { - emit_message( - "Function has no srcref metadata and deparse fallback is disabled; " - "no changes were made. Set " - "options(imputesrcref.allow_deparse_fallback = TRUE) to enable " - "deparse-based fallback."); + /* `imputesrcref_quiet` only suppresses the per-function warning + during batch processing; it does NOT override the user's + deparse-fallback preference. If the user enabled fallback they + have already accepted that line numbers for no-srcref functions + may shift to deparsed-source coordinates. */ + if (!imputesrcref_quiet) { + emit_message( + "Function has no srcref metadata and deparse fallback is disabled; " + "no changes were made. Set " + "options(imputesrcref.allow_deparse_fallback = TRUE) to enable " + "deparse-based fallback."); + } return R_NilValue; } SEXP dep = PROTECT(call_deparse_500(fn)); diff --git a/src/source_text.o b/src/source_text.o deleted file mode 100644 index f418cee49d5f1358cfb717de21200d97df5c8846..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41896 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

VDil;&Q?s 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 ipF05=bEn`ZJin<`49nvI_buDYzn|<+(K2XJ +#include +#include + +typedef struct { + char **strs; + int n; + int cap; +} strbuf; + +static void strbuf_init(strbuf *b) { b->strs = NULL; b->n = 0; b->cap = 0; } + +static void strbuf_push(strbuf *b, const char *s) { + if (b->n == b->cap) { + int new_cap = b->cap == 0 ? 16 : b->cap * 2; + char **new_strs = (char**) R_alloc((size_t) new_cap, sizeof(char*)); + if (b->strs && b->n > 0) memcpy(new_strs, b->strs, (size_t) b->n * sizeof(char*)); + b->strs = new_strs; + b->cap = new_cap; + } + size_t len = strlen(s); + char *copy = (char*) R_alloc(len + 1, sizeof(char)); + memcpy(copy, s, len + 1); + b->strs[b->n++] = copy; +} + +static SEXP strbuf_to_strsxp(const strbuf *b) { + SEXP out = PROTECT(Rf_allocVector(STRSXP, b->n)); + for (int i = 0; i < b->n; i++) SET_STRING_ELT(out, i, Rf_mkChar(b->strs[i])); + UNPROTECT(1); + return out; +} + +static SEXP base_fn(const char *name) { + return Rf_findFun(Rf_install(name), R_BaseEnv); +} + +/* Wrap a SEXP value in `quote(...)` so that when the surrounding call is + evaluated, the value is passed unchanged instead of being re-evaluated as + a symbol lookup or function call. */ +static SEXP quote_wrap(SEXP x) { + return Rf_lang2(Rf_install("quote"), x); +} + +static SEXP do_deparse_one_line(SEXP expr) { + SEXP dep_fn = PROTECT(base_fn("deparse")); + SEXP qx = PROTECT(quote_wrap(expr)); + SEXP w = PROTECT(Rf_ScalarInteger(500)); + SEXP call = PROTECT(Rf_lang3(dep_fn, qx, w)); + SET_TAG(CDDR(call), Rf_install("width.cutoff")); + SEXP lines = PROTECT(Rf_eval(call, R_GlobalEnv)); + + SEXP paste_fn = PROTECT(base_fn("paste")); + SEXP sep = PROTECT(Rf_mkString(" ")); + SEXP call2 = PROTECT(Rf_lang3(paste_fn, lines, sep)); + SET_TAG(CDDR(call2), Rf_install("collapse")); + SEXP txt = PROTECT(Rf_eval(call2, R_GlobalEnv)); + + SEXP gsub_fn = PROTECT(base_fn("gsub")); + SEXP pat = PROTECT(Rf_mkString("[[:space:]]+")); + SEXP rep = PROTECT(Rf_mkString(" ")); + SEXP call3 = PROTECT(Rf_lang4(gsub_fn, pat, rep, txt)); + SEXP result = Rf_eval(call3, R_GlobalEnv); + UNPROTECT(13); + return result; +} + +static void format_srcref_tuple(SEXP sr, char *buf, size_t buf_size) { + if (sr == R_NilValue || TYPEOF(sr) != INTSXP) { + snprintf(buf, buf_size, "NA"); + return; + } + R_xlen_t n = Rf_xlength(sr); + int *p = INTEGER(sr); + int pos = snprintf(buf, buf_size, "("); + for (R_xlen_t i = 0; i < n; i++) { + if (pos < 0 || (size_t) pos >= buf_size) break; + int w = snprintf(buf + pos, buf_size - pos, "%s%d", + i == 0 ? "" : ",", p[i]); + if (w < 0) break; + pos += w; + } + if (pos >= 0 && (size_t) pos < buf_size) { + snprintf(buf + pos, buf_size - pos, ")"); + } +} + +static SEXP do_srcref_to_text(SEXP sr) { + if (TYPEOF(sr) != INTSXP || Rf_xlength(sr) < 4) { + return Rf_ScalarString(NA_STRING); + } + SEXP srcfile = Rf_getAttrib(sr, Rf_install("srcfile")); + if (srcfile == R_NilValue) return Rf_ScalarString(NA_STRING); + + int *p = INTEGER(sr); + int sl = p[0], sc = p[1], el = p[2], ec = p[3]; + + SEXP gsl_fn = PROTECT(base_fn("getSrcLines")); + SEXP a = PROTECT(Rf_ScalarInteger(sl)); + SEXP b = PROTECT(Rf_ScalarInteger(el)); + SEXP call = PROTECT(Rf_lang4(gsl_fn, srcfile, a, b)); + int err = 0; + SEXP lines = PROTECT(R_tryEval(call, R_GlobalEnv, &err)); + if (err) { + UNPROTECT(5); + return Rf_ScalarString(NA_STRING); + } + R_xlen_t n = Rf_xlength(lines); + if (n == 0) { + UNPROTECT(5); + return Rf_ScalarString(NA_STRING); + } + + SEXP new_lines = PROTECT(Rf_duplicate(lines)); + if (n == 1) { + const char *l = CHAR(STRING_ELT(new_lines, 0)); + int len = (int) strlen(l); + int s = sc, e = ec; + if (s < 1) s = 1; + if (e > len) e = len; + int slen = e - s + 1; + if (slen < 0) slen = 0; + char *buf = (char*) R_alloc((size_t)(slen + 1), sizeof(char)); + if (slen > 0) memcpy(buf, l + s - 1, (size_t) slen); + buf[slen] = '\0'; + SET_STRING_ELT(new_lines, 0, Rf_mkChar(buf)); + } else { + const char *l0 = CHAR(STRING_ELT(new_lines, 0)); + int len0 = (int) strlen(l0); + int s = sc; + if (s < 1) s = 1; + int slen0 = len0 - s + 1; + if (slen0 < 0) slen0 = 0; + char *buf0 = (char*) R_alloc((size_t)(slen0 + 1), sizeof(char)); + if (slen0 > 0) memcpy(buf0, l0 + s - 1, (size_t) slen0); + buf0[slen0] = '\0'; + SET_STRING_ELT(new_lines, 0, Rf_mkChar(buf0)); + + const char *ln = CHAR(STRING_ELT(new_lines, n - 1)); + int lenN = (int) strlen(ln); + int e = ec; + if (e > lenN) e = lenN; + int slenN = e; + if (slenN < 0) slenN = 0; + char *bufN = (char*) R_alloc((size_t)(slenN + 1), sizeof(char)); + if (slenN > 0) memcpy(bufN, ln, (size_t) slenN); + bufN[slenN] = '\0'; + SET_STRING_ELT(new_lines, n - 1, Rf_mkChar(bufN)); + } + + SEXP paste_fn = PROTECT(base_fn("paste")); + SEXP nl = PROTECT(Rf_mkString("\n")); + SEXP call_p = PROTECT(Rf_lang3(paste_fn, new_lines, nl)); + SET_TAG(CDDR(call_p), Rf_install("collapse")); + SEXP r = Rf_eval(call_p, R_GlobalEnv); + UNPROTECT(9); + return r; +} + +static SEXP do_canonicalize_text(SEXP text) { + if (TYPEOF(text) != STRSXP || Rf_xlength(text) < 1) { + return Rf_ScalarString(NA_STRING); + } + if (STRING_ELT(text, 0) == NA_STRING) { + return Rf_ScalarString(NA_STRING); + } + SEXP parse_fn = PROTECT(base_fn("parse")); + SEXP keep = PROTECT(Rf_ScalarLogical(0)); + SEXP call = PROTECT(Rf_lang3(parse_fn, text, keep)); + SET_TAG(CDR(call), Rf_install("text")); + SET_TAG(CDDR(call), Rf_install("keep.source")); + int err = 0; + SEXP parsed = R_tryEval(call, R_GlobalEnv, &err); + if (err) { UNPROTECT(3); return Rf_ScalarString(NA_STRING); } + PROTECT(parsed); + if (Rf_xlength(parsed) != 1) { + UNPROTECT(4); + return Rf_ScalarString(NA_STRING); + } + SEXP single = VECTOR_ELT(parsed, 0); + SEXP res = do_deparse_one_line(single); + UNPROTECT(4); + return res; +} + +static SEXP do_transparent_srcref(SEXP node) { + SEXP sr = Rf_getAttrib(node, Rf_install("srcref")); + if (sr == R_NilValue) return R_NilValue; + SEXP sr_list; + int allocated_list = 0; + if (TYPEOF(sr) == VECSXP) { + sr_list = sr; + } else { + sr_list = PROTECT(Rf_allocVector(VECSXP, 1)); + SET_VECTOR_ELT(sr_list, 0, sr); + allocated_list = 1; + } + R_xlen_t n = Rf_xlength(sr_list); + if (n < 2) { + if (allocated_list) UNPROTECT(1); + return R_NilValue; + } + SEXP a = VECTOR_ELT(sr_list, 0); + SEXP b = VECTOR_ELT(sr_list, 1); + + SEXP id_fn = PROTECT(base_fn("identical")); + SEXP qa = PROTECT(quote_wrap(a)); + SEXP qb = PROTECT(quote_wrap(b)); + SEXP call = PROTECT(Rf_lang3(id_fn, qa, qb)); + SEXP r = Rf_eval(call, R_GlobalEnv); + int eq = TYPEOF(r) == LGLSXP && LOGICAL(r)[0] == TRUE; + UNPROTECT(4); + if (allocated_list) UNPROTECT(1); + return eq ? a : R_NilValue; +} + +static SEXP do_strip_transparent_braces(SEXP expr) { + if (TYPEOF(expr) != LANGSXP) return expr; + + if (imputesrcref_is_braced(expr)) { + SEXP cdr = CDR(expr); + if (cdr != R_NilValue) { + SEXP tr_sr = do_transparent_srcref(expr); + if (tr_sr != R_NilValue) { + return do_strip_transparent_braces(CAR(cdr)); + } + } + } + + int n = 0; + for (SEXP it = expr; it != R_NilValue; it = CDR(it)) n++; + + SEXP out = PROTECT(Rf_allocList(n)); + SET_TYPEOF(out, LANGSXP); + SEXP src = expr; + SEXP dst = out; + while (src != R_NilValue) { + SEXP new_v = PROTECT(do_strip_transparent_braces(CAR(src))); + SETCAR(dst, new_v); + SET_TAG(dst, TAG(src)); + UNPROTECT(1); + src = CDR(src); + dst = CDR(dst); + } + UNPROTECT(1); + return out; +} + +/* Path manipulation helpers --- writes into provided buffer and returns the + new length (excluding terminator). If the suffix would overflow, leaves + the buffer unchanged and returns the original length. */ +static int path_push(char *path, int path_len, int path_cap, const char *suffix) { + int slen = (int) strlen(suffix); + if (path_len + slen + 1 > path_cap) return path_len; + memcpy(path + path_len, suffix, (size_t) slen); + path[path_len + slen] = '\0'; + return path_len + slen; +} + +static void emit_line(strbuf *b, const char *path, const char *dep, + const char *sr1, const char *sr2) { + int needed = snprintf(NULL, 0, "path=%s node=%s sr1=%s sr2=%s", + path, dep, sr1, sr2); + if (needed < 0) return; + char *buf = (char*) R_alloc((size_t)(needed + 1), sizeof(char)); + snprintf(buf, (size_t)(needed + 1), "path=%s node=%s sr1=%s sr2=%s", + path, dep, sr1, sr2); + strbuf_push(b, buf); +} + +static void emit_check(strbuf *lines, strbuf *failures, int *checked, + const char *path, int ok, + const char *sr_text_encoded, + const char *expr_text_encoded) { + *checked = *checked + 1; + int needed = snprintf(NULL, 0, "check path=%s ok=%s sr_text=%s expr_text=%s", + path, ok ? "TRUE" : "FALSE", + sr_text_encoded, expr_text_encoded); + if (needed < 0) return; + char *buf = (char*) R_alloc((size_t)(needed + 1), sizeof(char)); + snprintf(buf, (size_t)(needed + 1), "check path=%s ok=%s sr_text=%s expr_text=%s", + path, ok ? "TRUE" : "FALSE", sr_text_encoded, expr_text_encoded); + strbuf_push(lines, buf); + if (!ok) strbuf_push(failures, path); +} + +static SEXP encode_string_quote(SEXP s_strsxp) { + SEXP fn = PROTECT(base_fn("encodeString")); + SEXP q = PROTECT(Rf_mkString("\"")); + SEXP call = PROTECT(Rf_lang3(fn, s_strsxp, q)); + SET_TAG(CDDR(call), Rf_install("quote")); + SEXP r = Rf_eval(call, R_GlobalEnv); + UNPROTECT(3); + return r; +} + +/* ---- collect_srcref_sites walker ---- */ +static void walk_sites(SEXP node, char *path, int path_len, int path_cap, strbuf *b) { + if (TYPEOF(node) == LANGSXP) { + if (imputesrcref_is_braced(node)) { + SEXP sr = Rf_getAttrib(node, Rf_install("srcref")); + if (sr != R_NilValue) { + SEXP dep = PROTECT(do_deparse_one_line(node)); + const char *dep_str = (TYPEOF(dep) == STRSXP && Rf_xlength(dep) >= 1) + ? CHAR(STRING_ELT(dep, 0)) : ""; + + char sr1_buf[256], sr2_buf[256]; + if (TYPEOF(sr) == VECSXP) { + R_xlen_t n = Rf_xlength(sr); + if (n >= 1) format_srcref_tuple(VECTOR_ELT(sr, 0), sr1_buf, sizeof(sr1_buf)); + else snprintf(sr1_buf, sizeof(sr1_buf), "NA"); + if (n >= 2) format_srcref_tuple(VECTOR_ELT(sr, 1), sr2_buf, sizeof(sr2_buf)); + else snprintf(sr2_buf, sizeof(sr2_buf), "NA"); + } else { + format_srcref_tuple(sr, sr1_buf, sizeof(sr1_buf)); + snprintf(sr2_buf, sizeof(sr2_buf), "NA"); + } + emit_line(b, path, dep_str, sr1_buf, sr2_buf); + UNPROTECT(1); + } + } + int i = 1; + for (SEXP it = node; it != R_NilValue; it = CDR(it), i++) { + char suffix[32]; + snprintf(suffix, sizeof(suffix), "/%d", i); + int new_len = path_push(path, path_len, path_cap, suffix); + walk_sites(CAR(it), path, new_len, path_cap, b); + path[path_len] = '\0'; + } + return; + } + if (TYPEOF(node) == EXPRSXP || TYPEOF(node) == VECSXP) { + R_xlen_t n = Rf_xlength(node); + for (R_xlen_t j = 0; j < n; j++) { + char suffix[32]; + snprintf(suffix, sizeof(suffix), "/%d", (int)(j + 1)); + int new_len = path_push(path, path_len, path_cap, suffix); + walk_sites(VECTOR_ELT(node, j), path, new_len, path_cap, b); + path[path_len] = '\0'; + } + return; + } + if (TYPEOF(node) == LISTSXP) { + int i = 1; + for (SEXP it = node; it != R_NilValue; it = CDR(it), i++) { + char suffix[32]; + snprintf(suffix, sizeof(suffix), "/%d", i); + int new_len = path_push(path, path_len, path_cap, suffix); + walk_sites(CAR(it), path, new_len, path_cap, b); + path[path_len] = '\0'; + } + } +} + +/* ---- collect_transparent_srcref_checks walker ---- */ +static void walk_checks(SEXP node, char *path, int path_len, int path_cap, + strbuf *lines, strbuf *failures, int *checked) { + if (TYPEOF(node) == LANGSXP) { + if (imputesrcref_is_braced(node)) { + SEXP tr_sr = do_transparent_srcref(node); + if (tr_sr != R_NilValue && CDR(node) != R_NilValue) { + PROTECT(tr_sr); + SEXP sr_text = PROTECT(do_srcref_to_text(tr_sr)); + SEXP sr_canon = PROTECT(do_canonicalize_text(sr_text)); + SEXP stripped = PROTECT(do_strip_transparent_braces(CADR(node))); + SEXP expr_text = PROTECT(do_deparse_one_line(stripped)); + + int ok = 0; + if (TYPEOF(sr_canon) == STRSXP && Rf_xlength(sr_canon) >= 1 && + STRING_ELT(sr_canon, 0) != NA_STRING && + TYPEOF(expr_text) == STRSXP && Rf_xlength(expr_text) >= 1) { + const char *a = CHAR(STRING_ELT(sr_canon, 0)); + const char *b = CHAR(STRING_ELT(expr_text, 0)); + ok = strcmp(a, b) == 0; + } + + SEXP sr_enc = PROTECT(encode_string_quote(sr_canon)); + SEXP exp_enc = PROTECT(encode_string_quote(expr_text)); + const char *sa = (TYPEOF(sr_enc) == STRSXP && Rf_xlength(sr_enc) >= 1) + ? CHAR(STRING_ELT(sr_enc, 0)) : "NA"; + const char *eb = (TYPEOF(exp_enc) == STRSXP && Rf_xlength(exp_enc) >= 1) + ? CHAR(STRING_ELT(exp_enc, 0)) : "NA"; + emit_check(lines, failures, checked, path, ok, sa, eb); + UNPROTECT(7); + } + } + int i = 1; + for (SEXP it = node; it != R_NilValue; it = CDR(it), i++) { + char suffix[32]; + snprintf(suffix, sizeof(suffix), "/%d", i); + int new_len = path_push(path, path_len, path_cap, suffix); + walk_checks(CAR(it), path, new_len, path_cap, lines, failures, checked); + path[path_len] = '\0'; + } + return; + } + if (TYPEOF(node) == EXPRSXP || TYPEOF(node) == VECSXP) { + R_xlen_t n = Rf_xlength(node); + for (R_xlen_t j = 0; j < n; j++) { + char suffix[32]; + snprintf(suffix, sizeof(suffix), "/%d", (int)(j + 1)); + int new_len = path_push(path, path_len, path_cap, suffix); + walk_checks(VECTOR_ELT(node, j), path, new_len, path_cap, lines, failures, checked); + path[path_len] = '\0'; + } + return; + } + if (TYPEOF(node) == LISTSXP) { + int i = 1; + for (SEXP it = node; it != R_NilValue; it = CDR(it), i++) { + char suffix[32]; + snprintf(suffix, sizeof(suffix), "/%d", i); + int new_len = path_push(path, path_len, path_cap, suffix); + walk_checks(CAR(it), path, new_len, path_cap, lines, failures, checked); + path[path_len] = '\0'; + } + } +} + +static SEXP call_formals(SEXP fn) { + SEXP f = PROTECT(base_fn("formals")); + SEXP call = PROTECT(Rf_lang2(f, fn)); + SEXP r = Rf_eval(call, R_GlobalEnv); + UNPROTECT(2); + return r; +} + +static SEXP call_body(SEXP fn) { + SEXP f = PROTECT(base_fn("body")); + SEXP call = PROTECT(Rf_lang2(f, fn)); + SEXP r = Rf_eval(call, R_GlobalEnv); + UNPROTECT(2); + return r; +} + +static int is_function_kind(SEXP x) { + return TYPEOF(x) == CLOSXP || TYPEOF(x) == BUILTINSXP || TYPEOF(x) == SPECIALSXP; +} + +/* Common helper: build path "formals/" for each formal with non-missing + default, or "body" for the body — calls the given walker on each. */ +typedef void (*walker_fn)(SEXP, char *, int, int, void*); + +static void walk_function_struct_sites(SEXP fn, char *path, int path_cap, strbuf *b) { + SEXP fmls = PROTECT(call_formals(fn)); + int idx = 1; + for (SEXP cell = fmls; cell != R_NilValue; cell = CDR(cell), idx++) { + SEXP val = CAR(cell); + if (val == R_MissingArg) continue; + SEXP tag = TAG(cell); + const char *nm; + char idx_str[32]; + if (tag != R_NilValue && TYPEOF(tag) == SYMSXP && + CHAR(PRINTNAME(tag))[0] != '\0') { + nm = CHAR(PRINTNAME(tag)); + } else { + snprintf(idx_str, sizeof(idx_str), "%d", idx); + nm = idx_str; + } + int plen = snprintf(path, path_cap, "formals/%s", nm); + if (plen < 0 || plen >= path_cap) plen = path_cap - 1; + walk_sites(val, path, plen, path_cap, b); + } + UNPROTECT(1); + int plen = snprintf(path, path_cap, "body"); + SEXP body = PROTECT(call_body(fn)); + walk_sites(body, path, plen, path_cap, b); + UNPROTECT(1); +} + +static void walk_function_struct_checks(SEXP fn, char *path, int path_cap, + strbuf *lines, strbuf *failures, int *checked) { + SEXP fmls = PROTECT(call_formals(fn)); + int idx = 1; + for (SEXP cell = fmls; cell != R_NilValue; cell = CDR(cell), idx++) { + SEXP val = CAR(cell); + if (val == R_MissingArg) continue; + SEXP tag = TAG(cell); + const char *nm; + char idx_str[32]; + if (tag != R_NilValue && TYPEOF(tag) == SYMSXP && + CHAR(PRINTNAME(tag))[0] != '\0') { + nm = CHAR(PRINTNAME(tag)); + } else { + snprintf(idx_str, sizeof(idx_str), "%d", idx); + nm = idx_str; + } + int plen = snprintf(path, path_cap, "formals/%s", nm); + if (plen < 0 || plen >= path_cap) plen = path_cap - 1; + walk_checks(val, path, plen, path_cap, lines, failures, checked); + } + UNPROTECT(1); + int plen = snprintf(path, path_cap, "body"); + SEXP body = PROTECT(call_body(fn)); + walk_checks(body, path, plen, path_cap, lines, failures, checked); + UNPROTECT(1); +} + +SEXP C_collect_srcref_sites(SEXP expr_or_fn) { + strbuf b; strbuf_init(&b); + enum { PATH_CAP = 8192 }; + char path[PATH_CAP]; + if (is_function_kind(expr_or_fn)) { + walk_function_struct_sites(expr_or_fn, path, PATH_CAP, &b); + } else { + int plen = snprintf(path, PATH_CAP, "root"); + walk_sites(expr_or_fn, path, plen, PATH_CAP, &b); + } + return strbuf_to_strsxp(&b); +} + +SEXP C_collect_transparent_srcref_checks(SEXP expr_or_fn) { + strbuf lines; strbuf_init(&lines); + strbuf failures; strbuf_init(&failures); + int checked = 0; + enum { PATH_CAP = 8192 }; + char path[PATH_CAP]; + if (is_function_kind(expr_or_fn)) { + walk_function_struct_checks(expr_or_fn, path, PATH_CAP, &lines, &failures, &checked); + } else { + int plen = snprintf(path, PATH_CAP, "root"); + walk_checks(expr_or_fn, path, plen, PATH_CAP, &lines, &failures, &checked); + } + + SEXP lines_sx = PROTECT(strbuf_to_strsxp(&lines)); + SEXP failures_sx = PROTECT(strbuf_to_strsxp(&failures)); + SEXP out = PROTECT(Rf_allocVector(VECSXP, 4)); + SEXP nms = PROTECT(Rf_allocVector(STRSXP, 4)); + SET_STRING_ELT(nms, 0, Rf_mkChar("checked")); + SET_STRING_ELT(nms, 1, Rf_mkChar("ok")); + SET_STRING_ELT(nms, 2, Rf_mkChar("failures")); + SET_STRING_ELT(nms, 3, Rf_mkChar("lines")); + Rf_setAttrib(out, R_NamesSymbol, nms); + SET_VECTOR_ELT(out, 0, Rf_ScalarInteger(checked)); + SET_VECTOR_ELT(out, 1, Rf_ScalarLogical(failures.n == 0)); + SET_VECTOR_ELT(out, 2, failures_sx); + SET_VECTOR_ELT(out, 3, lines_sx); + UNPROTECT(4); + return out; +} + +SEXP C_assert_transparent_srcref_consistency(SEXP expr_or_fn) { + SEXP checks = PROTECT(C_collect_transparent_srcref_checks(expr_or_fn)); + int ok = LOGICAL(VECTOR_ELT(checks, 1))[0]; + if (!ok) { + SEXP fails = VECTOR_ELT(checks, 2); + R_xlen_t nf = Rf_xlength(fails); + size_t total = 64; + for (R_xlen_t i = 0; i < nf; i++) total += strlen(CHAR(STRING_ELT(fails, i))) + 2; + char *buf = (char*) R_alloc(total, sizeof(char)); + int pos = snprintf(buf, total, "Transparent srcref text mismatch at: "); + for (R_xlen_t i = 0; i < nf; i++) { + if (i > 0) pos += snprintf(buf + pos, total - (size_t) pos, ", "); + pos += snprintf(buf + pos, total - (size_t) pos, "%s", CHAR(STRING_ELT(fails, i))); + } + UNPROTECT(1); + Rf_error("%s", buf); + } + UNPROTECT(1); + return checks; +} diff --git a/src/transform.c b/src/transform.c index b92a06f..ad49750 100644 --- a/src/transform.c +++ b/src/transform.c @@ -199,7 +199,12 @@ SEXP imputesrcref_transform_expr(SEXP expr, int node_id, parse_ctx *ctx) { SEXP cell = fmls; while (cell != R_NilValue) { SEXP val = CAR(cell); - if (val != R_MissingArg && val != R_NilValue && !(TYPEOF(val) == SYMSXP && CHAR(PRINTNAME(val))[0] == '\0')) { + /* Skip only true "missing" defaults: R_MissingArg (the empty + symbol). Legitimate NULL defaults (function(x = NULL)) still + appear as expr nodes in parse data and must advance the + cursor, otherwise subsequent defaults are mis-paired with + prior expr children. */ + if (val != R_MissingArg) { if (cursor >= n_ids) Rf_error("Parse mapping mismatch for function formals"); int cid = child_ids[cursor++]; SEXP newv = PROTECT(imputesrcref_transform_expr(val, cid, ctx)); diff --git a/src/transform.o b/src/transform.o deleted file mode 100644 index 581f75f837dc86d9ca88f4959a58d0c50855edf0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94872 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 Date: Thu, 4 Jun 2026 17:00:55 +0200 Subject: [PATCH 4/7] Fix error handling --- R/blacklist_api.R | 36 +++++++++++++++++++----------------- R/impute_srcrefs.R | 8 ++++++-- man/impute_blacklist.Rd | 25 +++++++++++++++---------- man/impute_srcrefs.Rd | 11 ++++++++--- src/imputesrcref.h | 2 +- src/init.c | 2 +- src/source_impute.c | 4 +++- src/source_text.c | 2 +- src/srcref_sites.c | 4 ++-- src/transform.c | 23 ++++++++++++++++++++--- 10 files changed, 76 insertions(+), 41 deletions(-) diff --git a/R/blacklist_api.R b/R/blacklist_api.R index facb26e..52b30fa 100644 --- a/R/blacklist_api.R +++ b/R/blacklist_api.R @@ -1,41 +1,43 @@ -#' Get call names blacklisted from generic argument wrapping. +#' Manage call blacklist for generic argument wrapping #' #' By default, [impute_srcrefs()] skips argument wrapping for primitive -#' `SPECIALSXP` calls discovered from [builtins()]. This getter can return only -#' user-configured entries, or the effective blacklist including defaults. +#' `SPECIALSXP` calls discovered from [builtins()]. These functions inspect and +#' modify the user-configured portion of that blacklist. +#' +#' - `get_impute_blacklist()` returns the blacklist, optionally including the +#' built-in defaults. +#' - `set_impute_blacklist()` adds or replaces user entries, stored in +#' `options(imputesrcref.wrap_arg_blacklist = ...)`. +#' - `reset_impute_blacklist()` clears user entries. #' #' @param include_default If `TRUE`, include built-in `SPECIALSXP` names. +#' @param functions Character vector of call names. `NULL` clears user entries. +#' @param append If `TRUE` append to existing user entries; otherwise replace. #' -#' @return A sorted unique character vector of call names. +#' @return +#' `get_impute_blacklist()` returns a sorted unique character vector of call +#' names. `set_impute_blacklist()` invisibly returns the current +#' user-configured entries. `reset_impute_blacklist()` invisibly returns an +#' empty character vector. #' #' @examples #' head(get_impute_blacklist()) #' set_impute_blacklist(c("str_c", "paste")) #' get_impute_blacklist(include_default = FALSE) #' reset_impute_blacklist() +#' @rdname impute_blacklist #' @export get_impute_blacklist <- function(include_default = TRUE) { .Call(C_get_impute_blacklist, include_default) } -#' Set user call names blacklisted from generic argument wrapping. -#' -#' User entries are stored in `options(imputesrcref.wrap_arg_blacklist = ...)`. -#' -#' @param functions Character vector of call names. `NULL` clears user entries. -#' @param append If `TRUE` append to existing user entries; otherwise replace. -#' -#' @return Invisibly returns current user-configured blacklist entries. +#' @rdname impute_blacklist #' @export set_impute_blacklist <- function(functions, append = TRUE) { invisible(.Call(C_set_impute_blacklist, functions, append)) } -#' Reset user call names blacklisted from generic argument wrapping. -#' -#' Clears user entries configured via [set_impute_blacklist()]. -#' -#' @return Invisibly returns an empty character vector. +#' @rdname impute_blacklist #' @export reset_impute_blacklist <- function() { invisible(.Call(C_reset_impute_blacklist)) diff --git a/R/impute_srcrefs.R b/R/impute_srcrefs.R index 83dba24..f7dd79b 100644 --- a/R/impute_srcrefs.R +++ b/R/impute_srcrefs.R @@ -16,6 +16,10 @@ #' @param fn A function. #' @param wrap_call_args If `TRUE` (default), wrap generic function-call #' arguments that are call expressions. +#' @param quiet If `TRUE`, suppress the informational message emitted when a +#' function has no srcref metadata and deparse fallback is disabled. Useful +#' for callers (such as coverage tools) that process many functions and +#' handle the no-srcref case themselves. Defaults to `FALSE`. #' #' @return A function with transformed body/formals and preserved function-level #' attributes (including srcref/srcfile metadata when present). @@ -35,6 +39,6 @@ #' g <- impute_srcrefs(f) #' g #' @export -impute_srcrefs <- function(fn, wrap_call_args = TRUE) { - .Call(C_impute_srcrefs, fn, wrap_call_args) +impute_srcrefs <- function(fn, wrap_call_args = TRUE, quiet = FALSE) { + .Call(C_impute_srcrefs, fn, wrap_call_args, quiet) } diff --git a/man/impute_blacklist.Rd b/man/impute_blacklist.Rd index 947ad5a..059107d 100644 --- a/man/impute_blacklist.Rd +++ b/man/impute_blacklist.Rd @@ -1,4 +1,5 @@ -% Generated by hand; update if API changes. +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/blacklist_api.R \name{get_impute_blacklist} \alias{get_impute_blacklist} \alias{set_impute_blacklist} @@ -19,20 +20,24 @@ reset_impute_blacklist() \item{append}{If \code{TRUE} append to existing user entries; otherwise replace.} } \value{ -\code{get_impute_blacklist()} returns a sorted unique character vector. - -\code{set_impute_blacklist()} invisibly returns current user-configured entries. - -\code{reset_impute_blacklist()} invisibly returns \code{character()}. +\code{get_impute_blacklist()} returns a sorted unique character vector of call +names. \code{set_impute_blacklist()} invisibly returns the current +user-configured entries. \code{reset_impute_blacklist()} invisibly returns an +empty character vector. } \description{ -\code{impute_srcrefs()} skips argument wrapping for blacklisted generic callee -names. By default, that blacklist includes primitive \code{SPECIALSXP} -functions discovered from \code{builtins()}. +By default, \code{\link[=impute_srcrefs]{impute_srcrefs()}} skips argument wrapping for primitive +\code{SPECIALSXP} calls discovered from \code{\link[=builtins]{builtins()}}. These functions inspect and +modify the user-configured portion of that blacklist. } \details{ -User entries are stored in +\itemize{ +\item \code{get_impute_blacklist()} returns the blacklist, optionally including the +built-in defaults. +\item \code{set_impute_blacklist()} adds or replaces user entries, stored in \code{options(imputesrcref.wrap_arg_blacklist = ...)}. +\item \code{reset_impute_blacklist()} clears user entries. +} } \examples{ head(get_impute_blacklist()) diff --git a/man/impute_srcrefs.Rd b/man/impute_srcrefs.Rd index 2e7b31b..2950f55 100644 --- a/man/impute_srcrefs.Rd +++ b/man/impute_srcrefs.Rd @@ -4,13 +4,18 @@ \alias{impute_srcrefs} \title{Impute transparent srcrefs for injected braces in a function AST.} \usage{ -impute_srcrefs(fn, wrap_call_args = TRUE) +impute_srcrefs(fn, wrap_call_args = TRUE, quiet = FALSE) } \arguments{ \item{fn}{A function.} \item{wrap_call_args}{If \code{TRUE} (default), wrap generic function-call arguments that are call expressions.} + +\item{quiet}{If \code{TRUE}, suppress the informational message emitted when a +function has no srcref metadata and deparse fallback is disabled. Useful +for callers (such as coverage tools) that process many functions and +handle the no-srcref case themselves. Defaults to \code{FALSE}.} } \value{ A function with transformed body/formals and preserved function-level @@ -38,8 +43,8 @@ default. To allow fallback, set \code{options(impuresrcref.allow_deparse_fallback = TRUE)}. Generic call argument wrapping skips blacklisted callee names. By default the -blacklist includes primitive \code{SPECIALSXP} calls from \code{builtins()}. -Use \code{\link[=set_impute_blacklist]{set_impute_blacklist()}} / \code{\link[=reset_impute_blacklist]{reset_impute_blacklist()}} to customize. +blacklist includes primitive \code{SPECIALSXP} calls from \code{builtins()}. Use +\code{\link[=set_impute_blacklist]{set_impute_blacklist()}} / \code{\link[=reset_impute_blacklist]{reset_impute_blacklist()}} to customize. } \examples{ options(keep.source = TRUE) diff --git a/src/imputesrcref.h b/src/imputesrcref.h index 8fd83d0..993d9a5 100644 --- a/src/imputesrcref.h +++ b/src/imputesrcref.h @@ -55,7 +55,7 @@ int imputesrcref_visual_col_to_byte_col(const char *line, int visual_col); SEXP imputesrcref_effective_blacklist(void); -SEXP C_impute_srcrefs(SEXP fn, SEXP wrap_call_args); +SEXP C_impute_srcrefs(SEXP fn, SEXP wrap_call_args, SEXP quiet); SEXP C_get_impute_blacklist(SEXP include_default); SEXP C_set_impute_blacklist(SEXP functions, SEXP append); SEXP C_reset_impute_blacklist(void); diff --git a/src/init.c b/src/init.c index eb816b7..3ab7d22 100644 --- a/src/init.c +++ b/src/init.c @@ -1,7 +1,7 @@ #include "imputesrcref.h" static const R_CallMethodDef CallEntries[] = { - {"C_impute_srcrefs", (DL_FUNC) &C_impute_srcrefs, 2}, + {"C_impute_srcrefs", (DL_FUNC) &C_impute_srcrefs, 3}, {"C_get_impute_blacklist", (DL_FUNC) &C_get_impute_blacklist, 1}, {"C_set_impute_blacklist", (DL_FUNC) &C_set_impute_blacklist, 2}, {"C_reset_impute_blacklist", (DL_FUNC) &C_reset_impute_blacklist, 0}, diff --git a/src/source_impute.c b/src/source_impute.c index 76393e3..a9bdfc2 100644 --- a/src/source_impute.c +++ b/src/source_impute.c @@ -176,7 +176,9 @@ typedef struct { static SEXP impute_body(void *data) { impute_body_data *bd = (impute_body_data*) data; - return C_impute_srcrefs(bd->fn, Rf_ScalarLogical(1)); + /* Batch ops already raise the ambient `imputesrcref_quiet` flag, so the + per-call quiet arg can stay FALSE here. */ + return C_impute_srcrefs(bd->fn, Rf_ScalarLogical(1), Rf_ScalarLogical(0)); } static char err_msg_buf[2048]; diff --git a/src/source_text.c b/src/source_text.c index b75fea0..b2e1a7c 100644 --- a/src/source_text.c +++ b/src/source_text.c @@ -23,7 +23,7 @@ static SEXP call_getSrcLines(SEXP srcfile, int start_line, int end_line) { SEXP a = PROTECT(Rf_ScalarInteger(start_line)); SEXP b = PROTECT(Rf_ScalarInteger(end_line)); SEXP call = PROTECT(Rf_lang4(fn, srcfile, a, b)); - SEXP res = R_tryEval(call, R_GlobalEnv, NULL); + SEXP res = R_tryEvalSilent(call, R_GlobalEnv, NULL); UNPROTECT(4); if (res == NULL) return R_NilValue; return res; diff --git a/src/srcref_sites.c b/src/srcref_sites.c index a85e4b2..2d0a1c4 100644 --- a/src/srcref_sites.c +++ b/src/srcref_sites.c @@ -101,7 +101,7 @@ static SEXP do_srcref_to_text(SEXP sr) { SEXP b = PROTECT(Rf_ScalarInteger(el)); SEXP call = PROTECT(Rf_lang4(gsl_fn, srcfile, a, b)); int err = 0; - SEXP lines = PROTECT(R_tryEval(call, R_GlobalEnv, &err)); + SEXP lines = PROTECT(R_tryEvalSilent(call, R_GlobalEnv, &err)); if (err) { UNPROTECT(5); return Rf_ScalarString(NA_STRING); @@ -171,7 +171,7 @@ static SEXP do_canonicalize_text(SEXP text) { SET_TAG(CDR(call), Rf_install("text")); SET_TAG(CDDR(call), Rf_install("keep.source")); int err = 0; - SEXP parsed = R_tryEval(call, R_GlobalEnv, &err); + SEXP parsed = R_tryEvalSilent(call, R_GlobalEnv, &err); if (err) { UNPROTECT(3); return Rf_ScalarString(NA_STRING); } PROTECT(parsed); if (Rf_xlength(parsed) != 1) { diff --git a/src/transform.c b/src/transform.c index ad49750..f5e37b8 100644 --- a/src/transform.c +++ b/src/transform.c @@ -506,7 +506,13 @@ static SEXP call_parse(const char *text) { SET_TAG(CDR(call), Rf_install("text")); SET_TAG(CDDR(call), Rf_install("keep.source")); int err = 0; - SEXP res = R_tryEval(call, R_GlobalEnv, &err); + /* Use the silent variant: the first parse attempt is expected to fail for + functions defined inside a call (e.g. setMethod) whose extracted source + has `else` starting a line. The caller retries wrapped in `(...)`, so the + failure is normal control flow and must not be printed to stderr. + R_tryEval (non-silent) would emit the error via the C error handler, + which suppressMessages()/suppressWarnings() on the R side cannot catch. */ + SEXP res = R_tryEvalSilent(call, R_GlobalEnv, &err); UNPROTECT(4); if (err) return R_NilValue; return res; @@ -516,7 +522,7 @@ static SEXP call_getParseData(SEXP parsed) { SEXP fn = PROTECT(Rf_findFun(Rf_install("getParseData"), R_BaseNamespace)); SEXP call = PROTECT(Rf_lang2(fn, parsed)); int err = 0; - SEXP res = R_tryEval(call, R_GlobalEnv, &err); + SEXP res = R_tryEvalSilent(call, R_GlobalEnv, &err); UNPROTECT(2); if (err) return R_NilValue; return res; @@ -538,7 +544,7 @@ static SEXP call_body(SEXP fn) { return res; } -SEXP C_impute_srcrefs(SEXP fn, SEXP wrap_call_args_sxp) { +SEXP C_impute_srcrefs(SEXP fn, SEXP wrap_call_args_sxp, SEXP quiet_sxp) { if (TYPEOF(fn) != CLOSXP) { Rf_error("`fn` must be a function"); } @@ -546,11 +552,22 @@ SEXP C_impute_srcrefs(SEXP fn, SEXP wrap_call_args_sxp) { LOGICAL(wrap_call_args_sxp)[0] == NA_LOGICAL) { Rf_error("`wrap_call_args` must be TRUE or FALSE"); } + if (TYPEOF(quiet_sxp) != LGLSXP || Rf_xlength(quiet_sxp) != 1 || + LOGICAL(quiet_sxp)[0] == NA_LOGICAL) { + Rf_error("`quiet` must be TRUE or FALSE"); + } int wrap_call_args = LOGICAL(wrap_call_args_sxp)[0]; + int quiet = LOGICAL(quiet_sxp)[0]; SEXP fn_attrs = PROTECT(Rf_duplicate(ATTRIB(fn))); + /* `quiet` only suppresses the "no srcref" message emitted by + source_text. OR with the ambient flag so a `quiet = TRUE` call inside an + already-quiet batch op never un-quiets it; restore immediately after. */ + int saved_quiet = imputesrcref_quiet; + if (quiet) imputesrcref_quiet = 1; SEXP src = PROTECT(imputesrcref_source_text(fn)); + imputesrcref_quiet = saved_quiet; if (src == R_NilValue) { UNPROTECT(2); return fn; From 8b15e4a698d5c9a883496ed6f07e1f0c2c4af25b Mon Sep 17 00:00:00 2001 From: Matej Kocourek Date: Thu, 4 Jun 2026 17:14:27 +0200 Subject: [PATCH 5/7] Fix bug --- src/srcref_sites.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/srcref_sites.c b/src/srcref_sites.c index 2d0a1c4..e2d1535 100644 --- a/src/srcref_sites.c +++ b/src/srcref_sites.c @@ -113,10 +113,14 @@ static SEXP do_srcref_to_text(SEXP sr) { } SEXP new_lines = PROTECT(Rf_duplicate(lines)); + /* sc/ec are visual (tab-expanded) columns; substr() needs byte positions. + For tab-indented source these differ, so convert before slicing + (matches the conversion done in source_text_from_srcref). */ if (n == 1) { const char *l = CHAR(STRING_ELT(new_lines, 0)); int len = (int) strlen(l); - int s = sc, e = ec; + int s = imputesrcref_visual_col_to_byte_col(l, sc); + int e = imputesrcref_visual_col_to_byte_col(l, ec); if (s < 1) s = 1; if (e > len) e = len; int slen = e - s + 1; @@ -128,7 +132,7 @@ static SEXP do_srcref_to_text(SEXP sr) { } else { const char *l0 = CHAR(STRING_ELT(new_lines, 0)); int len0 = (int) strlen(l0); - int s = sc; + int s = imputesrcref_visual_col_to_byte_col(l0, sc); if (s < 1) s = 1; int slen0 = len0 - s + 1; if (slen0 < 0) slen0 = 0; @@ -139,7 +143,7 @@ static SEXP do_srcref_to_text(SEXP sr) { const char *ln = CHAR(STRING_ELT(new_lines, n - 1)); int lenN = (int) strlen(ln); - int e = ec; + int e = imputesrcref_visual_col_to_byte_col(ln, ec); if (e > lenN) e = lenN; int slenN = e; if (slenN < 0) slenN = 0; From d2e98aa851eb94ce50a6bc96c2cf36ae1592c3b8 Mon Sep 17 00:00:00 2001 From: Matej Kocourek Date: Thu, 4 Jun 2026 17:14:40 +0200 Subject: [PATCH 6/7] Add package integration testing --- Makefile | 21 ++- README.md | 29 +++- .../testthat/test-package-srcref-imputation.R | 131 +++++++++++++++--- 3 files changed, 157 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index ed02a41..8a9fa4b 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,32 @@ R = R RSCRIPT = Rscript -.PHONY: install test clean +# Library holding the corpus packages installed WITH srcref retention, used by +# the FULL_TEST whole-package tests. Prepended to .libPaths() for `test-full` +# and `corpus-install`. Override with: make test-full CORPUS_LIB=/path/to/lib +CORPUS_LIB ?= $(HOME)/Rlib_test + +# Packages exercised by the corpus tests (must be installed with srcref). +CORPUS_PKGS = 'data.table','dplyr','fs','ggplot2','glue','jsonlite','stringr','zoo' + +.PHONY: install test test-full corpus-install clean install: $(R) CMD INSTALL . +# Fast suite: blacklist + snapshot tests. Corpus tests skip (no FULL_TEST). test: $(RSCRIPT) -e "testthat::test_dir('tests/testthat', load_package = 'source')" +# Full suite including the slow whole-package corpus tests. Requires the corpus +# installed with srcref retention (run `make corpus-install` once first). +test-full: + FULL_TEST=1 $(RSCRIPT) -e ".libPaths(c('$(CORPUS_LIB)', .libPaths())); testthat::test_dir('tests/testthat', load_package = 'source')" + +# Install the corpus packages from source with srcref retained into CORPUS_LIB. +corpus-install: + mkdir -p "$(CORPUS_LIB)" + $(RSCRIPT) -e ".libPaths(c('$(CORPUS_LIB)', .libPaths())); install.packages(c($(CORPUS_PKGS)), lib='$(CORPUS_LIB)', INSTALL_opts='--with-keep.source', type='source', dependencies=c('Depends','Imports','LinkingTo'))" + clean: rm -rf *.tar.gz *.Rcheck diff --git a/README.md b/README.md index 5452dd2..b256554 100644 --- a/README.md +++ b/README.md @@ -154,10 +154,33 @@ UPDATE_SNAPSHOTS=1 Rscript -e "testthat::test_dir('tests/testthat', load_package By default, mismatches fail. With `UPDATE_SNAPSHOTS=1`, the snapshot file is rewritten. -Run the optional full ggplot2 package test: +### Package corpus tests -```r -FULL_TEST=1 Rscript -e "testthat::test_file('tests/testthat/test-package-srcref-imputation.R')" +`tests/testthat/test-package-srcref-imputation.R` runs whole-package srcref +imputation over a corpus of popular packages (`data.table`, `dplyr`, `fs`, +`ggplot2`, `glue`, `jsonlite`, `stringr`, `zoo`). For each it asserts that no +function fails for an unexpected reason and that a sample of patched functions +is idempotent and srcref-text-consistent (the line-accuracy guarantee). + +These tests are slow and require the corpus packages to be installed **with +srcref retention**, so they are gated behind `FULL_TEST=1` and skip gracefully +when a package is absent or was installed without srcref. + +The `Makefile` provides targets for this. The corpus is installed into a +separate library (`CORPUS_LIB`, default `~/Rlib_test`) so your main library is +left untouched: + +```sh +make corpus-install # install the 8 corpus packages with srcref (run once) +make test-full # run the full suite including the corpus tests +``` + +Use a different library with `make test-full CORPUS_LIB=/path/to/lib`. Or run +it directly: + +```sh +FULL_TEST=1 Rscript -e \ + ".libPaths(c('~/Rlib_test', .libPaths())); testthat::test_file('tests/testthat/test-package-srcref-imputation.R')" ``` ## Implementation diff --git a/tests/testthat/test-package-srcref-imputation.R b/tests/testthat/test-package-srcref-imputation.R index 8847cfd..dabba8c 100644 --- a/tests/testthat/test-package-srcref-imputation.R +++ b/tests/testthat/test-package-srcref-imputation.R @@ -1,20 +1,111 @@ -test_that("ggplot2 full package srcref imputation is stable", { - skip_if_not( - identical(Sys.getenv("FULL_TEST", unset = ""), "1"), - "Set FULL_TEST=1 to run." - ) - skip_if_not_installed("ggplot2") - - res <- imputesrcref::impute_package_srcrefs("ggplot2", include_internal = TRUE, verbose = FALSE) - - fn_names <- res$fn_names - failed <- sum(!is.na(res$failed)) - patched <- res$patched_count - skippable <- c("missing parse data", "no srcref") - non_missing <- sum(!is.na(res$failed) & !(res$failed %in% skippable)) - - expect_gt(length(fn_names), 0L) - expect_gt(patched, 0L) - expect_lt(failed, length(fn_names)) - expect_equal(non_missing, 0L) -}) +# Whole-package srcref imputation over a corpus of popular packages. +# +# These are heavy integration tests: they patch every function in each +# package's namespace and verify correctness invariants. They require the +# packages to be installed with srcref retention (install from source with +# `--with-keep.source`), so they are gated behind FULL_TEST=1 and degrade +# gracefully when a package is absent or was installed without srcref. +# +# FULL_TEST=1 Rscript -e "testthat::test_dir('tests/testthat')" + +corpus_packages <- c( + "data.table", + "dplyr", + "fs", + "ggplot2", + "glue", + "jsonlite", + "stringr", + "zoo" +) + +# Reasons a function may legitimately be left unpatched. Anything else counts +# as a real failure that the test should surface. +skippable_failures <- c("missing parse data", "no srcref") + +# How many patched functions to deep-check per package (idempotence + srcref +# text consistency). Bounded so the suite stays reasonably fast on large +# namespaces such as ggplot2 / dplyr. +sample_size <- 40L + +for (pkg in corpus_packages) { + local({ + p <- pkg + + test_that(sprintf("whole-package srcref imputation is correct: %s", p), { + skip_if_not( + identical(Sys.getenv("FULL_TEST", unset = ""), "1"), + "Set FULL_TEST=1 to run." + ) + skip_if_not_installed(p) + + res <- imputesrcref::impute_package_srcrefs( + p, + include_internal = TRUE, + verbose = FALSE + ) + + expect_gt(length(res$fn_names), 0L) + + # Every inspected function must either patch cleanly or be skipped for a + # legitimate reason. A failure with any other message is a real bug. + real_failures <- res$failed[ + !is.na(res$failed) & !(res$failed %in% skippable_failures) + ] + expect_equal( + length(real_failures), + 0L, + info = sprintf( + "%s: unexpected failures: %s", + p, + paste(unique(real_failures), collapse = "; ") + ) + ) + + # If the package was installed without srcref retention there is nothing + # to patch; skip the per-function correctness checks rather than fail. + skip_if( + res$patched_count == 0L, + sprintf("%s installed without srcref retention; nothing patched", p) + ) + + # Deep-check a sample of patched functions. `impute_package_srcrefs` has + # already replaced the namespace bindings with their imputed versions, so + # `get()` returns the patched function. + ns <- loadNamespace(p) + patched_idx <- which(is.na(res$failed)) + sample_idx <- patched_idx[seq_len(min(sample_size, length(patched_idx)))] + + for (i in sample_idx) { + nm <- res$fn_names[[i]] + fn <- get(nm, envir = ns, inherits = FALSE) + if (!is.function(fn) || is.primitive(fn)) { + next + } + + # Re-imputation must be a no-op: the transform is idempotent. + again <- imputesrcref::impute_srcrefs(fn, quiet = TRUE) + expect_identical( + body(again), body(fn), + info = sprintf("%s::%s is not idempotent (body)", p, nm) + ) + expect_identical( + formals(again), formals(fn), + info = sprintf("%s::%s is not idempotent (formals)", p, nm) + ) + + # Every injected transparent brace must carry a srcref whose source + # text matches the wrapped expression. This is the line-accuracy + # guarantee: a wrong line/column would make the text mismatch. + checks <- imputesrcref:::collect_transparent_srcref_checks(fn) + expect_true( + isTRUE(checks$ok), + info = sprintf( + "%s::%s srcref text mismatch at: %s", + p, nm, paste(checks$failures, collapse = ", ") + ) + ) + } + }) + }) +} From 9b097d33e8bc96f541c1d7fd2f2f9c3142433c65 Mon Sep 17 00:00:00 2001 From: Matej Kocourek Date: Thu, 4 Jun 2026 17:45:58 +0200 Subject: [PATCH 7/7] Fix bug --- src/helpers.c | 38 +++++++++++++-- src/imputesrcref.h | 7 +++ src/source_text.c | 17 +++++-- src/srcref_sites.c | 13 +++-- src/transform.c | 9 ++++ tests/testthat/test-tab-columns.R | 80 +++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 16 deletions(-) create mode 100644 tests/testthat/test-tab-columns.R diff --git a/src/helpers.c b/src/helpers.c index 09360c5..5866359 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -124,6 +124,9 @@ void imputesrcref_build_ctx(parse_ctx *ctx, SEXP pd, SEXP srcfile, ctx->srcfile = srcfile; ctx->line_offset = line_offset; ctx->first_col_offset = first_col_offset; + ctx->abs_lines = R_NilValue; + ctx->n_abs_lines = 0; + ctx->abs_lines_start = 1; ctx->arg_wrap_blacklist = arg_wrap_blacklist; ctx->wrap_call_args = wrap_call_args; @@ -218,14 +221,39 @@ SEXP imputesrcref_node_srcref(int node_id, parse_ctx *ctx) { if (node_id < 0 || node_id > ctx->max_id) Rf_error("Missing parse node id %d", node_id); int row = ctx->id_to_pdrow[node_id]; if (row < 0) Rf_error("Missing parse node id %d", node_id); - int l1 = ctx->line1[row] + ctx->line_offset; - int l2 = ctx->line2[row] + ctx->line_offset; - int c1 = ctx->col1[row] + (ctx->line1[row] == 1 ? ctx->first_col_offset : 0); - int c2 = ctx->col2[row] + (ctx->line2[row] == 1 ? ctx->first_col_offset : 0); + int rl1 = ctx->line1[row]; + int rl2 = ctx->line2[row]; + + int l1 = rl1 + ctx->line_offset; + int l2 = rl2 + ctx->line_offset; + /* Visual (tab-expanded) columns in the original-file coordinate system. */ + int c1 = ctx->col1[row] + (rl1 == 1 ? ctx->first_col_offset : 0); + int c2 = ctx->col2[row] + (rl2 == 1 ? ctx->first_col_offset : 0); + + /* Byte offsets for the srcref byte slots (2 and 4). R stores bytes there + and visual columns in slots 5/6; `as.character.srcref` (used by deparse, + getSrcref and covr) slices source by the byte slots. The parse data only + gives visual columns, so convert against the absolute source line. `l1` + and `c1` are already absolute coordinates, so this works uniformly for + the paren-wrapped and deparse-fallback cases. Fall back to the visual + value when the line is unavailable. */ + int b1 = c1, b2 = c2; + if (ctx->abs_lines != R_NilValue) { + int idx1 = l1 - ctx->abs_lines_start; + int idx2 = l2 - ctx->abs_lines_start; + if (idx1 >= 0 && idx1 < ctx->n_abs_lines) { + b1 = imputesrcref_visual_col_to_byte_col( + CHAR(STRING_ELT(ctx->abs_lines, idx1)), c1); + } + if (idx2 >= 0 && idx2 < ctx->n_abs_lines) { + b2 = imputesrcref_visual_col_to_byte_col( + CHAR(STRING_ELT(ctx->abs_lines, idx2)), c2); + } + } SEXP sr = PROTECT(Rf_allocVector(INTSXP, 8)); int *p = INTEGER(sr); - p[0] = l1; p[1] = c1; p[2] = l2; p[3] = c2; + p[0] = l1; p[1] = b1; p[2] = l2; p[3] = b2; p[4] = c1; p[5] = c2; p[6] = l1; p[7] = l2; Rf_setAttrib(sr, Rf_install("srcfile"), ctx->srcfile); SEXP cls = PROTECT(Rf_mkString("srcref")); diff --git a/src/imputesrcref.h b/src/imputesrcref.h index 993d9a5..627cd2d 100644 --- a/src/imputesrcref.h +++ b/src/imputesrcref.h @@ -27,6 +27,13 @@ typedef struct parse_ctx { SEXP srcfile; int line_offset; int first_col_offset; + /* Untrimmed source lines spanned by the function, used to convert the + parse data's visual (tab-expanded) columns into byte offsets for the + srcref byte slots. `abs_lines[k]` is original-file line + `abs_lines_start + k`. */ + SEXP abs_lines; + int n_abs_lines; + int abs_lines_start; SEXP arg_wrap_blacklist; int wrap_call_args; } parse_ctx; diff --git a/src/source_text.c b/src/source_text.c index b2e1a7c..bbdede1 100644 --- a/src/source_text.c +++ b/src/source_text.c @@ -107,17 +107,21 @@ SEXP imputesrcref_source_text(SEXP fn) { const char *txt = CHAR(STRING_ELT(collapsed, 0)); SEXP srcfile = PROTECT(call_srcfilecopy("", txt)); - SEXP out = PROTECT(Rf_allocVector(VECSXP, 4)); - SEXP nms = PROTECT(Rf_allocVector(STRSXP, 4)); + SEXP out = PROTECT(Rf_allocVector(VECSXP, 5)); + SEXP nms = PROTECT(Rf_allocVector(STRSXP, 5)); SET_STRING_ELT(nms, 0, Rf_mkChar("text")); SET_STRING_ELT(nms, 1, Rf_mkChar("srcfile")); SET_STRING_ELT(nms, 2, Rf_mkChar("line_offset")); SET_STRING_ELT(nms, 3, Rf_mkChar("first_col_offset")); + SET_STRING_ELT(nms, 4, Rf_mkChar("lines")); Rf_setAttrib(out, R_NamesSymbol, nms); SET_VECTOR_ELT(out, 0, collapsed); SET_VECTOR_ELT(out, 1, srcfile); SET_VECTOR_ELT(out, 2, Rf_ScalarInteger(0)); SET_VECTOR_ELT(out, 3, Rf_ScalarInteger(0)); + /* Deparsed source lines: line_offset is 0, so absolute line k maps to + dep[k-1]. */ + SET_VECTOR_ELT(out, 4, dep); UNPROTECT(5); return out; } @@ -218,17 +222,22 @@ SEXP imputesrcref_source_text(SEXP fn) { SEXP collapsed = PROTECT(paste_lines(new_lines)); - SEXP out = PROTECT(Rf_allocVector(VECSXP, 4)); - SEXP nms = PROTECT(Rf_allocVector(STRSXP, 4)); + SEXP out = PROTECT(Rf_allocVector(VECSXP, 5)); + SEXP nms = PROTECT(Rf_allocVector(STRSXP, 5)); SET_STRING_ELT(nms, 0, Rf_mkChar("text")); SET_STRING_ELT(nms, 1, Rf_mkChar("srcfile")); SET_STRING_ELT(nms, 2, Rf_mkChar("line_offset")); SET_STRING_ELT(nms, 3, Rf_mkChar("first_col_offset")); + SET_STRING_ELT(nms, 4, Rf_mkChar("lines")); Rf_setAttrib(out, R_NamesSymbol, nms); SET_VECTOR_ELT(out, 0, collapsed); SET_VECTOR_ELT(out, 1, srcfile); SET_VECTOR_ELT(out, 2, Rf_ScalarInteger(chosen_start_line - 1)); SET_VECTOR_ELT(out, 3, Rf_ScalarInteger(chosen_start_col - 1)); + /* Untrimmed source lines for [chosen_start_line, chosen_end_line]; used to + convert visual columns to byte offsets. Absolute line k maps to + lines[k - chosen_start_line]. */ + SET_VECTOR_ELT(out, 4, lines); UNPROTECT(5); return out; diff --git a/src/srcref_sites.c b/src/srcref_sites.c index e2d1535..069850c 100644 --- a/src/srcref_sites.c +++ b/src/srcref_sites.c @@ -113,14 +113,13 @@ static SEXP do_srcref_to_text(SEXP sr) { } SEXP new_lines = PROTECT(Rf_duplicate(lines)); - /* sc/ec are visual (tab-expanded) columns; substr() needs byte positions. - For tab-indented source these differ, so convert before slicing - (matches the conversion done in source_text_from_srcref). */ + /* sc/ec are byte offsets (srcref slots 2/4), per R's convention, so slice + directly. node_srcref emits proper byte offsets even for tab-indented + source. */ if (n == 1) { const char *l = CHAR(STRING_ELT(new_lines, 0)); int len = (int) strlen(l); - int s = imputesrcref_visual_col_to_byte_col(l, sc); - int e = imputesrcref_visual_col_to_byte_col(l, ec); + int s = sc, e = ec; if (s < 1) s = 1; if (e > len) e = len; int slen = e - s + 1; @@ -132,7 +131,7 @@ static SEXP do_srcref_to_text(SEXP sr) { } else { const char *l0 = CHAR(STRING_ELT(new_lines, 0)); int len0 = (int) strlen(l0); - int s = imputesrcref_visual_col_to_byte_col(l0, sc); + int s = sc; if (s < 1) s = 1; int slen0 = len0 - s + 1; if (slen0 < 0) slen0 = 0; @@ -143,7 +142,7 @@ static SEXP do_srcref_to_text(SEXP sr) { const char *ln = CHAR(STRING_ELT(new_lines, n - 1)); int lenN = (int) strlen(ln); - int e = imputesrcref_visual_col_to_byte_col(ln, ec); + int e = ec; if (e > lenN) e = lenN; int slenN = e; if (slenN < 0) slenN = 0; diff --git a/src/transform.c b/src/transform.c index f5e37b8..a6f6563 100644 --- a/src/transform.c +++ b/src/transform.c @@ -653,6 +653,15 @@ SEXP C_impute_srcrefs(SEXP fn, SEXP wrap_call_args_sxp, SEXP quiet_sxp) { parse_ctx ctx; imputesrcref_build_ctx(&ctx, pd, srcfile, line_offset, first_col_offset, blacklist, wrap_call_args); + /* Source lines for visual->byte column conversion in node_srcref. Absolute + line k maps to lines[k - (line_offset + 1)]. */ + SEXP src_lines = VECTOR_ELT(src, 4); + if (TYPEOF(src_lines) == STRSXP) { + ctx.abs_lines = src_lines; + ctx.n_abs_lines = (int) Rf_xlength(src_lines); + ctx.abs_lines_start = line_offset + 1; + } + /* Build fn_expr = call("function", formals(fn), body(fn)) */ SEXP fmls = PROTECT(call_formals(fn)); SEXP bdy = PROTECT(call_body(fn)); diff --git a/tests/testthat/test-tab-columns.R b/tests/testthat/test-tab-columns.R new file mode 100644 index 0000000..cdff5ea --- /dev/null +++ b/tests/testthat/test-tab-columns.R @@ -0,0 +1,80 @@ +# Regression tests for tab-indented source. getParseData() reports visual +# (tab-expanded) columns, but R srcref byte slots (2 and 4) must hold byte +# offsets so that as.character.srcref() / getSrcref() (used by deparse and +# covr) extract the correct text. node_srcref converts visual -> byte. + +make_fn <- function(code) { + eval(parse(text = code, keep.source = TRUE)[[1L]], envir = new.env(parent = baseenv())) +} + +# Collect (srcref, wrapped-expression) pairs for every injected transparent +# brace in a function body. +brace_pairs <- function(fn) { + out <- list() + walk <- function(node) { + if (is.call(node)) { + if (is.symbol(node[[1L]]) && identical(as.character(node[[1L]]), "{") && + length(node) >= 2L) { + sr <- attr(node, "srcref", exact = TRUE) + if (!is.null(sr)) { + sr_list <- if (is.list(sr)) sr else list(sr) + if (length(sr_list) >= 2L && identical(sr_list[[1L]], sr_list[[2L]])) { + out[[length(out) + 1L]] <<- list( + sr = sr_list[[1L]], + expr = node[[2L]] + ) + } + } + } + for (i in seq_along(node)) walk(node[[i]]) + } + } + walk(body(fn)) + out +} + +test_that("tab-indented source yields byte-accurate srcref slots", { + # Tab + spaces before the call, so visual columns differ from byte columns. + code <- "function(e1, e2) {\n\t out <- zoo(e, index(e2), attr(e2, \"f\"))\n\tout\n}" + fn <- make_fn(code) + g <- imputesrcref::impute_srcrefs(fn) + + pairs <- brace_pairs(g) + expect_gt(length(pairs), 0L) + + for (pr in pairs) { + sr <- pr$sr + # Byte slots (2,4) must differ from visual slots (5,6) on the tab line for + # at least the deeper calls; more importantly, R's own extractor must + # return the wrapped expression verbatim. + extracted <- paste(as.character(sr), collapse = "\n") + expected <- paste(deparse(pr$expr), collapse = "\n") + expect_identical( + extracted, expected, + info = sprintf("srcref slots %s", paste(as.integer(sr), collapse = ",")) + ) + } +}) + +test_that("at least one tab-line brace has byte slot < visual slot", { + code <- "function(x) {\n\t\tg(h(x))\n}" + fn <- make_fn(code) + g <- imputesrcref::impute_srcrefs(fn) + pairs <- brace_pairs(g) + expect_gt(length(pairs), 0L) + + # The two leading tabs make visual column (slot 5) strictly greater than the + # byte column (slot 2) for content on that line. + has_offset <- any(vapply(pairs, function(pr) pr$sr[[2L]] < pr$sr[[5L]], logical(1))) + expect_true(has_offset) +}) + +test_that("space-indented source keeps byte slots equal to visual slots", { + code <- "function(x, y) if (x && y) f() else g()" + fn <- make_fn(code) + g <- imputesrcref::impute_srcrefs(fn) + for (pr in brace_pairs(g)) { + expect_identical(pr$sr[[2L]], pr$sr[[5L]]) + expect_identical(pr$sr[[4L]], pr$sr[[6L]]) + } +})