| 2 |
r |
1 |
/*
|
| 526 |
maechler |
2 |
* R : A Computer Language for Statistical Data Analysis
|
| 2 |
r |
3 |
* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka
|
| 24516 |
ripley |
4 |
* Copyright (C) 2002-3 The R Foundation
|
| 2 |
r |
5 |
*
|
|
|
6 |
* This program is free software; you can redistribute it and/or modify
|
|
|
7 |
* it under the terms of the GNU General Public License as published by
|
| 21352 |
maechler |
8 |
* the Free Software Foundation; either version 2, or (at your option)
|
|
|
9 |
* any later version.
|
| 2 |
r |
10 |
*
|
| 21812 |
maechler |
11 |
* This program is distributed in the hope that it will be useful,
|
|
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
14 |
* GNU General Public License for more details.
|
| 2 |
r |
15 |
*
|
| 21352 |
maechler |
16 |
* A copy of the GNU General Public License is available via WWW at
|
|
|
17 |
* http://www.gnu.org/copyleft/gpl.html. You can also obtain it by
|
|
|
18 |
* writing to the Free Software Foundation, Inc., 59 Temple Place,
|
|
|
19 |
* Suite 330, Boston, MA 02111-1307 USA.
|
| 2 |
r |
20 |
*/
|
|
|
21 |
|
| 1839 |
ihaka |
22 |
/* This module contains support for S-style generic */
|
|
|
23 |
/* functions and "class" support. Gag, barf ... */
|
| 2 |
r |
24 |
|
| 5187 |
hornik |
25 |
#ifdef HAVE_CONFIG_H
|
| 7701 |
hornik |
26 |
#include <config.h>
|
| 5187 |
hornik |
27 |
#endif
|
|
|
28 |
|
| 2 |
r |
29 |
#include "Defn.h"
|
| 18397 |
ripley |
30 |
#include <R_ext/RS.h> /* for Calloc, Realloc */
|
| 2 |
r |
31 |
|
|
|
32 |
static SEXP GetObject(RCNTXT *cptr)
|
|
|
33 |
{
|
| 8130 |
pd |
34 |
SEXP s, sysp, b, formals, funcall, tag;
|
|
|
35 |
sysp = R_GlobalContext->sysparent;
|
|
|
36 |
|
|
|
37 |
PROTECT(funcall = R_syscall(0, cptr));
|
|
|
38 |
|
|
|
39 |
if ( TYPEOF(CAR(funcall)) == SYMSXP )
|
|
|
40 |
PROTECT(b = findFun(CAR(funcall), sysp));
|
|
|
41 |
else
|
|
|
42 |
PROTECT(b = eval(CAR(funcall), sysp));
|
| 23840 |
luke |
43 |
/**** use R_sysfunction here insteas */
|
|
|
44 |
if (TYPEOF(b) != CLOSXP) error("non-closure generic function");
|
| 8130 |
pd |
45 |
formals = FORMALS(b);
|
|
|
46 |
|
|
|
47 |
tag = TAG(formals);
|
|
|
48 |
if (tag != R_NilValue && tag != R_DotsSymbol) {
|
|
|
49 |
s = R_NilValue;
|
|
|
50 |
/** exact matches **/
|
| 21352 |
maechler |
51 |
for (b = cptr->promargs ; b != R_NilValue ; b = CDR(b))
|
|
|
52 |
if (TAG(b) != R_NilValue && pmatch(tag, TAG(b), 1)) {
|
|
|
53 |
if ( s != R_NilValue)
|
| 8130 |
pd |
54 |
error("formal argument \"%s\" matched by multiple actual arguments", tag);
|
| 21352 |
maechler |
55 |
else
|
| 8130 |
pd |
56 |
s = CAR(b);
|
|
|
57 |
}
|
|
|
58 |
|
|
|
59 |
if ( s == R_NilValue )
|
|
|
60 |
/** partial matches **/
|
| 21352 |
maechler |
61 |
for (b = cptr->promargs ; b != R_NilValue ; b = CDR(b))
|
|
|
62 |
if (TAG(b) != R_NilValue && pmatch(tag, TAG(b), 0)) {
|
| 8130 |
pd |
63 |
if ( s != R_NilValue)
|
|
|
64 |
error("formal argument \"%s\" matched by multiple actual arguments", tag);
|
| 21352 |
maechler |
65 |
else
|
| 8130 |
pd |
66 |
s = CAR(b);
|
|
|
67 |
}
|
|
|
68 |
if ( s == R_NilValue )
|
|
|
69 |
/** first untagged argument **/
|
| 21352 |
maechler |
70 |
for (b = cptr->promargs ; b != R_NilValue ; b = CDR(b))
|
|
|
71 |
if (TAG(b) == R_NilValue )
|
| 8130 |
pd |
72 |
{
|
|
|
73 |
s = CAR(b);
|
|
|
74 |
break;
|
|
|
75 |
}
|
|
|
76 |
if ( s == R_NilValue )
|
|
|
77 |
s = CAR(cptr->promargs);
|
|
|
78 |
/*
|
|
|
79 |
error("failed to match argument for dispatch");
|
|
|
80 |
*/
|
|
|
81 |
}
|
|
|
82 |
else
|
|
|
83 |
s = CAR(cptr->promargs);
|
|
|
84 |
|
|
|
85 |
UNPROTECT(2);
|
| 1839 |
ihaka |
86 |
if (TYPEOF(s) == PROMSXP) {
|
| 8130 |
pd |
87 |
if (PRVALUE(s) == R_UnboundValue)
|
| 18214 |
luke |
88 |
s = eval(s, R_NilValue);
|
|
|
89 |
else
|
|
|
90 |
s = PRVALUE(s);
|
| 1839 |
ihaka |
91 |
}
|
|
|
92 |
return(s);
|
| 2 |
r |
93 |
}
|
|
|
94 |
|
|
|
95 |
static SEXP applyMethod(SEXP call, SEXP op, SEXP args, SEXP rho, SEXP newrho)
|
|
|
96 |
{
|
| 1839 |
ihaka |
97 |
SEXP ans;
|
|
|
98 |
if (TYPEOF(op) == SPECIALSXP) {
|
|
|
99 |
int save = R_PPStackTop;
|
|
|
100 |
R_Visible = 1 - PRIMPRINT(op);
|
|
|
101 |
ans = PRIMFUN(op) (call, op, args, rho);
|
|
|
102 |
if (save != R_PPStackTop) {
|
|
|
103 |
Rprintf("stack imbalance in %s, %d then %d\n",
|
|
|
104 |
PRIMNAME(op), save, R_PPStackTop);
|
| 526 |
maechler |
105 |
}
|
| 1839 |
ihaka |
106 |
}
|
|
|
107 |
else if (TYPEOF(op) == BUILTINSXP) {
|
|
|
108 |
int save = R_PPStackTop;
|
|
|
109 |
PROTECT(args = evalList(args, rho));
|
|
|
110 |
R_Visible = 1 - PRIMPRINT(op);
|
|
|
111 |
ans = PRIMFUN(op) (call, op, args, rho);
|
|
|
112 |
UNPROTECT(1);
|
|
|
113 |
if (save != R_PPStackTop) {
|
|
|
114 |
Rprintf("stack imbalance in %s, %d then %d\n",
|
|
|
115 |
PRIMNAME(op), save, R_PPStackTop);
|
| 2 |
r |
116 |
}
|
| 1839 |
ihaka |
117 |
}
|
|
|
118 |
else if (TYPEOF(op) == CLOSXP) {
|
|
|
119 |
ans = applyClosure(call, op, args, rho, newrho);
|
|
|
120 |
}
|
| 2342 |
maechler |
121 |
else
|
| 1839 |
ihaka |
122 |
ans = R_NilValue; /* for -Wall */
|
|
|
123 |
return ans;
|
| 2 |
r |
124 |
}
|
|
|
125 |
|
|
|
126 |
|
| 1839 |
ihaka |
127 |
/* "newintoold" - a destructive matching of arguments; */
|
|
|
128 |
/* newargs comes first; any element of oldargs with */
|
|
|
129 |
/* a name that matches a named newarg is deleted; the */
|
|
|
130 |
/* two resulting lists are appended and returned. */
|
|
|
131 |
/* S claims to do this (white book) but doesn't seem to. */
|
| 2 |
r |
132 |
|
| 526 |
maechler |
133 |
static SEXP newintoold(SEXP new, SEXP old)
|
| 2 |
r |
134 |
{
|
| 1839 |
ihaka |
135 |
if (new == R_NilValue)
|
|
|
136 |
return R_NilValue;
|
| 10172 |
luke |
137 |
SETCDR(new, newintoold(CDR(new),old));
|
| 1839 |
ihaka |
138 |
while (old != R_NilValue) {
|
|
|
139 |
if (TAG(old) != R_NilValue && TAG(old) == TAG(new)) {
|
| 10172 |
luke |
140 |
SETCAR(old, CAR(new));
|
| 1839 |
ihaka |
141 |
return CDR(new);
|
| 2 |
r |
142 |
}
|
| 1839 |
ihaka |
143 |
old = CDR(old);
|
|
|
144 |
}
|
|
|
145 |
return new;
|
| 2 |
r |
146 |
}
|
|
|
147 |
|
|
|
148 |
static SEXP matchmethargs(SEXP oldargs, SEXP newargs)
|
|
|
149 |
{
|
| 1839 |
ihaka |
150 |
newargs = newintoold(newargs, oldargs);
|
|
|
151 |
return listAppend(oldargs, newargs);
|
| 2 |
r |
152 |
}
|
|
|
153 |
|
|
|
154 |
/* usemethod - calling functions need to evaluate the object
|
| 526 |
maechler |
155 |
* (== 2nd argument). They also need to ensure that the
|
| 2 |
r |
156 |
* argument list is set up in the correct manner.
|
| 526 |
maechler |
157 |
*
|
| 2 |
r |
158 |
* 1. find the context for the calling function (i.e. the generic)
|
|
|
159 |
* this gives us the unevaluated arguments for the original call
|
|
|
160 |
*
|
|
|
161 |
* 2. create an environment for evaluating the method and insert
|
| 526 |
maechler |
162 |
* a handful of variables (.Generic, .Class and .Method) into
|
|
|
163 |
* that environment. Also copy any variables in the env of the
|
|
|
164 |
* generic that are not formal (or actual) arguments.
|
| 2 |
r |
165 |
*
|
|
|
166 |
* 3. fix up the argument list; it should be the arguments to the
|
| 526 |
maechler |
167 |
* generic matched to the formals of the method to be invoked */
|
| 2 |
r |
168 |
|
| 16335 |
luke |
169 |
SEXP R_LookupMethod(SEXP method, SEXP rho, SEXP callrho, SEXP defrho)
|
|
|
170 |
{
|
|
|
171 |
SEXP val;
|
|
|
172 |
|
|
|
173 |
if (R_UseNamespaceDispatch) {
|
|
|
174 |
if (TYPEOF(callrho) != ENVSXP && callrho != R_NilValue)
|
|
|
175 |
error("bad generic call environment");
|
|
|
176 |
if (TYPEOF(defrho) != ENVSXP && defrho != R_NilValue)
|
|
|
177 |
error("bad generic definition environment");
|
|
|
178 |
if (defrho == R_NilValue)
|
| 16634 |
luke |
179 |
defrho = R_BaseNamespace;
|
| 16335 |
luke |
180 |
|
|
|
181 |
val = findVar(method, callrho);
|
|
|
182 |
if (TYPEOF(val)==PROMSXP)
|
|
|
183 |
val = eval(val, rho);
|
|
|
184 |
if (isFunction(val))
|
|
|
185 |
return val;
|
|
|
186 |
else {
|
| 23319 |
luke |
187 |
SEXP table = findVarInFrame3(defrho,
|
|
|
188 |
install(".__S3MethodsTable__."),
|
| 17233 |
ripley |
189 |
TRUE);
|
| 16335 |
luke |
190 |
if (TYPEOF(table) == PROMSXP)
|
|
|
191 |
table = eval(table, R_NilValue);
|
|
|
192 |
if (TYPEOF(table) == ENVSXP) {
|
| 17233 |
ripley |
193 |
val = findVarInFrame3(table, method, TRUE);
|
| 25332 |
luke |
194 |
if (TYPEOF(val)==PROMSXP)
|
|
|
195 |
val = eval(val, rho);
|
| 16335 |
luke |
196 |
if (val != R_UnboundValue)
|
|
|
197 |
return val;
|
|
|
198 |
}
|
|
|
199 |
return R_UnboundValue;
|
|
|
200 |
}
|
|
|
201 |
}
|
|
|
202 |
else {
|
|
|
203 |
val = findVar(method, rho);
|
|
|
204 |
if (TYPEOF(val)==PROMSXP)
|
|
|
205 |
val = eval(val, rho);
|
|
|
206 |
return val;
|
|
|
207 |
}
|
|
|
208 |
}
|
|
|
209 |
|
| 1839 |
ihaka |
210 |
int usemethod(char *generic, SEXP obj, SEXP call, SEXP args,
|
| 16335 |
luke |
211 |
SEXP rho, SEXP callrho, SEXP defrho, SEXP *ans)
|
| 2 |
r |
212 |
{
|
| 1839 |
ihaka |
213 |
SEXP class, method, sxp, t, s, matchedarg;
|
| 10587 |
tlumley |
214 |
SEXP op, formals, newrho, newcall,tmp;
|
| 1839 |
ihaka |
215 |
char buf[512];
|
|
|
216 |
int i, j, nclass, matched;
|
|
|
217 |
RCNTXT *cptr;
|
| 2 |
r |
218 |
|
| 1839 |
ihaka |
219 |
/* Get the context which UseMethod was called from. */
|
| 2 |
r |
220 |
|
| 1839 |
ihaka |
221 |
cptr = R_GlobalContext;
|
| 6188 |
rgentlem |
222 |
if ( !(cptr->callflag & CTXT_FUNCTION) || cptr->cloenv != rho)
|
| 5731 |
ripley |
223 |
error("UseMethod used in an inappropriate fashion");
|
| 214 |
rgentlem |
224 |
|
| 1839 |
ihaka |
225 |
/* Create a new environment without any */
|
|
|
226 |
/* of the formals to the generic in it. */
|
| 2 |
r |
227 |
|
| 1839 |
ihaka |
228 |
PROTECT(newrho = allocSExp(ENVSXP));
|
| 8516 |
pd |
229 |
/*
|
| 1839 |
ihaka |
230 |
PROTECT(op = findFun(CAR(cptr->call), cptr->sysparent));
|
| 8516 |
pd |
231 |
*/
|
|
|
232 |
op = CAR(cptr->call);
|
|
|
233 |
switch (TYPEOF(op)) {
|
|
|
234 |
case SYMSXP:
|
|
|
235 |
PROTECT(op = findFun(op, cptr->sysparent));
|
|
|
236 |
break;
|
|
|
237 |
case LANGSXP:
|
|
|
238 |
PROTECT(op = eval(op, cptr->sysparent));
|
|
|
239 |
break;
|
|
|
240 |
case CLOSXP:
|
|
|
241 |
case BUILTINSXP:
|
|
|
242 |
case SPECIALSXP:
|
|
|
243 |
PROTECT(op);
|
|
|
244 |
break;
|
|
|
245 |
default:
|
|
|
246 |
error("Invalid generic function in usemethod");
|
|
|
247 |
}
|
|
|
248 |
|
| 1839 |
ihaka |
249 |
if (TYPEOF(op) == CLOSXP) {
|
|
|
250 |
formals = FORMALS(op);
|
|
|
251 |
for (s = FRAME(cptr->cloenv); s != R_NilValue; s = CDR(s)) {
|
|
|
252 |
matched = 0;
|
|
|
253 |
for (t = formals; t != R_NilValue; t = CDR(t))
|
|
|
254 |
if (TAG(t) == TAG(s))
|
|
|
255 |
matched = 1;
|
|
|
256 |
if (!matched)
|
|
|
257 |
defineVar(TAG(s),CAR(s),newrho);
|
| 2 |
r |
258 |
}
|
| 1839 |
ihaka |
259 |
}
|
| 2 |
r |
260 |
|
| 1839 |
ihaka |
261 |
PROTECT(matchedarg = cptr->promargs);
|
|
|
262 |
PROTECT(newcall = duplicate(cptr->call));
|
| 2 |
r |
263 |
|
| 26136 |
luke |
264 |
PROTECT(class = R_data_class(obj, FALSE));
|
| 22764 |
jmc |
265 |
nclass = length(class);
|
|
|
266 |
for (i = 0; i < nclass; i++) {
|
| 25630 |
ripley |
267 |
if(strlen(generic) + strlen(CHAR(STRING_ELT(class, i))) + 2 > 512)
|
|
|
268 |
error("class name too long in %s", generic);
|
|
|
269 |
sprintf(buf, "%s.%s", generic, CHAR(STRING_ELT(class, i)));
|
| 22764 |
jmc |
270 |
method = install(buf);
|
|
|
271 |
sxp = R_LookupMethod(method, rho, callrho, defrho);
|
|
|
272 |
/* autoloading requires that promises be evaluated <TSL>*/
|
| 25628 |
ripley |
273 |
if (TYPEOF(sxp) == PROMSXP){
|
| 25630 |
ripley |
274 |
PROTECT(tmp = eval(sxp, rho));
|
|
|
275 |
sxp = tmp;
|
|
|
276 |
UNPROTECT(1);
|
| 22764 |
jmc |
277 |
}
|
|
|
278 |
if (isFunction(sxp)) {
|
| 24519 |
ripley |
279 |
defineVar(install(".Generic"), mkString(generic), newrho);
|
|
|
280 |
if (i > 0) {
|
|
|
281 |
PROTECT(t = allocVector(STRSXP, nclass - i));
|
|
|
282 |
for (j = 0; j < length(t); j++, i++)
|
|
|
283 |
SET_STRING_ELT(t, j, STRING_ELT(class, i));
|
|
|
284 |
setAttrib(t, install("previous"), class);
|
|
|
285 |
defineVar(install(".Class"), t, newrho);
|
|
|
286 |
UNPROTECT(1);
|
|
|
287 |
}
|
|
|
288 |
else
|
|
|
289 |
defineVar(install(".Class"), class, newrho);
|
|
|
290 |
PROTECT(t = mkString(buf));
|
|
|
291 |
defineVar(install(".Method"), t, newrho);
|
| 22764 |
jmc |
292 |
UNPROTECT(1);
|
| 24519 |
ripley |
293 |
if (R_UseNamespaceDispatch) {
|
|
|
294 |
defineVar(install(".GenericCallEnv"), callrho, newrho);
|
|
|
295 |
defineVar(install(".GenericDefEnv"), defrho, newrho);
|
|
|
296 |
}
|
|
|
297 |
t = newcall;
|
|
|
298 |
SETCAR(t, method);
|
|
|
299 |
R_GlobalContext->callflag = CTXT_GENERIC;
|
|
|
300 |
*ans = applyMethod(t, sxp, matchedarg, rho, newrho);
|
|
|
301 |
R_GlobalContext->callflag = CTXT_RETURN;
|
| 26136 |
luke |
302 |
UNPROTECT(5);
|
| 24519 |
ripley |
303 |
return 1;
|
| 2 |
r |
304 |
}
|
| 1839 |
ihaka |
305 |
}
|
| 25630 |
ripley |
306 |
if(strlen(generic) + strlen("default") + 2 > 512)
|
|
|
307 |
error("class name too long in %s", generic);
|
|
|
308 |
sprintf(buf, "%s.default", generic);
|
| 1839 |
ihaka |
309 |
method = install(buf);
|
| 16335 |
luke |
310 |
sxp = R_LookupMethod(method, rho, callrho, defrho);
|
| 23318 |
luke |
311 |
if (TYPEOF(sxp) == PROMSXP)
|
|
|
312 |
sxp = eval(sxp, rho);
|
| 1839 |
ihaka |
313 |
if (isFunction(sxp)) {
|
|
|
314 |
defineVar(install(".Generic"), mkString(generic), newrho);
|
|
|
315 |
defineVar(install(".Class"), R_NilValue, newrho);
|
|
|
316 |
PROTECT(t = mkString(buf));
|
|
|
317 |
defineVar(install(".Method"), t, newrho);
|
|
|
318 |
UNPROTECT(1);
|
| 16335 |
luke |
319 |
if (R_UseNamespaceDispatch) {
|
|
|
320 |
defineVar(install(".GenericCallEnv"), callrho, newrho);
|
|
|
321 |
defineVar(install(".GenericDefEnv"), defrho, newrho);
|
|
|
322 |
}
|
| 1839 |
ihaka |
323 |
t = newcall;
|
| 10172 |
luke |
324 |
SETCAR(t, method);
|
| 1839 |
ihaka |
325 |
R_GlobalContext->callflag = CTXT_GENERIC;
|
|
|
326 |
*ans = applyMethod(t, sxp, matchedarg, rho, newrho);
|
|
|
327 |
R_GlobalContext->callflag = CTXT_RETURN;
|
| 26136 |
luke |
328 |
UNPROTECT(5);
|
| 1839 |
ihaka |
329 |
return 1;
|
|
|
330 |
}
|
| 26136 |
luke |
331 |
UNPROTECT(5);
|
| 1839 |
ihaka |
332 |
cptr->callflag = CTXT_RETURN;
|
|
|
333 |
return 0;
|
| 2 |
r |
334 |
}
|
|
|
335 |
|
| 1839 |
ihaka |
336 |
/* Note: "do_usemethod" is not the only entry point to */
|
|
|
337 |
/* "usemethod". Things like [ and [[ call usemethod directly, */
|
|
|
338 |
/* hence do_usemethod should just be an interface to usemethod. */
|
| 2 |
r |
339 |
|
|
|
340 |
SEXP do_usemethod(SEXP call, SEXP op, SEXP args, SEXP env)
|
|
|
341 |
{
|
| 24519 |
ripley |
342 |
SEXP ans, generic, obj;
|
| 16335 |
luke |
343 |
SEXP callenv, defenv;
|
| 1839 |
ihaka |
344 |
int nargs;
|
|
|
345 |
RCNTXT *cptr;
|
| 2 |
r |
346 |
|
| 1839 |
ihaka |
347 |
nargs = length(args);
|
| 2 |
r |
348 |
|
| 16335 |
luke |
349 |
if (nargs < 0)
|
|
|
350 |
errorcall(call, "corrupt internals!");
|
| 1858 |
ihaka |
351 |
|
| 16335 |
luke |
352 |
/* get environments needed for dispatching.
|
|
|
353 |
callenv = environment from which the generic was called
|
|
|
354 |
defenv = environment where the generic was defined */
|
|
|
355 |
cptr = R_GlobalContext;
|
|
|
356 |
if ( !(cptr->callflag & CTXT_FUNCTION) || cptr->cloenv != env)
|
|
|
357 |
error("UseMethod used in an inappropriate fashion");
|
|
|
358 |
callenv = cptr->sysparent;
|
|
|
359 |
defenv = TYPEOF(env) == ENVSXP ? ENCLOS(env) : R_NilValue;
|
|
|
360 |
|
| 1839 |
ihaka |
361 |
if (nargs)
|
| 24519 |
ripley |
362 |
PROTECT(generic = eval(CAR(args), env));
|
| 1839 |
ihaka |
363 |
else
|
| 24519 |
ripley |
364 |
generic = R_MissingArg;
|
| 2 |
r |
365 |
|
| 24499 |
ripley |
366 |
if (nargs > 2) /* R-lang says there should be a warning */
|
|
|
367 |
warningcall(call, "Arguments after the first two are ignored");
|
| 1839 |
ihaka |
368 |
if (nargs >= 2)
|
|
|
369 |
PROTECT(obj = eval(CADR(args), env));
|
|
|
370 |
else {
|
|
|
371 |
cptr = R_GlobalContext;
|
|
|
372 |
while (cptr != NULL) {
|
| 6188 |
rgentlem |
373 |
if ( (cptr->callflag & CTXT_FUNCTION) && cptr->cloenv == env)
|
| 1839 |
ihaka |
374 |
break;
|
|
|
375 |
cptr = cptr->nextcontext;
|
| 2 |
r |
376 |
}
|
| 1839 |
ihaka |
377 |
if (cptr == NULL)
|
| 5731 |
ripley |
378 |
error("UseMethod called from outside a closure");
|
| 24519 |
ripley |
379 |
if (generic == R_MissingArg)
|
|
|
380 |
PROTECT(generic = mkString(CHAR(PRINTNAME(CAR(cptr->call)))));
|
| 1839 |
ihaka |
381 |
PROTECT(obj = GetObject(cptr));
|
|
|
382 |
}
|
| 526 |
maechler |
383 |
|
| 24519 |
ripley |
384 |
if (TYPEOF(generic) != STRSXP ||
|
|
|
385 |
LENGTH(generic) < 1 ||
|
|
|
386 |
strlen(CHAR(STRING_ELT(generic, 0))) == 0)
|
|
|
387 |
errorcall(call, "first argument must be a generic name");
|
| 2 |
r |
388 |
|
| 25628 |
ripley |
389 |
if (usemethod(CHAR(STRING_ELT(generic, 0)), obj, call, CDR(args),
|
| 16335 |
luke |
390 |
env, callenv, defenv, &ans) == 1) {
|
| 1839 |
ihaka |
391 |
UNPROTECT(1);
|
| 2579 |
pd |
392 |
PROTECT(ans);
|
| 1839 |
ihaka |
393 |
findcontext(CTXT_RETURN, env, ans);
|
| 2579 |
pd |
394 |
UNPROTECT(1);
|
| 1839 |
ihaka |
395 |
}
|
|
|
396 |
else
|
| 25628 |
ripley |
397 |
error("no applicable method for \"%s\"", CHAR(STRING_ELT(generic, 0)));
|
| 1839 |
ihaka |
398 |
return R_NilValue; /* NOT Used */
|
| 2 |
r |
399 |
}
|
|
|
400 |
|
| 2524 |
maechler |
401 |
/*
|
| 2515 |
rgentlem |
402 |
fixcall: fixes up the call when arguments to the function may
|
|
|
403 |
have changed; for now we only worry about tagged args, appending
|
|
|
404 |
them if they are not already there
|
|
|
405 |
*/
|
|
|
406 |
|
|
|
407 |
static SEXP fixcall(SEXP call, SEXP args)
|
|
|
408 |
{
|
| 2524 |
maechler |
409 |
SEXP s, t;
|
| 2515 |
rgentlem |
410 |
int found;
|
|
|
411 |
|
|
|
412 |
for( t = args; t != R_NilValue; t=CDR(t) ) {
|
|
|
413 |
if( TAG(t) != R_NilValue ) {
|
|
|
414 |
found = 0;
|
| 2524 |
maechler |
415 |
for(s=call; CDR(s) != R_NilValue; s=CDR(s))
|
| 2515 |
rgentlem |
416 |
if( TAG(CDR(s)) == TAG(t) )
|
|
|
417 |
found = 1;
|
|
|
418 |
if( !found ) {
|
| 10172 |
luke |
419 |
SETCDR(s, allocList(1));
|
|
|
420 |
SET_TAG(CDR(s), TAG(t));
|
|
|
421 |
SETCAR(CDR(s), duplicate(CAR(t)));
|
| 2515 |
rgentlem |
422 |
}
|
|
|
423 |
}
|
|
|
424 |
}
|
| 2519 |
pd |
425 |
return call;
|
| 2515 |
rgentlem |
426 |
}
|
|
|
427 |
|
| 1839 |
ihaka |
428 |
/* If NextMethod has any arguments the first must be the generic */
|
|
|
429 |
/* the second the object and any remaining are matched with the */
|
|
|
430 |
/* formals of the chosen method. */
|
| 526 |
maechler |
431 |
|
| 2 |
r |
432 |
#define ARGUSED(x) LEVELS(x)
|
|
|
433 |
|
|
|
434 |
SEXP do_nextmethod(SEXP call, SEXP op, SEXP args, SEXP env)
|
|
|
435 |
{
|
| 25628 |
ripley |
436 |
char buf[512], b[512], bb[512], tbuf[10];
|
| 1839 |
ihaka |
437 |
SEXP ans, s, t, class, method, matchedarg, generic, nextfun;
|
|
|
438 |
SEXP sysp, m, formals, actuals, tmp, newcall;
|
| 6113 |
rgentlem |
439 |
SEXP a, group, basename;
|
| 16335 |
luke |
440 |
SEXP callenv, defenv;
|
| 1839 |
ihaka |
441 |
RCNTXT *cptr;
|
| 21349 |
tlumley |
442 |
int i,j,cftmp;
|
| 2 |
r |
443 |
|
| 1839 |
ihaka |
444 |
cptr = R_GlobalContext;
|
| 21349 |
tlumley |
445 |
cftmp = cptr->callflag;
|
| 1839 |
ihaka |
446 |
cptr->callflag = CTXT_GENERIC;
|
| 2 |
r |
447 |
|
| 1839 |
ihaka |
448 |
/* get the env NextMethod was called from */
|
|
|
449 |
sysp = R_GlobalContext->sysparent;
|
|
|
450 |
while (cptr != NULL) {
|
| 6188 |
rgentlem |
451 |
if (cptr->callflag & CTXT_FUNCTION && cptr->cloenv == sysp)
|
| 1839 |
ihaka |
452 |
break;
|
|
|
453 |
cptr = cptr->nextcontext;
|
| 21352 |
maechler |
454 |
}
|
| 1839 |
ihaka |
455 |
if (cptr == NULL)
|
| 5731 |
ripley |
456 |
error("NextMethod called from outside a closure");
|
| 2 |
r |
457 |
|
| 1839 |
ihaka |
458 |
PROTECT(newcall = duplicate(cptr->call));
|
| 214 |
rgentlem |
459 |
|
| 21349 |
tlumley |
460 |
/* eg get("print.ts")(1) */
|
|
|
461 |
if (TYPEOF(CAR(cptr->call)) == LANGSXP)
|
|
|
462 |
error("NextMethod called from anonymous function");
|
|
|
463 |
|
| 23391 |
luke |
464 |
/* Find dispatching environments. Promises shouldn't occur, but
|
|
|
465 |
check to be on the safe side. If the variables are not in the
|
|
|
466 |
environment (the method was called outside a method dispatch)
|
|
|
467 |
then chose reasonable defaults. */
|
|
|
468 |
if (R_UseNamespaceDispatch) {
|
|
|
469 |
callenv = findVarInFrame3(R_GlobalContext->sysparent,
|
|
|
470 |
install(".GenericCallEnv"), TRUE);
|
|
|
471 |
if (TYPEOF(callenv) == PROMSXP)
|
|
|
472 |
callenv = eval(callenv, R_NilValue);
|
|
|
473 |
else if (callenv == R_UnboundValue)
|
|
|
474 |
callenv = env;
|
|
|
475 |
defenv = findVarInFrame3(R_GlobalContext->sysparent,
|
|
|
476 |
install(".GenericDefEnv"), TRUE);
|
|
|
477 |
if (TYPEOF(defenv) == PROMSXP)
|
|
|
478 |
defenv = eval(defenv, R_NilValue);
|
|
|
479 |
else if (defenv == R_UnboundValue)
|
|
|
480 |
defenv = R_GlobalEnv;
|
|
|
481 |
}
|
|
|
482 |
else {
|
|
|
483 |
callenv = env;
|
|
|
484 |
defenv = R_GlobalEnv;
|
|
|
485 |
}
|
|
|
486 |
|
| 1839 |
ihaka |
487 |
/* set up the arglist */
|
| 23391 |
luke |
488 |
s = R_LookupMethod(CAR(cptr->call), env, callenv, defenv);
|
| 27412 |
ripley |
489 |
if (TYPEOF(s) == PROMSXP) /* looks like R_LookupMethod just did this */
|
| 23391 |
luke |
490 |
s = eval(s, env);
|
| 27412 |
ripley |
491 |
if (TYPEOF(s) == SYMSXP && s == R_UnboundValue)
|
|
|
492 |
error("No calling generic was found: was a method called directly?");
|
| 21349 |
tlumley |
493 |
if (TYPEOF(s) != CLOSXP){
|
| 27412 |
ripley |
494 |
errorcall(R_NilValue, "function is not a closure, but of type %d",
|
|
|
495 |
TYPEOF(s));
|
| 21352 |
maechler |
496 |
}
|
| 1975 |
rgentlem |
497 |
/* get formals and actuals; attach the names of the formals to
|
|
|
498 |
the actuals, expanding any ... that occurs */
|
| 1839 |
ihaka |
499 |
formals = FORMALS(s);
|
| 1975 |
rgentlem |
500 |
PROTECT(actuals = matchArgs(formals, cptr->promargs));
|
| 2 |
r |
501 |
|
| 1975 |
rgentlem |
502 |
i=0;
|
| 21352 |
maechler |
503 |
for(s=formals, t=actuals; s!=R_NilValue; s=CDR(s), t=CDR(t)) {
|
| 10172 |
luke |
504 |
SET_TAG(t, TAG(s));
|
| 21352 |
maechler |
505 |
if(TAG(t) == R_DotsSymbol) i=length(CAR(t));
|
| 1975 |
rgentlem |
506 |
}
|
|
|
507 |
if(i) { /* we need to expand out the dots */
|
| 2524 |
maechler |
508 |
PROTECT(t = allocList(i+length(actuals)-1));
|
| 2807 |
pd |
509 |
for( s=actuals, m=t; s!=R_NilValue; s=CDR(s) ) {
|
| 2758 |
pd |
510 |
if(TYPEOF(CAR(s)) == DOTSXP) {
|
| 2524 |
maechler |
511 |
i=1;
|
|
|
512 |
for(a=CAR(s); a!=R_NilValue; a=CDR(a), i++, m=CDR(m) ) {
|
| 2758 |
pd |
513 |
sprintf(tbuf,"..%d",i);
|
| 10172 |
luke |
514 |
SET_TAG(m, mkSYMSXP(mkChar(tbuf), R_UnboundValue));
|
|
|
515 |
SETCAR(m, CAR(a));
|
| 2524 |
maechler |
516 |
}
|
| 2758 |
pd |
517 |
}
|
|
|
518 |
else {
|
| 10172 |
luke |
519 |
SET_TAG(m, TAG(s));
|
|
|
520 |
SETCAR(m, CAR(s));
|
| 2807 |
pd |
521 |
m=CDR(m);
|
| 2758 |
pd |
522 |
}
|
| 21352 |
maechler |
523 |
}
|
| 2758 |
pd |
524 |
UNPROTECT(1);
|
|
|
525 |
actuals=t;
|
| 1975 |
rgentlem |
526 |
}
|
| 2758 |
pd |
527 |
PROTECT(actuals);
|
| 1975 |
rgentlem |
528 |
|
| 2757 |
pd |
529 |
|
| 1839 |
ihaka |
530 |
/* we can't duplicate because it would force the promises */
|
|
|
531 |
/* so we do our own duplication of the promargs */
|
| 1975 |
rgentlem |
532 |
|
| 1839 |
ihaka |
533 |
PROTECT(matchedarg = allocList(length(cptr->promargs)));
|
|
|
534 |
for (t = matchedarg, s = cptr->promargs; t != R_NilValue;
|
| 21352 |
maechler |
535 |
s = CDR(s), t=CDR(t)) {
|
|
|
536 |
SETCAR(t, CAR(s));
|
|
|
537 |
SET_TAG(t, TAG(s));
|
| 1839 |
ihaka |
538 |
}
|
|
|
539 |
for (t = matchedarg; t != R_NilValue; t = CDR(t)) {
|
| 1975 |
rgentlem |
540 |
for (m = actuals; m != R_NilValue; m = CDR(m))
|
|
|
541 |
if (CAR(m) == CAR(t)) {
|
| 1839 |
ihaka |
542 |
if (CAR(m) == R_MissingArg) {
|
| 17233 |
ripley |
543 |
tmp = findVarInFrame3(cptr->cloenv, TAG(m), TRUE);
|
| 1839 |
ihaka |
544 |
if (tmp == R_MissingArg)
|
|
|
545 |
break;
|
|
|
546 |
}
|
| 10172 |
luke |
547 |
SETCAR(t, mkPROMISE(TAG(m), cptr->cloenv));
|
| 1839 |
ihaka |
548 |
break;
|
| 2524 |
maechler |
549 |
}
|
| 1839 |
ihaka |
550 |
}
|
|
|
551 |
/*
|
|
|
552 |
Now see if there were any other arguments passed in
|
| 2515 |
rgentlem |
553 |
Currently we seem to only allow named args to change
|
|
|
554 |
or to be added, this is at variance with p. 470 of the
|
|
|
555 |
White Book
|
|
|
556 |
*/
|
| 2 |
r |
557 |
|
| 1839 |
ihaka |
558 |
s = CADDR(args); /* this is ... and we need to see if it's bound */
|
|
|
559 |
if (s == R_DotsSymbol) {
|
| 17233 |
ripley |
560 |
t = findVarInFrame3(env, s, TRUE);
|
| 1839 |
ihaka |
561 |
if (t != R_NilValue && t != R_MissingArg) {
|
| 10172 |
luke |
562 |
SET_TYPEOF(t, LISTSXP); /* a safe mutation */
|
| 1839 |
ihaka |
563 |
s = matchmethargs(matchedarg,t);
|
|
|
564 |
UNPROTECT(1);
|
|
|
565 |
PROTECT(matchedarg = s);
|
| 2515 |
rgentlem |
566 |
newcall = fixcall(newcall, matchedarg);
|
| 2 |
r |
567 |
}
|
| 1839 |
ihaka |
568 |
}
|
|
|
569 |
else
|
| 5731 |
ripley |
570 |
errorcall(call,"wrong argument ...");
|
| 2 |
r |
571 |
|
| 2515 |
rgentlem |
572 |
/*
|
|
|
573 |
.Class is used to determine the next method; if it doesn't
|
|
|
574 |
exist the first argument to the current method is used
|
|
|
575 |
the second argument to NextMethod is another option but
|
|
|
576 |
isn't currently used).
|
|
|
577 |
*/
|
| 21352 |
maechler |
578 |
class = findVarInFrame3(R_GlobalContext->sysparent,
|
| 17233 |
ripley |
579 |
install(".Class"), TRUE);
|
| 2515 |
rgentlem |
580 |
|
| 1839 |
ihaka |
581 |
if (class == R_UnboundValue) {
|
|
|
582 |
s = GetObject(cptr);
|
|
|
583 |
if (!isObject(s))
|
| 5731 |
ripley |
584 |
errorcall(call, "object not specified");
|
| 1839 |
ihaka |
585 |
class = getAttrib(s, R_ClassSymbol);
|
|
|
586 |
}
|
| 2 |
r |
587 |
|
| 6113 |
rgentlem |
588 |
/* the generic comes from either the sysparent or it's named */
|
| 21352 |
maechler |
589 |
generic = findVarInFrame3(R_GlobalContext->sysparent,
|
| 17233 |
ripley |
590 |
install(".Generic"), TRUE);
|
| 6113 |
rgentlem |
591 |
if (generic == R_UnboundValue)
|
|
|
592 |
generic = eval(CAR(args), env);
|
|
|
593 |
if( generic == R_NilValue )
|
|
|
594 |
errorcall(call,"generic function not specified");
|
| 1839 |
ihaka |
595 |
PROTECT(generic);
|
| 2 |
r |
596 |
|
| 1839 |
ihaka |
597 |
if (!isString(generic) || length(generic) > 1)
|
| 5731 |
ripley |
598 |
errorcall(call,"invalid generic argument to NextMethod");
|
| 21352 |
maechler |
599 |
|
| 10172 |
luke |
600 |
if (strlen(CHAR(STRING_ELT(generic, 0))) == 0)
|
| 5731 |
ripley |
601 |
errorcall(call,"generic function not specified");
|
| 2 |
r |
602 |
|
| 6113 |
rgentlem |
603 |
/* determine whether we are in a Group dispatch */
|
|
|
604 |
|
| 17233 |
ripley |
605 |
group = findVarInFrame3(R_GlobalContext->sysparent,install(".Group"), TRUE);
|
| 1839 |
ihaka |
606 |
if (group == R_UnboundValue){
|
| 6113 |
rgentlem |
607 |
PROTECT(group = mkString(""));
|
| 1839 |
ihaka |
608 |
}
|
| 6113 |
rgentlem |
609 |
else
|
|
|
610 |
PROTECT(group);
|
|
|
611 |
|
| 1839 |
ihaka |
612 |
if (!isString(group) || length(group) > 1)
|
| 5731 |
ripley |
613 |
errorcall(call, "invalid group argument found in NextMethod");
|
| 2 |
r |
614 |
|
| 6113 |
rgentlem |
615 |
/* determine the root: either the group or the generic will be it */
|
|
|
616 |
|
| 10172 |
luke |
617 |
if( strlen(CHAR(STRING_ELT(group, 0))) == 0 )
|
| 6113 |
rgentlem |
618 |
basename = generic;
|
|
|
619 |
else
|
|
|
620 |
basename = group;
|
|
|
621 |
|
|
|
622 |
nextfun = R_NilValue;
|
|
|
623 |
|
|
|
624 |
/* find the method currently being invoked and jump over the current call */
|
|
|
625 |
/* if t is R_UnboundValue then we called the current method directly */
|
|
|
626 |
|
| 17233 |
ripley |
627 |
method = findVarInFrame3(R_GlobalContext->sysparent,install(".Method"), TRUE);
|
| 6113 |
rgentlem |
628 |
if( method != R_UnboundValue) {
|
| 21352 |
maechler |
629 |
if( !isString(method) )
|
| 6113 |
rgentlem |
630 |
error("Wrong value for .Method");
|
| 25628 |
ripley |
631 |
for( i = 0; i < length(method); i++ ) {
|
| 25630 |
ripley |
632 |
if(strlen(CHAR(STRING_ELT(method, i))) >= 512)
|
|
|
633 |
error("method name too long in %s", CHAR(STRING_ELT(method, i)));
|
|
|
634 |
sprintf(b, "%s", CHAR(STRING_ELT(method, i)));
|
| 6113 |
rgentlem |
635 |
if( strlen(b) )
|
|
|
636 |
break;
|
|
|
637 |
}
|
| 21352 |
maechler |
638 |
/* for binary operators check that the second argument's method
|
| 21282 |
tlumley |
639 |
is the same or absent */
|
| 25630 |
ripley |
640 |
for(j = i; j < length(method); j++){
|
|
|
641 |
if(strlen(CHAR(STRING_ELT(method, j))) >= 512)
|
|
|
642 |
error("method name too long in %s", CHAR(STRING_ELT(method, j)));
|
|
|
643 |
sprintf(bb, "%s",CHAR(STRING_ELT(method, j)));
|
| 21282 |
tlumley |
644 |
if (strlen(bb) && strcmp(b,bb))
|
|
|
645 |
warning("Incompatible methods ignored");
|
|
|
646 |
}
|
| 6113 |
rgentlem |
647 |
}
|
|
|
648 |
else {
|
| 25630 |
ripley |
649 |
if(strlen(CHAR(PRINTNAME(CAR(cptr->call)))) >= 512)
|
|
|
650 |
error("call name too long in %s", CHAR(PRINTNAME(CAR(cptr->call))));
|
|
|
651 |
sprintf(b, "%s", CHAR(PRINTNAME(CAR(cptr->call))));
|
| 6113 |
rgentlem |
652 |
}
|
|
|
653 |
|
| 1839 |
ihaka |
654 |
for (j = 0; j < length(class); j++) {
|
| 25630 |
ripley |
655 |
if(strlen(CHAR(STRING_ELT(basename, 0))) +
|
|
|
656 |
strlen(CHAR(STRING_ELT(class, j))) + 2 > 512)
|
|
|
657 |
error("class name too long in %s", CHAR(STRING_ELT(basename, 0)));
|
|
|
658 |
sprintf(buf, "%s.%s", CHAR(STRING_ELT(basename, 0)),
|
|
|
659 |
CHAR(STRING_ELT(class, j)));
|
|
|
660 |
if ( !strcmp(buf, b) )
|
|
|
661 |
break;
|
| 1839 |
ihaka |
662 |
}
|
| 2 |
r |
663 |
|
| 6113 |
rgentlem |
664 |
if ( !strcmp(buf,b) ) /* we found a match and start from there */
|
|
|
665 |
j++;
|
|
|
666 |
else
|
|
|
667 |
j = 0; /*no match so start with the first element of .Class */
|
|
|
668 |
|
| 21282 |
tlumley |
669 |
/* we need the value of i on exit from the for loop to figure out
|
|
|
670 |
how many classes to drop. */
|
| 6113 |
rgentlem |
671 |
for (i = j ; i < length(class); i++) {
|
| 25630 |
ripley |
672 |
if(strlen(CHAR(STRING_ELT(generic, 0))) +
|
|
|
673 |
strlen(CHAR(STRING_ELT(class, i))) + 2 > 512)
|
|
|
674 |
error("class name too long in %s", CHAR(STRING_ELT(generic, 0)));
|
|
|
675 |
sprintf(buf, "%s.%s", CHAR(STRING_ELT(generic, 0)),
|
| 10172 |
luke |
676 |
CHAR(STRING_ELT(class, i)));
|
| 16335 |
luke |
677 |
nextfun = R_LookupMethod(install(buf), env, callenv, defenv);
|
| 23318 |
luke |
678 |
if (TYPEOF(nextfun) == PROMSXP)
|
|
|
679 |
nextfun = eval(nextfun, env);
|
| 21282 |
tlumley |
680 |
if (isFunction(nextfun))
|
|
|
681 |
break;
|
|
|
682 |
if (group !=R_UnboundValue){
|
|
|
683 |
/* if not Generic.foo, look for Group.foo */
|
| 25630 |
ripley |
684 |
sprintf(buf, "%s.%s", CHAR(STRING_ELT(basename, 0)),
|
| 21282 |
tlumley |
685 |
CHAR(STRING_ELT(class, i)));
|
| 21352 |
maechler |
686 |
nextfun = R_LookupMethod(install(buf), env, callenv, defenv);
|
| 23318 |
luke |
687 |
if (TYPEOF(nextfun) == PROMSXP)
|
|
|
688 |
nextfun = eval(nextfun, env);
|
| 21352 |
maechler |
689 |
if(isFunction(nextfun))
|
|
|
690 |
break;
|
| 21282 |
tlumley |
691 |
}
|
| 1839 |
ihaka |
692 |
if (isFunction(nextfun))
|
|
|
693 |
break;
|
|
|
694 |
}
|
|
|
695 |
if (!isFunction(nextfun)) {
|
| 25630 |
ripley |
696 |
sprintf(buf, "%s.default", CHAR(STRING_ELT(generic, 0)));
|
| 16335 |
luke |
697 |
nextfun = R_LookupMethod(install(buf), env, callenv, defenv);
|
| 23318 |
luke |
698 |
if (TYPEOF(nextfun) == PROMSXP)
|
|
|
699 |
nextfun = eval(nextfun, env);
|
| 1839 |
ihaka |
700 |
if (!isFunction(nextfun)) {
|
| 10172 |
luke |
701 |
t = install(CHAR(STRING_ELT(generic, 0)));
|
| 1839 |
ihaka |
702 |
nextfun = findVar(t,env);
|
| 22881 |
luke |
703 |
if (TYPEOF(nextfun)==PROMSXP)
|
|
|
704 |
nextfun = eval(nextfun, env);
|
| 1839 |
ihaka |
705 |
if (!isFunction(nextfun))
|
| 5731 |
ripley |
706 |
error("No method to invoke");
|
| 1839 |
ihaka |
707 |
if (TYPEOF(nextfun) == CLOSXP) {
|
|
|
708 |
if (INTERNAL(t) != R_NilValue)
|
|
|
709 |
nextfun = INTERNAL(t);
|
|
|
710 |
else
|
| 5731 |
ripley |
711 |
error("No method to invoke");
|
| 1839 |
ihaka |
712 |
}
|
| 2 |
r |
713 |
}
|
| 1839 |
ihaka |
714 |
}
|
|
|
715 |
PROTECT(s = allocVector(STRSXP, length(class) - i));
|
|
|
716 |
PROTECT(class = duplicate(class));
|
|
|
717 |
PROTECT(m = allocSExp(ENVSXP));
|
|
|
718 |
for (j = 0; j < length(s); j++)
|
| 10172 |
luke |
719 |
SET_STRING_ELT(s, j, duplicate(STRING_ELT(class, i++)));
|
| 1839 |
ihaka |
720 |
setAttrib(s, install("previous"), class);
|
|
|
721 |
defineVar(install(".Class"), s, m);
|
| 21282 |
tlumley |
722 |
/* for Ops we need `method' to be a vector */
|
|
|
723 |
PROTECT(method = duplicate(method));
|
|
|
724 |
for(j = 0; j < length(method); j++){
|
|
|
725 |
if (strlen(CHAR(STRING_ELT(method,j))))
|
|
|
726 |
SET_STRING_ELT(method,j, mkChar(buf));
|
|
|
727 |
}
|
| 1839 |
ihaka |
728 |
defineVar(install(".Method"), method, m);
|
| 16335 |
luke |
729 |
if (R_UseNamespaceDispatch) {
|
|
|
730 |
defineVar(install(".GenericCallEnv"), callenv, m);
|
|
|
731 |
defineVar(install(".GenericDefEnv"), defenv, m);
|
|
|
732 |
}
|
| 1839 |
ihaka |
733 |
method = install(buf);
|
| 2 |
r |
734 |
|
| 1839 |
ihaka |
735 |
defineVar(install(".Generic"), generic, m);
|
| 2 |
r |
736 |
|
| 6113 |
rgentlem |
737 |
defineVar(install(".Group"), group, m);
|
| 526 |
maechler |
738 |
|
| 10172 |
luke |
739 |
SETCAR(newcall, method);
|
| 1839 |
ihaka |
740 |
ans = applyMethod(newcall, nextfun, matchedarg, env, m);
|
| 6113 |
rgentlem |
741 |
UNPROTECT(10);
|
| 1839 |
ihaka |
742 |
return(ans);
|
| 2 |
r |
743 |
}
|
|
|
744 |
|
|
|
745 |
SEXP do_unclass(SEXP call, SEXP op, SEXP args, SEXP env)
|
|
|
746 |
{
|
| 1839 |
ihaka |
747 |
checkArity(op, args);
|
|
|
748 |
if (isObject(CAR(args))) {
|
| 10172 |
luke |
749 |
SETCAR(args, duplicate(CAR(args)));
|
| 1839 |
ihaka |
750 |
setAttrib(CAR(args), R_ClassSymbol, R_NilValue);
|
|
|
751 |
}
|
|
|
752 |
return CAR(args);
|
| 2 |
r |
753 |
}
|
|
|
754 |
|
| 21352 |
maechler |
755 |
/* ___unused___ InheritsClass() and RemoveClass() */
|
| 13606 |
maechler |
756 |
Rboolean InheritsClass(SEXP x, char *name)
|
| 2 |
r |
757 |
{
|
| 13606 |
maechler |
758 |
/* does an object inherit from a class ? */
|
| 1839 |
ihaka |
759 |
SEXP class;
|
|
|
760 |
int i, nclass;
|
|
|
761 |
if (isObject(x)) {
|
|
|
762 |
class = getAttrib(x, R_ClassSymbol);
|
|
|
763 |
nclass = length(class);
|
|
|
764 |
for (i = 0; i < nclass; i++)
|
| 10172 |
luke |
765 |
if (!strcmp(CHAR(STRING_ELT(class, i)), name))
|
| 13606 |
maechler |
766 |
return TRUE;
|
| 1839 |
ihaka |
767 |
}
|
| 13606 |
maechler |
768 |
return FALSE;
|
| 2 |
r |
769 |
}
|
|
|
770 |
|
|
|
771 |
void RemoveClass(SEXP x, char *name)
|
|
|
772 |
{
|
| 1839 |
ihaka |
773 |
SEXP class, newclass;
|
|
|
774 |
int i, j, nclass, nmatch;
|
| 2 |
r |
775 |
|
| 1839 |
ihaka |
776 |
if (isObject(x)) {
|
|
|
777 |
PROTECT(x);
|
|
|
778 |
class = getAttrib(x, R_ClassSymbol);
|
|
|
779 |
nclass = length(class);
|
|
|
780 |
nmatch = 0;
|
|
|
781 |
for (i = 0; i < nclass; i++)
|
| 10172 |
luke |
782 |
if (!strcmp(CHAR(STRING_ELT(class, i)), name))
|
| 1839 |
ihaka |
783 |
nmatch++;
|
|
|
784 |
if (nmatch == nclass) {
|
|
|
785 |
setAttrib(x, R_ClassSymbol, R_NilValue);
|
|
|
786 |
}
|
|
|
787 |
else if (nmatch > 0) {
|
|
|
788 |
PROTECT(newclass = allocVector(STRSXP, nclass-nmatch));
|
|
|
789 |
for (i = 0, j = 0; i < nclass; i++)
|
| 10172 |
luke |
790 |
if (strcmp(CHAR(STRING_ELT(class, i)), name)) {
|
|
|
791 |
SET_STRING_ELT(newclass, j++, STRING_ELT(class, i));
|
| 2 |
r |
792 |
}
|
| 1839 |
ihaka |
793 |
setAttrib(x, R_ClassSymbol, newclass);
|
|
|
794 |
UNPROTECT(1);
|
| 2 |
r |
795 |
}
|
| 1839 |
ihaka |
796 |
UNPROTECT(1);
|
|
|
797 |
}
|
| 2 |
r |
798 |
}
|
| 9608 |
rgentlem |
799 |
|
|
|
800 |
SEXP do_inherits(SEXP call, SEXP op, SEXP args, SEXP env)
|
|
|
801 |
{
|
| 9928 |
ripley |
802 |
SEXP x, class, what, which, rval = R_NilValue /* -Wall */;
|
|
|
803 |
int i, j, nwhat, isvec, nclass;
|
| 9608 |
rgentlem |
804 |
|
|
|
805 |
checkArity(op, args);
|
|
|
806 |
|
|
|
807 |
x = CAR(args);
|
|
|
808 |
/* if x isn't an object get out asap */
|
| 23630 |
ripley |
809 |
/* if( !isObject(x) )
|
|
|
810 |
return mkFalse();
|
| 21352 |
maechler |
811 |
|
| 23630 |
ripley |
812 |
class = getAttrib(x, R_ClassSymbol);*/
|
|
|
813 |
|
|
|
814 |
class = R_data_class(x, FALSE);
|
| 9608 |
rgentlem |
815 |
nclass = length(class);
|
|
|
816 |
|
|
|
817 |
what = CADR(args);
|
|
|
818 |
if( !isString(what) )
|
|
|
819 |
errorcall(call, "what must be a character vector");
|
|
|
820 |
nwhat = length(what);
|
|
|
821 |
|
|
|
822 |
which = CADDR(args);
|
|
|
823 |
if( !isLogical(which) || (length(which) != 1) )
|
|
|
824 |
errorcall(call, "which must be a length 1 logical vector");
|
|
|
825 |
isvec = asLogical(which);
|
|
|
826 |
|
|
|
827 |
if( isvec )
|
|
|
828 |
rval = allocVector(INTSXP, nwhat);
|
|
|
829 |
|
|
|
830 |
for(j=0; j<nwhat; j++) {
|
|
|
831 |
for(i=0; i<nclass; i++) {
|
|
|
832 |
if( isvec )
|
|
|
833 |
INTEGER(rval)[j] = 0;
|
| 10172 |
luke |
834 |
if(!strcmp(CHAR(STRING_ELT(class,i)), CHAR(STRING_ELT(what,j)))) {
|
| 21352 |
maechler |
835 |
if(isvec)
|
|
|
836 |
INTEGER(rval)[j] = i+1;
|
|
|
837 |
else
|
| 9608 |
rgentlem |
838 |
return mkTrue();
|
|
|
839 |
break;
|
|
|
840 |
}
|
|
|
841 |
}
|
|
|
842 |
}
|
| 21352 |
maechler |
843 |
if( !isvec )
|
| 9608 |
rgentlem |
844 |
return mkFalse();
|
|
|
845 |
return rval;
|
|
|
846 |
}
|
| 15890 |
jmc |
847 |
|
| 21352 |
maechler |
848 |
|
| 15890 |
jmc |
849 |
/* standardGeneric: uses a pointer to R_standardGeneric, to be
|
|
|
850 |
initialized when the methods package is attached. When and if the
|
|
|
851 |
methods code is automatically included, the pointer will not be
|
| 21352 |
maechler |
852 |
needed
|
| 15912 |
ripley |
853 |
|
| 15890 |
jmc |
854 |
*/
|
| 15938 |
jmc |
855 |
static R_stdGen_ptr_t R_standardGeneric_ptr = 0;
|
| 21102 |
jmc |
856 |
static SEXP dispatchNonGeneric(SEXP name, SEXP env, SEXP fdef);
|
|
|
857 |
#define NOT_METHODS_DISPATCH_PTR(ptr) (ptr == 0 || ptr == dispatchNonGeneric)
|
| 15890 |
jmc |
858 |
|
| 21352 |
maechler |
859 |
R_stdGen_ptr_t R_get_standardGeneric_ptr()
|
| 16747 |
ripley |
860 |
{
|
|
|
861 |
return R_standardGeneric_ptr;
|
| 15938 |
jmc |
862 |
}
|
|
|
863 |
|
| 26082 |
jmc |
864 |
R_stdGen_ptr_t R_set_standardGeneric_ptr(R_stdGen_ptr_t val, SEXP envir)
|
| 16747 |
ripley |
865 |
{
|
|
|
866 |
R_stdGen_ptr_t old = R_standardGeneric_ptr;
|
|
|
867 |
R_standardGeneric_ptr = val;
|
| 26082 |
jmc |
868 |
if(envir && !isNull(envir))
|
|
|
869 |
R_MethodsNamespace = envir;
|
|
|
870 |
/* just in case ... */
|
|
|
871 |
if(!R_MethodsNamespace)
|
|
|
872 |
R_MethodsNamespace = R_GlobalEnv;
|
| 16747 |
ripley |
873 |
return old;
|
| 15938 |
jmc |
874 |
}
|
|
|
875 |
|
| 21102 |
jmc |
876 |
SEXP R_isMethodsDispatchOn(SEXP onOff) {
|
|
|
877 |
SEXP value = allocVector(LGLSXP, 1);
|
|
|
878 |
Rboolean onOffValue;
|
|
|
879 |
R_stdGen_ptr_t old = R_get_standardGeneric_ptr();
|
|
|
880 |
LOGICAL(value)[0] = !NOT_METHODS_DISPATCH_PTR(old);
|
| 23073 |
ripley |
881 |
if(length(onOff) > 0) {
|
| 21102 |
jmc |
882 |
onOffValue = asLogical(onOff);
|
|
|
883 |
if(onOffValue == FALSE)
|
| 26082 |
jmc |
884 |
R_set_standardGeneric_ptr(0, 0);
|
| 21102 |
jmc |
885 |
else if(NOT_METHODS_DISPATCH_PTR(old)) {
|
|
|
886 |
SEXP call;
|
|
|
887 |
PROTECT(call = allocList(2));
|
|
|
888 |
SETCAR(call, install("initMethodsDispatch"));
|
|
|
889 |
eval(call, R_GlobalEnv); /* only works with
|
| 21352 |
maechler |
890 |
methods attached */
|
| 21102 |
jmc |
891 |
}
|
|
|
892 |
}
|
|
|
893 |
return value;
|
|
|
894 |
}
|
|
|
895 |
|
| 23073 |
ripley |
896 |
/* simpler version for internal use */
|
|
|
897 |
|
|
|
898 |
Rboolean isMethodsDispatchOn(void)
|
|
|
899 |
{
|
|
|
900 |
return !NOT_METHODS_DISPATCH_PTR(R_standardGeneric_ptr);
|
|
|
901 |
}
|
|
|
902 |
|
|
|
903 |
|
| 21352 |
maechler |
904 |
static SEXP dispatchNonGeneric(SEXP name, SEXP env, SEXP fdef)
|
| 16747 |
ripley |
905 |
{
|
|
|
906 |
/* dispatch the non-generic definition of `name'. Used to trap
|
|
|
907 |
calls to standardGeneric during the loading of the methods package */
|
|
|
908 |
SEXP e, value, rho, fun, symbol, dot_Generic;
|
|
|
909 |
RCNTXT *cptr;
|
|
|
910 |
/* find a non-generic function */
|
|
|
911 |
symbol = install(CHAR(asChar(name)));
|
|
|
912 |
dot_Generic = install(".Generic");
|
|
|
913 |
for(rho = ENCLOS(env); rho != R_NilValue && isEnvironment(rho);
|
|
|
914 |
rho = ENCLOS(rho)) {
|
| 17233 |
ripley |
915 |
fun = findVarInFrame3(rho, symbol, TRUE);
|
| 16747 |
ripley |
916 |
if(fun == R_UnboundValue) continue;
|
|
|
917 |
switch(TYPEOF(fun)) {
|
|
|
918 |
case BUILTINSXP: case SPECIALSXP: break;
|
|
|
919 |
case CLOSXP:
|
| 17233 |
ripley |
920 |
value = findVarInFrame3(CLOENV(fun), dot_Generic, TRUE);
|
| 16747 |
ripley |
921 |
if(value == R_UnboundValue) break;
|
|
|
922 |
/*in all other cases, go on to the parent environment */
|
|
|
923 |
}
|
|
|
924 |
fun = R_UnboundValue;
|
| 16372 |
jmc |
925 |
}
|
| 16747 |
ripley |
926 |
fun = SYMVALUE(symbol);
|
|
|
927 |
if(fun == R_UnboundValue)
|
| 21352 |
maechler |
928 |
error("Unable to find a non-generic version of function \"%s\"",
|
| 16747 |
ripley |
929 |
CHAR(asChar(name)));
|
|
|
930 |
cptr = R_GlobalContext;
|
|
|
931 |
/* check this is the right context */
|
| 16372 |
jmc |
932 |
while (cptr != R_ToplevelContext) {
|
|
|
933 |
if (cptr->callflag & CTXT_FUNCTION )
|
|
|
934 |
if (cptr->cloenv == env)
|
|
|
935 |
break;
|
|
|
936 |
cptr = cptr->nextcontext;
|
|
|
937 |
}
|
| 21352 |
maechler |
938 |
|
| 16747 |
ripley |
939 |
PROTECT(e = duplicate(R_syscall(0, cptr)));
|
|
|
940 |
SETCAR(e, fun);
|
|
|
941 |
/* evaluate a call the non-generic with the same arguments and from
|
|
|
942 |
the same environment as the call to the generic version */
|
|
|
943 |
value = eval(e, cptr->sysparent);
|
|
|
944 |
UNPROTECT(1);
|
|
|
945 |
return value;
|
| 16372 |
jmc |
946 |
}
|
|
|
947 |
|
| 20089 |
jmc |
948 |
|
| 16635 |
ripley |
949 |
#ifdef UNUSED
|
| 16302 |
jmc |
950 |
static void load_methods_package()
|
|
|
951 |
{
|
| 16747 |
ripley |
952 |
SEXP e;
|
| 26082 |
jmc |
953 |
R_set_standardGeneric_ptr(dispatchNonGeneric, NULL);
|
| 16747 |
ripley |
954 |
PROTECT(e = allocVector(LANGSXP, 2));
|
|
|
955 |
SETCAR(e, install("library"));
|
|
|
956 |
SETCAR(CDR(e), install("methods"));
|
|
|
957 |
eval(e, R_GlobalEnv);
|
|
|
958 |
UNPROTECT(1);
|
| 16302 |
jmc |
959 |
}
|
| 16635 |
ripley |
960 |
#endif
|
| 16302 |
jmc |
961 |
|
| 20089 |
jmc |
962 |
static SEXP get_this_generic(SEXP args);
|
|
|
963 |
|
| 15890 |
jmc |
964 |
SEXP do_standardGeneric(SEXP call, SEXP op, SEXP args, SEXP env)
|
|
|
965 |
{
|
| 20089 |
jmc |
966 |
SEXP arg, value, fdef; R_stdGen_ptr_t ptr = R_get_standardGeneric_ptr();
|
| 16747 |
ripley |
967 |
if(!ptr) {
|
| 21102 |
jmc |
968 |
warning("standardGeneric called without methods dispatch enabled (will be ignored)");
|
| 26082 |
jmc |
969 |
R_set_standardGeneric_ptr(dispatchNonGeneric, NULL);
|
| 16747 |
ripley |
970 |
ptr = R_get_standardGeneric_ptr();
|
|
|
971 |
}
|
| 20994 |
jmc |
972 |
PROTECT(args);
|
| 16747 |
ripley |
973 |
PROTECT(arg = CAR(args));
|
| 22291 |
jmc |
974 |
if(!isValidStringF(arg))
|
|
|
975 |
error("Argument to standardGeneric must be a non-empty character string");
|
|
|
976 |
|
| 20089 |
jmc |
977 |
PROTECT(fdef = get_this_generic(args));
|
| 15890 |
jmc |
978 |
|
| 22291 |
jmc |
979 |
if(isNull(fdef))
|
| 22293 |
jmc |
980 |
error("Call to standardGeneric(\"%s\") apparently not from the body of that generic function", CHAR(STRING_ELT(arg, 0)));
|
| 22291 |
jmc |
981 |
|
| 20089 |
jmc |
982 |
value = (*ptr)(arg, env, fdef);
|
| 21352 |
maechler |
983 |
|
| 20994 |
jmc |
984 |
UNPROTECT(3);
|
| 16747 |
ripley |
985 |
return value;
|
| 15890 |
jmc |
986 |
}
|
|
|
987 |
|
| 16707 |
jmc |
988 |
static int maxMethodsOffset = 0, curMaxOffset;
|
|
|
989 |
typedef enum {NO_METHODS, NEEDS_RESET, HAS_METHODS, SUPPRESSED} prim_methods_t;
|
|
|
990 |
|
|
|
991 |
static prim_methods_t *prim_methods;
|
|
|
992 |
static SEXP *prim_generics;
|
|
|
993 |
static SEXP *prim_mlist;
|
|
|
994 |
#define DEFAULT_N_PRIM_METHODS 100
|
|
|
995 |
|
|
|
996 |
SEXP R_set_prim_method(SEXP fname, SEXP op, SEXP code_vec, SEXP fundef,
|
|
|
997 |
SEXP mlist)
|
|
|
998 |
{
|
| 16747 |
ripley |
999 |
char *code_string;
|
|
|
1000 |
if(!isValidString(code_vec))
|
|
|
1001 |
error("Argument \"code\" must be a character string");
|
|
|
1002 |
code_string = CHAR(asChar(code_vec));
|
|
|
1003 |
do_set_prim_method(op, code_string, fundef, mlist);
|
|
|
1004 |
return(fname);
|
| 16707 |
jmc |
1005 |
}
|
|
|
1006 |
|
| 20089 |
jmc |
1007 |
SEXP R_primitive_methods(SEXP op)
|
|
|
1008 |
{
|
|
|
1009 |
int offset = PRIMOFFSET(op);
|
| 24661 |
ripley |
1010 |
if(offset < 0 || offset > curMaxOffset)
|
| 21352 |
maechler |
1011 |
return R_NilValue;
|
| 20089 |
jmc |
1012 |
else {
|
| 21352 |
maechler |
1013 |
SEXP value = prim_mlist[offset];
|
| 20089 |
jmc |
1014 |
return value ? value : R_NilValue;
|
|
|
1015 |
}
|
|
|
1016 |
}
|
|
|
1017 |
|
| 16707 |
jmc |
1018 |
SEXP do_set_prim_method(SEXP op, char *code_string, SEXP fundef, SEXP mlist)
|
|
|
1019 |
{
|
| 18397 |
ripley |
1020 |
int offset = 0;
|
|
|
1021 |
prim_methods_t code = NO_METHODS; /* -Wall */
|
|
|
1022 |
SEXP value;
|
|
|
1023 |
Rboolean errorcase = FALSE;
|
|
|
1024 |
switch(code_string[0]) {
|
|
|
1025 |
case 'c': /* clear */
|
|
|
1026 |
code = NO_METHODS; break;
|
|
|
1027 |
case 'r': /* reset */
|
|
|
1028 |
code = NEEDS_RESET; break;
|
| 20089 |
jmc |
1029 |
case 's': /* set or suppress */
|
| 18397 |
ripley |
1030 |
switch(code_string[1]) {
|
|
|
1031 |
case 'e': code = HAS_METHODS; break;
|
|
|
1032 |
case 'u': code = SUPPRESSED; break;
|
|
|
1033 |
default: errorcase = TRUE;
|
|
|
1034 |
}
|
|
|
1035 |
break;
|
|
|
1036 |
default:
|
|
|
1037 |
errorcase = TRUE;
|
| 16707 |
jmc |
1038 |
}
|
| 18397 |
ripley |
1039 |
if(errorcase) {
|
| 20994 |
jmc |
1040 |
error("Invalid primitive methods code (\"%s\"): should be \"clear\", \"reset\", \"set\", or \"suppress\"", code_string);
|
| 18397 |
ripley |
1041 |
return R_NilValue;
|
|
|
1042 |
}
|
|
|
1043 |
switch(TYPEOF(op)) {
|
|
|
1044 |
case BUILTINSXP: case SPECIALSXP:
|
|
|
1045 |
offset = PRIMOFFSET(op);
|
|
|
1046 |
break;
|
| 21352 |
maechler |
1047 |
default:
|
| 18397 |
ripley |
1048 |
error("Invalid object: must be a primitive function");
|
|
|
1049 |
}
|
|
|
1050 |
if(offset >= maxMethodsOffset) {
|
|
|
1051 |
int n;
|
| 24205 |
ripley |
1052 |
n = offset + 1;
|
| 18397 |
ripley |
1053 |
if(n < DEFAULT_N_PRIM_METHODS)
|
|
|
1054 |
n = DEFAULT_N_PRIM_METHODS;
|
|
|
1055 |
if(n < 2*maxMethodsOffset)
|
|
|
1056 |
n = 2 * maxMethodsOffset;
|
|
|
1057 |
if(prim_methods) {
|
| 19214 |
pd |
1058 |
int i;
|
| 19197 |
pd |
1059 |
|
| 19214 |
pd |
1060 |
prim_methods = Realloc(prim_methods, n, prim_methods_t);
|
|
|
1061 |
prim_generics = Realloc(prim_generics, n, SEXP);
|
| 21352 |
maechler |
1062 |
prim_mlist = Realloc(prim_mlist, n, SEXP);
|
| 19214 |
pd |
1063 |
|
|
|
1064 |
/* Realloc does not clear the added memory, hence: */
|
|
|
1065 |
for (i = maxMethodsOffset ; i < n ; i++) {
|
| 21352 |
maechler |
1066 |
prim_methods[i] = NO_METHODS;
|
| 19214 |
pd |
1067 |
prim_generics[i] = NULL;
|
| 21352 |
maechler |
1068 |
prim_mlist[i] = NULL;
|
| 19214 |
pd |
1069 |
}
|
| 18397 |
ripley |
1070 |
}
|
|
|
1071 |
else {
|
| 19214 |
pd |
1072 |
prim_methods = Calloc(n, prim_methods_t);
|
| 18397 |
ripley |
1073 |
prim_generics = Calloc(n, SEXP);
|
| 21352 |
maechler |
1074 |
prim_mlist = Calloc(n, SEXP);
|
| 18397 |
ripley |
1075 |
}
|
|
|
1076 |
maxMethodsOffset = n;
|
|
|
1077 |
}
|
|
|
1078 |
if(offset > curMaxOffset)
|
|
|
1079 |
curMaxOffset = offset;
|
|
|
1080 |
prim_methods[offset] = code;
|
|
|
1081 |
/* store a preserved pointer to the generic function if there is not
|
|
|
1082 |
one there currently. Unpreserve it if no more methods, but don't
|
|
|
1083 |
replace it otherwise: the generic definition is not allowed to
|
|
|
1084 |
change while it's still defined! (the stored methods list can,
|
|
|
1085 |
however) */
|
|
|
1086 |
value = prim_generics[offset];
|
| 20089 |
jmc |
1087 |
if(code == SUPPRESSED) {} /* leave the structure alone */
|
|
|
1088 |
else if(code == NO_METHODS && prim_generics[offset]) {
|
| 18397 |
ripley |
1089 |
R_ReleaseObject(prim_generics[offset]);
|
|
|
1090 |
prim_generics[offset] = 0;
|
|
|
1091 |
prim_mlist[offset] = 0;
|
|
|
1092 |
}
|
|
|
1093 |
else if(fundef && !isNull(fundef) && !prim_generics[offset]) {
|
|
|
1094 |
if(TYPEOF(fundef) != CLOSXP)
|
|
|
1095 |
error("The formal definition of a primitive generic must be a function object (got type %s)",
|
|
|
1096 |
type2str(TYPEOF(fundef)));
|
|
|
1097 |
R_PreserveObject(fundef);
|
|
|
1098 |
prim_generics[offset] = fundef;
|
|
|
1099 |
}
|
|
|
1100 |
if(code==HAS_METHODS) {
|
| 20089 |
jmc |
1101 |
if(!mlist || isNull(mlist)) {
|
|
|
1102 |
/* turning methods back on after a SUPPRESSED */
|
|
|
1103 |
}
|
|
|
1104 |
else {
|
|
|
1105 |
if(prim_mlist[offset])
|
| 18397 |
ripley |
1106 |
R_ReleaseObject(prim_mlist[offset]);
|
| 20089 |
jmc |
1107 |
R_PreserveObject(mlist);
|
|
|
1108 |
prim_mlist[offset] = mlist;
|
|
|
1109 |
}
|
| 18397 |
ripley |
1110 |
}
|
|
|
1111 |
return value;
|
| 16707 |
jmc |
1112 |
}
|
|
|
1113 |
|
| 17092 |
jmc |
1114 |
static SEXP get_primitive_methods(SEXP op, SEXP rho)
|
|
|
1115 |
{
|
|
|
1116 |
SEXP f, e;
|
|
|
1117 |
int nprotect = 0;
|
|
|
1118 |
f = PROTECT(allocVector(STRSXP, 1)); nprotect++;
|
|
|
1119 |
SET_STRING_ELT(f, 0, mkChar(PRIMNAME(op)));
|
|
|
1120 |
PROTECT(e = allocVector(LANGSXP, 2)); nprotect++;
|
|
|
1121 |
SETCAR(e, install("getMethods"));
|
|
|
1122 |
SETCAR(CDR(e), f);
|
|
|
1123 |
e = eval(e, rho);
|
|
|
1124 |
UNPROTECT(nprotect);
|
|
|
1125 |
return e;
|
|
|
1126 |
}
|
| 20089 |
jmc |
1127 |
|
|
|
1128 |
/* get the generic function, defined to be the function definition for
|
|
|
1129 |
the call to standardGeneric(), or for primitives, passed as the second
|
|
|
1130 |
argument to standardGeneric.
|
|
|
1131 |
*/
|
|
|
1132 |
static SEXP get_this_generic(SEXP args)
|
|
|
1133 |
{
|
| 22450 |
ripley |
1134 |
SEXP value = R_NilValue; static SEXP gen_name;
|
| 20994 |
jmc |
1135 |
int i, n;
|
|
|
1136 |
RCNTXT *cptr; char *fname;
|
| 20277 |
ripley |
1137 |
|
| 20089 |
jmc |
1138 |
/* a second argument to the call, if any, is taken as the function */
|
|
|
1139 |
if(CDR(args) != R_NilValue)
|
| 20277 |
ripley |
1140 |
return CAR(CDR(args));
|
| 20994 |
jmc |
1141 |
/* else use sys.function (this is fairly expensive-- would be good
|
|
|
1142 |
* to force a second argument if possible) */
|
| 20154 |
jmc |
1143 |
PROTECT(args);
|
| 20994 |
jmc |
1144 |
if(!gen_name)
|
|
|
1145 |
gen_name = install("generic");
|
| 20089 |
jmc |
1146 |
cptr = R_GlobalContext;
|
| 20994 |
jmc |
1147 |
fname = CHAR(asChar(CAR(args)));
|
|
|
1148 |
n = framedepth(cptr);
|
|
|
1149 |
/* check for a matching "generic" slot */
|
|
|
1150 |
for(i=0; i<n; i++) {
|
| 22379 |
ripley |
1151 |
SEXP rval = R_sysfunction(i, cptr);
|
| 20994 |
jmc |
1152 |
if(isObject(rval)) {
|
|
|
1153 |
SEXP generic = getAttrib(rval, gen_name);
|
|
|
1154 |
if(TYPEOF(generic) == STRSXP &&
|
| 22291 |
jmc |
1155 |
!strcmp(CHAR(asChar(generic)), fname)) {
|
|
|
1156 |
value = rval;
|
|
|
1157 |
break;
|
|
|
1158 |
}
|
| 20994 |
jmc |
1159 |
}
|
| 20154 |
jmc |
1160 |
}
|
|
|
1161 |
UNPROTECT(1);
|
| 22291 |
jmc |
1162 |
return(value);
|
| 20089 |
jmc |
1163 |
}
|
| 17092 |
jmc |
1164 |
|
| 21352 |
maechler |
1165 |
/* Could there be methods for this op? Checks
|
| 16707 |
jmc |
1166 |
only whether methods are currently being dispatched and, if so,
|
|
|
1167 |
whether methods are currently defined for this op. */
|
| 17000 |
jmc |
1168 |
Rboolean R_has_methods(SEXP op)
|
| 16707 |
jmc |
1169 |
{
|
| 16747 |
ripley |
1170 |
R_stdGen_ptr_t ptr = R_get_standardGeneric_ptr(); int offset;
|
| 21102 |
jmc |
1171 |
if(NOT_METHODS_DISPATCH_PTR(ptr))
|
| 17000 |
jmc |
1172 |
return(FALSE);
|
| 22764 |
jmc |
1173 |
if(!op || TYPEOF(op) == CLOSXP) /* except for primitives, just test for the package */
|
| 17000 |
jmc |
1174 |
return(TRUE);
|
| 16747 |
ripley |
1175 |
offset = PRIMOFFSET(op);
|
|
|
1176 |
if(offset > curMaxOffset || prim_methods[offset] == NO_METHODS
|
|
|
1177 |
|| prim_methods[offset] == SUPPRESSED)
|
| 17000 |
jmc |
1178 |
return(FALSE);
|
|
|
1179 |
return(TRUE);
|
| 16707 |
jmc |
1180 |
}
|
|
|
1181 |
|
|
|
1182 |
static SEXP deferred_default_object;
|
|
|
1183 |
|
|
|
1184 |
SEXP R_deferred_default_method()
|
|
|
1185 |
{
|
| 16747 |
ripley |
1186 |
if(!deferred_default_object)
|
|
|
1187 |
deferred_default_object = install("__Deferred_Default_Marker__");
|
|
|
1188 |
return(deferred_default_object);
|
| 16707 |
jmc |
1189 |
}
|
|
|
1190 |
|
|
|
1191 |
|
|
|
1192 |
static R_stdGen_ptr_t quick_method_check_ptr = NULL;
|
|
|
1193 |
void R_set_quick_method_check(R_stdGen_ptr_t value)
|
|
|
1194 |
{
|
| 16747 |
ripley |
1195 |
quick_method_check_ptr = value;
|
| 16707 |
jmc |
1196 |
}
|
| 23073 |
ripley |
1197 |
|
| 16707 |
jmc |
1198 |
/* try to dispatch the formal method for this primitive op, by calling
|
| 21352 |
maechler |
1199 |
the stored generic function corresponding to the op. Requires that
|
| 16707 |
jmc |
1200 |
the methods be set up to return a special object rather than trying
|
|
|
1201 |
to evaluate the default (which would get us into a loop). */
|
| 23073 |
ripley |
1202 |
SEXP R_possible_dispatch(SEXP call, SEXP op, SEXP args, SEXP rho)
|
| 16707 |
jmc |
1203 |
{
|
| 27710 |
ripley |
1204 |
SEXP fundef, value, mlist=R_NilValue;
|
|
|
1205 |
int offset;
|
|
|
1206 |
prim_methods_t current;
|
| 23073 |
ripley |
1207 |
offset = PRIMOFFSET(op);
|
|
|
1208 |
if(offset < 0 || offset > curMaxOffset)
|
|
|
1209 |
error("Invalid primitive operation given for dispatch");
|
|
|
1210 |
current = prim_methods[offset];
|
|
|
1211 |
if(current == NO_METHODS || current == SUPPRESSED)
|
|
|
1212 |
return(NULL);
|
|
|
1213 |
/* check that the methods for this function have been set */
|
|
|
1214 |
if(current == NEEDS_RESET) {
|
|
|
1215 |
/* get the methods and store them in the in-core primitive
|
|
|
1216 |
method table. The entries will be preserved via
|
|
|
1217 |
R_preserveobject, so later we can just grab mlist from
|
|
|
1218 |
prim_mlist */
|
| 26648 |
ripley |
1219 |
do_set_prim_method(op, "suppressed", R_NilValue, mlist);
|
| 23073 |
ripley |
1220 |
PROTECT(mlist = get_primitive_methods(op, rho));
|
|
|
1221 |
do_set_prim_method(op, "set", R_NilValue, mlist);
|
| 26016 |
jmc |
1222 |
current = prim_methods[offset]; /* as revised by do_set_prim_method */
|
| 23073 |
ripley |
1223 |
UNPROTECT(1);
|
|
|
1224 |
}
|
|
|
1225 |
mlist = prim_mlist[offset];
|
|
|
1226 |
if(mlist && !isNull(mlist)
|
|
|
1227 |
&& quick_method_check_ptr) {
|
|
|
1228 |
value = (*quick_method_check_ptr)(args, mlist, op);
|
|
|
1229 |
if(isPrimitive(value))
|
|
|
1230 |
return(NULL);
|
|
|
1231 |
if(isFunction(value))
|
|
|
1232 |
/* found a method, call it */
|
|
|
1233 |
return applyClosure(call, value, args, rho, R_NilValue);
|
|
|
1234 |
/* else, need to perform full method search */
|
|
|
1235 |
}
|
|
|
1236 |
fundef = prim_generics[offset];
|
|
|
1237 |
if(!fundef || TYPEOF(fundef) != CLOSXP)
|
|
|
1238 |
error("primitive function \"%s\" has been set for methods but no generic function supplied",
|
|
|
1239 |
PRIMNAME(op));
|
|
|
1240 |
/* To do: arrange for the setting to be restored in case of an
|
|
|
1241 |
error in method search */
|
|
|
1242 |
value = applyClosure(call, fundef, args, rho, R_NilValue);
|
|
|
1243 |
prim_methods[offset] = current;
|
|
|
1244 |
if(value == deferred_default_object)
|
|
|
1245 |
return NULL;
|
|
|
1246 |
else
|
|
|
1247 |
return value;
|
| 16707 |
jmc |
1248 |
}
|
| 21853 |
jmc |
1249 |
|
|
|
1250 |
SEXP R_do_MAKE_CLASS( char *what)
|
|
|
1251 |
{
|
|
|
1252 |
static SEXP s_getClass = NULL;
|
|
|
1253 |
SEXP e, call;
|
|
|
1254 |
if(!what)
|
|
|
1255 |
error("C level MAKE_CLASS macro called with NULL string pointer");
|
|
|
1256 |
if(!s_getClass)
|
|
|
1257 |
s_getClass = Rf_install("getClass");
|
|
|
1258 |
PROTECT(call = allocVector(LANGSXP, 2));
|
|
|
1259 |
SETCAR(call, s_getClass);
|
|
|
1260 |
SETCAR(CDR(call), mkString(what));
|
|
|
1261 |
e = eval(call, R_GlobalEnv);
|
|
|
1262 |
UNPROTECT(1);
|
|
|
1263 |
return(e);
|
|
|
1264 |
}
|
|
|
1265 |
|
|
|
1266 |
SEXP R_do_new_object(SEXP class_def)
|
|
|
1267 |
{
|
|
|
1268 |
static SEXP s_virtual = NULL, s_prototype, s_className;
|
|
|
1269 |
SEXP e, value;
|
|
|
1270 |
if(!s_virtual) {
|
|
|
1271 |
s_virtual = Rf_install("virtual");
|
|
|
1272 |
s_prototype = Rf_install("prototype");
|
|
|
1273 |
s_className = Rf_install("className");
|
|
|
1274 |
}
|
|
|
1275 |
if(!class_def)
|
|
|
1276 |
error("C level NEW macro called with null class definition pointer");
|
|
|
1277 |
e = R_do_slot(class_def, s_virtual);
|
|
|
1278 |
if(asLogical(e) != 0) { /* includes NA, TRUE, or anything other than FALSE */
|
|
|
1279 |
e = R_do_slot(class_def, s_className);
|
|
|
1280 |
error("Trying to generate an object in C from a virtual class (\"%s\")",
|
|
|
1281 |
CHAR(asChar(e)));
|
|
|
1282 |
}
|
|
|
1283 |
e = R_do_slot(class_def, s_className);
|
| 22303 |
jmc |
1284 |
value = duplicate(R_do_slot(class_def, s_prototype));
|
| 21853 |
jmc |
1285 |
setAttrib(value, R_ClassSymbol, e);
|
|
|
1286 |
return value;
|
|
|
1287 |
}
|