blob: 11f573ab81d6c86d62235a003729fc95511da44c [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 2001-2018 The R Core Team.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, a copy is available at
* https://www.R-project.org/Licenses/
*/
/* <UTF8>
Does byte-level handling in primitive_case, but only of ASCII chars
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Defn.h>
#undef _
#include "RSMethods.h"
#include "methods.h"
#include <Rinternals.h>
#define STRING_VALUE(x) CHAR(asChar(x))
#if !defined(snprintf) && defined(HAVE_DECL_SNPRINTF) && !HAVE_DECL_SNPRINTF
extern int snprintf (char *s, size_t n, const char *format, ...);
#endif
/* the following utilities are included here for now, as statics. But
they will eventually be C implementations of slot, data.class,
etc. */
static SEXP do_dispatch(SEXP fname, SEXP ev, SEXP mlist, int firstTry,
int evalArgs);
static SEXP R_loadMethod(SEXP f, SEXP fname, SEXP ev);
static SEXP R_selectByPackage(SEXP f, SEXP classes, int nargs);
/* objects, mostly symbols, that are initialized once to save a little time */
static int initialized = 0;
static SEXP s_dot_Methods, s_skeleton, s_expression, s_function,
s_getAllMethods, s_objectsEnv, s_MethodsListSelect,
s_sys_dot_frame, s_sys_dot_call, s_sys_dot_function, s_generic,
s_missing, s_generic_dot_skeleton, s_subset_gets, s_element_gets,
s_argument, s_allMethods, s_base;
static SEXP R_FALSE, R_TRUE;
static Rboolean table_dispatch_on = 1;
/* precomputed skeletons for special primitive calls */
static SEXP R_short_skeletons, R_empty_skeletons;
static SEXP f_x_i_skeleton, fgets_x_i_skeleton, f_x_skeleton, fgets_x_skeleton;
SEXP R_quick_method_check(SEXP object, SEXP fsym, SEXP fdef);
static SEXP R_target, R_defined, R_nextMethod, R_dot_nextMethod,
R_loadMethod_name, R_methods_name, R_tripleColon_name;
static SEXP Methods_Namespace = NULL;
static const char *check_single_string(SEXP, Rboolean, const char *);
static const char *check_symbol_or_string(SEXP obj, Rboolean nonEmpty,
const char *what);
static const char *class_string(SEXP obj);
static void init_loadMethod()
{
R_target = install("target");
R_defined = install("defined");
R_nextMethod = install("nextMethod");
R_loadMethod_name = install("loadMethod");
R_dot_nextMethod = install(".nextMethod");
R_methods_name = install("methods");
R_tripleColon_name = install(":::");
}
SEXP R_initMethodDispatch(SEXP envir)
{
if(envir && !isNull(envir))
Methods_Namespace = envir;
if(!Methods_Namespace)
Methods_Namespace = R_GlobalEnv;
if(initialized)
return(envir);
s_dot_Methods = install(".Methods");
s_skeleton = install("skeleton");
s_expression = install("expression");
s_function = install("function");
s_getAllMethods = install("getAllMethods");
s_objectsEnv = install("objectsEnv");
s_MethodsListSelect = install("MethodsListSelect");
s_sys_dot_frame = install("sys.frame");
s_sys_dot_call = install("sys.call");
s_sys_dot_function = install("sys.function");
s_generic = install("generic");
s_generic_dot_skeleton = install("generic.skeleton");
s_subset_gets = install("[<-");
s_element_gets = install("[[<-");
s_argument = install("argument");
s_allMethods = install("allMethods");
R_FALSE = ScalarLogical(FALSE);
R_PreserveObject(R_FALSE);
R_TRUE = ScalarLogical(TRUE);
R_PreserveObject(R_TRUE);
/* some strings (NOT symbols) */
s_missing = mkString("missing");
setAttrib(s_missing, R_PackageSymbol, mkString("methods"));
R_PreserveObject(s_missing);
s_base = mkString("base");
R_PreserveObject(s_base);
/* Initialize method dispatch, using the static */
R_set_standardGeneric_ptr(
(table_dispatch_on ? R_dispatchGeneric : R_standardGeneric)
, Methods_Namespace);
R_set_quick_method_check(
(table_dispatch_on ? R_quick_dispatch : R_quick_method_check));
/* Some special lists of primitive skeleton calls.
These will be promises under lazy-loading.
*/
PROTECT(R_short_skeletons =
findVar(install(".ShortPrimitiveSkeletons"),
Methods_Namespace));
if(TYPEOF(R_short_skeletons) == PROMSXP)
R_short_skeletons = eval(R_short_skeletons, Methods_Namespace);
R_PreserveObject(R_short_skeletons);
UNPROTECT(1);
PROTECT(R_empty_skeletons =
findVar(install(".EmptyPrimitiveSkeletons"),
Methods_Namespace));
if(TYPEOF(R_empty_skeletons) == PROMSXP)
R_empty_skeletons = eval(R_empty_skeletons, Methods_Namespace);
R_PreserveObject(R_empty_skeletons);
UNPROTECT(1);
if(R_short_skeletons == R_UnboundValue ||
R_empty_skeletons == R_UnboundValue)
error(_("could not find the skeleton calls for 'methods' (package detached?): expect very bad things to happen"));
f_x_i_skeleton = VECTOR_ELT(R_short_skeletons, 0);
fgets_x_i_skeleton = VECTOR_ELT(R_short_skeletons, 1);
f_x_skeleton = VECTOR_ELT(R_empty_skeletons, 0);
fgets_x_skeleton = VECTOR_ELT(R_empty_skeletons, 1);
init_loadMethod();
initialized = 1;
return(envir);
}
/* simplified version of do_subset2_dflt, with no partial matching */
static SEXP R_element_named(SEXP obj, const char * what)
{
int offset = -1, i, n;
SEXP names = getAttrib(obj, R_NamesSymbol);
n = length(names);
if(n > 0) {
for(i = 0; i < n; i++) {
if(streql(what, CHAR(STRING_ELT(names, i)))) {
offset = i; break;
}
}
}
if(offset < 0)
return R_NilValue;
else
return VECTOR_ELT(obj, offset);
}
static SEXP R_insert_element(SEXP mlist, const char * what, SEXP object)
{
SEXP sym = install(what);
return R_subassign3_dflt(R_NilValue, mlist, sym, object);
}
SEXP R_el_named(SEXP object, SEXP what)
{
const char * str;
str = CHAR(asChar(what));
return R_element_named(object, str);
}
SEXP R_set_el_named(SEXP object, SEXP what, SEXP value)
{
const char * str;
str = CHAR(asChar(what));
return R_insert_element(object, str, value);
}
/* */
static int n_ov = 0;
SEXP R_clear_method_selection()
{
n_ov = 0;
return R_NilValue;
}
static SEXP R_find_method(SEXP mlist, const char *class, SEXP fname)
{
/* find the element of the methods list that matches this class,
but not including inheritance. */
SEXP value, methods;
methods = R_do_slot(mlist, s_allMethods);
if(methods == R_NilValue) {
error(_("no \"allMethods\" slot found in object of class \"%s\" used as methods list for function '%s'"),
class_string(mlist), CHAR(asChar(fname)));
return(R_NilValue); /* -Wall */
}
value = R_element_named(methods, class);
return value;
}
SEXP R_quick_method_check(SEXP args, SEXP mlist, SEXP fdef)
{
/* Match the list of (evaluated) args to the methods list. */
SEXP object, methods, value, retValue = R_NilValue;
const char *class; int nprotect = 0;
if(!mlist)
return R_NilValue;
methods = R_do_slot(mlist, s_allMethods);
if(methods == R_NilValue)
return R_NilValue;
while(!isNull(args) && !isNull(methods)) {
object = CAR(args); args = CDR(args);
if(TYPEOF(object) == PROMSXP) {
if(PRVALUE(object) == R_UnboundValue) {
SEXP tmp = eval(PRCODE(object), PRENV(object));
PROTECT(tmp); nprotect++;
SET_PRVALUE(object, tmp);
object = tmp;
}
else
object = PRVALUE(object);
}
class = CHAR(STRING_ELT(R_data_class(object, TRUE), 0));
value = R_element_named(methods, class);
if(isNull(value) || isFunction(value)){
retValue = value;
break;
}
/* continue matching args down the tree */
methods = R_do_slot(value, s_allMethods);
}
UNPROTECT(nprotect);
return(retValue);
}
SEXP R_quick_dispatch(SEXP args, SEXP genericEnv, SEXP fdef)
{
/* Match the list of (evaluated) args to the methods table. */
static SEXP R_allmtable = NULL, R_siglength;
SEXP object, value, mtable;
const char *class; int nprotect = 0, nsig, nargs;
#define NBUF 200
char buf[NBUF]; char *ptr;
if(!R_allmtable) {
R_allmtable = install(".AllMTable");
R_siglength = install(".SigLength");
}
if(!genericEnv || TYPEOF(genericEnv) != ENVSXP)
return R_NilValue; /* a bug or not initialized yet */
mtable = findVarInFrame(genericEnv, R_allmtable);
if(mtable == R_UnboundValue || TYPEOF(mtable) != ENVSXP)
return R_NilValue;
PROTECT(mtable);
object = findVarInFrame(genericEnv, R_siglength);
if(object == R_UnboundValue) {
UNPROTECT(1); /* mtable */
return R_NilValue;
}
switch(TYPEOF(object)) {
case REALSXP:
if(LENGTH(object) > 0)
nsig = (int) REAL(object)[0];
else {
UNPROTECT(1); /* mtable */
return R_NilValue;
}
break;
case INTSXP:
if(LENGTH(object) > 0)
nsig = (int) INTEGER(object)[0];
else {
UNPROTECT(1); /* mtable */
return R_NilValue;
}
break;
default:
UNPROTECT(1); /* mtable */
return R_NilValue;
}
buf[0] = '\0'; ptr = buf;
nargs = 0;
nprotect = 1; /* mtable */
while(!isNull(args) && nargs < nsig) {
object = CAR(args); args = CDR(args);
if(TYPEOF(object) == PROMSXP) {
if(PRVALUE(object) == R_UnboundValue) {
SEXP tmp = eval(PRCODE(object), PRENV(object));
PROTECT(tmp); nprotect++;
SET_PRVALUE(object, tmp);
object = tmp;
}
else
object = PRVALUE(object);
}
if(object == R_MissingArg)
class = "missing";
else
class = CHAR(STRING_ELT(R_data_class(object, TRUE), 0));
if(ptr - buf + strlen(class) + 2 > NBUF) {
UNPROTECT(nprotect);
return R_NilValue;
}
/* NB: this code replicates .SigLabel().
If that changes, e.g. to include
the package, the code here must change too.
Or, better, the two should use the same C code. */
if(ptr > buf) { ptr = strcpy(ptr, "#"); ptr += 1;}
ptr = strcpy(ptr, class); ptr += strlen(class);
nargs++;
}
for(; nargs < nsig; nargs++) {
if(ptr - buf + strlen("missing") + 2 > NBUF) {
UNPROTECT(nprotect);
return R_NilValue;
}
ptr = strcpy(ptr, "#"); ptr +=1;
ptr = strcpy(ptr, "missing"); ptr += strlen("missing");
}
value = findVarInFrame(mtable, install(buf));
if(value == R_UnboundValue)
value = R_NilValue;
UNPROTECT(nprotect);
return(value);
}
/* call some S language functions */
static SEXP R_S_MethodsListSelect(SEXP fname, SEXP ev, SEXP mlist, SEXP f_env)
{
SEXP e, val; int n, check_err;
n = isNull(f_env) ? 4 : 5;
PROTECT(e = allocVector(LANGSXP, n));
SETCAR(e, s_MethodsListSelect);
val = CDR(e);
SETCAR(val, fname);
val = CDR(val);
SETCAR(val, ev);
val = CDR(val);
SETCAR(val, mlist);
if(n == 5) {
val = CDR(val);
SETCAR(val, f_env);
}
val = R_tryEvalSilent(e, Methods_Namespace, &check_err);
if(check_err)
error("S language method selection got an error when called from internal dispatch for function '%s'",
check_symbol_or_string(fname, TRUE,
"Function name for method selection called internally"));
UNPROTECT(1);
return val;
}
/* quick tests for generic and non-generic functions. May mistakenly
identify non-generics as generics: a class with data part of type
CLOSXP and with a slot/attribute named "generic" will qualify.
*/
#define IS_NON_GENERIC(vl) (TYPEOF(vl) == BUILTINSXP ||TYPEOF(vl) == SPECIALSXP || \
(TYPEOF(vl) == CLOSXP && getAttrib(vl, s_generic) == R_NilValue))
#define IS_GENERIC(vl) (TYPEOF(vl) == CLOSXP && getAttrib(vl, s_generic) != R_NilValue)
#define PACKAGE_SLOT(vl) getAttrib(vl, R_PackageSymbol)
static SEXP get_generic(SEXP symbol, SEXP rho, SEXP package)
{
SEXP vl, generic = R_UnboundValue, gpackage; const char *pkg; Rboolean ok;
if(!isSymbol(symbol))
symbol = installTrChar(asChar(symbol));
pkg = CHAR(STRING_ELT(package, 0)); /* package is guaranteed single string */
while (rho != R_NilValue) {
vl = findVarInFrame(rho, symbol);
if (vl != R_UnboundValue) {
if (TYPEOF(vl) == PROMSXP) {
PROTECT(vl);
vl = eval(vl, rho);
UNPROTECT(1);
}
ok = FALSE;
if(IS_GENERIC(vl)) {
if(strlen(pkg)) {
gpackage = PACKAGE_SLOT(vl);
check_single_string(gpackage, FALSE, "The \"package\" slot in generic function object");
ok = !strcmp(pkg, CHAR(STRING_ELT(gpackage, 0)));
}
else
ok = TRUE;
}
if(ok) {
generic = vl;
break;
} else
vl = R_UnboundValue;
}
rho = ENCLOS(rho);
}
/* look in base if either generic is missing */
if(generic == R_UnboundValue) {
vl = SYMVALUE(symbol);
if(IS_GENERIC(vl)) {
generic = vl;
if(strlen(pkg)) {
gpackage = PACKAGE_SLOT(vl);
check_single_string(gpackage, FALSE, "The \"package\" slot in generic function object");
if(strcmp(pkg, CHAR(STRING_ELT(gpackage, 0)))) generic = R_UnboundValue;
}
}
}
return generic;
}
SEXP R_getGeneric(SEXP name, SEXP mustFind, SEXP env, SEXP package)
{
SEXP value;
if(isSymbol(name)) {}
else check_single_string(name, TRUE, "The argument \"f\" to getGeneric");
check_single_string(package, FALSE, "The argument \"package\" to getGeneric");
value = get_generic(name, env, package);
if(value == R_UnboundValue) {
if(asLogical(mustFind)) {
if(env == R_GlobalEnv)
error(_("no generic function definition found for '%s'"),
CHAR(asChar(name)));
else
error(_("no generic function definition found for '%s' in the supplied environment"),
CHAR(asChar(name)));
}
value = R_NilValue;
}
return value;
}
/* C version of the standardGeneric R function. */
SEXP R_standardGeneric(SEXP fname, SEXP ev, SEXP fdef)
{
SEXP f_env=R_BaseEnv, mlist=R_NilValue, f, val=R_NilValue, fsym; /* -Wall */
int nprotect = 0;
if(!initialized)
R_initMethodDispatch(NULL);
fsym = fname;
/* TODO: the code for do_standardGeneric does a test of fsym,
* with a less informative error message. Should combine them.*/
if(!isSymbol(fsym)) {
const char *fname = check_single_string(fsym, TRUE, "The function name in the call to standardGeneric");
fsym = install(fname);
}
switch(TYPEOF(fdef)) {
case CLOSXP:
f_env = CLOENV(fdef);
PROTECT(mlist = findVar(s_dot_Methods, f_env)); nprotect++;
if(mlist == R_UnboundValue)
mlist = R_NilValue;
break;
case SPECIALSXP: case BUILTINSXP:
f_env = R_BaseEnv;
PROTECT(mlist = R_primitive_methods(fdef)); nprotect++;
break;
default: error(_("invalid generic function object for method selection for function '%s': expected a function or a primitive, got an object of class \"%s\""),
CHAR(asChar(fsym)), class_string(fdef));
}
switch(TYPEOF(mlist)) {
case NILSXP:
case CLOSXP:
case SPECIALSXP: case BUILTINSXP:
f = mlist; break;
default:
f = do_dispatch(fname, ev, mlist, TRUE, TRUE);
}
if(isNull(f)) {
SEXP value;
PROTECT(value = R_S_MethodsListSelect(fname, ev, mlist, f_env)); nprotect++;
if(isNull(value))
error(_("no direct or inherited method for function '%s' for this call"),
CHAR(asChar(fname)));
mlist = value;
/* now look again. This time the necessary method should
have been inserted in the MethodsList object */
f = do_dispatch(fname, ev, mlist, FALSE, TRUE);
}
/* loadMethod methods */
if(isObject(f))
f = R_loadMethod(f, fname, ev);
switch(TYPEOF(f)) {
case CLOSXP:
{
if (inherits(f, "internalDispatchMethod")) {
val = R_deferred_default_method();
} else {
SEXP R_execMethod(SEXP, SEXP);
PROTECT(f); nprotect++; /* is this needed?? */
val = R_execMethod(f, ev);
}
}
break;
case SPECIALSXP: case BUILTINSXP:
/* primitives can't be methods; they arise only as the
default method when a primitive is made generic. In this
case, return a special marker telling the C code to go on
with the internal computations. */
val = R_deferred_default_method();
break;
default:
error(_("invalid object (non-function) used as method"));
break;
}
UNPROTECT(nprotect);
return val;
}
/* Is the argument missing? This _approximates_ the classic S sense of
the question (is the argument missing in the call), rather than the
R semantics (is the value of the argument R_MissingArg), but not if
computations in the body of the function may have assigned to the
argument name.
*/
static Rboolean is_missing_arg(SEXP symbol, SEXP ev)
{
R_varloc_t loc;
/* Sanity check, so don't translate */
if (!isSymbol(symbol)) error("'symbol' must be a SYMSXP");
loc = R_findVarLocInFrame(ev, symbol);
if (R_VARLOC_IS_NULL(loc))
error(_("could not find symbol '%s' in frame of call"),
CHAR(PRINTNAME(symbol)));
return R_GetVarLocMISSING(loc);
}
SEXP R_missingArg(SEXP symbol, SEXP ev)
{
if(!isSymbol(symbol))
error(_("invalid symbol in checking for missing argument in method dispatch: expected a name, got an object of class \"%s\""),
class_string(symbol));
if (isNull(ev)) {
error(_("use of NULL environment is defunct"));
ev = R_BaseEnv;
} else
if(!isEnvironment(ev))
error(_("invalid environment in checking for missing argument, '%s', in methods dispatch: got an object of class \"%s\""),
CHAR(PRINTNAME(symbol)), class_string(ev));
if(is_missing_arg(symbol, ev))
return R_TRUE;
else
return R_FALSE;
}
SEXP R_selectMethod(SEXP fname, SEXP ev, SEXP mlist, SEXP evalArgs)
{
return do_dispatch(fname, ev, mlist, TRUE, asLogical(evalArgs));
}
static SEXP do_dispatch(SEXP fname, SEXP ev, SEXP mlist, int firstTry,
int evalArgs)
{
const char *class;
SEXP arg_slot, arg_sym, method, value = R_NilValue;
int nprotect = 0;
/* check for dispatch turned off inside MethodsListSelect */
if(isFunction(mlist))
return mlist;
PROTECT(arg_slot = R_do_slot(mlist, s_argument)); nprotect++;
if(arg_slot == R_NilValue) {
error(_("object of class \"%s\" used as methods list for function '%s' ( no 'argument' slot)"),
class_string(mlist), CHAR(asChar(fname)));
return(R_NilValue); /* -Wall */
}
if(TYPEOF(arg_slot) == SYMSXP)
arg_sym = arg_slot;
else
/* shouldn't happen, since argument in class MethodsList has class
"name" */
arg_sym = installTrChar(asChar(arg_slot));
if(arg_sym == R_DotsSymbol || DDVAL(arg_sym) > 0)
error(_("(in selecting a method for function '%s') '...' and related variables cannot be used for methods dispatch"),
CHAR(asChar(fname)));
if(TYPEOF(ev) != ENVSXP) {
error(_("(in selecting a method for function '%s') the 'environment' argument for dispatch must be an R environment; got an object of class \"%s\""),
CHAR(asChar(fname)), class_string(ev));
return(R_NilValue); /* -Wall */
}
/* find the symbol in the frame, but don't use eval, yet, because
missing arguments are ok & don't require defaults */
if(evalArgs) {
if(is_missing_arg(arg_sym, ev))
class = "missing";
else {
/* get its class */
SEXP arg, class_obj; int check_err;
PROTECT(arg = R_tryEvalSilent(arg_sym, ev, &check_err)); nprotect++;
if(check_err)
error(_("error in evaluating the argument '%s' in selecting a method for function '%s': %s"),
CHAR(PRINTNAME(arg_sym)),CHAR(asChar(fname)),
R_curErrorBuf());
PROTECT(class_obj = R_data_class(arg, TRUE)); nprotect++;
class = CHAR(STRING_ELT(class_obj, 0));
}
}
else {
/* the arg contains the class as a string */
SEXP arg; int check_err;
PROTECT(arg = R_tryEvalSilent(arg_sym, ev, &check_err)); nprotect++;
if(check_err)
error(_("error in evaluating the argument '%s' in selecting a method for function '%s': %s"),
CHAR(PRINTNAME(arg_sym)),CHAR(asChar(fname)),
R_curErrorBuf());
class = CHAR(asChar(arg));
}
method = R_find_method(mlist, class, fname);
if(isNull(method)) {
if(!firstTry)
error(_("no matching method for function '%s' (argument '%s', with class \"%s\")"),
EncodeChar(asChar(fname)), EncodeChar(PRINTNAME(arg_sym)), class);
UNPROTECT(nprotect);
return(R_NilValue);
}
if(value == R_MissingArg) {/* the check put in before calling
function MethodListSelect in R */
error(_("recursive use of function '%s' in method selection, with no default method"),
CHAR(asChar(fname)));
return(R_NilValue);
}
if(!isFunction(method)) {
/* assumes method is a methods list itself. */
/* call do_dispatch recursively. Note the NULL for fname; this is
passed on to the S language search function for inherited
methods, to indicate a recursive call, not one to be stored in
the methods metadata */
method = do_dispatch(R_NilValue, ev, method, firstTry, evalArgs);
}
UNPROTECT(nprotect); nprotect = 0;
return method;
}
SEXP R_M_setPrimitiveMethods(SEXP fname, SEXP op, SEXP code_vec,
SEXP fundef, SEXP mlist)
{
return R_set_prim_method(fname, op, code_vec, fundef, mlist);
// -> ../../../main/objects.c
}
SEXP R_nextMethodCall(SEXP matched_call, SEXP ev)
{
SEXP e, val, args, this_sym, op;
int i, nargs = length(matched_call)-1, error_flag;
Rboolean prim_case;
/* for primitive .nextMethod's, suppress further dispatch to avoid
* going into an infinite loop of method calls
*/
PROTECT(op = findVarInFrame3(ev, R_dot_nextMethod, TRUE));
if(op == R_UnboundValue)
error("internal error in 'callNextMethod': '.nextMethod' was not assigned in the frame of the method call");
PROTECT(e = shallow_duplicate(matched_call));
prim_case = isPrimitive(op);
if (!prim_case) {
if (inherits(op, "internalDispatchMethod")) {
SEXP generic = findVarInFrame3(ev, R_dot_Generic, TRUE);
if(generic == R_UnboundValue)
error("internal error in 'callNextMethod': '.Generic' was not assigned in the frame of the method call");
PROTECT(generic);
op = INTERNAL(installTrChar(asChar(generic)));
UNPROTECT(1); /* generic */
prim_case = TRUE;
}
}
if(prim_case) {
/* retain call to primitive function, suppress method
dispatch for it */
do_set_prim_method(op, "suppress", R_NilValue, R_NilValue);
}
else
SETCAR(e, R_dot_nextMethod); /* call .nextMethod instead */
args = CDR(e);
/* e is a copy of a match.call, with expand.dots=FALSE. Turn each
<TAG>=value into <TAG> = <TAG>, except ...= is skipped (if it
appears) in which case ... was appended. */
for(i=0; i<nargs; i++) {
this_sym = TAG(args);
/* "missing" only possible in primitive */
if(this_sym != R_NilValue && CAR(args) != R_MissingArg)
SETCAR(args, this_sym);
args = CDR(args);
}
if(prim_case) {
val = R_tryEvalSilent(e, ev, &error_flag);
/* reset the methods: R_NilValue for the mlist argument
leaves the previous function, methods list unchanged */
do_set_prim_method(op, "set", R_NilValue, R_NilValue);
if(error_flag)
Rf_error(_("error in evaluating a 'primitive' next method: %s"),
R_curErrorBuf());
}
else
val = eval(e, ev);
UNPROTECT(2);
return val;
}
static SEXP R_loadMethod(SEXP def, SEXP fname, SEXP ev)
{
/* since this is called every time a method is dispatched with a
definition that has a class, it should be as efficient as
possible => we build in knowledge of the standard
MethodDefinition and MethodWithNext slots. If these (+ the
class slot) don't account for all the attributes, regular
dispatch is done. */
SEXP s, attrib;
int found = 1; /* we "know" the class attribute is there */
PROTECT(def);
for(s = attrib = ATTRIB(def); s != R_NilValue; s = CDR(s)) {
SEXP t = TAG(s);
if(t == R_target) {
defineVar(R_dot_target, CAR(s), ev); found++;
}
else if(t == R_defined) {
defineVar(R_dot_defined, CAR(s), ev); found++;
}
else if(t == R_nextMethod) {
defineVar(R_dot_nextMethod, CAR(s), ev); found++;
}
else if(t == R_SrcrefSymbol || t == s_generic) {
/* ignore */ found++;
}
}
defineVar(R_dot_Method, def, ev);
if(found < length(attrib)) {
/* this shouldn't be needed but check the generic being
"loadMethod", which would produce a recursive loop */
if(strcmp(CHAR(asChar(fname)), "loadMethod") == 0) {
UNPROTECT(1);
return def;
}
SEXP e, val;
PROTECT(e = allocVector(LANGSXP, 4));
SETCAR(e,
lang3(R_tripleColon_name, R_methods_name, R_loadMethod_name));
val = CDR(e);
SETCAR(val, def); val = CDR(val);
SETCAR(val, fname); val = CDR(val);
SETCAR(val, ev);
val = eval(e, ev);
UNPROTECT(2);
return val;
}
else {
UNPROTECT(1);
return def;
}
}
static SEXP R_selectByPackage(SEXP table, SEXP classes, int nargs) {
int lwidth, i; SEXP thisPkg;
char *buf, *bufptr;
lwidth = 0;
for(i = 0; i<nargs; i++) {
thisPkg = PACKAGE_SLOT(VECTOR_ELT(classes, i));
if(thisPkg == R_NilValue)
thisPkg = s_base;
lwidth += (int) strlen(STRING_VALUE(thisPkg)) + 1;
}
/* make the label */
const void *vmax = vmaxget();
buf = (char *) R_alloc(lwidth + 1, sizeof(char));
bufptr = buf;
for(i = 0; i<nargs; i++) {
if(i > 0)
*bufptr++ = '#';
thisPkg = PACKAGE_SLOT(VECTOR_ELT(classes, i));
if(thisPkg == R_NilValue)
thisPkg = s_base;
strcpy(bufptr, STRING_VALUE(thisPkg));
while(*bufptr)
bufptr++;
}
/* look up the method by package -- if R_unboundValue, will go on
to do inherited calculation */
SEXP sym = install(buf);
vmaxset(vmax);
return findVarInFrame(table, sym);
}
static const char *
check_single_string(SEXP obj, Rboolean nonEmpty, const char *what)
{
const char *string = "<unset>"; /* -Wall */
if(isString(obj)) {
if(length(obj) != 1)
error(_("'%s' must be a single string (got a character vector of length %d)"),
what, length(obj));
string = CHAR(STRING_ELT(obj, 0));
if(nonEmpty && (! string || !string[0]))
error(_("'%s' must be a non-empty string; got an empty string"),
what);
}
else {
error(_("'%s' must be a single string (got an object of class \"%s\")"),
what, class_string(obj));
}
return string;
}
static const char *check_symbol_or_string(SEXP obj, Rboolean nonEmpty,
const char *what)
{
if(isSymbol(obj))
return CHAR(PRINTNAME(obj));
else
return check_single_string(obj, nonEmpty, what);
}
static const char *class_string(SEXP obj)
{
return CHAR(STRING_ELT(R_data_class(obj, TRUE), 0));
}
/* internal version of paste(".", prefix, name, sep="__"),
for speed so few checks
If you decide to change this:
- don't, you will break all installed S4-using packages!
- change the hard-coded ".__M__" in namespace.R
*/
SEXP R_methodsPackageMetaName(SEXP prefix, SEXP name, SEXP pkg)
{
char str[501];
const char *prefixString, *nameString, *pkgString;
prefixString = check_single_string(prefix, TRUE,
"The internal prefix (e.g., \"C\") for a meta-data object");
nameString = check_single_string(name, FALSE,
"The name of the object (e.g,. a class or generic function) to find in the meta-data");
pkgString = check_single_string(pkg, FALSE,
"The name of the package for a meta-data object");
if(*pkgString)
snprintf(str, 500, ".__%s__%s:%s", prefixString, nameString, pkgString);
else
snprintf(str, 500, ".__%s__%s", prefixString, nameString);
return mkString(str);
}
SEXP R_identC(SEXP e1, SEXP e2)
{
if(TYPEOF(e1) == STRSXP && TYPEOF(e2) == STRSXP &&
LENGTH(e1) == 1 && LENGTH(e2) == 1 &&
STRING_ELT(e1, 0) == STRING_ELT(e2, 0))
return R_TRUE;
else
return R_FALSE;
}
SEXP R_getClassFromCache(SEXP class, SEXP table)
{
SEXP value;
if(TYPEOF(class) == STRSXP) {
if (LENGTH(class) == 0) return R_NilValue;
SEXP package = PACKAGE_SLOT(class);
value = findVarInFrame(table, installTrChar(STRING_ELT(class, 0)));
if(value == R_UnboundValue)
return R_NilValue;
else if(TYPEOF(package) == STRSXP) {
SEXP defPkg = PACKAGE_SLOT(value);
/* check equality of package */
if(TYPEOF(defPkg) == STRSXP && length(defPkg) ==1 &&
STRING_ELT(defPkg,0) != STRING_ELT(package, 0))
return R_NilValue;
else
return value;
}
else /* may return a list if multiple instances of class */
return value;
}
else if(TYPEOF(class) != S4SXP) {
error(_("class should be either a character-string name or a class definition"));
return R_NilValue; /* NOT REACHED */
} else /* assumes a class def, but might check */
return class;
}
static SEXP do_inherited_table(SEXP class_objs, SEXP fdef, SEXP mtable, SEXP ev)
{
static SEXP dotFind = NULL, f; SEXP e, ee;
if(dotFind == NULL) {
dotFind = install(".InheritForDispatch");
f = findFun(dotFind, R_MethodsNamespace);
}
PROTECT(e = allocVector(LANGSXP, 4));
SETCAR(e, f); ee = CDR(e);
SETCAR(ee, class_objs); ee = CDR(ee);
SETCAR(ee, fdef); ee = CDR(ee);
SETCAR(ee, mtable);
ee = eval(e, ev);
UNPROTECT(1);
return ee;
}
static SEXP dots_class(SEXP ev, int *checkerrP)
{
static SEXP call = NULL; SEXP ee;
if(call == NULL) {
SEXP dotFind, f, R_dots;
dotFind = install(".dotsClass");
PROTECT(f = findFun(dotFind, R_MethodsNamespace));
R_dots = install("...");
call = allocVector(LANGSXP, 2);
R_PreserveObject(call);
SETCAR(call,f); ee = CDR(call);
SETCAR(ee, R_dots);
UNPROTECT(1);
}
return R_tryEvalSilent(call, ev, checkerrP);
}
static SEXP do_mtable(SEXP fdef, SEXP ev)
{
static SEXP dotFind = NULL, f; SEXP e, ee;
if(dotFind == NULL) {
dotFind = install(".getMethodsTable");
f = findFun(dotFind, R_MethodsNamespace);
R_PreserveObject(f);
}
PROTECT(e = allocVector(LANGSXP, 2));
SETCAR(e, f); ee = CDR(e);
SETCAR(ee, fdef);
ee = eval(e, ev);
UNPROTECT(1);
return ee;
}
SEXP R_dispatchGeneric(SEXP fname, SEXP ev, SEXP fdef)
{
static SEXP R_mtable = NULL, R_allmtable, R_sigargs, R_siglength, R_dots;
int nprotect = 0;
SEXP mtable, classes, thisClass = R_NilValue /* -Wall */, sigargs,
siglength, f_env = R_NilValue, method, f, val = R_NilValue;
char *buf, *bufptr;
int nargs, i, lwidth = 0;
if(!R_mtable) {
R_mtable = install(".MTable");
R_allmtable = install(".AllMTable");
R_sigargs = install(".SigArgs");
R_siglength = install(".SigLength");
R_dots = install("...");
}
switch(TYPEOF(fdef)) {
case CLOSXP:
f_env = CLOENV(fdef);
break;
case SPECIALSXP: case BUILTINSXP:
PROTECT(fdef = R_primitive_generic(fdef)); nprotect++;
if(TYPEOF(fdef) != CLOSXP) {
error(_("failed to get the generic for the primitive \"%s\""),
CHAR(asChar(fname)));
return R_NilValue;
}
f_env = CLOENV(fdef);
break;
default:
error(_("expected a generic function or a primitive for dispatch, got an object of class \"%s\""),
class_string(fdef));
}
PROTECT(mtable = findVarInFrame(f_env, R_allmtable)); nprotect++;
if(mtable == R_UnboundValue) {
do_mtable(fdef, ev); /* Should initialize the generic */
PROTECT(mtable = findVarInFrame(f_env, R_allmtable)); nprotect++;
}
PROTECT(sigargs = findVarInFrame(f_env, R_sigargs)); nprotect++;
PROTECT(siglength = findVarInFrame(f_env, R_siglength)); nprotect++;
if(sigargs == R_UnboundValue || siglength == R_UnboundValue ||
mtable == R_UnboundValue)
error("generic \"%s\" seems not to have been initialized for table dispatch---need to have '.SigArgs' and '.AllMtable' assigned in its environment");
nargs = asInteger(siglength);
PROTECT(classes = allocVector(VECSXP, nargs)); nprotect++;
if (nargs > LENGTH(sigargs))
error("'.SigArgs' is shorter than '.SigLength' says it should be");
for(i = 0; i < nargs; i++) {
SEXP arg_sym = VECTOR_ELT(sigargs, i);
if(is_missing_arg(arg_sym, ev))
thisClass = s_missing;
else {
/* get its class */
SEXP arg; int check_err = 0;
if(arg_sym == R_dots) {
thisClass = dots_class(ev, &check_err);
}
else {
PROTECT(arg = eval(arg_sym, ev));
/* PROTECT(arg = R_tryEvalSilent(arg_sym, ev, &check_err)); // <- related to bug PR#16111 */
/* if(!check_err) */
thisClass = R_data_class(arg, TRUE);
UNPROTECT(1); /* arg */
}
if(check_err)
error(_("error in evaluating the argument '%s' in selecting a method for function '%s': %s"),
CHAR(PRINTNAME(arg_sym)), CHAR(asChar(fname)),
R_curErrorBuf());
}
SET_VECTOR_ELT(classes, i, thisClass);
lwidth += (int) strlen(STRING_VALUE(thisClass)) + 1;
}
/* make the label */
const void *vmax = vmaxget();
buf = (char *) R_alloc(lwidth + 1, sizeof(char));
bufptr = buf;
for(i = 0; i<nargs; i++) {
if(i > 0)
*bufptr++ = '#';
thisClass = VECTOR_ELT(classes, i);
strcpy(bufptr, STRING_VALUE(thisClass));
while(*bufptr)
bufptr++;
}
method = findVarInFrame(mtable, install(buf));
vmaxset(vmax);
if(DUPLICATE_CLASS_CASE(method)) {
PROTECT(method);
method = R_selectByPackage(method, classes, nargs);
UNPROTECT(1);
}
if(method == R_UnboundValue) {
method = do_inherited_table(classes, fdef, mtable, ev);
}
/* the rest of this is identical to R_standardGeneric;
hence the f=method to remind us */
f = method;
switch(TYPEOF(f)) {
case CLOSXP:
{
if (inherits(f, "internalDispatchMethod")) {
val = R_deferred_default_method();
} else {
SEXP R_execMethod(SEXP, SEXP);
if(isObject(f))
f = R_loadMethod(f, fname, ev);
PROTECT(f); nprotect++; /* is this needed?? */
val = R_execMethod(f, ev);
}
}
break;
case SPECIALSXP: case BUILTINSXP:
/* primitives can't be methods; they arise only as the
default method when a primitive is made generic. In this
case, return a special marker telling the C code to go on
with the internal computations. */
val = R_deferred_default_method();
break;
default:
error(_("invalid object (non-function) used as method"));
break;
}
UNPROTECT(nprotect);
return val;
}
SEXP R_set_method_dispatch(SEXP onOff)
{
Rboolean prev = table_dispatch_on, value = asLogical(onOff);
if(value == NA_LOGICAL) /* just return previous*/
value = prev;
table_dispatch_on = value;
if(value != prev) {
R_set_standardGeneric_ptr(
(table_dispatch_on ? R_dispatchGeneric : R_standardGeneric),
Methods_Namespace);
R_set_quick_method_check(
(table_dispatch_on ? R_quick_dispatch : R_quick_method_check));
}
return ScalarLogical(prev);
}