1 /*
2 * itcl2TclOO.c --
3 *
4 * This file contains code to create and manage methods.
5 *
6 * Copyright (c) 2007 by Arnulf P. Wiedemann
7 *
8 * See the file "license.terms" for information on usage and redistribution of
9 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 */
11
12 #include <tclInt.h>
13 #include <tclOOInt.h>
14 #undef FOREACH_HASH_DECLS
15 #undef FOREACH_HASH
16 #undef FOREACH_HASH_VALUE
17 #include "itclInt.h"
18
19 void *
Itcl_GetCurrentCallbackPtr(Tcl_Interp * interp)20 Itcl_GetCurrentCallbackPtr(
21 Tcl_Interp *interp)
22 {
23 return TOP_CB(interp);
24 }
25
26 int
Itcl_NRRunCallbacks(Tcl_Interp * interp,void * rootPtr)27 Itcl_NRRunCallbacks(
28 Tcl_Interp *interp,
29 void *rootPtr)
30 {
31 return TclNRRunCallbacks(interp, TCL_OK, (NRE_callback*)rootPtr);
32 }
33
34 static int
CallFinalizePMCall(void * data[],Tcl_Interp * interp,int result)35 CallFinalizePMCall(
36 void *data[],
37 Tcl_Interp *interp,
38 int result)
39 {
40 Tcl_Namespace *nsPtr = (Tcl_Namespace *)data[0];
41 TclOO_PostCallProc *postCallProc = (TclOO_PostCallProc *)data[1];
42 void *clientData = data[2];
43
44 /*
45 * Give the post-call callback a chance to do some cleanup. Note that at
46 * this point the call frame itself is invalid; it's already been popped.
47 */
48
49 return postCallProc(clientData, interp, NULL, nsPtr, result);
50 }
51
52 static int
FreeCommand(void * data[],Tcl_Interp * dummy,int result)53 FreeCommand(
54 void *data[],
55 Tcl_Interp *dummy,
56 int result)
57 {
58 Command *cmdPtr = (Command *)data[0];
59 Proc *procPtr = (Proc *)data[1];
60 (void)dummy;
61
62 ckfree(cmdPtr);
63 procPtr->cmdPtr = NULL;
64
65 return result;
66 }
67
68 static int
Tcl_InvokeClassProcedureMethod(Tcl_Interp * interp,Tcl_Obj * namePtr,Tcl_Namespace * nsPtr,ProcedureMethod * pmPtr,int objc,Tcl_Obj * const * objv)69 Tcl_InvokeClassProcedureMethod(
70 Tcl_Interp *interp,
71 Tcl_Obj *namePtr, /* name of the method */
72 Tcl_Namespace *nsPtr, /* namespace for calling method */
73 ProcedureMethod *pmPtr, /* method type specific data */
74 int objc, /* Number of arguments. */
75 Tcl_Obj *const *objv) /* Arguments as actually seen. */
76 {
77 Proc *procPtr = pmPtr->procPtr;
78 CallFrame *framePtr = NULL;
79 CallFrame **framePtrPtr1 = &framePtr;
80 Tcl_CallFrame **framePtrPtr = (Tcl_CallFrame **)framePtrPtr1;
81 int result;
82
83 if (procPtr->cmdPtr == NULL) {
84 Command *cmdPtr = (Command *)ckalloc(sizeof(Command));
85
86 memset(cmdPtr, 0, sizeof(Command));
87 cmdPtr->nsPtr = (Namespace *) nsPtr;
88 cmdPtr->clientData = NULL;
89 procPtr->cmdPtr = cmdPtr;
90 Tcl_NRAddCallback(interp, FreeCommand, cmdPtr, procPtr, NULL, NULL);
91 }
92
93 result = TclProcCompileProc(interp, procPtr, procPtr->bodyPtr,
94 (Namespace *) nsPtr, "body of method", Tcl_GetString(namePtr));
95 if (result != TCL_OK) {
96 return result;
97 }
98 /*
99 * Make the stack frame and fill it out with information about this call.
100 * This operation may fail.
101 */
102
103
104 result = TclPushStackFrame(interp, framePtrPtr, nsPtr, FRAME_IS_PROC);
105 if (result != TCL_OK) {
106 return result;
107 }
108
109 framePtr->clientData = NULL;
110 framePtr->objc = objc;
111 framePtr->objv = objv;
112 framePtr->procPtr = procPtr;
113
114 /*
115 * Give the pre-call callback a chance to do some setup and, possibly,
116 * veto the call.
117 */
118
119 if (pmPtr->preCallProc != NULL) {
120 int isFinished;
121
122 result = pmPtr->preCallProc(pmPtr->clientData, interp, NULL,
123 (Tcl_CallFrame *) framePtr, &isFinished);
124 if (isFinished || result != TCL_OK) {
125 Tcl_PopCallFrame(interp);
126 TclStackFree(interp, framePtr);
127 goto done;
128 }
129 }
130
131 /*
132 * Now invoke the body of the method. Note that we need to take special
133 * action when doing unknown processing to ensure that the missing method
134 * name is passed as an argument.
135 */
136
137 if (pmPtr->postCallProc) {
138 Tcl_NRAddCallback(interp, CallFinalizePMCall, nsPtr,
139 (void *)pmPtr->postCallProc, pmPtr->clientData, NULL);
140 }
141 return TclNRInterpProcCore(interp, namePtr, 1, pmPtr->errProc);
142
143 done:
144 return result;
145 }
146
147 int
Itcl_InvokeProcedureMethod(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)148 Itcl_InvokeProcedureMethod(
149 void *clientData, /* Pointer to some per-method context. */
150 Tcl_Interp *interp,
151 int objc, /* Number of arguments. */
152 Tcl_Obj *const *objv) /* Arguments as actually seen. */
153 {
154 Tcl_Namespace *nsPtr;
155 Method *mPtr;
156
157 mPtr = (Method *)clientData;
158 if (mPtr->declaringClassPtr == NULL) {
159 /* that is the case for typemethods */
160 nsPtr = mPtr->declaringObjectPtr->namespacePtr;
161 } else {
162 nsPtr = mPtr->declaringClassPtr->thisPtr->namespacePtr;
163 }
164
165 return Tcl_InvokeClassProcedureMethod(interp, mPtr->namePtr, nsPtr,
166 (ProcedureMethod *)mPtr->clientData, objc, objv);
167 }
168
169 static int
FreeProcedureMethod(void * data[],Tcl_Interp * dummy,int result)170 FreeProcedureMethod(
171 void *data[],
172 Tcl_Interp *dummy,
173 int result)
174 {
175 ProcedureMethod *pmPtr = (ProcedureMethod *)data[0];
176 (void)dummy;
177
178 ckfree(pmPtr);
179 return result;
180 }
181
182 static void
EnsembleErrorProc(Tcl_Interp * interp,Tcl_Obj * procNameObj)183 EnsembleErrorProc(
184 Tcl_Interp *interp,
185 Tcl_Obj *procNameObj)
186 {
187 int overflow, limit = 60, nameLen;
188 const char *procName = Tcl_GetStringFromObj(procNameObj, &nameLen);
189
190 overflow = (nameLen > limit);
191 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
192 "\n (itcl ensemble part \"%.*s%s\" line %d)",
193 (overflow ? limit : nameLen), procName,
194 (overflow ? "..." : ""), Tcl_GetErrorLine(interp)));
195 }
196
197 int
Itcl_InvokeEnsembleMethod(Tcl_Interp * interp,Tcl_Namespace * nsPtr,Tcl_Obj * namePtr,Tcl_Proc * procPtr,int objc,Tcl_Obj * const * objv)198 Itcl_InvokeEnsembleMethod(
199 Tcl_Interp *interp,
200 Tcl_Namespace *nsPtr, /* namespace to call the method in */
201 Tcl_Obj *namePtr, /* name of the method */
202 Tcl_Proc *procPtr,
203 int objc, /* Number of arguments. */
204 Tcl_Obj *const *objv) /* Arguments as actually seen. */
205 {
206 ProcedureMethod *pmPtr = (ProcedureMethod *)ckalloc(sizeof(ProcedureMethod));
207
208 memset(pmPtr, 0, sizeof(ProcedureMethod));
209 pmPtr->version = TCLOO_PROCEDURE_METHOD_VERSION;
210 pmPtr->procPtr = (Proc *)procPtr;
211 pmPtr->flags = USE_DECLARER_NS;
212 pmPtr->errProc = EnsembleErrorProc;
213
214 Tcl_NRAddCallback(interp, FreeProcedureMethod, pmPtr, NULL, NULL, NULL);
215 return Tcl_InvokeClassProcedureMethod(interp, namePtr, nsPtr,
216 pmPtr, objc, objv);
217 }
218
219
220 /*
221 * ----------------------------------------------------------------------
222 *
223 * Itcl_PublicObjectCmd, Itcl_PrivateObjectCmd --
224 *
225 * Main entry point for object invokations. The Public* and Private*
226 * wrapper functions are just thin wrappers around the main ObjectCmd
227 * function that does call chain creation, management and invokation.
228 *
229 * ----------------------------------------------------------------------
230 */
231
232 int
Itcl_PublicObjectCmd(void * clientData,Tcl_Interp * interp,Tcl_Class clsPtr,int objc,Tcl_Obj * const * objv)233 Itcl_PublicObjectCmd(
234 void *clientData,
235 Tcl_Interp *interp,
236 Tcl_Class clsPtr,
237 int objc,
238 Tcl_Obj *const *objv)
239 {
240 Tcl_Object oPtr = (Tcl_Object)clientData;
241 int result;
242
243 if (oPtr) {
244 result = TclOOInvokeObject(interp, oPtr, clsPtr, PUBLIC_METHOD,
245 objc, objv);
246 } else {
247 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
248 "cannot access object-specific info without an object context",
249 NULL);
250 return TCL_ERROR;
251 }
252 return result;
253 }
254
255 /*
256 * ----------------------------------------------------------------------
257 *
258 * Itcl_NewProcClassMethod --
259 *
260 * Create a new procedure-like method for a class for Itcl.
261 *
262 * ----------------------------------------------------------------------
263 */
264
265 Tcl_Method
Itcl_NewProcClassMethod(Tcl_Interp * interp,Tcl_Class clsPtr,TclOO_PreCallProc * preCallPtr,TclOO_PostCallProc * postCallPtr,ProcErrorProc * errProc,void * clientData,Tcl_Obj * nameObj,Tcl_Obj * argsObj,Tcl_Obj * bodyObj,void ** clientData2)266 Itcl_NewProcClassMethod(
267 Tcl_Interp *interp, /* The interpreter containing the class. */
268 Tcl_Class clsPtr, /* The class to modify. */
269 TclOO_PreCallProc *preCallPtr,
270 TclOO_PostCallProc *postCallPtr,
271 ProcErrorProc *errProc,
272 void *clientData,
273 Tcl_Obj *nameObj, /* The name of the method, which may be NULL;
274 * if so, up to caller to manage storage
275 * (e.g., because it is a constructor or
276 * destructor). */
277 Tcl_Obj *argsObj, /* The formal argument list for the method,
278 * which may be NULL; if so, it is equivalent
279 * to an empty list. */
280 Tcl_Obj *bodyObj, /* The body of the method, which must not be
281 * NULL. */
282 void **clientData2)
283 {
284 Tcl_Method result;
285
286 result = TclOONewProcMethodEx(interp, clsPtr, preCallPtr, postCallPtr,
287 errProc, clientData, nameObj, argsObj, bodyObj,
288 PUBLIC_METHOD | USE_DECLARER_NS, clientData2);
289 return result;
290 }
291
292 /*
293 * ----------------------------------------------------------------------
294 *
295 * Itcl_NewProcMethod --
296 *
297 * Create a new procedure-like method for an object for Itcl.
298 *
299 * ----------------------------------------------------------------------
300 */
301
302 Tcl_Method
Itcl_NewProcMethod(Tcl_Interp * interp,Tcl_Object oPtr,TclOO_PreCallProc * preCallPtr,TclOO_PostCallProc * postCallPtr,ProcErrorProc * errProc,void * clientData,Tcl_Obj * nameObj,Tcl_Obj * argsObj,Tcl_Obj * bodyObj,void ** clientData2)303 Itcl_NewProcMethod(
304 Tcl_Interp *interp, /* The interpreter containing the object. */
305 Tcl_Object oPtr, /* The object to modify. */
306 TclOO_PreCallProc *preCallPtr,
307 TclOO_PostCallProc *postCallPtr,
308 ProcErrorProc *errProc,
309 void *clientData,
310 Tcl_Obj *nameObj, /* The name of the method, which must not be
311 * NULL. */
312 Tcl_Obj *argsObj, /* The formal argument list for the method,
313 * which must not be NULL. */
314 Tcl_Obj *bodyObj, /* The body of the method, which must not be
315 * NULL. */
316 void **clientData2)
317 {
318 return TclOONewProcInstanceMethodEx(interp, oPtr, preCallPtr, postCallPtr,
319 errProc, clientData, nameObj, argsObj, bodyObj,
320 PUBLIC_METHOD | USE_DECLARER_NS, clientData2);
321 }
322
323 /*
324 * ----------------------------------------------------------------------
325 *
326 * Itcl_NewForwardClassMethod --
327 *
328 * Create a new forwarded method for a class for Itcl.
329 *
330 * ----------------------------------------------------------------------
331 */
332
333 Tcl_Method
Itcl_NewForwardClassMethod(Tcl_Interp * interp,Tcl_Class clsPtr,int flags,Tcl_Obj * nameObj,Tcl_Obj * prefixObj)334 Itcl_NewForwardClassMethod(
335 Tcl_Interp *interp,
336 Tcl_Class clsPtr,
337 int flags,
338 Tcl_Obj *nameObj,
339 Tcl_Obj *prefixObj)
340 {
341 return (Tcl_Method)TclOONewForwardMethod(interp, (Class *)clsPtr,
342 flags, nameObj, prefixObj);
343 }
344
345
346 static Tcl_Obj *
Itcl_TclOOObjectName(Tcl_Interp * interp,Object * oPtr)347 Itcl_TclOOObjectName(
348 Tcl_Interp *interp,
349 Object *oPtr)
350 {
351 Tcl_Obj *namePtr;
352
353 if (oPtr->cachedNameObj) {
354 return oPtr->cachedNameObj;
355 }
356 namePtr = Tcl_NewObj();
357 Tcl_GetCommandFullName(interp, oPtr->command, namePtr);
358 Tcl_IncrRefCount(namePtr);
359 oPtr->cachedNameObj = namePtr;
360 return namePtr;
361 }
362
363 int
Itcl_SelfCmd(void * dummy,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)364 Itcl_SelfCmd(
365 void *dummy,
366 Tcl_Interp *interp,
367 int objc,
368 Tcl_Obj *const *objv)
369 {
370 Interp *iPtr = (Interp *) interp;
371 CallFrame *framePtr = iPtr->varFramePtr;
372 CallContext *contextPtr;
373 (void)dummy;
374
375 if (!Itcl_IsMethodCallFrame(interp)) {
376 Tcl_AppendResult(interp, TclGetString(objv[0]),
377 " may only be called from inside a method", NULL);
378 return TCL_ERROR;
379 }
380
381 contextPtr = (CallContext *)framePtr->clientData;
382
383 if (objc == 1) {
384 Tcl_SetObjResult(interp, Itcl_TclOOObjectName(interp, contextPtr->oPtr));
385 return TCL_OK;
386 }
387 return TCL_ERROR;
388 }
389
390 int
Itcl_IsMethodCallFrame(Tcl_Interp * interp)391 Itcl_IsMethodCallFrame(
392 Tcl_Interp *interp)
393 {
394 Interp *iPtr = (Interp *) interp;
395 CallFrame *framePtr = iPtr->varFramePtr;
396 if (framePtr == NULL || !(framePtr->isProcCallFrame & FRAME_IS_METHOD)) {
397 return 0;
398 }
399 return 1;
400 }
401