/src/r-source/src/main/objects.c
Line | Count | Source |
1 | | /* |
2 | | * R : A Computer Language for Statistical Data Analysis |
3 | | * Copyright (C) 1999-2026 The R Core Team. |
4 | | * Copyright (C) 2002-2023 The R Foundation |
5 | | * Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2, or (at your option) |
10 | | * any later version. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, a copy is available at |
19 | | * https://www.R-project.org/Licenses/ |
20 | | */ |
21 | | |
22 | | /* This module contains support for S-style generic */ |
23 | | /* functions and "class" support. Gag, barf ... */ |
24 | | |
25 | | #ifdef HAVE_CONFIG_H |
26 | | #include <config.h> |
27 | | #endif |
28 | | |
29 | | #define R_USE_SIGNALS 1 |
30 | | #include <Defn.h> |
31 | | #include <Internal.h> |
32 | | #include <R_ext/RS.h> /* for R_Calloc, R_Realloc and for S4 object bit, R_allocObject() */ |
33 | | |
34 | | static SEXP GetObject(RCNTXT *cptr) |
35 | 821k | { |
36 | 821k | SEXP s, b, formals, tag; |
37 | | |
38 | 821k | b = cptr->callfun; |
39 | 821k | if (TYPEOF(b) != CLOSXP) error(_("generic 'function' is not a function")); |
40 | 821k | formals = FORMALS(b); |
41 | | |
42 | 821k | tag = TAG(formals); |
43 | 821k | if (tag != R_NilValue && tag != R_DotsSymbol) { |
44 | 820k | s = NULL; |
45 | | /** exact matches **/ |
46 | 3.00M | for (b = cptr->promargs ; b != R_NilValue ; b = CDR(b)) |
47 | 2.18M | if (TAG(b) != R_NilValue && pmatch(tag, TAG(b), 1)) { |
48 | 0 | if (s != NULL) |
49 | 0 | error(_("formal argument \"%s\" matched by multiple actual arguments"), |
50 | 0 | CHAR(PRINTNAME(tag))); |
51 | 0 | else |
52 | 0 | s = CAR(b); |
53 | 0 | } |
54 | | |
55 | 820k | if (s == NULL) |
56 | | /** partial matches **/ |
57 | 3.00M | for (b = cptr->promargs ; b != R_NilValue ; b = CDR(b)) |
58 | 2.18M | if (TAG(b) != R_NilValue && pmatch(tag, TAG(b), 0)) { |
59 | 0 | if ( s != NULL) |
60 | 0 | error(_("formal argument \"%s\" matched by multiple actual arguments"), |
61 | 0 | CHAR(PRINTNAME(tag))); |
62 | 0 | else |
63 | 0 | s = CAR(b); |
64 | 0 | } |
65 | 820k | if (s == NULL) |
66 | | /** first untagged argument **/ |
67 | 820k | for (b = cptr->promargs ; b != R_NilValue ; b = CDR(b)) |
68 | 820k | if (TAG(b) == R_NilValue ) |
69 | 820k | { |
70 | 820k | s = CAR(b); |
71 | 820k | break; |
72 | 820k | } |
73 | 820k | if (s == NULL) |
74 | 0 | s = CAR(cptr->promargs); |
75 | | /* |
76 | | error("failed to match argument for dispatch"); |
77 | | */ |
78 | 820k | } |
79 | 436 | else |
80 | 436 | s = CAR(cptr->promargs); |
81 | | |
82 | 821k | if (TYPEOF(s) == PROMSXP) { |
83 | 821k | if (! PROMISE_IS_EVALUATED(s)) |
84 | 480k | s = eval(s, R_BaseEnv); |
85 | 341k | else |
86 | 341k | s = PRVALUE(s); |
87 | 821k | } |
88 | 821k | return(s); |
89 | 821k | } |
90 | | |
91 | | static SEXP applyMethod(SEXP call, SEXP op, SEXP args, SEXP rho, SEXP newvars) |
92 | 1.36M | { |
93 | 1.36M | SEXP ans; |
94 | 1.36M | if (TYPEOF(op) == SPECIALSXP) { |
95 | 4.53k | int save = R_PPStackTop, flag = PRIMPRINT(op); |
96 | 4.53k | const void *vmax = vmaxget(); |
97 | 4.53k | R_Visible = flag != 1; |
98 | 4.53k | ans = PRIMFUN(op) (call, op, args, rho); |
99 | 4.53k | if (flag < 2) R_Visible = flag != 1; |
100 | 4.53k | check_stack_balance(op, save); |
101 | 4.53k | vmaxset(vmax); |
102 | 4.53k | } |
103 | | /* In other places we add a context to builtins when profiling, |
104 | | but we have not bothered here (as there seem to be no primitives |
105 | | used as methods, and this would have to be a primitive to be |
106 | | found). |
107 | | */ |
108 | 1.36M | else if (TYPEOF(op) == BUILTINSXP) { |
109 | 0 | int save = R_PPStackTop, flag = PRIMPRINT(op); |
110 | 0 | const void *vmax = vmaxget(); |
111 | 0 | PROTECT(args = evalList(args, rho, call, 0)); |
112 | 0 | R_Visible = flag != 1; |
113 | 0 | ans = PRIMFUN(op) (call, op, args, rho); |
114 | 0 | if (flag < 2) R_Visible = flag != 1; |
115 | 0 | UNPROTECT(1); |
116 | 0 | check_stack_balance(op, save); |
117 | 0 | vmaxset(vmax); |
118 | 0 | } |
119 | 1.36M | else if (TYPEOF(op) == CLOSXP) { |
120 | 1.36M | ans = applyClosure(call, op, args, rho, newvars, FALSE); |
121 | 1.36M | } |
122 | 0 | else |
123 | 0 | ans = R_NilValue; /* for -Wall */ |
124 | 1.36M | return ans; |
125 | 1.36M | } |
126 | | |
127 | | |
128 | | /* "newintoold" - a destructive matching of arguments; */ |
129 | | /* newargs comes first; any element of oldargs with */ |
130 | | /* a name that matches a named newarg is deleted; the */ |
131 | | /* two resulting lists are appended and returned. */ |
132 | | /* S claims to do this (white book) but doesn't seem to. */ |
133 | | |
134 | | static SEXP newintoold(SEXP _new, SEXP old) |
135 | 872 | { |
136 | 872 | if (_new == R_NilValue) return R_NilValue; |
137 | 436 | SETCDR(_new, newintoold(CDR(_new),old)); |
138 | 872 | while (old != R_NilValue) { |
139 | 872 | if (TAG(old) != R_NilValue && TAG(old) == TAG(_new)) { |
140 | 436 | SETCAR(old, CAR(_new)); |
141 | 436 | return CDR(_new); |
142 | 436 | } |
143 | 436 | old = CDR(old); |
144 | 436 | } |
145 | 0 | return _new; |
146 | 436 | } |
147 | | |
148 | | static SEXP matchmethargs(SEXP oldargs, SEXP newargs) |
149 | 436 | { |
150 | 436 | newargs = newintoold(newargs, oldargs); |
151 | 436 | return listAppend(oldargs, newargs); |
152 | 436 | } |
153 | | |
154 | | /* R_MethodsNamespace is initialized to R_GlobalEnv when R is |
155 | | initialized. If it set to the methods namespace when the latter is |
156 | | loaded, and back to R_GlobalEnv when it is unloaded. */ |
157 | | |
158 | | #ifdef S3_for_S4_warn /* not currently used */ |
159 | | static SEXP s_check_S3_for_S4 = NULL; |
160 | | void R_warn_S3_for_S4(SEXP method) { |
161 | | SEXP call; |
162 | | if(!s_check_S3_for_S4) |
163 | | s_check_S3_for_S4 = install(".checkS3forS4"); |
164 | | PROTECT(call = lang2(s_check_S3_for_S4, method)); |
165 | | eval(call, R_MethodsNamespace); |
166 | | UNPROTECT(1); |
167 | | } |
168 | | #endif |
169 | | |
170 | | static SEXP findFunInEnvRange(SEXP symbol, SEXP rho, SEXP target) |
171 | 12.9M | { |
172 | 12.9M | SEXP vl; |
173 | 28.7M | while(rho != R_EmptyEnv) { |
174 | 28.7M | vl = R_findVarInFrame(rho, symbol); |
175 | 28.7M | if (vl != R_UnboundValue) { |
176 | 1.40M | if (TYPEOF(vl) == PROMSXP) { |
177 | 1.40M | PROTECT(vl); |
178 | 1.40M | vl = eval(vl, rho); |
179 | 1.40M | UNPROTECT(1); |
180 | 1.40M | } |
181 | 1.40M | if ((TYPEOF(vl) == CLOSXP || |
182 | 0 | TYPEOF(vl) == BUILTINSXP || |
183 | 0 | TYPEOF(vl) == SPECIALSXP)) |
184 | 1.40M | return (vl); |
185 | 1.40M | } |
186 | 27.3M | if(rho == target) |
187 | 11.5M | return (R_UnboundValue); |
188 | 15.8M | else |
189 | 15.8M | rho = ENCLOS(rho); |
190 | 27.3M | } |
191 | 0 | return (R_UnboundValue); |
192 | 12.9M | } |
193 | | |
194 | | static SEXP findFunWithBaseEnvAfterGlobalEnv(SEXP symbol, SEXP rho) |
195 | 11.5M | { |
196 | 11.5M | SEXP vl; |
197 | 34.5M | while(rho != R_EmptyEnv) { |
198 | 23.0M | vl = R_findVarInFrame(rho, symbol); |
199 | 23.0M | if (vl != R_UnboundValue) { |
200 | 0 | if (TYPEOF(vl) == PROMSXP) { |
201 | 0 | PROTECT(vl); |
202 | 0 | vl = eval(vl, rho); |
203 | 0 | UNPROTECT(1); |
204 | 0 | } |
205 | 0 | if ((TYPEOF(vl) == CLOSXP || |
206 | 0 | TYPEOF(vl) == BUILTINSXP || |
207 | 0 | TYPEOF(vl) == SPECIALSXP)) |
208 | 0 | return (vl); |
209 | 0 | } |
210 | 23.0M | if(rho == R_GlobalEnv) |
211 | 11.4M | rho = R_BaseEnv; |
212 | 11.5M | else |
213 | 11.5M | rho = ENCLOS(rho); |
214 | 23.0M | } |
215 | 11.5M | return (R_UnboundValue); |
216 | 11.5M | } |
217 | | |
218 | | /* usemethod - calling functions need to evaluate the object |
219 | | * (== 2nd argument). They also need to ensure that the |
220 | | * argument list is set up in the correct manner. |
221 | | * |
222 | | * 1. find the context for the calling function (i.e. the generic) |
223 | | * this gives us the unevaluated arguments for the original call |
224 | | * |
225 | | * 2. create an environment for evaluating the method and insert |
226 | | * a handful of variables (.Generic, .Class and .Method) into |
227 | | * that environment. Also copy any variables in the env of the |
228 | | * generic that are not formal (or actual) arguments. |
229 | | * |
230 | | * 3. fix up the argument list; it should be the arguments to the |
231 | | * generic matched to the formals of the method to be invoked */ |
232 | | |
233 | | attribute_hidden |
234 | | SEXP R_LookupMethod(SEXP method, SEXP rho, SEXP callrho, SEXP defrho) |
235 | 12.9M | { |
236 | 12.9M | SEXP val, top = R_NilValue; /* -Wall */ |
237 | 12.9M | static SEXP s_S3MethodsTable = NULL; |
238 | 12.9M | PROTECT_INDEX validx; |
239 | | |
240 | 12.9M | if (TYPEOF(callrho) != ENVSXP) { |
241 | 0 | if (TYPEOF(callrho) == NILSXP) |
242 | 0 | error(_("use of NULL environment is defunct")); |
243 | 0 | else |
244 | 0 | error(_("bad generic call environment")); |
245 | 0 | } |
246 | 12.9M | if (defrho == R_BaseEnv) |
247 | 11.0M | defrho = R_BaseNamespace; |
248 | 1.93M | else if (TYPEOF(defrho) != ENVSXP) { |
249 | 0 | if (TYPEOF(defrho) == NILSXP) |
250 | 0 | error(_("use of NULL environment is defunct")); |
251 | 0 | else |
252 | 0 | error(_("bad generic definition environment")); |
253 | 0 | } |
254 | | |
255 | | /* This evaluates promises */ |
256 | 12.9M | PROTECT(top = topenv(R_NilValue, callrho)); |
257 | 12.9M | val = findFunInEnvRange(method, callrho, top); |
258 | 12.9M | if(val != R_UnboundValue) { |
259 | 1.40M | UNPROTECT(1); /* top */ |
260 | 1.40M | return val; |
261 | 1.40M | } |
262 | | |
263 | 11.5M | PROTECT_WITH_INDEX(val, &validx); |
264 | | /* We assume here that no one registered a non-function */ |
265 | 11.5M | if (!s_S3MethodsTable) |
266 | 32 | s_S3MethodsTable = install(".__S3MethodsTable__."); |
267 | 11.5M | SEXP table = R_findVarInFrame(defrho, s_S3MethodsTable); |
268 | 11.5M | if (TYPEOF(table) == PROMSXP) { |
269 | 11.5M | PROTECT(table); |
270 | 11.5M | table = eval(table, R_BaseEnv); |
271 | 11.5M | UNPROTECT(1); /* table */ |
272 | 11.5M | } |
273 | 11.5M | if (TYPEOF(table) == ENVSXP) { |
274 | 11.5M | PROTECT(table); |
275 | 11.5M | REPROTECT(val = R_findVarInFrame(table, method), validx); |
276 | 11.5M | UNPROTECT(1); /* table */ |
277 | 11.5M | if (TYPEOF(val) == PROMSXP) |
278 | 8.87k | REPROTECT(val = eval(val, rho), validx); |
279 | 11.5M | if(val != R_UnboundValue) { |
280 | 8.87k | UNPROTECT(2); /* top, val */ |
281 | 8.87k | return val; |
282 | 8.87k | } |
283 | 11.5M | } |
284 | | |
285 | 11.5M | if (top == R_GlobalEnv) |
286 | 2.00k | top = R_BaseEnv; |
287 | 11.5M | else |
288 | 11.5M | top = ENCLOS(top); |
289 | 11.5M | REPROTECT(val = findFunWithBaseEnvAfterGlobalEnv(method, top), |
290 | 11.5M | validx); |
291 | | |
292 | 11.5M | UNPROTECT(2); /* top, val */ |
293 | 11.5M | return val; |
294 | 11.5M | } |
295 | | |
296 | | #ifdef UNUSED |
297 | | static int match_to_obj(SEXP arg, SEXP obj) { |
298 | | return (arg == obj) || |
299 | | (TYPEOF(arg) == PROMSXP && PRVALUE(arg) == obj); |
300 | | } |
301 | | #endif |
302 | | |
303 | | /* look up the class name in the methods package table of S3 classes |
304 | | which should be explicitly converted when an S3 method is applied |
305 | | to an object from an S4 subclass. |
306 | | */ |
307 | 0 | attribute_hidden int isBasicClass(const char *ss) { |
308 | 0 | static SEXP s_S3table = NULL; |
309 | 0 | if(!s_S3table) { |
310 | 0 | s_S3table = R_findVarInFrame(R_MethodsNamespace, |
311 | 0 | install(".S3MethodsClasses")); |
312 | 0 | if(s_S3table == R_UnboundValue) |
313 | 0 | error(_("no '.S3MethodsClass' table, cannot use S4 objects with S3 methods ('methods' package not attached?)")); |
314 | 0 | if (TYPEOF(s_S3table) == PROMSXP) /* findVar... ignores lazy data */ |
315 | 0 | s_S3table = eval(s_S3table, R_MethodsNamespace); |
316 | 0 | } |
317 | 0 | if(s_S3table == R_UnboundValue) |
318 | 0 | return FALSE; /* too screwed up to do conversions */ |
319 | 0 | return R_existsVarInFrame(s_S3table, install(ss)); |
320 | 0 | } |
321 | | |
322 | | /* Note that ./attrib.c 's S4_extends() has an alternative |
323 | | 'sanity check for methods package available' */ |
324 | 0 | attribute_hidden Rboolean R_has_methods_attached(void) { |
325 | 0 | return( |
326 | 0 | isMethodsDispatchOn() && |
327 | | // based on unlockBinding() in ../library/methods/R/zzz.R {since 2003}: |
328 | 0 | !R_BindingIsLocked(install(".BasicFunsList"), R_MethodsNamespace)); |
329 | 0 | } |
330 | | |
331 | | static R_INLINE |
332 | 8.48M | SEXP addS3Var(SEXP vars, SEXP name, SEXP value) { |
333 | | |
334 | 8.48M | SEXP res = CONS(value, vars); |
335 | 8.48M | SET_TAG(res, name); |
336 | 8.48M | return res; |
337 | 8.48M | } |
338 | | |
339 | | attribute_hidden |
340 | | SEXP createS3Vars(SEXP dotGeneric, SEXP dotGroup, SEXP dotClass, SEXP dotMethod, |
341 | 1.41M | SEXP dotGenericCallEnv, SEXP dotGenericDefEnv) { |
342 | | |
343 | 1.41M | SEXP v = R_NilValue; |
344 | 1.41M | v = addS3Var(v, R_dot_GenericDefEnv, dotGenericDefEnv); |
345 | 1.41M | v = addS3Var(v, R_dot_GenericCallEnv, dotGenericCallEnv); |
346 | 1.41M | v = addS3Var(v, R_dot_Group, dotGroup); |
347 | 1.41M | v = addS3Var(v, R_dot_Method, dotMethod); |
348 | 1.41M | v = addS3Var(v, R_dot_Class, dotClass); |
349 | 1.41M | v = addS3Var(v, R_dot_Generic, dotGeneric); |
350 | | |
351 | 1.41M | return v; |
352 | 1.41M | } |
353 | | |
354 | | |
355 | | static |
356 | | SEXP dispatchMethod(SEXP op, SEXP sxp, SEXP dotClass, RCNTXT *cptr, SEXP method, |
357 | 1.36M | const char *generic, SEXP rho, SEXP callrho, SEXP defrho) { |
358 | | |
359 | 1.36M | SEXP newvars = PROTECT(createS3Vars( |
360 | 1.36M | PROTECT(mkString(generic)), |
361 | 1.36M | R_BlankScalarString, |
362 | 1.36M | dotClass, |
363 | 1.36M | PROTECT(ScalarString(PRINTNAME(method))), |
364 | 1.36M | callrho, |
365 | 1.36M | defrho |
366 | 1.36M | )); |
367 | | |
368 | | /* Create a new environment without any */ |
369 | | /* of the formals to the generic in it. */ |
370 | | |
371 | 1.36M | if (TYPEOF(op) == CLOSXP) { |
372 | 821k | SEXP formals = FORMALS(op); |
373 | 821k | SEXP s, t; |
374 | 821k | int matched; |
375 | | |
376 | 2.34M | for (s = FRAME(cptr->cloenv); s != R_NilValue; s = CDR(s)) { |
377 | 1.52M | matched = 0; |
378 | 2.51M | for (t = formals; t != R_NilValue; t = CDR(t)) |
379 | 2.51M | if (TAG(t) == TAG(s)) { |
380 | 1.52M | matched = 1; |
381 | 1.52M | break; |
382 | 1.52M | } |
383 | 1.52M | if (!matched) { |
384 | | #ifdef USEMETHOD_FORWARD_LOCALS |
385 | | UNPROTECT(1); /* newvars */ |
386 | | newvars = PROTECT(CONS(CAR(s), newvars)); |
387 | | SET_TAG(newvars, TAG(s)); |
388 | | #else |
389 | 0 | static int option = -1; |
390 | 0 | if (option == -1) { |
391 | 0 | option = 2; // none: the default |
392 | 0 | const char *val = getenv("R_USEMETHOD_FORWARD_LOCALS"); |
393 | 0 | if (val != NULL) { |
394 | 0 | if (strcmp(val, "all") == 0) |
395 | 0 | option = 0; |
396 | 0 | else if (strcmp(val, "S4") == 0) |
397 | 0 | option = 1; |
398 | 0 | else if (strcmp(val, "none") == 0) |
399 | 0 | option = 2; |
400 | 0 | else if (strcmp(val, "error") == 0) |
401 | 0 | option = 3; |
402 | 0 | else if (strcmp(val, "warning") == 0) |
403 | 0 | option = 4; |
404 | 0 | else |
405 | 0 | warning("bad value for R_USEMETHOD_FORWARD_LOCALS"); |
406 | 0 | } |
407 | 0 | } |
408 | 0 | SEXP val; |
409 | 0 | char buf[8192]; |
410 | 0 | switch(option) { |
411 | 0 | case 0: // forward all, as in the past before R 4.4.0 |
412 | 0 | UNPROTECT(1); /* newvars */ |
413 | 0 | newvars = PROTECT(CONS(CAR(s), newvars)); |
414 | 0 | SET_TAG(newvars, TAG(s)); |
415 | 0 | break; |
416 | 0 | case 1: // forward only S4 variables |
417 | 0 | if (TAG(s) == R_dot_defined || |
418 | 0 | TAG(s) == R_dot_Method || |
419 | 0 | TAG(s) == R_dot_target || |
420 | 0 | TAG(s) == R_dot_Generic || |
421 | 0 | TAG(s) == R_dot_Methods) { |
422 | 0 | UNPROTECT(1); /* newvars */ |
423 | 0 | newvars = PROTECT(CONS(CAR(s), newvars)); |
424 | 0 | SET_TAG(newvars, TAG(s)); |
425 | 0 | } |
426 | 0 | break; |
427 | 0 | case 2: // don't forward any variables |
428 | 0 | break; |
429 | 0 | case 3: // forward all, with an error when used |
430 | | #ifdef WARN_ON_FORWARDING |
431 | | if (TAG(s) != R_dot_defined && |
432 | | TAG(s) != R_dot_Method && |
433 | | TAG(s) != R_dot_target && |
434 | | TAG(s) != R_dot_Generic && |
435 | | TAG(s) != R_dot_Methods) |
436 | | warningcall_immediate(R_NilValue, |
437 | | "UseMethod forwarding '%s' " |
438 | | "in generic '%s'", |
439 | | CHAR(PRINTNAME(TAG(s))), |
440 | | generic); |
441 | | #endif |
442 | 0 | snprintf(buf, sizeof(buf), |
443 | 0 | "stop(\"getting UseMethod variable '%s' " |
444 | 0 | "from generic '%s'; " |
445 | 0 | "this is no longer supported\")", |
446 | 0 | CHAR(PRINTNAME(TAG(s))), |
447 | 0 | generic); |
448 | 0 | val = mkPROMISE(R_ParseString(buf), R_GlobalEnv); |
449 | 0 | UNPROTECT(1); /* newvars */ |
450 | 0 | newvars = PROTECT(CONS(val, newvars)); |
451 | 0 | SET_TAG(newvars, TAG(s)); |
452 | 0 | break; |
453 | 0 | default: |
454 | 0 | option = 0; |
455 | 0 | warning("bad value for R_USEMETHOD_FORWARD_LOCALS"); |
456 | 0 | } |
457 | 0 | #endif |
458 | 0 | } |
459 | 1.52M | } |
460 | 821k | } |
461 | | |
462 | | /* Debug a method when debugging the generic. When called via UseMethod or |
463 | | NextMethod, RSTEP(op) will always be zero because the bit is cleared by |
464 | | applyClosure. We thus approximate and enter the debugger also when |
465 | | RDEBUG(rho) is set. */ |
466 | 1.36M | if ((RDEBUG(op) && R_current_debug_state()) || RSTEP(op) || RDEBUG(rho)) |
467 | 0 | SET_RSTEP(sxp, 1); |
468 | | |
469 | 1.36M | SEXP newcall = PROTECT(shallow_duplicate(cptr->call)); |
470 | 1.36M | SETCAR(newcall, method); |
471 | 1.36M | R_GlobalContext->callflag = CTXT_GENERIC; |
472 | 1.36M | SEXP matchedarg = PROTECT(cptr->promargs); /* ? is this PROTECT needed ? */ |
473 | 1.36M | SEXP ans = applyMethod(newcall, sxp, matchedarg, rho, newvars); |
474 | 1.36M | R_GlobalContext->callflag = CTXT_RETURN; |
475 | 1.36M | UNPROTECT(5); /* "generic,method", newvars, newcall, matchedarg */ |
476 | | |
477 | 1.36M | return ans; |
478 | 1.36M | } |
479 | | |
480 | | attribute_hidden |
481 | | int usemethod(const char *generic, SEXP obj, SEXP call, SEXP args, |
482 | | SEXP rho, SEXP callrho, SEXP defrho, SEXP *ans) |
483 | 5.90M | { |
484 | 5.90M | SEXP klass, method, sxp; |
485 | 5.90M | SEXP op; |
486 | 5.90M | int i, nclass; |
487 | 5.90M | RCNTXT *cptr; |
488 | | |
489 | | /* Get the context which UseMethod was called from. */ |
490 | | |
491 | 5.90M | cptr = R_GlobalContext; |
492 | 5.90M | op = cptr->callfun; |
493 | 5.90M | PROTECT(klass = R_data_class2(obj)); |
494 | | |
495 | 5.90M | nclass = length(klass); |
496 | 12.8M | for (i = 0; i < nclass; i++) { |
497 | 7.57M | const void *vmax = vmaxget(); |
498 | 7.57M | const char *ss = translateChar(STRING_ELT(klass, i)); |
499 | 7.57M | method = installS3Signature(generic, ss); |
500 | 7.57M | vmaxset(vmax); |
501 | 7.57M | sxp = R_LookupMethod(method, rho, callrho, defrho); |
502 | 7.57M | if (isFunction(sxp)) { |
503 | 621k | if(method == R_SortListSymbol && CLOENV(sxp) == R_BaseNamespace) |
504 | 0 | continue; /* kludge because sort.list is not a method */ |
505 | 621k | PROTECT(sxp); |
506 | 621k | if (i > 0) { |
507 | 63.3k | SEXP dotClass = PROTECT(stringSuffix(klass, i)); |
508 | 63.3k | setAttrib(dotClass, R_PreviousSymbol, klass); |
509 | 63.3k | *ans = dispatchMethod(op, sxp, dotClass, cptr, method, generic, |
510 | 63.3k | rho, callrho, defrho); |
511 | 63.3k | UNPROTECT(1); /* dotClass */ |
512 | 558k | } else { |
513 | 558k | *ans = dispatchMethod(op, sxp, klass, cptr, method, generic, |
514 | 558k | rho, callrho, defrho); |
515 | 558k | } |
516 | 621k | UNPROTECT(2); /* klass, sxp */ |
517 | 621k | return 1; |
518 | 621k | } |
519 | 7.57M | } |
520 | 5.28M | method = installS3Signature(generic, "default"); |
521 | 5.28M | PROTECT(sxp = R_LookupMethod(method, rho, callrho, defrho)); |
522 | 5.28M | if (isFunction(sxp)) { |
523 | 739k | *ans = dispatchMethod(op, sxp, R_NilValue, cptr, method, generic, |
524 | 739k | rho, callrho, defrho); |
525 | 739k | UNPROTECT(2); /* klass, sxp */ |
526 | 739k | return 1; |
527 | 739k | } |
528 | 4.54M | UNPROTECT(2); /* klass, sxp */ |
529 | 4.54M | cptr->callflag = CTXT_RETURN; |
530 | 4.54M | return 0; |
531 | 5.28M | } |
532 | | |
533 | | /* Note: "do_usemethod" is not the only entry point to |
534 | | "usemethod". Things like [ and [[ call usemethod directly, |
535 | | hence do_usemethod should just be an interface to usemethod. |
536 | | */ |
537 | | |
538 | | /* This is a primitive SPECIALSXP */ |
539 | | NORET attribute_hidden |
540 | | SEXP do_usemethod(SEXP call, SEXP op, SEXP args, SEXP env) |
541 | 821k | { |
542 | 821k | static SEXP do_usemethod_formals = NULL; |
543 | 821k | if (do_usemethod_formals == NULL) |
544 | 32 | do_usemethod_formals = allocFormalsList2(install("generic"), |
545 | 32 | install("object")); |
546 | | |
547 | 821k | SEXP argList = PROTECT(matchArgs_NR(do_usemethod_formals, args, call)); |
548 | 821k | if (CAR(argList) == R_MissingArg) |
549 | 0 | errorcall(call, _("there must be a 'generic' argument")); |
550 | | // else |
551 | 821k | SEXP generic = PROTECT(eval(CAR(argList), env)); |
552 | 821k | if(!isString(generic) || LENGTH(generic) != 1) |
553 | 0 | errorcall(call, _("'generic' argument must be a character string")); |
554 | | |
555 | | /* get environments needed for dispatching. |
556 | | callenv = environment from which the generic was called |
557 | | defenv = environment where the generic was defined */ |
558 | 821k | RCNTXT *cptr = R_GlobalContext; |
559 | 821k | if ( !(cptr->callflag & CTXT_FUNCTION) || cptr->cloenv != env) |
560 | 0 | errorcall(call, _("'UseMethod' used in an inappropriate fashion")); |
561 | 821k | SEXP callenv = cptr->sysparent; |
562 | | /* We need to find the generic to find out where it is defined. |
563 | | This is set up to avoid getting caught by things like |
564 | | |
565 | | mycoef <- function(x) |
566 | | { |
567 | | mycoef <- function(x) stop("not this one") |
568 | | UseMethod("mycoef") |
569 | | } |
570 | | |
571 | | The generic need not be a closure (Henrik Bengtsson writes |
572 | | UseMethod("$"), although only functions are documented.) |
573 | | */ |
574 | 821k | SEXP defenv = topenv(R_NilValue, env); |
575 | 821k | SEXP obj = PROTECT((CADR(argList) != R_MissingArg) |
576 | 821k | ? eval(CADR(argList), env) |
577 | 821k | : GetObject(cptr)); |
578 | 821k | SEXP ans; |
579 | 821k | if (usemethod(translateChar(STRING_ELT(generic, 0)), obj, call, CDR(args), |
580 | 821k | env, callenv, defenv, &ans) == 1) { |
581 | 820k | UNPROTECT(3); /* obj, generic, argList */ |
582 | 820k | findcontext(CTXT_RETURN, env, ans); /* does not return */ |
583 | 820k | } |
584 | 731 | else { // no method found |
585 | 1.46k | #define CLLEN 1024 |
586 | 731 | char cl[CLLEN] = {0}; |
587 | 731 | SEXP klass = PROTECT(R_data_class2(obj)); |
588 | 731 | int nclass = length(klass); |
589 | 731 | if(!nclass) |
590 | 0 | errorcall(call, _("illegal class of length 0 when using method for '%s'"), |
591 | 0 | translateChar(STRING_ELT(generic, 0))); |
592 | 731 | const char *c_name = translateChar(STRING_ELT(klass, 0)); |
593 | 731 | if (nclass == 1) { |
594 | 0 | strncpy(cl, c_name, CLLEN-1); cl[CLLEN-1] = '\0'; |
595 | 731 | } else { // nclass >= 2 |
596 | 731 | size_t cl_count = 3; // 3 : "c(" + \0 |
597 | 731 | strcpy(cl, "c('"); |
598 | 731 | for (int i = 0; i < nclass; i++) { |
599 | 0 | if(i > 0) { |
600 | 0 | c_name = translateChar(STRING_ELT(klass, i)); |
601 | 0 | cl_count += 4; // "', '" |
602 | 0 | if (cl_count < CLLEN) strcat(cl, "', '"); |
603 | 0 | } |
604 | 0 | size_t len_nm = strlen(c_name); |
605 | 0 | if (cl_count + len_nm >= CLLEN) { |
606 | 0 | cl_count += 2; |
607 | 0 | if(cl_count < CLLEN) strcat(cl, ".."); |
608 | 0 | break; |
609 | 0 | } |
610 | | // else |
611 | 0 | cl_count += len_nm; |
612 | 0 | strcat(cl, c_name); |
613 | 0 | } |
614 | 731 | if (++cl_count < CLLEN) strcat(cl, "'"); |
615 | 731 | if (++cl_count < CLLEN) strcat(cl, ")"); |
616 | 731 | } |
617 | 731 | errorcall(call, _("no applicable method for '%s' applied to an object of class \"%s\""), |
618 | 731 | translateChar(STRING_ELT(generic, 0)), cl); |
619 | 731 | } |
620 | 821k | } |
621 | | |
622 | | /* |
623 | | fixcall: fixes up the call when arguments to the function may |
624 | | have changed; for now we only worry about tagged args, appending |
625 | | them if they are not already there |
626 | | */ |
627 | | |
628 | | static SEXP fixcall(SEXP call, SEXP args) |
629 | 436 | { |
630 | 436 | SEXP s, t; |
631 | 436 | int found; |
632 | | |
633 | 1.74k | for(t = args; t != R_NilValue; t = CDR(t)) { |
634 | 1.30k | if(TAG(t) != R_NilValue) { |
635 | 872 | found = 0; |
636 | 2.18k | for(s = call; CDR(s) != R_NilValue; s = CDR(s)) |
637 | 2.18k | if(TAG(CDR(s)) == TAG(t)) { |
638 | 872 | found = 1; |
639 | 872 | break; |
640 | 872 | } |
641 | 872 | if( !found ) { |
642 | 0 | SETCDR(s, allocList(1)); |
643 | 0 | SET_TAG(CDR(s), TAG(t)); |
644 | 0 | SETCAR(CDR(s), lazy_duplicate(CAR(t))); |
645 | 0 | } |
646 | 872 | } |
647 | 1.30k | } |
648 | 436 | return call; |
649 | 436 | } |
650 | | |
651 | | /* |
652 | | equalS3Signature: compares "signature" and "left.right" |
653 | | all arguments must be non-null |
654 | | */ |
655 | | static |
656 | | Rboolean equalS3Signature(const char *signature, const char *left, |
657 | 5.40k | const char *right) { |
658 | | |
659 | 5.40k | const char *s = signature; |
660 | 5.40k | const char *a; |
661 | | |
662 | 14.8k | for(a = left; *a; s++, a++) { |
663 | 9.40k | if (*s != *a) |
664 | 0 | return FALSE; |
665 | 9.40k | } |
666 | 5.40k | if (*s++ != '.') |
667 | 0 | return FALSE; |
668 | 44.6k | for(a = right; *a; s++, a++) { |
669 | 39.2k | if (*s != *a) |
670 | 0 | return FALSE; |
671 | 39.2k | } |
672 | 5.40k | return (*s == 0) ? TRUE : FALSE; |
673 | 5.40k | } |
674 | | |
675 | | |
676 | | static R_INLINE SEXP getPrimitive(SEXP symbol) |
677 | 0 | { |
678 | 0 | SEXP value = SYMVALUE(symbol); |
679 | 0 | if (TYPEOF(value) == PROMSXP) { |
680 | 0 | PROTECT(value); |
681 | 0 | value = eval(value, R_GlobalEnv); |
682 | 0 | UNPROTECT(1); |
683 | 0 | ENSURE_NAMEDMAX(value); |
684 | 0 | } |
685 | 0 | if (TYPEOF(value) == BUILTINSXP || TYPEOF(value) == SPECIALSXP) |
686 | 0 | return value; |
687 | | |
688 | 0 | if (TYPEOF(value) == CLOSXP) { |
689 | | /* probably means a package redefined the base function so |
690 | | try to get the real thing from the internal table of |
691 | | primitives */ |
692 | 0 | value = R_Primitive(CHAR(PRINTNAME(symbol))); |
693 | 0 | } else |
694 | 0 | value = R_NilValue; |
695 | |
|
696 | 0 | return value; |
697 | 0 | } |
698 | | |
699 | | /* If NextMethod has any arguments the first must be the generic */ |
700 | | /* the second the object and any remaining are matched with the */ |
701 | | /* formals of the chosen method. */ |
702 | | |
703 | | |
704 | | /* This is a special .Internal */ |
705 | | attribute_hidden SEXP do_nextmethod(SEXP call, SEXP op, SEXP args, SEXP env) |
706 | 5.40k | { |
707 | 5.40k | const char *sb, *sg, *sk; |
708 | 5.40k | SEXP ans, s, t, klass, method, matchedarg, generic; |
709 | 5.40k | SEXP nextfun, nextfunSignature; |
710 | 5.40k | SEXP sysp, formals, newcall; |
711 | 5.40k | SEXP group, basename; |
712 | 5.40k | SEXP callenv, defenv; |
713 | 5.40k | RCNTXT *cptr; |
714 | 5.40k | int i, j; |
715 | | |
716 | 5.40k | cptr = R_GlobalContext; |
717 | 5.40k | cptr->callflag = CTXT_GENERIC; |
718 | | |
719 | | /* get the env NextMethod was called from */ |
720 | 5.40k | sysp = R_GlobalContext->sysparent; |
721 | 11.0k | while (cptr != NULL) { |
722 | 11.0k | if (cptr->callflag & CTXT_FUNCTION && cptr->cloenv == sysp) break; |
723 | 5.66k | cptr = cptr->nextcontext; |
724 | 5.66k | } |
725 | 5.40k | if (cptr == NULL) |
726 | 0 | error(_("'NextMethod' called from outside a function")); |
727 | | |
728 | 5.40k | PROTECT(newcall = shallow_duplicate(cptr->call)); |
729 | | |
730 | | /* eg get("print.ts")(1) or do.call() */ |
731 | 5.40k | if (TYPEOF(CAR(cptr->call)) != SYMSXP) |
732 | 0 | error(_("'NextMethod' called from an anonymous function")); |
733 | | |
734 | 5.40k | readS3VarsFromFrame(sysp, &generic, &group, &klass, &method, |
735 | 5.40k | &callenv, &defenv); |
736 | | |
737 | | /* Find dispatching environments. Promises shouldn't occur, but |
738 | | check to be on the safe side. If the variables are not in the |
739 | | environment (the method was called outside a method dispatch) |
740 | | then chose reasonable defaults. */ |
741 | 5.40k | if (TYPEOF(callenv) == PROMSXP) |
742 | 0 | callenv = eval(callenv, R_BaseEnv); |
743 | 5.40k | else if (callenv == R_UnboundValue) |
744 | 0 | callenv = env; |
745 | 5.40k | if (TYPEOF(defenv) == PROMSXP) defenv = eval(defenv, R_BaseEnv); |
746 | 5.40k | else if (defenv == R_UnboundValue) defenv = R_GlobalEnv; |
747 | | |
748 | | /* set up the arglist */ |
749 | 5.40k | s = cptr->callfun; |
750 | | |
751 | 5.40k | if (TYPEOF(s) != CLOSXP){ /* R_LookupMethod looked for a function */ |
752 | 0 | if (s == R_UnboundValue) |
753 | 0 | error(_("no calling generic was found: was a method called directly?")); |
754 | 0 | else |
755 | 0 | errorcall(R_NilValue, |
756 | 0 | _("'function' is not a function, but of type %d"), |
757 | 0 | TYPEOF(s)); |
758 | 0 | } |
759 | | /* get formals and actuals; attach the names of the formals to |
760 | | the actuals, expanding any ... that occurs */ |
761 | 5.40k | formals = FORMALS(s); |
762 | 5.40k | PROTECT(matchedarg = patchArgsByActuals(formals, cptr->promargs, cptr->cloenv)); |
763 | | |
764 | | /* |
765 | | Now see if there were any other arguments passed in |
766 | | Currently we seem to only allow named args to change |
767 | | or to be added, this is at variance with p. 470 of the |
768 | | White Book |
769 | | */ |
770 | | |
771 | 5.40k | s = CADDR(args); /* this is ... and we need to see if it's bound */ |
772 | 5.40k | if (s == R_DotsSymbol) { |
773 | 5.40k | t = R_findVarInFrame(env, s); |
774 | 5.40k | if (t != R_NilValue && t != R_MissingArg) { |
775 | 436 | SET_TYPEOF(t, LISTSXP); /* a safe mutation */ |
776 | 436 | s = matchmethargs(matchedarg, t); |
777 | 436 | UNPROTECT(1); |
778 | 436 | PROTECT(matchedarg = s); |
779 | 436 | newcall = fixcall(newcall, matchedarg); |
780 | 436 | } |
781 | 5.40k | } |
782 | 0 | else |
783 | 0 | error(_("wrong argument ...")); |
784 | | |
785 | | /* |
786 | | .Class is used to determine the next method; if it doesn't |
787 | | exist the first argument to the current method is used |
788 | | the second argument to NextMethod is another option but |
789 | | isn't currently used). |
790 | | */ |
791 | 5.40k | if (klass == R_UnboundValue) { |
792 | | /* we can get the object from actuals directly, but this |
793 | | branch seems to be very cold if not dead */ |
794 | 0 | s = GetObject(cptr); |
795 | 0 | if (!isObject(s)) error(_("object not specified")); |
796 | 0 | klass = getAttrib(s, R_ClassSymbol); |
797 | 0 | } |
798 | | |
799 | | /* the generic comes from either the sysparent or it's named */ |
800 | 5.40k | if (generic == R_UnboundValue) |
801 | 0 | generic = eval(CAR(args), env); |
802 | 5.40k | if (generic == R_NilValue) |
803 | 0 | error(_("generic function not specified")); |
804 | 5.40k | PROTECT(generic); |
805 | | |
806 | 5.40k | if (!isString(generic) || LENGTH(generic) != 1) |
807 | 0 | error(_("invalid generic argument to 'NextMethod'")); |
808 | | |
809 | 5.40k | if (CHAR(STRING_ELT(generic, 0))[0] == '\0') |
810 | 0 | error(_("generic function not specified")); |
811 | | |
812 | | /* determine whether we are in a Group dispatch */ |
813 | | /* determine the root: either the group or the generic will be it */ |
814 | 5.40k | if (group == R_UnboundValue) { |
815 | 0 | group = R_BlankScalarString; |
816 | 0 | basename = generic; |
817 | 5.40k | } else { |
818 | 5.40k | if (!isString(group) || LENGTH(group) != 1) |
819 | 0 | error(_("invalid 'group' argument found in 'NextMethod'")); |
820 | 5.40k | if (CHAR(STRING_ELT(group, 0))[0] == '\0') basename = generic; |
821 | 0 | else basename = group; |
822 | 5.40k | } |
823 | 5.40k | PROTECT(group); |
824 | | |
825 | 5.40k | nextfun = R_NilValue; |
826 | 5.40k | nextfunSignature = R_NilValue; |
827 | | |
828 | | /* |
829 | | Find the method currently being invoked and jump over the current call |
830 | | If t is R_UnboundValue then we called the current method directly |
831 | | */ |
832 | 5.40k | const void *vmax = vmaxget(); /* needed for translateChar */ |
833 | 5.40k | const char *b = NULL; |
834 | 5.40k | if (method != R_UnboundValue) { |
835 | 5.40k | if (!isString(method)) |
836 | 0 | error(_("wrong value for .Method")); |
837 | 5.40k | for(i = 0; i < LENGTH(method); i++) { |
838 | 5.40k | b = translateChar(STRING_ELT(method, i)); |
839 | 5.40k | if (strlen(b)) break; |
840 | 5.40k | } |
841 | | /* for binary operators check that the second argument's method |
842 | | is the same or absent */ |
843 | 10.8k | for(j = i; j < LENGTH(method); j++) { |
844 | 5.40k | const char *bb = translateChar(STRING_ELT(method, j)); |
845 | 5.40k | if (strlen(bb) && strcmp(b,bb)) |
846 | 0 | warning(_("Incompatible methods ignored")); |
847 | 5.40k | } |
848 | 5.40k | } |
849 | 0 | else { |
850 | 0 | b = CHAR(PRINTNAME(CAR(cptr->call))); |
851 | 0 | } |
852 | | |
853 | 5.40k | sb = translateChar(STRING_ELT(basename, 0)); |
854 | 5.40k | Rboolean foundSignature = FALSE; |
855 | 5.40k | for (j = 0; j < LENGTH(klass); j++) { |
856 | 5.40k | sk = translateChar(STRING_ELT(klass, j)); |
857 | 5.40k | if (equalS3Signature(b, sb, sk)) { /* b == sb.sk */ |
858 | 5.40k | foundSignature = TRUE; |
859 | 5.40k | break; |
860 | 5.40k | } |
861 | 5.40k | } |
862 | | |
863 | 5.40k | if (foundSignature) /* we found a match and start from there */ |
864 | 5.40k | j++; |
865 | 0 | else |
866 | 0 | j = 0; /* no match so start with the first element of .Class */ |
867 | | |
868 | | /* we need the value of i on exit from the for loop to figure out |
869 | | how many classes to drop. */ |
870 | | |
871 | 5.40k | sg = translateChar(STRING_ELT(generic, 0)); |
872 | 5.88k | for (i = j ; i < LENGTH(klass); i++) { |
873 | 909 | sk = translateChar(STRING_ELT(klass, i)); |
874 | 909 | nextfunSignature = installS3Signature(sg, sk); |
875 | 909 | nextfun = R_LookupMethod(nextfunSignature, env, callenv, defenv); |
876 | 909 | if (isFunction(nextfun)) break; |
877 | 473 | if (group != R_UnboundValue) { |
878 | | /* if not Generic.foo, look for Group.foo */ |
879 | 473 | nextfunSignature = installS3Signature(sb, sk); |
880 | 473 | nextfun = R_LookupMethod(nextfunSignature, env, callenv, defenv); |
881 | 473 | if(isFunction(nextfun)) |
882 | 0 | break; |
883 | 473 | } |
884 | 473 | if (isFunction(nextfun)) |
885 | 0 | break; |
886 | 473 | } |
887 | 5.40k | if (!isFunction(nextfun)) { |
888 | 4.97k | nextfunSignature = installS3Signature(sg, "default"); |
889 | 4.97k | nextfun = R_LookupMethod(nextfunSignature, env, callenv, defenv); |
890 | | /* If there is no default method, try the generic itself, |
891 | | provided it is primitive or a wrapper for a .Internal |
892 | | function of the same name. |
893 | | */ |
894 | 4.97k | if (!isFunction(nextfun)) { |
895 | 4.53k | t = install(sg); |
896 | 4.53k | nextfun = R_findVar(t, env); |
897 | 4.53k | if (TYPEOF(nextfun) == PROMSXP) { |
898 | 0 | PROTECT(nextfun); |
899 | 0 | nextfun = eval(nextfun, env); |
900 | 0 | UNPROTECT(1); |
901 | 0 | } |
902 | 4.53k | if (!isFunction(nextfun)) |
903 | 0 | error(_("no method to invoke")); |
904 | 4.53k | if (TYPEOF(nextfun) == CLOSXP) { |
905 | 0 | if (INTERNAL(t) != R_NilValue) |
906 | 0 | nextfun = INTERNAL(t); |
907 | 0 | else { |
908 | 0 | nextfun = getPrimitive(t); |
909 | 0 | if (nextfun == R_NilValue) |
910 | 0 | error(_("no method to invoke")); |
911 | 0 | } |
912 | 0 | } |
913 | 4.53k | } |
914 | 4.97k | } |
915 | 5.40k | PROTECT(nextfun); |
916 | 5.40k | PROTECT(s = stringSuffix(klass, i)); |
917 | 5.40k | setAttrib(s, R_PreviousSymbol, klass); |
918 | | /* It is possible that if a method was called directly that |
919 | | 'method' is unset */ |
920 | 5.40k | if (method != R_UnboundValue) { |
921 | | /* for Ops we need `method' to be a vector */ |
922 | 5.40k | PROTECT(method = duplicate(method)); |
923 | 10.8k | for(j = 0; j < LENGTH(method); j++) { |
924 | 5.40k | if (strlen(CHAR(STRING_ELT(method,j)))) |
925 | 5.40k | SET_STRING_ELT(method, j, PRINTNAME(nextfunSignature)); |
926 | 5.40k | } |
927 | 5.40k | } else |
928 | 0 | PROTECT(method = PRINTNAME(nextfunSignature)); |
929 | | |
930 | 5.40k | SEXP newvars = PROTECT(createS3Vars( |
931 | 5.40k | generic, |
932 | 5.40k | group, |
933 | 5.40k | s, |
934 | 5.40k | method, |
935 | 5.40k | callenv, |
936 | 5.40k | defenv |
937 | 5.40k | )); |
938 | | |
939 | 5.40k | SETCAR(newcall, nextfunSignature); |
940 | | |
941 | | /* applyMethod expects that the parent of the caller is the caller |
942 | | of the generic, so fixup by brute force. This should fix |
943 | | PR#15267 --pd */ |
944 | 5.40k | R_GlobalContext->sysparent = callenv; |
945 | | |
946 | 5.40k | ans = applyMethod(newcall, nextfun, matchedarg, env, newvars); |
947 | 5.40k | vmaxset(vmax); |
948 | 5.40k | UNPROTECT(8); |
949 | 5.40k | return(ans); |
950 | 5.40k | } |
951 | | |
952 | | /* primitive */ |
953 | | attribute_hidden SEXP do_unclass(SEXP call, SEXP op, SEXP args, SEXP env) |
954 | 14.1k | { |
955 | 14.1k | checkArity(op, args); |
956 | 14.1k | check1arg(args, call, "x"); |
957 | | |
958 | 14.1k | if (isObject(CAR(args))) { |
959 | 2.47k | switch(TYPEOF(CAR(args))) { |
960 | 0 | case ENVSXP: |
961 | 0 | errorcall(call, _("cannot unclass an environment")); |
962 | 0 | break; |
963 | 0 | case EXTPTRSXP: |
964 | 0 | errorcall(call, _("cannot unclass an external pointer")); |
965 | 0 | break; |
966 | 2.47k | default: |
967 | 2.47k | break; |
968 | 2.47k | } |
969 | 2.47k | if (MAYBE_REFERENCED(CAR(args))) |
970 | 1.82k | SETCAR(args, R_shallow_duplicate_attr(CAR(args))); |
971 | 2.47k | setAttrib(CAR(args), R_ClassSymbol, R_NilValue); |
972 | 2.47k | } |
973 | 14.1k | return CAR(args); |
974 | 14.1k | } |
975 | | |
976 | | /** Version of inherits() that supports S4 inheritance and implicit |
977 | | classes. The inlined inherits() does not have access to private |
978 | | entry points R_data_class() and R_data_class2(). The semantics of |
979 | | inherits2() are identical to that of the R-level inherits(), |
980 | | except there is no translation. |
981 | | */ |
982 | | |
983 | 2.75k | attribute_hidden Rboolean inherits2(SEXP x, const char *what) { |
984 | 2.75k | if (OBJECT(x)) { |
985 | 2.75k | SEXP klass; |
986 | | |
987 | 2.75k | if(IS_S4_OBJECT(x)) |
988 | 0 | PROTECT(klass = R_data_class2(x)); |
989 | 2.75k | else |
990 | 2.75k | PROTECT(klass = R_data_class(x, FALSE)); |
991 | 2.75k | int nclass = length(klass); |
992 | 2.75k | for (int i = 0; i < nclass; i++) { |
993 | 2.75k | if (!strcmp(CHAR(STRING_ELT(klass, i)), what)) { |
994 | 2.75k | UNPROTECT(1); |
995 | 2.75k | return TRUE; |
996 | 2.75k | } |
997 | 2.75k | } |
998 | 0 | UNPROTECT(1); |
999 | 0 | } |
1000 | 0 | return FALSE; |
1001 | 2.75k | } |
1002 | | |
1003 | | /* NOTE: Fast inherits(x, what) in ../include/Rinlinedfuns.h |
1004 | | * ---- ----------------- */ |
1005 | | /** C API for R inherits(x, what, which) |
1006 | | * |
1007 | | * @param x any R object |
1008 | | * @param what character vector |
1009 | | * @param which logical: "want vector result" ? |
1010 | | * |
1011 | | * @return if which is false, logical TRUE or FALSE |
1012 | | * if which is true, integer vector of length(what) .. |
1013 | | */ |
1014 | | static SEXP inherits3(SEXP x, SEXP what, SEXP which) |
1015 | 2.38M | { |
1016 | 2.38M | const void *vmax = vmaxget(); |
1017 | 2.38M | SEXP klass, rval = R_NilValue /* -Wall */; |
1018 | | |
1019 | 2.38M | if(IS_S4_OBJECT(x)) |
1020 | 0 | PROTECT(klass = R_data_class2(x)); // -> := S4_extends( "class(x)" ) |
1021 | 2.38M | else |
1022 | 2.38M | PROTECT(klass = R_data_class(x, FALSE)); |
1023 | | |
1024 | 2.38M | if(!isString(what)) |
1025 | 0 | error(_("'what' must be a character vector " |
1026 | 0 | "or an object with a nameOfClass() method")); |
1027 | 2.38M | int j, nwhat = LENGTH(what); |
1028 | | |
1029 | 2.38M | if( !isLogical(which) || (LENGTH(which) != 1) ) |
1030 | 0 | error(_("'which' must be a length 1 logical vector")); |
1031 | 2.38M | bool isvec = asRbool(which, R_NilValue); |
1032 | | |
1033 | 2.38M | if(isvec) |
1034 | 0 | PROTECT(rval = allocVector(INTSXP, nwhat)); |
1035 | | |
1036 | 3.55M | for(j = 0; j < nwhat; j++) { |
1037 | 2.38M | const char *ss = translateChar(STRING_ELT(what, j)); |
1038 | 2.38M | int i = stringPositionTr(klass, ss); |
1039 | 2.38M | if (isvec) |
1040 | 0 | INTEGER(rval)[j] = i+1; /* 0 when ss is not in klass */ |
1041 | 2.38M | else if (i >= 0) { |
1042 | 1.21M | vmaxset(vmax); |
1043 | 1.21M | UNPROTECT(1); |
1044 | 1.21M | return mkTrue(); |
1045 | 1.21M | } |
1046 | 2.38M | } |
1047 | 1.16M | vmaxset(vmax); |
1048 | 1.16M | if(!isvec) { |
1049 | 1.16M | UNPROTECT(1); |
1050 | 1.16M | return mkFalse(); |
1051 | 1.16M | } |
1052 | 0 | UNPROTECT(2); |
1053 | 0 | return rval; |
1054 | 1.16M | } |
1055 | | |
1056 | | static R_INLINE SEXP nameOfClass(SEXP what, SEXP env) |
1057 | 0 | { |
1058 | 0 | static SEXP call = NULL; |
1059 | 0 | static SEXP Xsym = NULL; |
1060 | 0 | if (call == NULL) { |
1061 | 0 | Xsym = install("X"); |
1062 | 0 | call = R_ParseString("base::nameOfClass(X)"); |
1063 | 0 | R_PreserveObject(call); |
1064 | 0 | } |
1065 | |
|
1066 | 0 | SEXP callenv = PROTECT(R_NewEnv(env, FALSE, 0)); |
1067 | 0 | defineVar(Xsym, what, callenv); |
1068 | 0 | INCREMENT_NAMED(what); |
1069 | 0 | SEXP name = eval(call, callenv); |
1070 | | /* could remove 'what' from callenv to drop REFCNT */ |
1071 | 0 | UNPROTECT(1); /* callenv */ |
1072 | |
|
1073 | 0 | return name; |
1074 | 0 | } |
1075 | | |
1076 | | attribute_hidden SEXP do_inherits(SEXP call, SEXP op, SEXP args, SEXP env) |
1077 | 2.38M | { |
1078 | 2.38M | checkArity(op, args); |
1079 | | |
1080 | 2.38M | SEXP x = CAR(args); |
1081 | 2.38M | SEXP what = CADR(args); |
1082 | 2.38M | SEXP which = CADDR(args); |
1083 | | |
1084 | 2.38M | if (OBJECT(what) && TYPEOF(what) != STRSXP) { |
1085 | 0 | SEXP name = nameOfClass(what, env); |
1086 | 0 | if (name != R_NilValue) { |
1087 | 0 | PROTECT(name); |
1088 | 0 | SEXP val = inherits3(x, name, which); |
1089 | 0 | UNPROTECT(1); /* name */ |
1090 | 0 | return val; |
1091 | 0 | } |
1092 | | /* fall through */ |
1093 | 0 | } |
1094 | | |
1095 | 2.38M | return inherits3(x, what, which); |
1096 | 2.38M | } |
1097 | | |
1098 | | |
1099 | | /* |
1100 | | ============================================================== |
1101 | | |
1102 | | code from here on down is support for the methods package |
1103 | | |
1104 | | ============================================================== |
1105 | | */ |
1106 | | |
1107 | | /** |
1108 | | * Return the 0-based index of an is() match in a vector of class-name |
1109 | | * strings terminated by an empty string. Returns -1 for no match. |
1110 | | * |
1111 | | * @param x an R object, about which we want is(x, .) information. |
1112 | | * @param valid vector of possible matches terminated by an empty string. |
1113 | | * @param rho the environment in which the class definitions exist. |
1114 | | * |
1115 | | * @return index of match or -1 for no match |
1116 | | */ |
1117 | | attribute_hidden |
1118 | | int R_check_class_and_super(SEXP x, const char **valid, SEXP rho) |
1119 | 0 | { |
1120 | 0 | if(isObject(x)) { |
1121 | 0 | int ans; |
1122 | 0 | SEXP clattr = getAttrib(x, R_ClassSymbol); |
1123 | 0 | SEXP cl = PROTECT(asChar(clattr)); |
1124 | 0 | const char *class = CHAR(cl); |
1125 | 0 | for (ans = 0; strlen(valid[ans]); ans++) |
1126 | 0 | if (!strcmp(class, valid[ans])) { |
1127 | 0 | UNPROTECT(1); /* cl */ |
1128 | 0 | return ans; |
1129 | 0 | } |
1130 | | /* if not found directly, then look for a match among the nonvirtual |
1131 | | superclasses, possibly after finding the environment 'rho' in which |
1132 | | class(x) is defined */ |
1133 | 0 | if(IS_S4_OBJECT(x)) { |
1134 | 0 | if (rho == NULL) { |
1135 | 0 | SEXP pkg = getAttrib(clattr, R_PackageSymbol); |
1136 | 0 | if (!isNull(pkg)) { |
1137 | 0 | static SEXP meth_classEnv = NULL; |
1138 | 0 | if(!meth_classEnv) |
1139 | 0 | meth_classEnv = install(".classEnv"); |
1140 | | /* FIXME: fails if 'methods' is not loaded */ |
1141 | 0 | SEXP clEnvCall = PROTECT(lang2(meth_classEnv, clattr)); |
1142 | 0 | rho = eval(clEnvCall, R_MethodsNamespace); |
1143 | 0 | UNPROTECT(1); /* clEnvCall */ |
1144 | 0 | if(!isEnvironment(rho)) |
1145 | 0 | error(_("could not find correct environment; " |
1146 | 0 | "please report!")); |
1147 | 0 | } else |
1148 | 0 | rho = R_GlobalEnv; |
1149 | 0 | } |
1150 | 0 | PROTECT(rho); |
1151 | 0 | SEXP classExts, superCl, _call; |
1152 | 0 | static SEXP s_contains = NULL, s_selectSuperCl = NULL; |
1153 | 0 | if(!s_contains) { |
1154 | 0 | s_contains = install("contains"); |
1155 | 0 | s_selectSuperCl = install(".selectSuperClasses"); |
1156 | 0 | } |
1157 | 0 | SEXP classDef = PROTECT(R_getClassDef(class)); |
1158 | 0 | PROTECT(classExts = R_do_slot(classDef, s_contains)); |
1159 | | /* .selectSuperClasses(getClassDef(class)@contains, |
1160 | | * dropVirtual = TRUE, namesOnly = TRUE, |
1161 | | * directOnly = FALSE, simpleOnly = TRUE): |
1162 | | */ |
1163 | 0 | PROTECT(_call = lang6(s_selectSuperCl, classExts, |
1164 | 0 | ScalarLogical(1), ScalarLogical(1), |
1165 | 0 | ScalarLogical(0), ScalarLogical(1))); |
1166 | 0 | superCl = eval(_call, rho); |
1167 | 0 | UNPROTECT(3); /* _call, classExts, classDef */ |
1168 | 0 | PROTECT(superCl); |
1169 | 0 | for(int i = 0; i < LENGTH(superCl); i++) { |
1170 | 0 | const char *s_class = CHAR(STRING_ELT(superCl, i)); |
1171 | 0 | for (ans = 0; strlen(valid[ans]); ans++) |
1172 | 0 | if (!strcmp(s_class, valid[ans])) { |
1173 | 0 | UNPROTECT(3); /* superCl, rho, cl */ |
1174 | 0 | return ans; |
1175 | 0 | } |
1176 | 0 | } |
1177 | 0 | UNPROTECT(2); /* superCl, rho */ |
1178 | 0 | } |
1179 | 0 | UNPROTECT(1); /* cl */ |
1180 | 0 | } // only if isObject(x) |
1181 | 0 | return -1; |
1182 | 0 | } |
1183 | | |
1184 | | /** |
1185 | | * Return the 0-based index of an is() match in a vector of class-name |
1186 | | * strings terminated by an empty string. Returns -1 for no match. |
1187 | | * Strives to find the correct environment() for is(), using .classEnv() |
1188 | | * (from \pkg{methods}). |
1189 | | * |
1190 | | * @param x an R object, about which we want is(x, .) information. |
1191 | | * @param valid vector of possible matches terminated by an empty string. |
1192 | | * |
1193 | | * @return index of match or -1 for no match |
1194 | | */ |
1195 | | int R_check_class_etc(SEXP x, const char **valid) |
1196 | 0 | { |
1197 | 0 | return R_check_class_and_super(x, valid, NULL); |
1198 | 0 | } |
1199 | | |
1200 | | /* standardGeneric: uses a pointer to R_standardGeneric, to be |
1201 | | initialized when the methods namespace is loaded, |
1202 | | via R_initMethodDispatch. |
1203 | | */ |
1204 | | static R_stdGen_ptr_t R_standardGeneric_ptr = 0; |
1205 | | static SEXP dispatchNonGeneric(SEXP name, SEXP env, SEXP fdef); |
1206 | 1.99M | #define NOT_METHODS_DISPATCH_PTR(ptr) (ptr == 0 || ptr == dispatchNonGeneric) |
1207 | | |
1208 | | static |
1209 | | R_stdGen_ptr_t R_get_standardGeneric_ptr(void) |
1210 | 1.95M | { |
1211 | 1.95M | return R_standardGeneric_ptr; |
1212 | 1.95M | } |
1213 | | |
1214 | | /* Also called from R_initMethodDispatch in methods C code, which is |
1215 | | called when the methods namespace is loaded. */ |
1216 | | R_stdGen_ptr_t R_set_standardGeneric_ptr(R_stdGen_ptr_t val, SEXP envir) |
1217 | 32 | { |
1218 | 32 | R_stdGen_ptr_t old = R_standardGeneric_ptr; |
1219 | 32 | R_standardGeneric_ptr = val; |
1220 | 32 | if(envir && !isNull(envir)) |
1221 | 32 | R_MethodsNamespace = envir; |
1222 | | /* just in case ... */ |
1223 | 32 | if(!R_MethodsNamespace) |
1224 | 0 | R_MethodsNamespace = R_GlobalEnv; |
1225 | 32 | return old; |
1226 | 32 | } |
1227 | | |
1228 | | // R's .isMethodsDispatchOn() -> do_S4on() -> |
1229 | | static SEXP R_isMethodsDispatchOn(SEXP onOff) |
1230 | 0 | { |
1231 | 0 | R_stdGen_ptr_t old = R_get_standardGeneric_ptr(); |
1232 | 0 | int ival = !NOT_METHODS_DISPATCH_PTR(old); |
1233 | 0 | if(length(onOff) > 0) { |
1234 | 0 | bool onOffValue = asRbool(onOff, R_NilValue); |
1235 | 0 | if(onOffValue == NA_INTEGER) |
1236 | 0 | error(_("'onOff' must be TRUE or FALSE")); |
1237 | 0 | else if(onOffValue == FALSE) |
1238 | 0 | R_set_standardGeneric_ptr(NULL, R_GlobalEnv); |
1239 | | // TRUE is not currently used |
1240 | 0 | else if(NOT_METHODS_DISPATCH_PTR(old)) { |
1241 | | // so not already on |
1242 | | // This may not work correctly: the default arg is incorrect. |
1243 | 0 | warning("R_isMethodsDispatchOn(TRUE) called -- may not work correctly"); |
1244 | 0 | SEXP call = PROTECT(lang1(install("initMethodDispatch"))); |
1245 | 0 | eval(call, R_MethodsNamespace); // only works with methods loaded |
1246 | 0 | UNPROTECT(1); |
1247 | 0 | } |
1248 | 0 | } |
1249 | 0 | return ScalarLogical(ival); |
1250 | 0 | } |
1251 | | |
1252 | | /* simpler version for internal use, in attrib.c and print.c */ |
1253 | | attribute_hidden |
1254 | | Rboolean isMethodsDispatchOn(void) |
1255 | 41.8k | { |
1256 | 41.8k | return !NOT_METHODS_DISPATCH_PTR(R_standardGeneric_ptr); |
1257 | 41.8k | } |
1258 | | |
1259 | | |
1260 | | /* primitive for .isMethodsDispatchOn |
1261 | | This is generally called without an arg, but is call with |
1262 | | onOff=FALSE when package methods is detached/unloaded. |
1263 | | |
1264 | | It seems it is not currently called with onOff = TRUE (and would |
1265 | | not have worked prior to 3.0.2). |
1266 | | */ |
1267 | | attribute_hidden |
1268 | | SEXP do_S4on(SEXP call, SEXP op, SEXP args, SEXP env) |
1269 | 3.32k | { |
1270 | 3.32k | if(length(args) == 0) return ScalarLogical(isMethodsDispatchOn()); |
1271 | 0 | return R_isMethodsDispatchOn(CAR(args)); |
1272 | 3.32k | } |
1273 | | |
1274 | | |
1275 | | static SEXP dispatchNonGeneric(SEXP name, SEXP env, SEXP fdef) |
1276 | 0 | { |
1277 | | /* dispatch the non-generic definition of `name'. Used to trap |
1278 | | calls to standardGeneric during the loading of the methods package */ |
1279 | 0 | SEXP e, value, rho, fun, symbol; |
1280 | 0 | RCNTXT *cptr; |
1281 | | |
1282 | | /* find a non-generic function */ |
1283 | 0 | symbol = installTrChar(asChar(name)); |
1284 | 0 | for(rho = ENCLOS(env); rho != R_EmptyEnv; |
1285 | 0 | rho = ENCLOS(rho)) { |
1286 | 0 | fun = R_findVarInFrame(rho, symbol); |
1287 | 0 | if(fun == R_UnboundValue) continue; |
1288 | 0 | switch(TYPEOF(fun)) { |
1289 | 0 | case CLOSXP: |
1290 | 0 | value = R_findVarInFrame(CLOENV(fun), R_dot_Generic); |
1291 | 0 | if(value == R_UnboundValue) break; |
1292 | 0 | case BUILTINSXP: case SPECIALSXP: |
1293 | 0 | default: |
1294 | | /* in all other cases, go on to the parent environment */ |
1295 | 0 | break; |
1296 | 0 | } |
1297 | 0 | fun = R_UnboundValue; |
1298 | 0 | } |
1299 | 0 | fun = SYMVALUE(symbol); |
1300 | 0 | if(fun == R_UnboundValue) |
1301 | 0 | error(_("unable to find a non-generic version of function \"%s\""), |
1302 | 0 | translateChar(asChar(name))); |
1303 | 0 | cptr = R_GlobalContext; |
1304 | | /* check this is the right context */ |
1305 | 0 | while (cptr != R_ToplevelContext) { |
1306 | 0 | if (cptr->callflag & CTXT_FUNCTION ) |
1307 | 0 | if (cptr->cloenv == env) |
1308 | 0 | break; |
1309 | 0 | cptr = cptr->nextcontext; |
1310 | 0 | } |
1311 | |
|
1312 | 0 | PROTECT(e = shallow_duplicate(R_syscall(0, cptr))); |
1313 | 0 | SETCAR(e, fun); |
1314 | | /* evaluate a call the non-generic with the same arguments and from |
1315 | | the same environment as the call to the generic version */ |
1316 | 0 | value = eval(e, cptr->sysparent); |
1317 | 0 | UNPROTECT(1); |
1318 | 0 | return value; |
1319 | 0 | } |
1320 | | |
1321 | | |
1322 | | static SEXP get_this_generic(SEXP args); |
1323 | | |
1324 | | attribute_hidden SEXP do_standardGeneric(SEXP call, SEXP op, SEXP args, SEXP env) |
1325 | 0 | { |
1326 | 0 | SEXP arg, value, fdef; R_stdGen_ptr_t ptr = R_get_standardGeneric_ptr(); |
1327 | |
|
1328 | 0 | checkArity(op, args); /* set to -1 */ |
1329 | 0 | check1arg(args, call, "f"); |
1330 | |
|
1331 | 0 | if(!ptr) { |
1332 | 0 | warningcall(call, |
1333 | 0 | _("'standardGeneric' called without 'methods' dispatch enabled (will be ignored)")); |
1334 | 0 | R_set_standardGeneric_ptr(dispatchNonGeneric, NULL); |
1335 | 0 | ptr = R_get_standardGeneric_ptr(); |
1336 | 0 | } |
1337 | |
|
1338 | 0 | arg = CAR(args); |
1339 | 0 | if(!isValidStringF(arg)) |
1340 | 0 | errorcall(call, |
1341 | 0 | _("argument to 'standardGeneric' must be a non-empty character string")); |
1342 | |
|
1343 | 0 | PROTECT(fdef = get_this_generic(args)); |
1344 | |
|
1345 | 0 | if(isNull(fdef)) |
1346 | 0 | error(_("call to standardGeneric(\"%s\") apparently not from the body of that generic function"), translateChar(STRING_ELT(arg, 0))); |
1347 | |
|
1348 | 0 | value = (*ptr)(arg, env, fdef); |
1349 | |
|
1350 | 0 | UNPROTECT(1); |
1351 | 0 | return value; |
1352 | 0 | } |
1353 | | |
1354 | | static int maxMethodsOffset = 0, curMaxOffset; |
1355 | | static Rboolean allowPrimitiveMethods = TRUE; |
1356 | | typedef enum {NO_METHODS, NEEDS_RESET, HAS_METHODS, SUPPRESSED} prim_methods_t; |
1357 | | |
1358 | | static prim_methods_t *prim_methods; |
1359 | | static SEXP *prim_generics; |
1360 | | static SEXP *prim_mlist; |
1361 | 64 | #define DEFAULT_N_PRIM_METHODS 100 |
1362 | | |
1363 | | // Called from methods package, ../library/methods/src/methods_list_dispatch.c |
1364 | | SEXP R_set_prim_method(SEXP fname, SEXP op, SEXP code_vec, SEXP fundef, |
1365 | | SEXP mlist) |
1366 | 32 | { |
1367 | 32 | const char *code_string; |
1368 | 32 | const void *vmax = vmaxget(); |
1369 | 32 | if(!isValidString(code_vec)) |
1370 | 0 | error(_("argument '%s' must be a character string"), "code"); |
1371 | 32 | code_string = translateChar(asChar(code_vec)); |
1372 | | /* with a NULL op, turns all primitive matching off or on (used to avoid possible infinite |
1373 | | recursion in methods computations*/ |
1374 | 32 | if(op == R_NilValue) { |
1375 | 0 | SEXP value = allowPrimitiveMethods ? mkTrue() : mkFalse(); |
1376 | 0 | switch(code_string[0]) { |
1377 | 0 | case 'c': case 'C':/* clear */ |
1378 | 0 | allowPrimitiveMethods = FALSE; break; |
1379 | 0 | case 's': case 'S': /* set */ |
1380 | 0 | allowPrimitiveMethods = TRUE; break; |
1381 | 0 | default: /* just report the current state */ |
1382 | 0 | break; |
1383 | 0 | } |
1384 | 0 | return value; |
1385 | 0 | } |
1386 | 32 | if (!isPrimitive(op)) { |
1387 | 0 | SEXP internal = R_do_slot(op, install("internal")); |
1388 | 0 | op = INTERNAL(installTrChar(asChar(internal))); |
1389 | 0 | if (op == R_NilValue) { |
1390 | 0 | error("'internal' slot does not name an internal function: %s", |
1391 | 0 | CHAR(asChar(internal))); |
1392 | 0 | } |
1393 | 0 | } |
1394 | 32 | do_set_prim_method(op, code_string, fundef, mlist); |
1395 | 32 | vmaxset(vmax); |
1396 | 32 | return fname; |
1397 | 32 | } |
1398 | | |
1399 | | SEXP R_primitive_methods(SEXP op) |
1400 | 0 | { |
1401 | 0 | int offset = PRIMOFFSET(op); |
1402 | 0 | if(offset < 0 || offset > curMaxOffset) |
1403 | 0 | return R_NilValue; |
1404 | 0 | else { |
1405 | 0 | SEXP value = prim_mlist[offset]; |
1406 | 0 | return value ? value : R_NilValue; |
1407 | 0 | } |
1408 | 0 | } |
1409 | | |
1410 | | SEXP R_primitive_generic(SEXP op) |
1411 | 0 | { |
1412 | 0 | int offset = PRIMOFFSET(op); |
1413 | 0 | if(offset < 0 || offset > curMaxOffset) |
1414 | 0 | return R_NilValue; |
1415 | 0 | else { |
1416 | 0 | SEXP value = prim_generics[offset]; |
1417 | 0 | return value ? value : R_NilValue; |
1418 | 0 | } |
1419 | 0 | } |
1420 | | |
1421 | | // used in the methods package, but also here |
1422 | | SEXP do_set_prim_method(SEXP op, const char *code_string, SEXP fundef, |
1423 | | SEXP mlist) |
1424 | 32 | { |
1425 | 32 | int offset = 0; |
1426 | 32 | prim_methods_t code = NO_METHODS; /* -Wall */ |
1427 | 32 | SEXP value; |
1428 | 32 | Rboolean errorcase = FALSE; |
1429 | 32 | switch(code_string[0]) { |
1430 | 0 | case 'c': /* clear */ |
1431 | 0 | code = NO_METHODS; break; |
1432 | 32 | case 'r': /* reset */ |
1433 | 32 | code = NEEDS_RESET; break; |
1434 | 0 | case 's': /* set or suppress */ |
1435 | 0 | switch(code_string[1]) { |
1436 | 0 | case 'e': code = HAS_METHODS; break; |
1437 | 0 | case 'u': code = SUPPRESSED; break; |
1438 | 0 | default: errorcase = TRUE; |
1439 | 0 | } |
1440 | 0 | break; |
1441 | 0 | default: |
1442 | 0 | errorcase = TRUE; |
1443 | 32 | } |
1444 | 32 | if(errorcase) { |
1445 | 0 | error(_("invalid primitive methods code (\"%s\"): should be \"clear\", \"reset\", \"set\", or \"suppress\""), code_string); |
1446 | 0 | return R_NilValue; |
1447 | 0 | } |
1448 | 32 | switch(TYPEOF(op)) { |
1449 | 32 | case BUILTINSXP: case SPECIALSXP: |
1450 | 32 | offset = PRIMOFFSET(op); |
1451 | 32 | break; |
1452 | 0 | default: |
1453 | 0 | error(_("invalid object: must be a primitive function")); |
1454 | 32 | } |
1455 | 32 | if(offset >= maxMethodsOffset) { |
1456 | 32 | int n; |
1457 | 32 | n = offset + 1; |
1458 | 32 | if(n < DEFAULT_N_PRIM_METHODS) |
1459 | 32 | n = DEFAULT_N_PRIM_METHODS; |
1460 | 32 | if(n < 2*maxMethodsOffset) |
1461 | 0 | n = 2 * maxMethodsOffset; |
1462 | 32 | if(prim_methods) { |
1463 | 0 | int i; |
1464 | |
|
1465 | 0 | prim_methods = R_Realloc(prim_methods, n, prim_methods_t); |
1466 | 0 | prim_generics = R_Realloc(prim_generics, n, SEXP); |
1467 | 0 | prim_mlist = R_Realloc(prim_mlist, n, SEXP); |
1468 | | |
1469 | | /* R_Realloc does not clear the added memory, hence: */ |
1470 | 0 | for (i = maxMethodsOffset ; i < n ; i++) { |
1471 | 0 | prim_methods[i] = NO_METHODS; |
1472 | 0 | prim_generics[i] = NULL; |
1473 | 0 | prim_mlist[i] = NULL; |
1474 | 0 | } |
1475 | 0 | } |
1476 | 32 | else { |
1477 | 32 | prim_methods = R_Calloc(n, prim_methods_t); |
1478 | 32 | prim_generics = R_Calloc(n, SEXP); |
1479 | 32 | prim_mlist = R_Calloc(n, SEXP); |
1480 | 32 | } |
1481 | 32 | maxMethodsOffset = n; |
1482 | 32 | } |
1483 | 32 | if(offset > curMaxOffset) |
1484 | 32 | curMaxOffset = offset; |
1485 | 32 | prim_methods[offset] = code; |
1486 | | /* store a preserved pointer to the generic function if there is not |
1487 | | one there currently. Unpreserve it if no more methods, but don't |
1488 | | replace it otherwise: the generic definition is not allowed to |
1489 | | change while it's still defined! (the stored methods list can, |
1490 | | however) */ |
1491 | 32 | value = prim_generics[offset]; |
1492 | 32 | if(code == SUPPRESSED) {} /* leave the structure alone */ |
1493 | 32 | else if(code == NO_METHODS && prim_generics[offset]) { |
1494 | 0 | R_ReleaseObject(prim_generics[offset]); |
1495 | 0 | prim_generics[offset] = 0; |
1496 | 0 | prim_mlist[offset] = 0; |
1497 | 0 | } |
1498 | 32 | else if(fundef && !isNull(fundef) && !prim_generics[offset]) { |
1499 | 32 | if(TYPEOF(fundef) != CLOSXP) |
1500 | 0 | error(_("the formal definition of a primitive generic must be a function object (got type '%s')"), |
1501 | 0 | R_typeToChar(fundef)); |
1502 | 32 | R_PreserveObject(fundef); |
1503 | 32 | prim_generics[offset] = fundef; |
1504 | 32 | } |
1505 | 32 | if(code == HAS_METHODS) { |
1506 | 0 | if(!mlist || isNull(mlist)) { |
1507 | | /* turning methods back on after a SUPPRESSED */ |
1508 | 0 | } else { |
1509 | 0 | if(prim_mlist[offset]) |
1510 | 0 | R_ReleaseObject(prim_mlist[offset]); |
1511 | 0 | R_PreserveObject(mlist); |
1512 | 0 | prim_mlist[offset] = mlist; |
1513 | 0 | } |
1514 | 0 | } |
1515 | 32 | return value; |
1516 | 32 | } |
1517 | | |
1518 | | static SEXP get_primitive_methods(SEXP op, SEXP rho) |
1519 | 0 | { |
1520 | 0 | SEXP f, e, val; |
1521 | 0 | int nprotect = 0; |
1522 | 0 | f = PROTECT(allocVector(STRSXP, 1)); nprotect++; |
1523 | 0 | SET_STRING_ELT(f, 0, mkChar(PRIMNAME(op))); |
1524 | 0 | PROTECT(e = allocVector(LANGSXP, 2)); nprotect++; |
1525 | 0 | SETCAR(e, install("getGeneric")); |
1526 | 0 | val = CDR(e); SETCAR(val, f); |
1527 | 0 | val = eval(e, rho); |
1528 | | /* a rough sanity check that this looks like a generic function */ |
1529 | 0 | if(TYPEOF(val) != CLOSXP || !IS_S4_OBJECT(val)) |
1530 | 0 | error(_("object returned as generic function \"%s\" does not appear to be one"), PRIMNAME(op)); |
1531 | 0 | UNPROTECT(nprotect); |
1532 | 0 | return CLOENV(val); |
1533 | 0 | } |
1534 | | |
1535 | | |
1536 | | /* get the generic function, defined to be the function definition for |
1537 | | the call to standardGeneric(), or for primitives, passed as the second |
1538 | | argument to standardGeneric. |
1539 | | */ |
1540 | | static SEXP get_this_generic(SEXP args) |
1541 | 0 | { |
1542 | 0 | static SEXP gen_name = NULL; |
1543 | 0 | RCNTXT *cptr; |
1544 | 0 | SEXP fname; |
1545 | | |
1546 | | /* a second argument to the call, if any, is taken as the function */ |
1547 | 0 | if(CDR(args) != R_NilValue) |
1548 | 0 | return CAR(CDR(args)); |
1549 | 0 | if(!gen_name) |
1550 | 0 | gen_name = install("generic"); |
1551 | 0 | fname = STRING_ELT(CAR(args), 0); /* type and length checked by caller */ |
1552 | | |
1553 | | /* check for a matching "generic" slot */ |
1554 | 0 | for(cptr = R_GlobalContext; cptr != NULL; cptr = cptr->nextcontext) |
1555 | 0 | if((cptr->callflag & CTXT_FUNCTION) && isObject(cptr->callfun)) { |
1556 | 0 | SEXP generic = getAttrib(cptr->callfun, gen_name); |
1557 | 0 | if(isValidString(generic) && Seql(fname, STRING_ELT(generic, 0))) |
1558 | | /* not duplicating/marking immutable, used read-only */ |
1559 | 0 | return cptr->callfun; |
1560 | 0 | } |
1561 | 0 | return R_NilValue; |
1562 | 0 | } |
1563 | | |
1564 | | /* Could there be methods for this op? Checks |
1565 | | only whether methods are currently being dispatched and, if so, |
1566 | | whether methods are currently defined for this op. */ |
1567 | | attribute_hidden |
1568 | | Rboolean R_has_methods(SEXP op) |
1569 | 1.95M | { |
1570 | 1.95M | R_stdGen_ptr_t ptr = R_get_standardGeneric_ptr(); int offset; |
1571 | 1.95M | if(NOT_METHODS_DISPATCH_PTR(ptr)) |
1572 | 2.97k | return(FALSE); |
1573 | 1.94M | if(!op || TYPEOF(op) == CLOSXP) /* except for primitives, just test for the package */ |
1574 | 0 | return(TRUE); |
1575 | 1.94M | if(!allowPrimitiveMethods) /* all primitives turned off by a call to R_set_prim */ |
1576 | 0 | return FALSE; |
1577 | 1.94M | offset = PRIMOFFSET(op); |
1578 | 1.94M | if(offset > curMaxOffset || prim_methods[offset] == NO_METHODS |
1579 | 0 | || prim_methods[offset] == SUPPRESSED) |
1580 | 1.94M | return(FALSE); |
1581 | 0 | return(TRUE); |
1582 | 1.94M | } |
1583 | | |
1584 | | static SEXP deferred_default_object; |
1585 | | |
1586 | | SEXP R_deferred_default_method(void) |
1587 | 0 | { |
1588 | 0 | if(!deferred_default_object) |
1589 | 0 | deferred_default_object = install("__Deferred_Default_Marker__"); |
1590 | 0 | return(deferred_default_object); |
1591 | 0 | } |
1592 | | |
1593 | | |
1594 | | static R_stdGen_ptr_t quick_method_check_ptr = NULL; |
1595 | | void R_set_quick_method_check(R_stdGen_ptr_t value) |
1596 | 32 | { |
1597 | 32 | quick_method_check_ptr = value; |
1598 | 32 | } |
1599 | | |
1600 | | /* try to dispatch the formal method for this primitive op, by calling |
1601 | | the stored generic function corresponding to the op. Requires that |
1602 | | the methods be set up to return a special object rather than trying |
1603 | | to evaluate the default (which would get us into a loop). */ |
1604 | | |
1605 | | /* called from DispatchOrEval, DispatchGroup, do_matprod |
1606 | | When called from the first the arguments have been enclosed in |
1607 | | promises, but not from the other two: there all the arguments have |
1608 | | already been evaluated. |
1609 | | */ |
1610 | | attribute_hidden SEXP |
1611 | | R_possible_dispatch(SEXP call, SEXP op, SEXP args, SEXP rho, |
1612 | | Rboolean promisedArgs) |
1613 | 0 | { |
1614 | 0 | SEXP fundef, value, mlist=R_NilValue, s, a, b, suppliedvars; |
1615 | 0 | int offset; |
1616 | 0 | prim_methods_t current; |
1617 | 0 | offset = PRIMOFFSET(op); |
1618 | 0 | if(offset < 0 || offset > curMaxOffset) |
1619 | 0 | error(_("invalid primitive operation given for dispatch")); |
1620 | 0 | current = prim_methods[offset]; |
1621 | 0 | if(current == NO_METHODS || current == SUPPRESSED) |
1622 | 0 | return(NULL); |
1623 | | /* check that the methods for this function have been set */ |
1624 | 0 | if(current == NEEDS_RESET) { |
1625 | | /* get the methods and store them in the in-core primitive |
1626 | | method table. The entries will be preserved via |
1627 | | R_preserveobject, so later we can just grab mlist from |
1628 | | prim_mlist */ |
1629 | 0 | do_set_prim_method(op, "suppressed", R_NilValue, mlist); |
1630 | 0 | PROTECT(mlist = get_primitive_methods(op, rho)); |
1631 | 0 | do_set_prim_method(op, "set", R_NilValue, mlist); |
1632 | 0 | current = prim_methods[offset]; /* as revised by do_set_prim_method */ |
1633 | 0 | UNPROTECT(1); |
1634 | 0 | } |
1635 | 0 | mlist = prim_mlist[offset]; |
1636 | 0 | if(mlist && !isNull(mlist) |
1637 | 0 | && quick_method_check_ptr) { |
1638 | 0 | value = (*quick_method_check_ptr)(args, mlist, op); |
1639 | 0 | if(isPrimitive(value)) |
1640 | 0 | return(NULL); |
1641 | 0 | if(isFunction(value)) { |
1642 | 0 | if (inherits(value, "internalDispatchMethod")) { |
1643 | 0 | return(NULL); |
1644 | 0 | } |
1645 | 0 | PROTECT(suppliedvars = list1(mkString(PRIMNAME(op)))); |
1646 | 0 | SET_TAG(suppliedvars, R_dot_Generic); |
1647 | | /* found a method, call it with promised args */ |
1648 | 0 | if(!promisedArgs) { |
1649 | 0 | PROTECT(s = promiseArgs(CDR(call), rho)); |
1650 | 0 | if (length(s) != length(args)) error(_("dispatch error")); |
1651 | 0 | for (a = args, b = s; a != R_NilValue; a = CDR(a), b = CDR(b)) |
1652 | 0 | IF_PROMSXP_SET_PRVALUE(CAR(b), CAR(a)); |
1653 | 0 | value = applyClosure(call, value, s, rho, suppliedvars, TRUE); |
1654 | 0 | UNPROTECT(2); |
1655 | 0 | return value; |
1656 | 0 | } else { |
1657 | | /* INC/DEC of REFCNT needed for non-tracking args */ |
1658 | 0 | for (SEXP a = args; a != R_NilValue; a = CDR(a)) |
1659 | 0 | INCREMENT_REFCNT(CAR(a)); |
1660 | 0 | value = applyClosure(call, value, args, rho, |
1661 | 0 | suppliedvars, FALSE); |
1662 | 0 | for (SEXP a = args; a != R_NilValue; a = CDR(a)) |
1663 | 0 | DECREMENT_REFCNT(CAR(a)); |
1664 | 0 | UNPROTECT(1); |
1665 | 0 | return value; |
1666 | 0 | } |
1667 | 0 | } |
1668 | | /* else, need to perform full method search */ |
1669 | 0 | } |
1670 | 0 | fundef = prim_generics[offset]; |
1671 | 0 | if(!fundef || TYPEOF(fundef) != CLOSXP) |
1672 | 0 | error(_("primitive function \"%s\" has been set for methods but no generic function supplied"), |
1673 | 0 | PRIMNAME(op)); |
1674 | | /* To do: arrange for the setting to be restored in case of an |
1675 | | error in method search */ |
1676 | 0 | if(!promisedArgs) { |
1677 | 0 | PROTECT(s = promiseArgs(CDR(call), rho)); |
1678 | 0 | if (length(s) != length(args)) error(_("dispatch error")); |
1679 | 0 | for (a = args, b = s; a != R_NilValue; a = CDR(a), b = CDR(b)) |
1680 | 0 | IF_PROMSXP_SET_PRVALUE(CAR(b), CAR(a)); |
1681 | 0 | value = applyClosure(call, fundef, s, rho, R_NilValue, TRUE); |
1682 | 0 | UNPROTECT(1); |
1683 | 0 | } else { |
1684 | | /* INC/DEC of REFCNT needed for non-tracking args */ |
1685 | 0 | for (SEXP a = args; a != R_NilValue; a = CDR(a)) |
1686 | 0 | INCREMENT_REFCNT(CAR(a)); |
1687 | 0 | value = applyClosure(call, fundef, args, rho, R_NilValue, FALSE); |
1688 | 0 | for (SEXP a = args; a != R_NilValue; a = CDR(a)) |
1689 | 0 | DECREMENT_REFCNT(CAR(a)); |
1690 | 0 | } |
1691 | 0 | prim_methods[offset] = current; |
1692 | 0 | if(value == deferred_default_object) |
1693 | 0 | return NULL; |
1694 | 0 | else |
1695 | 0 | return value; |
1696 | 0 | } |
1697 | | |
1698 | | SEXP R_do_MAKE_CLASS(const char *what) |
1699 | 0 | { |
1700 | 0 | static SEXP s_getClass = NULL; |
1701 | 0 | SEXP e, call; |
1702 | 0 | if(!what) |
1703 | 0 | error(_("C level MAKE_CLASS macro called with NULL string pointer")); |
1704 | 0 | if(!s_getClass) s_getClass = install("getClass"); |
1705 | 0 | PROTECT(call = allocVector(LANGSXP, 2)); |
1706 | 0 | SETCAR(call, s_getClass); |
1707 | 0 | SETCAR(CDR(call), mkString(what)); |
1708 | 0 | e = eval(call, R_MethodsNamespace); |
1709 | 0 | UNPROTECT(1); |
1710 | 0 | return(e); |
1711 | 0 | } |
1712 | | |
1713 | | // similar, but gives NULL instead of an error for a non-existing class |
1714 | | // and 'what' is never checked |
1715 | | attribute_hidden SEXP R_getClassDef_R(SEXP what) |
1716 | 0 | { |
1717 | 0 | static SEXP s_getClassDef = NULL; |
1718 | 0 | if(!s_getClassDef) s_getClassDef = install("getClassDef"); |
1719 | 0 | if(!isMethodsDispatchOn()) error(_("'methods' package not yet loaded")); |
1720 | 0 | SEXP call = PROTECT(lang2(s_getClassDef, what)); |
1721 | 0 | SEXP e = eval(call, R_MethodsNamespace); |
1722 | 0 | UNPROTECT(1); |
1723 | 0 | return(e); |
1724 | 0 | } |
1725 | | |
1726 | | SEXP R_getClassDef(const char *what) |
1727 | 0 | { |
1728 | 0 | if(!what) |
1729 | 0 | error(_("R_getClassDef(.) called with NULL string pointer")); |
1730 | 0 | SEXP s = PROTECT(mkString(what)); |
1731 | 0 | SEXP ans = R_getClassDef_R(s); |
1732 | 0 | UNPROTECT(1); /* s */ |
1733 | 0 | return ans; |
1734 | 0 | } |
1735 | | |
1736 | | attribute_hidden Rboolean R_isVirtualClass(SEXP class_def, SEXP env) |
1737 | 0 | { |
1738 | 0 | if(!isMethodsDispatchOn()) return(FALSE); |
1739 | 0 | static SEXP isVCl_sym = NULL; |
1740 | 0 | if(!isVCl_sym) isVCl_sym = install("isVirtualClass"); |
1741 | 0 | SEXP call = PROTECT(lang2(isVCl_sym, class_def)); |
1742 | 0 | SEXP e = PROTECT(eval(call, env)); |
1743 | | // return(LOGICAL(e)[0]); |
1744 | | // more cautious: |
1745 | 0 | Rboolean ans = (asLogical(e) == TRUE); |
1746 | 0 | UNPROTECT(2); /* call, e */ |
1747 | 0 | return ans; |
1748 | 0 | } |
1749 | | |
1750 | | attribute_hidden Rboolean R_extends(SEXP class1, SEXP class2, SEXP env) |
1751 | 0 | { |
1752 | 0 | if(!isMethodsDispatchOn()) return(FALSE); |
1753 | 0 | static SEXP extends_sym = NULL; |
1754 | 0 | if(!extends_sym) extends_sym = install("extends"); |
1755 | 0 | SEXP call = PROTECT(lang3(extends_sym, class1, class2)); |
1756 | 0 | SEXP e = PROTECT(eval(call, env)); |
1757 | | // return(LOGICAL(e)[0]); |
1758 | | // more cautious: |
1759 | 0 | Rboolean ans = (asLogical(e) == TRUE); |
1760 | 0 | UNPROTECT(2); /* call, e */ |
1761 | 0 | return ans; |
1762 | 0 | } |
1763 | | |
1764 | | /* in Rinternals.h */ |
1765 | | SEXP R_do_new_object(SEXP class_def) |
1766 | 0 | { |
1767 | 0 | static SEXP s_virtual = NULL, s_prototype, s_className; |
1768 | 0 | SEXP e, value; |
1769 | 0 | const void *vmax = vmaxget(); |
1770 | 0 | if(!s_virtual) { |
1771 | 0 | s_virtual = install("virtual"); |
1772 | 0 | s_prototype = install("prototype"); |
1773 | 0 | s_className = install("className"); |
1774 | 0 | } |
1775 | 0 | if(!class_def) |
1776 | 0 | error(_("C level NEW macro called with null class definition pointer")); |
1777 | 0 | e = R_do_slot(class_def, s_virtual); |
1778 | 0 | if(asLogical(e) != 0) { /* includes NA, TRUE, or anything other than FALSE */ |
1779 | 0 | e = R_do_slot(class_def, s_className); |
1780 | 0 | error(_("trying to generate an object from a virtual class (\"%s\")"), |
1781 | 0 | translateChar(asChar(e))); |
1782 | 0 | } |
1783 | 0 | PROTECT(e = R_do_slot(class_def, s_className)); |
1784 | 0 | PROTECT(value = duplicate(R_do_slot(class_def, s_prototype))); |
1785 | 0 | bool xDataType = TYPEOF(value) == ENVSXP || TYPEOF(value) == SYMSXP || |
1786 | 0 | TYPEOF(value) == EXTPTRSXP; |
1787 | 0 | if((TYPEOF(value) == OBJSXP || getAttrib(e, R_PackageSymbol) != R_NilValue) && |
1788 | 0 | !xDataType) |
1789 | 0 | { |
1790 | 0 | setAttrib(value, R_ClassSymbol, e); |
1791 | 0 | SET_S4_OBJECT(value); |
1792 | 0 | } |
1793 | 0 | UNPROTECT(2); /* value, e */ |
1794 | 0 | vmaxset(vmax); |
1795 | 0 | return value; |
1796 | 0 | } |
1797 | | |
1798 | | attribute_hidden Rboolean R_seemsOldStyleS4Object(SEXP object) |
1799 | 42 | { |
1800 | 42 | SEXP klass; |
1801 | 42 | if(!isObject(object) || IS_S4_OBJECT(object)) return FALSE; |
1802 | | /* We want to know about S4SXPs with no S4 bit */ |
1803 | | /* if(TYPEOF(object) == S4SXP) return FALSE; */ |
1804 | 0 | klass = getAttrib(object, R_ClassSymbol); |
1805 | 0 | return (klass != R_NilValue && LENGTH(klass) == 1 && |
1806 | 0 | getAttrib(klass, R_PackageSymbol) != R_NilValue) ? TRUE: FALSE; |
1807 | 42 | } |
1808 | | |
1809 | | // a bare object of type "object" (OBJSXP without the S4 bit), as used e.g. by S7 |
1810 | | attribute_hidden SEXP do_objsxp(SEXP call, SEXP op, SEXP args, SEXP env) |
1811 | 0 | { |
1812 | 0 | checkArity(op, args); |
1813 | 0 | return R_allocObject(); |
1814 | 0 | } |
1815 | | |
1816 | | attribute_hidden SEXP do_setS4Object(SEXP call, SEXP op, SEXP args, SEXP env) |
1817 | 0 | { |
1818 | 0 | checkArity(op, args); |
1819 | 0 | SEXP object = CAR(args); |
1820 | 0 | Rboolean flag = asRbool(CADR(args), call); |
1821 | 0 | int complete = asInteger(CADDR(args)); |
1822 | 0 | if(length(CADR(args)) != 1 || flag == NA_INTEGER) |
1823 | 0 | error("invalid '%s' argument", "flag"); |
1824 | 0 | if(complete == NA_INTEGER) |
1825 | 0 | error("invalid '%s' argument", "complete"); |
1826 | 0 | if(flag == IS_S4_OBJECT(object)) |
1827 | 0 | return object; |
1828 | 0 | else |
1829 | 0 | return asS4(object, flag, complete); |
1830 | 0 | } |
1831 | | |
1832 | | #ifdef UNUSED |
1833 | | SEXP R_get_primname(SEXP object) |
1834 | | { |
1835 | | SEXP f; |
1836 | | if(TYPEOF(object) != BUILTINSXP && TYPEOF(object) != SPECIALSXP) |
1837 | | error("'R_get_primname' called on a non-primitive"); |
1838 | | PROTECT(f = allocVector(STRSXP, 1)); |
1839 | | SET_STRING_ELT(f, 0, mkChar(PRIMNAME(object))); |
1840 | | UNPROTECT(1); |
1841 | | return f; |
1842 | | } |
1843 | | #endif |
1844 | | |
1845 | | // in Rinternals.h |
1846 | | Rboolean isS4(SEXP s) |
1847 | 6.53k | { |
1848 | 6.53k | return IS_S4_OBJECT(s); |
1849 | 6.53k | } |
1850 | | |
1851 | | // in Rinternals.h |
1852 | | SEXP asS4(SEXP s, Rboolean flag, int complete) |
1853 | 0 | { |
1854 | 0 | if(flag == IS_S4_OBJECT(s)) |
1855 | 0 | return s; |
1856 | 0 | PROTECT(s); |
1857 | 0 | if(MAYBE_SHARED(s)) { |
1858 | 0 | s = shallow_duplicate(s); |
1859 | 0 | UNPROTECT(1); |
1860 | 0 | PROTECT(s); |
1861 | 0 | } |
1862 | 0 | if(flag) SET_S4_OBJECT(s); |
1863 | 0 | else { |
1864 | 0 | if(complete) { |
1865 | 0 | SEXP value; |
1866 | | /* TENTATIVE: how much does this change? */ |
1867 | 0 | if((value = R_getS4DataSlot(s, ANYSXP)) |
1868 | 0 | != R_NilValue && !IS_S4_OBJECT(value)) { |
1869 | 0 | UNPROTECT(1); |
1870 | 0 | return value; |
1871 | 0 | } |
1872 | | /* else no plausible S3 object*/ |
1873 | 0 | else if(complete == 1) /* ordinary case (2, for conditional) */ |
1874 | 0 | error(_("object of class \"%s\" does not correspond to a valid S3 object"), |
1875 | 0 | CHAR(STRING_ELT(R_data_class(s, FALSE), 0))); |
1876 | 0 | else { |
1877 | 0 | UNPROTECT(1); |
1878 | 0 | return s; /* unchanged */ |
1879 | 0 | } |
1880 | 0 | } |
1881 | 0 | UNSET_S4_OBJECT(s); |
1882 | 0 | } |
1883 | 0 | UNPROTECT(1); |
1884 | 0 | return s; |
1885 | 0 | } |