OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [itcl/] [itcl/] [generic/] [itcl_methods.c] - Blame information for rev 1773

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * ------------------------------------------------------------------------
3
 *      PACKAGE:  [incr Tcl]
4
 *  DESCRIPTION:  Object-Oriented Extensions to Tcl
5
 *
6
 *  [incr Tcl] provides object-oriented extensions to Tcl, much as
7
 *  C++ provides object-oriented extensions to C.  It provides a means
8
 *  of encapsulating related procedures together with their shared data
9
 *  in a local namespace that is hidden from the outside world.  It
10
 *  promotes code re-use through inheritance.  More than anything else,
11
 *  it encourages better organization of Tcl applications through the
12
 *  object-oriented paradigm, leading to code that is easier to
13
 *  understand and maintain.
14
 *
15
 *  These procedures handle commands available within a class scope.
16
 *  In [incr Tcl], the term "method" is used for a procedure that has
17
 *  access to object-specific data, while the term "proc" is used for
18
 *  a procedure that has access only to common class data.
19
 *
20
 * ========================================================================
21
 *  AUTHOR:  Michael J. McLennan
22
 *           Bell Labs Innovations for Lucent Technologies
23
 *           mmclennan@lucent.com
24
 *           http://www.tcltk.com/itcl
25
 *
26
 *     RCS:  $Id: itcl_methods.c,v 1.1.1.1 2002-01-16 10:24:46 markom Exp $
27
 * ========================================================================
28
 *           Copyright (c) 1993-1998  Lucent Technologies, Inc.
29
 * ------------------------------------------------------------------------
30
 * See the file "license.terms" for information on usage and redistribution
31
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
32
 */
33
#include "itclInt.h"
34
#include "tclCompile.h"
35
 
36
/* CYGNUS LOCAL */
37
/* FIXME - it looks like Michael removed the dependance on these... */
38
#if 0
39
#ifdef __CYGWIN32__
40
 
41
/* On cygwin32, this is how we import these variables from the Tcl DLL.  */
42
 
43
extern int              *_imp__tclTraceCompile;
44
 
45
#define tclTraceCompile (*_imp__tclTraceCompile)
46
 
47
extern int              *_imp__tclTraceExec;
48
 
49
#define tclTraceExec (*_imp__tclTraceExec)
50
 
51
extern Tcl_ObjType      *_imp__tclByteCodeType;
52
 
53
#define tclByteCodeType (*_imp__tclByteCodeType)
54
 
55
#endif
56
#endif
57
/* END CYGNUS LOCAL */
58
 
59
/*
60
 *  FORWARD DECLARATIONS
61
 */
62
static int ItclParseConfig _ANSI_ARGS_((Tcl_Interp *interp,
63
    int objc, Tcl_Obj *CONST objv[], ItclObject *contextObj,
64
    int *rargc, ItclVarDefn ***rvars, char ***rvals));
65
 
66
static int ItclHandleConfig _ANSI_ARGS_((Tcl_Interp *interp,
67
    int argc, ItclVarDefn **vars, char **vals, ItclObject *contextObj));
68
 
69
 
70
/*
71
 * ------------------------------------------------------------------------
72
 *  Itcl_BodyCmd()
73
 *
74
 *  Invoked by Tcl whenever the user issues an "itcl::body" command to
75
 *  define or redefine the implementation for a class method/proc.
76
 *  Handles the following syntax:
77
 *
78
 *    itcl::body <class>::<func> <arglist> <body>
79
 *
80
 *  Looks for an existing class member function with the name <func>,
81
 *  and if found, tries to assign the implementation.  If an argument
82
 *  list was specified in the original declaration, it must match
83
 *  <arglist> or an error is flagged.  If <body> has the form "@name"
84
 *  then it is treated as a reference to a C handling procedure;
85
 *  otherwise, it is taken as a body of Tcl statements.
86
 *
87
 *  Returns TCL_OK/TCL_ERROR to indicate success/failure.
88
 * ------------------------------------------------------------------------
89
 */
90
/* ARGSUSED */
91
int
92
Itcl_BodyCmd(dummy, interp, objc, objv)
93
    ClientData dummy;        /* unused */
94
    Tcl_Interp *interp;      /* current interpreter */
95
    int objc;                /* number of arguments */
96
    Tcl_Obj *CONST objv[];   /* argument objects */
97
{
98
    int status = TCL_OK;
99
 
100
    char *head, *tail, *token, *arglist, *body;
101
    ItclClass *cdefn;
102
    ItclMemberFunc *mfunc;
103
    Tcl_HashEntry *entry;
104
    Tcl_DString buffer;
105
 
106
    if (objc != 4) {
107
        token = Tcl_GetStringFromObj(objv[0], (int*)NULL);
108
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
109
            "wrong # args: should be \"",
110
            token, " class::func arglist body\"",
111
            (char*)NULL);
112
        return TCL_ERROR;
113
    }
114
 
115
    /*
116
     *  Parse the member name "namesp::namesp::class::func".
117
     *  Make sure that a class name was specified, and that the
118
     *  class exists.
119
     */
120
    token = Tcl_GetStringFromObj(objv[1], (int*)NULL);
121
    Itcl_ParseNamespPath(token, &buffer, &head, &tail);
122
 
123
    if (!head || *head == '\0') {
124
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
125
            "missing class specifier for body declaration \"", token, "\"",
126
            (char*)NULL);
127
        status = TCL_ERROR;
128
        goto bodyCmdDone;
129
    }
130
 
131
    cdefn = Itcl_FindClass(interp, head, /* autoload */ 1);
132
    if (cdefn == NULL) {
133
        status = TCL_ERROR;
134
        goto bodyCmdDone;
135
    }
136
 
137
    /*
138
     *  Find the function and try to change its implementation.
139
     *  Note that command resolution table contains *all* functions,
140
     *  even those in a base class.  Make sure that the class
141
     *  containing the method definition is the requested class.
142
     */
143
    if (objc != 4) {
144
        token = Tcl_GetStringFromObj(objv[0], (int*)NULL);
145
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
146
            "wrong # args: should be \"",
147
            token, " class::func arglist body\"",
148
            (char*)NULL);
149
        status = TCL_ERROR;
150
        goto bodyCmdDone;
151
    }
152
 
153
    mfunc = NULL;
154
    entry = Tcl_FindHashEntry(&cdefn->resolveCmds, tail);
155
    if (entry) {
156
        mfunc = (ItclMemberFunc*)Tcl_GetHashValue(entry);
157
        if (mfunc->member->classDefn != cdefn) {
158
            mfunc = NULL;
159
        }
160
    }
161
 
162
    if (mfunc == NULL) {
163
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
164
            "function \"", tail, "\" is not defined in class \"",
165
            cdefn->fullname, "\"",
166
            (char*)NULL);
167
        status = TCL_ERROR;
168
        goto bodyCmdDone;
169
    }
170
 
171
    arglist = Tcl_GetStringFromObj(objv[2], (int*)NULL);
172
    body    = Tcl_GetStringFromObj(objv[3], (int*)NULL);
173
 
174
    if (Itcl_ChangeMemberFunc(interp, mfunc, arglist, body) != TCL_OK) {
175
        status = TCL_ERROR;
176
        goto bodyCmdDone;
177
    }
178
 
179
bodyCmdDone:
180
    Tcl_DStringFree(&buffer);
181
    return status;
182
}
183
 
184
 
185
/*
186
 * ------------------------------------------------------------------------
187
 *  Itcl_ConfigBodyCmd()
188
 *
189
 *  Invoked by Tcl whenever the user issues an "itcl::configbody" command
190
 *  to define or redefine the configuration code associated with a
191
 *  public variable.  Handles the following syntax:
192
 *
193
 *    itcl::configbody <class>::<publicVar> <body>
194
 *
195
 *  Looks for an existing public variable with the name <publicVar>,
196
 *  and if found, tries to assign the implementation.  If <body> has
197
 *  the form "@name" then it is treated as a reference to a C handling
198
 *  procedure; otherwise, it is taken as a body of Tcl statements.
199
 *
200
 *  Returns TCL_OK/TCL_ERROR to indicate success/failure.
201
 * ------------------------------------------------------------------------
202
 */
203
/* ARGSUSED */
204
int
205
Itcl_ConfigBodyCmd(dummy, interp, objc, objv)
206
    ClientData dummy;        /* unused */
207
    Tcl_Interp *interp;      /* current interpreter */
208
    int objc;                /* number of arguments */
209
    Tcl_Obj *CONST objv[];   /* argument objects */
210
{
211
    int status = TCL_OK;
212
 
213
    char *head, *tail, *token;
214
    Tcl_DString buffer;
215
    ItclClass *cdefn;
216
    ItclVarLookup *vlookup;
217
    ItclMember *member;
218
    ItclMemberCode *mcode;
219
    Tcl_HashEntry *entry;
220
 
221
    if (objc != 3) {
222
        Tcl_WrongNumArgs(interp, 1, objv, "class::option body");
223
        return TCL_ERROR;
224
    }
225
 
226
    /*
227
     *  Parse the member name "namesp::namesp::class::option".
228
     *  Make sure that a class name was specified, and that the
229
     *  class exists.
230
     */
231
    token = Tcl_GetStringFromObj(objv[1], (int*)NULL);
232
    Itcl_ParseNamespPath(token, &buffer, &head, &tail);
233
 
234
    if (!head || *head == '\0') {
235
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
236
            "missing class specifier for body declaration \"", token, "\"",
237
            (char*)NULL);
238
        status = TCL_ERROR;
239
        goto configBodyCmdDone;
240
    }
241
 
242
    cdefn = Itcl_FindClass(interp, head, /* autoload */ 1);
243
    if (cdefn == NULL) {
244
        status = TCL_ERROR;
245
        goto configBodyCmdDone;
246
    }
247
 
248
    /*
249
     *  Find the variable and change its implementation.
250
     *  Note that variable resolution table has *all* variables,
251
     *  even those in a base class.  Make sure that the class
252
     *  containing the variable definition is the requested class.
253
     */
254
    vlookup = NULL;
255
    entry = Tcl_FindHashEntry(&cdefn->resolveVars, tail);
256
    if (entry) {
257
        vlookup = (ItclVarLookup*)Tcl_GetHashValue(entry);
258
        if (vlookup->vdefn->member->classDefn != cdefn) {
259
            vlookup = NULL;
260
        }
261
    }
262
 
263
    if (vlookup == NULL) {
264
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
265
            "option \"", tail, "\" is not defined in class \"",
266
            cdefn->fullname, "\"",
267
            (char*)NULL);
268
        status = TCL_ERROR;
269
        goto configBodyCmdDone;
270
    }
271
    member = vlookup->vdefn->member;
272
 
273
    if (member->protection != ITCL_PUBLIC) {
274
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
275
            "option \"", member->fullname,
276
            "\" is not a public configuration option",
277
            (char*)NULL);
278
        status = TCL_ERROR;
279
        goto configBodyCmdDone;
280
    }
281
 
282
    token = Tcl_GetStringFromObj(objv[2], (int*)NULL);
283
 
284
    if (Itcl_CreateMemberCode(interp, cdefn, (char*)NULL, token,
285
        &mcode) != TCL_OK) {
286
 
287
        status = TCL_ERROR;
288
        goto configBodyCmdDone;
289
    }
290
 
291
    Itcl_PreserveData((ClientData)mcode);
292
    Itcl_EventuallyFree((ClientData)mcode, Itcl_DeleteMemberCode);
293
 
294
    if (member->code) {
295
        Itcl_ReleaseData((ClientData)member->code);
296
    }
297
    member->code = mcode;
298
 
299
configBodyCmdDone:
300
    Tcl_DStringFree(&buffer);
301
    return status;
302
}
303
 
304
 
305
/*
306
 * ------------------------------------------------------------------------
307
 *  Itcl_CreateMethod()
308
 *
309
 *  Installs a method into the namespace associated with a class.
310
 *  If another command with the same name is already installed, then
311
 *  it is overwritten.
312
 *
313
 *  Returns TCL_OK on success, or TCL_ERROR (along with an error message
314
 *  in the specified interp) if anything goes wrong.
315
 * ------------------------------------------------------------------------
316
 */
317
int
318
Itcl_CreateMethod(interp, cdefn, name, arglist, body)
319
    Tcl_Interp* interp;  /* interpreter managing this action */
320
    ItclClass *cdefn;    /* class definition */
321
    char* name;          /* name of new method */
322
    char* arglist;       /* space-separated list of arg names */
323
    char* body;          /* body of commands for the method */
324
{
325
    ItclMemberFunc *mfunc;
326
    Tcl_DString buffer;
327
 
328
    /*
329
     *  Make sure that the method name does not contain anything
330
     *  goofy like a "::" scope qualifier.
331
     */
332
    if (strstr(name,"::")) {
333
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
334
            "bad method name \"", name, "\"",
335
            (char*)NULL);
336
        return TCL_ERROR;
337
    }
338
 
339
    /*
340
     *  Create the method definition.
341
     */
342
    if (Itcl_CreateMemberFunc(interp, cdefn, name, arglist, body, &mfunc)
343
        != TCL_OK) {
344
        return TCL_ERROR;
345
    }
346
 
347
    /*
348
     *  Build a fully-qualified name for the method, and install
349
     *  the command handler.
350
     */
351
    Tcl_DStringInit(&buffer);
352
    Tcl_DStringAppend(&buffer, cdefn->namesp->fullName, -1);
353
    Tcl_DStringAppend(&buffer, "::", 2);
354
    Tcl_DStringAppend(&buffer, name, -1);
355
    name = Tcl_DStringValue(&buffer);
356
 
357
    Itcl_PreserveData((ClientData)mfunc);
358
    mfunc->accessCmd = Tcl_CreateObjCommand(interp, name, Itcl_ExecMethod,
359
        (ClientData)mfunc, Itcl_ReleaseData);
360
 
361
    Tcl_DStringFree(&buffer);
362
    return TCL_OK;
363
}
364
 
365
 
366
/*
367
 * ------------------------------------------------------------------------
368
 *  Itcl_CreateProc()
369
 *
370
 *  Installs a class proc into the namespace associated with a class.
371
 *  If another command with the same name is already installed, then
372
 *  it is overwritten.  Returns TCL_OK on success, or TCL_ERROR (along
373
 *  with an error message in the specified interp) if anything goes
374
 *  wrong.
375
 * ------------------------------------------------------------------------
376
 */
377
int
378
Itcl_CreateProc(interp, cdefn, name, arglist, body)
379
    Tcl_Interp* interp;  /* interpreter managing this action */
380
    ItclClass *cdefn;    /* class definition */
381
    char* name;          /* name of new proc */
382
    char* arglist;       /* space-separated list of arg names */
383
    char* body;          /* body of commands for the proc */
384
{
385
    ItclMemberFunc *mfunc;
386
    Tcl_DString buffer;
387
 
388
    /*
389
     *  Make sure that the proc name does not contain anything
390
     *  goofy like a "::" scope qualifier.
391
     */
392
    if (strstr(name,"::")) {
393
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
394
            "bad proc name \"", name, "\"",
395
            (char*)NULL);
396
        return TCL_ERROR;
397
    }
398
 
399
    /*
400
     *  Create the proc definition.
401
     */
402
    if (Itcl_CreateMemberFunc(interp, cdefn, name, arglist, body, &mfunc)
403
        != TCL_OK) {
404
        return TCL_ERROR;
405
    }
406
 
407
    /*
408
     *  Mark procs as "common".  This distinguishes them from methods.
409
     */
410
    mfunc->member->flags |= ITCL_COMMON;
411
 
412
    /*
413
     *  Build a fully-qualified name for the proc, and install
414
     *  the command handler.
415
     */
416
    Tcl_DStringInit(&buffer);
417
    Tcl_DStringAppend(&buffer, cdefn->namesp->fullName, -1);
418
    Tcl_DStringAppend(&buffer, "::", 2);
419
    Tcl_DStringAppend(&buffer, name, -1);
420
    name = Tcl_DStringValue(&buffer);
421
 
422
    Itcl_PreserveData((ClientData)mfunc);
423
    mfunc->accessCmd = Tcl_CreateObjCommand(interp, name, Itcl_ExecProc,
424
        (ClientData)mfunc, Itcl_ReleaseData);
425
 
426
    Tcl_DStringFree(&buffer);
427
    return TCL_OK;
428
}
429
 
430
 
431
/*
432
 * ------------------------------------------------------------------------
433
 *  Itcl_CreateMemberFunc()
434
 *
435
 *  Creates the data record representing a member function.  This
436
 *  includes the argument list and the body of the function.  If the
437
 *  body is of the form "@name", then it is treated as a label for
438
 *  a C procedure registered by Itcl_RegisterC().
439
 *
440
 *  If any errors are encountered, this procedure returns TCL_ERROR
441
 *  along with an error message in the interpreter.  Otherwise, it
442
 *  returns TCL_OK, and "mfuncPtr" returns a pointer to the new
443
 *  member function.
444
 * ------------------------------------------------------------------------
445
 */
446
int
447
Itcl_CreateMemberFunc(interp, cdefn, name, arglist, body, mfuncPtr)
448
    Tcl_Interp* interp;            /* interpreter managing this action */
449
    ItclClass *cdefn;              /* class definition */
450
    char* name;                    /* name of new member */
451
    char* arglist;                 /* space-separated list of arg names */
452
    char* body;                    /* body of commands for the method */
453
    ItclMemberFunc** mfuncPtr;     /* returns: pointer to new method defn */
454
{
455
    int newEntry;
456
    ItclMemberFunc *mfunc;
457
    ItclMemberCode *mcode;
458
    Tcl_HashEntry *entry;
459
 
460
    /*
461
     *  Add the member function to the list of functions for
462
     *  the class.  Make sure that a member function with the
463
     *  same name doesn't already exist.
464
     */
465
    entry = Tcl_CreateHashEntry(&cdefn->functions, name, &newEntry);
466
 
467
    if (!newEntry) {
468
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
469
            "\"", name, "\" already defined in class \"",
470
            cdefn->fullname, "\"",
471
            (char*)NULL);
472
        return TCL_ERROR;
473
    }
474
 
475
    /*
476
     *  Try to create the implementation for this command member.
477
     */
478
    if (Itcl_CreateMemberCode(interp, cdefn, arglist, body,
479
        &mcode) != TCL_OK) {
480
 
481
        Tcl_DeleteHashEntry(entry);
482
        return TCL_ERROR;
483
    }
484
    Itcl_PreserveData((ClientData)mcode);
485
    Itcl_EventuallyFree((ClientData)mcode, Itcl_DeleteMemberCode);
486
 
487
    /*
488
     *  Allocate a member function definition and return.
489
     */
490
    mfunc = (ItclMemberFunc*)ckalloc(sizeof(ItclMemberFunc));
491
    mfunc->member = Itcl_CreateMember(interp, cdefn, name);
492
    mfunc->member->code = mcode;
493
 
494
    if (mfunc->member->protection == ITCL_DEFAULT_PROTECT) {
495
        mfunc->member->protection = ITCL_PUBLIC;
496
    }
497
 
498
    mfunc->arglist   = NULL;
499
    mfunc->argcount  = 0;
500
    mfunc->accessCmd = NULL;
501
 
502
    if (arglist) {
503
        mfunc->member->flags |= ITCL_ARG_SPEC;
504
    }
505
    if (mcode->arglist) {
506
        Itcl_CreateArgList(interp, arglist, &mfunc->argcount, &mfunc->arglist);
507
    }
508
 
509
    if (strcmp(name,"constructor") == 0) {
510
        mfunc->member->flags |= ITCL_CONSTRUCTOR;
511
    }
512
    if (strcmp(name,"destructor") == 0) {
513
        mfunc->member->flags |= ITCL_DESTRUCTOR;
514
    }
515
 
516
    Tcl_SetHashValue(entry, (ClientData)mfunc);
517
    Itcl_PreserveData((ClientData)mfunc);
518
    Itcl_EventuallyFree((ClientData)mfunc, Itcl_DeleteMemberFunc);
519
 
520
    *mfuncPtr = mfunc;
521
    return TCL_OK;
522
}
523
 
524
 
525
/*
526
 * ------------------------------------------------------------------------
527
 *  Itcl_ChangeMemberFunc()
528
 *
529
 *  Modifies the data record representing a member function.  This
530
 *  is usually the body of the function, but can include the argument
531
 *  list if it was not defined when the member was first created.
532
 *  If the body is of the form "@name", then it is treated as a label
533
 *  for a C procedure registered by Itcl_RegisterC().
534
 *
535
 *  If any errors are encountered, this procedure returns TCL_ERROR
536
 *  along with an error message in the interpreter.  Otherwise, it
537
 *  returns TCL_OK, and "mfuncPtr" returns a pointer to the new
538
 *  member function.
539
 * ------------------------------------------------------------------------
540
 */
541
int
542
Itcl_ChangeMemberFunc(interp, mfunc, arglist, body)
543
    Tcl_Interp* interp;            /* interpreter managing this action */
544
    ItclMemberFunc* mfunc;         /* command member being changed */
545
    char* arglist;                 /* space-separated list of arg names */
546
    char* body;                    /* body of commands for the method */
547
{
548
    ItclMemberCode *mcode = NULL;
549
    Tcl_Obj *objPtr;
550
 
551
    /*
552
     *  Try to create the implementation for this command member.
553
     */
554
    if (Itcl_CreateMemberCode(interp, mfunc->member->classDefn,
555
        arglist, body, &mcode) != TCL_OK) {
556
 
557
        return TCL_ERROR;
558
    }
559
 
560
    /*
561
     *  If the argument list was defined when the function was
562
     *  created, compare the arg lists or usage strings to make sure
563
     *  that the interface is not being redefined.
564
     */
565
    if ((mfunc->member->flags & ITCL_ARG_SPEC) != 0 &&
566
        !Itcl_EquivArgLists(mfunc->arglist, mfunc->argcount,
567
            mcode->arglist, mcode->argcount)) {
568
 
569
        objPtr = Itcl_ArgList(mfunc->argcount, mfunc->arglist);
570
        Tcl_IncrRefCount(objPtr);
571
 
572
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
573
            "argument list changed for function \"",
574
            mfunc->member->fullname, "\": should be \"",
575
            Tcl_GetStringFromObj(objPtr, (int*)NULL), "\"",
576
            (char*)NULL);
577
        Tcl_DecrRefCount(objPtr);
578
 
579
        Itcl_DeleteMemberCode((char*)mcode);
580
        return TCL_ERROR;
581
    }
582
 
583
    /*
584
     *  Free up the old implementation and install the new one.
585
     */
586
    Itcl_PreserveData((ClientData)mcode);
587
    Itcl_EventuallyFree((ClientData)mcode, Itcl_DeleteMemberCode);
588
 
589
    Itcl_ReleaseData((ClientData)mfunc->member->code);
590
    mfunc->member->code = mcode;
591
 
592
    return TCL_OK;
593
}
594
 
595
 
596
/*
597
 * ------------------------------------------------------------------------
598
 *  Itcl_DeleteMemberFunc()
599
 *
600
 *  Destroys all data associated with the given member function definition.
601
 *  Usually invoked by the interpreter when a member function is deleted.
602
 * ------------------------------------------------------------------------
603
 */
604
void
605
Itcl_DeleteMemberFunc(cdata)
606
    char* cdata;  /* pointer to member function definition */
607
{
608
    ItclMemberFunc* mfunc = (ItclMemberFunc*)cdata;
609
 
610
    if (mfunc) {
611
        Itcl_DeleteMember(mfunc->member);
612
 
613
        if (mfunc->arglist) {
614
            Itcl_DeleteArgList(mfunc->arglist);
615
        }
616
        ckfree((char*)mfunc);
617
    }
618
}
619
 
620
 
621
/*
622
 * ------------------------------------------------------------------------
623
 *  Itcl_CreateMemberCode()
624
 *
625
 *  Creates the data record representing the implementation behind a
626
 *  class member function.  This includes the argument list and the body
627
 *  of the function.  If the body is of the form "@name", then it is
628
 *  treated as a label for a C procedure registered by Itcl_RegisterC().
629
 *
630
 *  The implementation is kept by the member function definition, and
631
 *  controlled by a preserve/release paradigm.  That way, if it is in
632
 *  use while it is being redefined, it will stay around long enough
633
 *  to avoid a core dump.
634
 *
635
 *  If any errors are encountered, this procedure returns TCL_ERROR
636
 *  along with an error message in the interpreter.  Otherwise, it
637
 *  returns TCL_OK, and "mcodePtr" returns a pointer to the new
638
 *  implementation.
639
 * ------------------------------------------------------------------------
640
 */
641
int
642
Itcl_CreateMemberCode(interp, cdefn, arglist, body, mcodePtr)
643
    Tcl_Interp* interp;            /* interpreter managing this action */
644
    ItclClass *cdefn;              /* class containing this member */
645
    char* arglist;                 /* space-separated list of arg names */
646
    char* body;                    /* body of commands for the method */
647
    ItclMemberCode** mcodePtr;     /* returns: pointer to new implementation */
648
{
649
    int argc;
650
    CompiledLocal *args, *localPtr;
651
    ItclMemberCode *mcode;
652
    Proc *procPtr;
653
 
654
    /*
655
     *  Allocate some space to hold the implementation.
656
     */
657
    mcode = (ItclMemberCode*)ckalloc(sizeof(ItclMemberCode));
658
    mcode->flags        = 0;
659
    mcode->argcount     = 0;
660
    mcode->arglist      = NULL;
661
    mcode->procPtr      = NULL;
662
    mcode->cfunc.objCmd = NULL;
663
    mcode->clientData   = NULL;
664
 
665
    if (arglist) {
666
        if (Itcl_CreateArgList(interp, arglist, &argc, &args)
667
            != TCL_OK) {
668
 
669
            Itcl_DeleteMemberCode((char*)mcode);
670
            return TCL_ERROR;
671
        }
672
        mcode->argcount = argc;
673
        mcode->arglist  = args;
674
        mcode->flags   |= ITCL_ARG_SPEC;
675
    } else {
676
        argc = 0;
677
        args = NULL;
678
    }
679
 
680
    /*
681
     *  Create a standard Tcl Proc representation for this code body.
682
     *  This is required, since the Tcl compiler looks for a proc
683
     *  when handling things such as the call frame context and
684
     *  compiled locals.
685
     */
686
    procPtr = (Proc*)ckalloc(sizeof(Proc));
687
    mcode->procPtr = procPtr;
688
 
689
    procPtr->iPtr = (Interp*)interp;
690
    procPtr->refCount = 1;
691
    procPtr->cmdPtr = (Command*)ckalloc(sizeof(Command));
692
    procPtr->cmdPtr->nsPtr = (Namespace*)cdefn->namesp;
693
 
694
    if (body) {
695
        procPtr->bodyPtr = Tcl_NewStringObj(body, -1);
696
        Tcl_IncrRefCount(procPtr->bodyPtr);
697
    } else {
698
        procPtr->bodyPtr = NULL;
699
    }
700
 
701
    /*
702
     *  Plug the argument list into the "compiled locals" list.
703
     *
704
     *  NOTE:  The storage for this argument list is owned by
705
     *    the caller, so although we plug it in here, it is not
706
     *    our responsibility to free it.
707
     */
708
    procPtr->firstLocalPtr = args;
709
    procPtr->lastLocalPtr = NULL;
710
 
711
    for (localPtr=mcode->arglist; localPtr; localPtr=localPtr->nextPtr) {
712
        procPtr->lastLocalPtr = localPtr;
713
    }
714
    procPtr->numArgs = argc;
715
    procPtr->numCompiledLocals = argc;
716
 
717
    /*
718
     *  If the body definition starts with '@', then treat the value
719
     *  as a symbolic name for a C procedure.
720
     */
721
    if (body == NULL) {
722
        mcode->flags |= ITCL_IMPLEMENT_NONE;
723
    }
724
    else if (*body == '@') {
725
        Tcl_CmdProc *argCmdProc;
726
        Tcl_ObjCmdProc *objCmdProc;
727
        ClientData cdata;
728
 
729
        if (!Itcl_FindC(interp, body+1, &argCmdProc, &objCmdProc, &cdata)) {
730
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
731
                "no registered C procedure with name \"", body+1, "\"",
732
                (char*)NULL);
733
            Itcl_DeleteMemberCode((char*)mcode);
734
            return TCL_ERROR;
735
        }
736
 
737
        if (objCmdProc != NULL) {
738
            mcode->flags |= ITCL_IMPLEMENT_OBJCMD;
739
            mcode->cfunc.objCmd = objCmdProc;
740
            mcode->clientData = cdata;
741
        }
742
        else if (argCmdProc != NULL) {
743
            mcode->flags |= ITCL_IMPLEMENT_ARGCMD;
744
            mcode->cfunc.argCmd = argCmdProc;
745
            mcode->clientData = cdata;
746
        }
747
    }
748
 
749
    /*
750
     *  Otherwise, treat the body as a chunk of Tcl code.
751
     */
752
    else {
753
        mcode->flags |= ITCL_IMPLEMENT_TCL;
754
    }
755
 
756
    *mcodePtr = mcode;
757
    return TCL_OK;
758
}
759
 
760
 
761
/*
762
 * ------------------------------------------------------------------------
763
 *  Itcl_DeleteMemberCode()
764
 *
765
 *  Destroys all data associated with the given command implementation.
766
 *  Invoked automatically by Itcl_ReleaseData() when the implementation
767
 *  is no longer being used.
768
 * ------------------------------------------------------------------------
769
 */
770
void
771
Itcl_DeleteMemberCode(cdata)
772
    char* cdata;  /* pointer to member function definition */
773
{
774
    ItclMemberCode* mcode = (ItclMemberCode*)cdata;
775
 
776
    if (mcode->arglist) {
777
        Itcl_DeleteArgList(mcode->arglist);
778
    }
779
    if (mcode->procPtr) {
780
        ckfree((char*) mcode->procPtr->cmdPtr);
781
 
782
        /* don't free compiled locals -- that is handled by arglist above */
783
 
784
        if (mcode->procPtr->bodyPtr) {
785
            Tcl_DecrRefCount(mcode->procPtr->bodyPtr);
786
        }
787
        ckfree((char*)mcode->procPtr);
788
    }
789
    ckfree((char*)mcode);
790
}
791
 
792
 
793
/*
794
 * ------------------------------------------------------------------------
795
 *  Itcl_GetMemberCode()
796
 *
797
 *  Makes sure that the implementation for an [incr Tcl] code body is
798
 *  ready to run.  Note that a member function can be declared without
799
 *  being defined.  The class definition may contain a declaration of
800
 *  the member function, but its body may be defined in a separate file.
801
 *  If an undefined function is encountered, this routine automatically
802
 *  attempts to autoload it.  If the body is implemented via Tcl code,
803
 *  then it is compiled here as well.
804
 *
805
 *  Returns TCL_ERROR (along with an error message in the interpreter)
806
 *  if an error is encountered, or if the implementation is not defined
807
 *  and cannot be autoloaded.  Returns TCL_OK if implementation is
808
 *  ready to use.
809
 * ------------------------------------------------------------------------
810
 */
811
int
812
Itcl_GetMemberCode(interp, member)
813
    Tcl_Interp* interp;        /* interpreter managing this action */
814
    ItclMember* member;        /* member containing code body */
815
{
816
    ItclMemberCode *mcode = member->code;
817
 
818
    int result;
819
 
820
    /*
821
     *  If the implementation has not yet been defined, try to
822
     *  autoload it now.
823
     */
824
    if ((mcode->flags & ITCL_IMPLEMENT_NONE) != 0) {
825
        result = Tcl_VarEval(interp, "::auto_load ", member->fullname,
826
            (char*)NULL);
827
 
828
        if (result != TCL_OK) {
829
            char msg[256];
830
            sprintf(msg, "\n    (while autoloading code for \"%.100s\")",
831
                member->fullname);
832
            Tcl_AddErrorInfo(interp, msg);
833
            return result;
834
        }
835
        Tcl_ResetResult(interp);  /* get rid of 1/0 status */
836
    }
837
 
838
    /*
839
     *  If the implementation is still not available, then
840
     *  autoloading must have failed.
841
     *
842
     *  TRICKY NOTE:  If code has been autoloaded, then the
843
     *    old mcode pointer is probably invalid.  Go back to
844
     *    the member and look at the current code pointer again.
845
     */
846
    mcode = member->code;
847
 
848
    if ((mcode->flags & ITCL_IMPLEMENT_NONE) != 0) {
849
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
850
            "member function \"", member->fullname,
851
            "\" is not defined and cannot be autoloaded",
852
            (char*)NULL);
853
        return TCL_ERROR;
854
    }
855
 
856
    /*
857
     *  If the member is a constructor and the class has an
858
     *  initialization command, compile it here.
859
     */
860
    if ((member->flags & ITCL_CONSTRUCTOR) != 0 &&
861
        (member->classDefn->initCode != NULL)) {
862
 
863
        result = TclProcCompileProc(interp, mcode->procPtr,
864
            member->classDefn->initCode, (Namespace*)member->classDefn->namesp,
865
            "initialization code for", member->fullname);
866
 
867
        if (result != TCL_OK) {
868
            return result;
869
        }
870
    }
871
 
872
    /*
873
     *  If the code body has a Tcl implementation, then compile it here.
874
     */
875
    if ((mcode->flags & ITCL_IMPLEMENT_TCL) != 0) {
876
 
877
        result = TclProcCompileProc(interp, mcode->procPtr,
878
            mcode->procPtr->bodyPtr, (Namespace*)member->classDefn->namesp,
879
            "body for", member->fullname);
880
 
881
        if (result != TCL_OK) {
882
            return result;
883
        }
884
    }
885
    return TCL_OK;
886
}
887
 
888
 
889
/*
890
 * ------------------------------------------------------------------------
891
 *  Itcl_EvalMemberCode()
892
 *
893
 *  Used to execute an ItclMemberCode representation of a code
894
 *  fragment.  This code may be a body of Tcl commands, or a C handler
895
 *  procedure.
896
 *
897
 *  Executes the command with the given arguments (objc,objv) and
898
 *  returns an integer status code (TCL_OK/TCL_ERROR).  Returns the
899
 *  result string or an error message in the interpreter.
900
 * ------------------------------------------------------------------------
901
 */
902
int
903
Itcl_EvalMemberCode(interp, mfunc, member, contextObj, objc, objv)
904
    Tcl_Interp *interp;       /* current interpreter */
905
    ItclMemberFunc *mfunc;    /* member func, or NULL (for error messages) */
906
    ItclMember *member;       /* command member containing code */
907
    ItclObject *contextObj;   /* object context, or NULL */
908
    int objc;                 /* number of arguments */
909
    Tcl_Obj *CONST objv[];    /* argument objects */
910
{
911
    int result = TCL_OK;
912
    Tcl_CallFrame *oldFramePtr = NULL;
913
 
914
    int i, transparent, newEntry;
915
    ItclObjectInfo *info;
916
    ItclMemberCode *mcode;
917
    ItclContext context;
918
    Tcl_CallFrame *framePtr, *transFramePtr;
919
 
920
    /*
921
     *  If this code does not have an implementation yet, then
922
     *  try to autoload one.  Also, if this is Tcl code, make sure
923
     *  that it's compiled and ready to use.
924
     */
925
    if (Itcl_GetMemberCode(interp, member) != TCL_OK) {
926
        return TCL_ERROR;
927
    }
928
    mcode = member->code;
929
 
930
    /*
931
     *  Bump the reference count on this code, in case it is
932
     *  redefined or deleted during execution.
933
     */
934
    Itcl_PreserveData((ClientData)mcode);
935
 
936
    /*
937
     *  Install a new call frame context for the current code.
938
     *  If the current call frame is marked as "transparent", then
939
     *  do an "uplevel" operation to move past it.  Transparent
940
     *  call frames are installed by Itcl_HandleInstance.  They
941
     *  provide a way of entering an object context without
942
     *  interfering with the normal call stack.
943
     */
944
    transparent = 0;
945
 
946
    info = member->classDefn->info;
947
    framePtr = _Tcl_GetCallFrame(interp, 0);
948
    for (i = Itcl_GetStackSize(&info->transparentFrames)-1; i >= 0; i--) {
949
        transFramePtr = (Tcl_CallFrame*)
950
            Itcl_GetStackValue(&info->transparentFrames, i);
951
 
952
        if (framePtr == transFramePtr) {
953
            transparent = 1;
954
            break;
955
        }
956
    }
957
 
958
    if (transparent) {
959
        framePtr = _Tcl_GetCallFrame(interp, 1);
960
        oldFramePtr = _Tcl_ActivateCallFrame(interp, framePtr);
961
    }
962
 
963
    if (Itcl_PushContext(interp, member, member->classDefn, contextObj,
964
        &context) != TCL_OK) {
965
 
966
        return TCL_ERROR;
967
    }
968
 
969
    /*
970
     *  If this is a method with a Tcl implementation, or a
971
     *  constructor with initCode, then parse its arguments now.
972
     */
973
    if (mfunc && objc > 0) {
974
        if ((mcode->flags & ITCL_IMPLEMENT_TCL) != 0 ||
975
            ( (member->flags & ITCL_CONSTRUCTOR) != 0 &&
976
              (member->classDefn->initCode != NULL) ) ) {
977
 
978
            if (Itcl_AssignArgs(interp, objc, objv, mfunc) != TCL_OK) {
979
                result = TCL_ERROR;
980
                goto evalMemberCodeDone;
981
            }
982
        }
983
    }
984
 
985
    /*
986
     *  If this code is a constructor, and if it is being invoked
987
     *  when an object is first constructed (i.e., the "constructed"
988
     *  table is still active within the object), then handle the
989
     *  "initCode" associated with the constructor and make sure that
990
     *  all base classes are properly constructed.
991
     *
992
     *  TRICKY NOTE:
993
     *    The "initCode" must be executed here.  This is the only
994
     *    opportunity where the arguments of the constructor are
995
     *    available in a call frame.
996
     */
997
    if ((member->flags & ITCL_CONSTRUCTOR) && contextObj &&
998
        contextObj->constructed) {
999
 
1000
        result = Itcl_ConstructBase(interp, contextObj, member->classDefn);
1001
 
1002
        if (result != TCL_OK) {
1003
            goto evalMemberCodeDone;
1004
        }
1005
    }
1006
 
1007
    /*
1008
     *  Execute the code body...
1009
     */
1010
    if ((mcode->flags & ITCL_IMPLEMENT_OBJCMD) != 0) {
1011
        result = (*mcode->cfunc.objCmd)(mcode->clientData,
1012
            interp, objc, objv);
1013
    }
1014
    else if ((mcode->flags & ITCL_IMPLEMENT_ARGCMD) != 0) {
1015
        char **argv;
1016
        argv = (char**)ckalloc( (unsigned)(objc*sizeof(char*)) );
1017
        for (i=0; i < objc; i++) {
1018
            argv[i] = Tcl_GetStringFromObj(objv[i], (int*)NULL);
1019
        }
1020
 
1021
        result = (*mcode->cfunc.argCmd)(mcode->clientData,
1022
            interp, objc, argv);
1023
 
1024
        ckfree((char*)argv);
1025
    }
1026
    else if ((mcode->flags & ITCL_IMPLEMENT_TCL) != 0) {
1027
      /* CYGNUS LOCAL - Fix for Tcl8.1 */
1028
#if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0
1029
      result = Tcl_EvalObj(interp, mcode->procPtr->bodyPtr);
1030
#else
1031
      result = Tcl_EvalObj(interp, mcode->procPtr->bodyPtr, 0);
1032
#endif
1033
      /* END CYGNUS LOCAL */
1034
    }
1035
    else {
1036
        panic("itcl: bad implementation flag for %s", member->fullname);
1037
    }
1038
 
1039
    /*
1040
     *  If this is a constructor or destructor, and if it is being
1041
     *  invoked at the appropriate time, keep track of which methods
1042
     *  have been called.  This information is used to implicitly
1043
     *  invoke constructors/destructors as needed.
1044
     */
1045
    if ((member->flags & ITCL_DESTRUCTOR) && contextObj &&
1046
         contextObj->destructed) {
1047
 
1048
        Tcl_CreateHashEntry(contextObj->destructed,
1049
            member->classDefn->name, &newEntry);
1050
    }
1051
    if ((member->flags & ITCL_CONSTRUCTOR) && contextObj &&
1052
         contextObj->constructed) {
1053
 
1054
        Tcl_CreateHashEntry(contextObj->constructed,
1055
            member->classDefn->name, &newEntry);
1056
    }
1057
 
1058
evalMemberCodeDone:
1059
    Itcl_PopContext(interp, &context);
1060
 
1061
    if (transparent) {
1062
        (void) _Tcl_ActivateCallFrame(interp, oldFramePtr);
1063
    }
1064
    Itcl_ReleaseData((ClientData)mcode);
1065
 
1066
    return result;
1067
}
1068
 
1069
 
1070
/*
1071
 * ------------------------------------------------------------------------
1072
 *  Itcl_CreateArgList()
1073
 *
1074
 *  Parses a Tcl list representing an argument declaration and returns
1075
 *  a linked list of CompiledLocal values.  Usually invoked as part
1076
 *  of Itcl_CreateMemberFunc() when a new method or procedure is being
1077
 *  defined.
1078
 * ------------------------------------------------------------------------
1079
 */
1080
int
1081
Itcl_CreateArgList(interp, decl, argcPtr, argPtr)
1082
    Tcl_Interp* interp;       /* interpreter managing this function */
1083
    char* decl;               /* string representing argument list */
1084
    int* argcPtr;             /* returns number of args in argument list */
1085
    CompiledLocal** argPtr;   /* returns pointer to parsed argument list */
1086
{
1087
    int status = TCL_OK;  /* assume that this will succeed */
1088
 
1089
    int i, argc, fargc;
1090
    char **argv, **fargv;
1091
    CompiledLocal *localPtr, *last;
1092
 
1093
    *argPtr = last = NULL;
1094
    *argcPtr = 0;
1095
 
1096
    if (decl) {
1097
        if (Tcl_SplitList(interp, decl, &argc, &argv) != TCL_OK) {
1098
            return TCL_ERROR;
1099
        }
1100
 
1101
        for (i=0; i < argc && status == TCL_OK; i++) {
1102
            if (Tcl_SplitList(interp, argv[i], &fargc, &fargv) != TCL_OK) {
1103
                status = TCL_ERROR;
1104
            }
1105
            else {
1106
                localPtr = NULL;
1107
 
1108
                if (fargc == 0 || *fargv[0] == '\0') {
1109
                    char mesg[100];
1110
                    sprintf(mesg, "argument #%d has no name", i);
1111
                    Tcl_SetResult(interp, mesg, TCL_VOLATILE);
1112
                    status = TCL_ERROR;
1113
                }
1114
                else if (fargc > 2) {
1115
                    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1116
                        "too many fields in argument specifier \"",
1117
                        argv[i], "\"",
1118
                        (char*)NULL);
1119
                    status = TCL_ERROR;
1120
                }
1121
                else if (strstr(fargv[0],"::")) {
1122
                    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1123
                        "bad argument name \"", fargv[0], "\"",
1124
                        (char*)NULL);
1125
                    status = TCL_ERROR;
1126
                }
1127
                else if (fargc == 1) {
1128
                    localPtr = Itcl_CreateArg(fargv[0], (char*)NULL);
1129
                }
1130
                else {
1131
                    localPtr = Itcl_CreateArg(fargv[0], fargv[1]);
1132
                }
1133
 
1134
                if (localPtr) {
1135
                    localPtr->frameIndex = i;
1136
 
1137
                    if (*argPtr == NULL) {
1138
                        *argPtr = last = localPtr;
1139
                    }
1140
                    else {
1141
                        last->nextPtr = localPtr;
1142
                        last = localPtr;
1143
                    }
1144
                }
1145
            }
1146
            ckfree((char*)fargv);
1147
        }
1148
        ckfree((char*)argv);
1149
    }
1150
 
1151
    /*
1152
     *  If anything went wrong, destroy whatever arguments were
1153
     *  created and return an error.
1154
     */
1155
    if (status == TCL_OK) {
1156
        *argcPtr = argc;
1157
    } else {
1158
        Itcl_DeleteArgList(*argPtr);
1159
        *argPtr = NULL;
1160
    }
1161
    return status;
1162
}
1163
 
1164
 
1165
/*
1166
 * ------------------------------------------------------------------------
1167
 *  Itcl_CreateArg()
1168
 *
1169
 *  Creates a new Tcl Arg structure and fills it with the given
1170
 *  information.  Returns a pointer to the new Arg structure.
1171
 * ------------------------------------------------------------------------
1172
 */
1173
CompiledLocal*
1174
Itcl_CreateArg(name, init)
1175
    char* name;     /* name of new argument */
1176
    char* init;     /* initial value */
1177
{
1178
    CompiledLocal *localPtr = NULL;
1179
    int nameLen;
1180
 
1181
    if (name == NULL) {
1182
        name = "";
1183
    }
1184
    nameLen = strlen(name);
1185
 
1186
    localPtr = (CompiledLocal*)ckalloc(
1187
        (unsigned)(sizeof(CompiledLocal)-sizeof(localPtr->name) + nameLen+1)
1188
    );
1189
 
1190
    localPtr->nextPtr = NULL;
1191
    localPtr->nameLength = nameLen;
1192
    localPtr->frameIndex = 0;  /* set this later */
1193
    localPtr->flags  = VAR_SCALAR | VAR_ARGUMENT;
1194
    localPtr->resolveInfo = NULL;
1195
 
1196
    if (init != NULL) {
1197
        localPtr->defValuePtr = Tcl_NewStringObj(init, -1);
1198
        Tcl_IncrRefCount(localPtr->defValuePtr);
1199
    } else {
1200
        localPtr->defValuePtr = NULL;
1201
    }
1202
 
1203
    strcpy(localPtr->name, name);
1204
 
1205
    return localPtr;
1206
}
1207
 
1208
/*
1209
 * ------------------------------------------------------------------------
1210
 *  Itcl_DeleteArgList()
1211
 *
1212
 *  Destroys a chain of arguments acting as an argument list.  Usually
1213
 *  invoked when a method/proc is being destroyed, to discard its
1214
 *  argument list.
1215
 * ------------------------------------------------------------------------
1216
 */
1217
void
1218
Itcl_DeleteArgList(arglist)
1219
    CompiledLocal *arglist;   /* first argument in arg list chain */
1220
{
1221
    CompiledLocal *localPtr, *next;
1222
 
1223
    for (localPtr=arglist; localPtr; localPtr=next) {
1224
        if (localPtr->defValuePtr != NULL) {
1225
            Tcl_DecrRefCount(localPtr->defValuePtr);
1226
        }
1227
        if (localPtr->resolveInfo) {
1228
            if (localPtr->resolveInfo->deleteProc) {
1229
                localPtr->resolveInfo->deleteProc(localPtr->resolveInfo);
1230
            } else {
1231
                ckfree((char*)localPtr->resolveInfo);
1232
            }
1233
            localPtr->resolveInfo = NULL;
1234
        }
1235
        next = localPtr->nextPtr;
1236
        ckfree((char*)localPtr);
1237
    }
1238
}
1239
 
1240
/*
1241
 * ------------------------------------------------------------------------
1242
 *  Itcl_ArgList()
1243
 *
1244
 *  Returns a Tcl_Obj containing the string representation for the
1245
 *  given argument list.  This object has a reference count of 1.
1246
 *  The reference count should be decremented when the string is no
1247
 *  longer needed, and it will free itself.
1248
 * ------------------------------------------------------------------------
1249
 */
1250
Tcl_Obj*
1251
Itcl_ArgList(argc, arglist)
1252
    int argc;                   /* number of arguments */
1253
    CompiledLocal* arglist;     /* first argument in arglist */
1254
{
1255
    char *val;
1256
    Tcl_Obj *objPtr;
1257
    Tcl_DString buffer;
1258
 
1259
    Tcl_DStringInit(&buffer);
1260
 
1261
    while (arglist && argc-- > 0) {
1262
        if (arglist->defValuePtr) {
1263
            val = Tcl_GetStringFromObj(arglist->defValuePtr, (int*)NULL);
1264
            Tcl_DStringStartSublist(&buffer);
1265
            Tcl_DStringAppendElement(&buffer, arglist->name);
1266
            Tcl_DStringAppendElement(&buffer, val);
1267
            Tcl_DStringEndSublist(&buffer);
1268
        }
1269
        else {
1270
            Tcl_DStringAppendElement(&buffer, arglist->name);
1271
        }
1272
        arglist = arglist->nextPtr;
1273
    }
1274
 
1275
    objPtr = Tcl_NewStringObj(Tcl_DStringValue(&buffer),
1276
        Tcl_DStringLength(&buffer));
1277
 
1278
    Tcl_DStringFree(&buffer);
1279
 
1280
    return objPtr;
1281
}
1282
 
1283
 
1284
/*
1285
 * ------------------------------------------------------------------------
1286
 *  Itcl_EquivArgLists()
1287
 *
1288
 *  Compares two argument lists to see if they are equivalent.  The
1289
 *  first list is treated as a prototype, and the second list must
1290
 *  match it.  Argument names may be different, but they must match in
1291
 *  meaning.  If one argument is optional, the corresponding argument
1292
 *  must also be optional.  If the prototype list ends with the magic
1293
 *  "args" argument, then it matches everything in the other list.
1294
 *
1295
 *  Returns non-zero if the argument lists are equivalent.
1296
 * ------------------------------------------------------------------------
1297
 */
1298
int
1299
Itcl_EquivArgLists(arg1, arg1c, arg2, arg2c)
1300
    CompiledLocal* arg1;   /* prototype argument list */
1301
    int arg1c;             /* number of args in prototype arg list */
1302
    CompiledLocal* arg2;   /* another argument list to match against */
1303
    int arg2c;             /* number of args in matching list */
1304
{
1305
    char *dval1, *dval2;
1306
 
1307
    while (arg1 && arg1c > 0 && arg2 && arg2c > 0) {
1308
        /*
1309
         *  If the prototype argument list ends with the magic "args"
1310
         *  argument, then it matches everything in the other list.
1311
         */
1312
        if (arg1c == 1 && strcmp(arg1->name,"args") == 0) {
1313
            return 1;
1314
        }
1315
 
1316
        /*
1317
         *  If one has a default value, then the other must have the
1318
         *  same default value.
1319
         */
1320
        if (arg1->defValuePtr) {
1321
            if (arg2->defValuePtr == NULL) {
1322
                return 0;
1323
            }
1324
 
1325
            dval1 = Tcl_GetStringFromObj(arg1->defValuePtr, (int*)NULL);
1326
            dval2 = Tcl_GetStringFromObj(arg2->defValuePtr, (int*)NULL);
1327
            if (strcmp(dval1, dval2) != 0) {
1328
                return 0;
1329
            }
1330
        }
1331
        else if (arg2->defValuePtr) {
1332
            return 0;
1333
        }
1334
 
1335
        arg1 = arg1->nextPtr;  arg1c--;
1336
        arg2 = arg2->nextPtr;  arg2c--;
1337
    }
1338
    if (arg1c == 1 && strcmp(arg1->name,"args") == 0) {
1339
        return 1;
1340
    }
1341
    return (arg1c == 0 && arg2c == 0);
1342
}
1343
 
1344
 
1345
/*
1346
 * ------------------------------------------------------------------------
1347
 *  Itcl_GetMemberFuncUsage()
1348
 *
1349
 *  Returns a string showing how a command member should be invoked.
1350
 *  If the command member is a method, then the specified object name
1351
 *  is reported as part of the invocation path:
1352
 *
1353
 *      obj method arg ?arg arg ...?
1354
 *
1355
 *  Otherwise, the "obj" pointer is ignored, and the class name is
1356
 *  used as the invocation path:
1357
 *
1358
 *      class::proc arg ?arg arg ...?
1359
 *
1360
 *  Returns the string by appending it onto the Tcl_Obj passed in as
1361
 *  an argument.
1362
 * ------------------------------------------------------------------------
1363
 */
1364
void
1365
Itcl_GetMemberFuncUsage(mfunc, contextObj, objPtr)
1366
    ItclMemberFunc *mfunc;      /* command member being examined */
1367
    ItclObject *contextObj;     /* invoked with respect to this object */
1368
    Tcl_Obj *objPtr;            /* returns: string showing usage */
1369
{
1370
    int argcount;
1371
    char *name;
1372
    CompiledLocal *arglist, *argPtr;
1373
    Tcl_HashEntry *entry;
1374
    ItclMemberFunc *mf;
1375
    ItclClass *cdefnPtr;
1376
 
1377
    /*
1378
     *  If the command is a method and an object context was
1379
     *  specified, then add the object context.  If the method
1380
     *  was a constructor, and if the object is being created,
1381
     *  then report the invocation via the class creation command.
1382
     */
1383
    if ((mfunc->member->flags & ITCL_COMMON) == 0) {
1384
        if ((mfunc->member->flags & ITCL_CONSTRUCTOR) != 0 &&
1385
            contextObj->constructed) {
1386
 
1387
            cdefnPtr = (ItclClass*)contextObj->classDefn;
1388
            mf = NULL;
1389
            entry = Tcl_FindHashEntry(&cdefnPtr->resolveCmds, "constructor");
1390
            if (entry) {
1391
                mf = (ItclMemberFunc*)Tcl_GetHashValue(entry);
1392
            }
1393
 
1394
            if (mf == mfunc) {
1395
                Tcl_GetCommandFullName(contextObj->classDefn->interp,
1396
                    contextObj->classDefn->accessCmd, objPtr);
1397
                Tcl_AppendToObj(objPtr, " ", -1);
1398
                name = Tcl_GetCommandName(contextObj->classDefn->interp,
1399
                    contextObj->accessCmd);
1400
                Tcl_AppendToObj(objPtr, name, -1);
1401
            } else {
1402
                Tcl_AppendToObj(objPtr, mfunc->member->fullname, -1);
1403
            }
1404
        }
1405
        else if (contextObj && contextObj->accessCmd) {
1406
            name = Tcl_GetCommandName(contextObj->classDefn->interp,
1407
                contextObj->accessCmd);
1408
            Tcl_AppendStringsToObj(objPtr, name, " ", mfunc->member->name,
1409
                (char*)NULL);
1410
        }
1411
        else {
1412
            Tcl_AppendStringsToObj(objPtr, "<object> ", mfunc->member->name,
1413
                (char*)NULL);
1414
        }
1415
    }
1416
    else {
1417
        Tcl_AppendToObj(objPtr, mfunc->member->fullname, -1);
1418
    }
1419
 
1420
    /*
1421
     *  Add the argument usage info.
1422
     */
1423
    if (mfunc->member->code) {
1424
        arglist = mfunc->member->code->arglist;
1425
        argcount = mfunc->member->code->argcount;
1426
    } else if (mfunc->arglist) {
1427
        arglist = mfunc->arglist;
1428
        argcount = mfunc->argcount;
1429
    } else {
1430
        arglist = NULL;
1431
        argcount = 0;
1432
    }
1433
 
1434
    if (arglist) {
1435
        for (argPtr=arglist;
1436
             argPtr && argcount > 0;
1437
             argPtr=argPtr->nextPtr, argcount--) {
1438
 
1439
            if (argcount == 1 && strcmp(argPtr->name, "args") == 0) {
1440
                Tcl_AppendToObj(objPtr, " ?arg arg ...?", -1);
1441
            }
1442
            else if (argPtr->defValuePtr) {
1443
                Tcl_AppendStringsToObj(objPtr, " ?", argPtr->name, "?",
1444
                    (char*)NULL);
1445
            }
1446
            else {
1447
                Tcl_AppendStringsToObj(objPtr, " ", argPtr->name,
1448
                    (char*)NULL);
1449
            }
1450
        }
1451
    }
1452
}
1453
 
1454
 
1455
/*
1456
 * ------------------------------------------------------------------------
1457
 *  Itcl_ExecMethod()
1458
 *
1459
 *  Invoked by Tcl to handle the execution of a user-defined method.
1460
 *  A method is similar to the usual Tcl proc, but has access to
1461
 *  object-specific data.  If for some reason there is no current
1462
 *  object context, then a method call is inappropriate, and an error
1463
 *  is returned.
1464
 *
1465
 *  Methods are implemented either as Tcl code fragments, or as C-coded
1466
 *  procedures.  For Tcl code fragments, command arguments are parsed
1467
 *  according to the argument list, and the body is executed in the
1468
 *  scope of the class where it was defined.  For C procedures, the
1469
 *  arguments are passed in "as-is", and the procedure is executed in
1470
 *  the most-specific class scope.
1471
 * ------------------------------------------------------------------------
1472
 */
1473
int
1474
Itcl_ExecMethod(clientData, interp, objc, objv)
1475
    ClientData clientData;   /* method definition */
1476
    Tcl_Interp *interp;      /* current interpreter */
1477
    int objc;                /* number of arguments */
1478
    Tcl_Obj *CONST objv[];   /* argument objects */
1479
{
1480
    ItclMemberFunc *mfunc = (ItclMemberFunc*)clientData;
1481
    ItclMember *member = mfunc->member;
1482
    int result = TCL_OK;
1483
 
1484
    char *token;
1485
    Tcl_HashEntry *entry;
1486
    ItclClass *contextClass;
1487
    ItclObject *contextObj;
1488
 
1489
    /*
1490
     *  Make sure that the current namespace context includes an
1491
     *  object that is being manipulated.  Methods can be executed
1492
     *  only if an object context exists.
1493
     */
1494
    if (Itcl_GetContext(interp, &contextClass, &contextObj) != TCL_OK) {
1495
        return TCL_ERROR;
1496
    }
1497
    if (contextObj == NULL) {
1498
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1499
            "cannot access object-specific info without an object context",
1500
            (char*)NULL);
1501
        return TCL_ERROR;
1502
    }
1503
 
1504
    /*
1505
     *  Make sure that this command member can be accessed from
1506
     *  the current namespace context.
1507
     */
1508
    if (mfunc->member->protection != ITCL_PUBLIC) {
1509
        Tcl_Namespace *contextNs = Itcl_GetTrueNamespace(interp,
1510
            contextClass->info);
1511
 
1512
        if (!Itcl_CanAccessFunc(mfunc, contextNs)) {
1513
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1514
                "can't access \"", member->fullname, "\": ",
1515
                Itcl_ProtectionStr(member->protection), " function",
1516
                (char*)NULL);
1517
            return TCL_ERROR;
1518
        }
1519
    }
1520
 
1521
    /*
1522
     *  All methods should be "virtual" unless they are invoked with
1523
     *  a "::" scope qualifier.
1524
     *
1525
     *  To implement the "virtual" behavior, find the most-specific
1526
     *  implementation for the method by looking in the "resolveCmds"
1527
     *  table for this class.
1528
     */
1529
    token = Tcl_GetStringFromObj(objv[0], (int*)NULL);
1530
    if (strstr(token, "::") == NULL) {
1531
        entry = Tcl_FindHashEntry(&contextObj->classDefn->resolveCmds,
1532
            member->name);
1533
 
1534
        if (entry) {
1535
            mfunc = (ItclMemberFunc*)Tcl_GetHashValue(entry);
1536
            member = mfunc->member;
1537
        }
1538
    }
1539
 
1540
    /*
1541
     *  Execute the code for the method.  Be careful to protect
1542
     *  the method in case it gets deleted during execution.
1543
     */
1544
    Itcl_PreserveData((ClientData)mfunc);
1545
 
1546
    result = Itcl_EvalMemberCode(interp, mfunc, member, contextObj,
1547
        objc, objv);
1548
 
1549
    result = Itcl_ReportFuncErrors(interp, mfunc, contextObj, result);
1550
 
1551
    Itcl_ReleaseData((ClientData)mfunc);
1552
 
1553
    return result;
1554
}
1555
 
1556
 
1557
/*
1558
 * ------------------------------------------------------------------------
1559
 *  Itcl_ExecProc()
1560
 *
1561
 *  Invoked by Tcl to handle the execution of a user-defined proc.
1562
 *
1563
 *  Procs are implemented either as Tcl code fragments, or as C-coded
1564
 *  procedures.  For Tcl code fragments, command arguments are parsed
1565
 *  according to the argument list, and the body is executed in the
1566
 *  scope of the class where it was defined.  For C procedures, the
1567
 *  arguments are passed in "as-is", and the procedure is executed in
1568
 *  the most-specific class scope.
1569
 * ------------------------------------------------------------------------
1570
 */
1571
int
1572
Itcl_ExecProc(clientData, interp, objc, objv)
1573
    ClientData clientData;   /* proc definition */
1574
    Tcl_Interp *interp;      /* current interpreter */
1575
    int objc;                /* number of arguments */
1576
    Tcl_Obj *CONST objv[];   /* argument objects */
1577
{
1578
    ItclMemberFunc *mfunc = (ItclMemberFunc*)clientData;
1579
    ItclMember *member = mfunc->member;
1580
    int result = TCL_OK;
1581
 
1582
    /*
1583
     *  Make sure that this command member can be accessed from
1584
     *  the current namespace context.
1585
     */
1586
    if (mfunc->member->protection != ITCL_PUBLIC) {
1587
        Tcl_Namespace *contextNs = Itcl_GetTrueNamespace(interp,
1588
            mfunc->member->classDefn->info);
1589
 
1590
        if (!Itcl_CanAccessFunc(mfunc, contextNs)) {
1591
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1592
                "can't access \"", member->fullname, "\": ",
1593
                Itcl_ProtectionStr(member->protection), " function",
1594
                (char*)NULL);
1595
            return TCL_ERROR;
1596
        }
1597
    }
1598
 
1599
    /*
1600
     *  Execute the code for the proc.  Be careful to protect
1601
     *  the proc in case it gets deleted during execution.
1602
     */
1603
    Itcl_PreserveData((ClientData)mfunc);
1604
 
1605
    result = Itcl_EvalMemberCode(interp, mfunc, member, (ItclObject*)NULL,
1606
        objc, objv);
1607
 
1608
    result = Itcl_ReportFuncErrors(interp, mfunc, (ItclObject*)NULL, result);
1609
 
1610
    Itcl_ReleaseData((ClientData)mfunc);
1611
 
1612
    return result;
1613
}
1614
 
1615
 
1616
/*
1617
 * ------------------------------------------------------------------------
1618
 *  Itcl_PushContext()
1619
 *
1620
 *  Sets up the class/object context so that a body of [incr Tcl]
1621
 *  code can be executed.  This procedure pushes a call frame with
1622
 *  the proper namespace context for the class.  If an object context
1623
 *  is supplied, the object's instance variables are integrated into
1624
 *  the call frame so they can be accessed as local variables.
1625
 * ------------------------------------------------------------------------
1626
 */
1627
int
1628
Itcl_PushContext(interp, member, contextClass, contextObj, contextPtr)
1629
    Tcl_Interp *interp;       /* interpreter managing this body of code */
1630
    ItclMember *member;       /* member containing code body */
1631
    ItclClass *contextClass;  /* class context */
1632
    ItclObject *contextObj;   /* object context, or NULL */
1633
    ItclContext *contextPtr;  /* storage space for class/object context */
1634
{
1635
    CallFrame *framePtr = &contextPtr->frame;
1636
 
1637
    int result, localCt, newEntry;
1638
    ItclMemberCode *mcode;
1639
    Proc *procPtr;
1640
    Tcl_HashEntry *entry;
1641
 
1642
    /*
1643
     *  Activate the call frame.  If this fails, we'll bail out
1644
     *  before allocating any resources.
1645
     *
1646
     *  NOTE:  Always push a call frame that looks like a proc.
1647
     *    This causes global variables to be handled properly
1648
     *    inside methods/procs.
1649
     */
1650
    result = Tcl_PushCallFrame(interp, (Tcl_CallFrame*)framePtr,
1651
                 contextClass->namesp, /* isProcCallFrame */ 1);
1652
 
1653
    if (result != TCL_OK) {
1654
        return result;
1655
    }
1656
 
1657
    contextPtr->classDefn = contextClass;
1658
    contextPtr->compiledLocals = &contextPtr->localStorage[0];
1659
 
1660
    /*
1661
     *  If this is an object context, register it in a hash table
1662
     *  of all known contexts.  We'll need this later if we
1663
     *  call Itcl_GetContext to get the object context for the
1664
     *  current call frame.
1665
     */
1666
    if (contextObj) {
1667
        entry = Tcl_CreateHashEntry(&contextClass->info->contextFrames,
1668
            (char*)framePtr, &newEntry);
1669
 
1670
        Itcl_PreserveData((ClientData)contextObj);
1671
        Tcl_SetHashValue(entry, (ClientData)contextObj);
1672
    }
1673
 
1674
    /*
1675
     *  Set up the compiled locals in the call frame and assign
1676
     *  argument variables.
1677
     */
1678
    if (member) {
1679
        mcode = member->code;
1680
        procPtr = mcode->procPtr;
1681
 
1682
        /*
1683
         *  If there are too many compiled locals to fit in the default
1684
         *  storage space for the context, then allocate more space.
1685
         */
1686
        localCt = procPtr->numCompiledLocals;
1687
        if (localCt > sizeof(contextPtr->localStorage)/sizeof(Var)) {
1688
            contextPtr->compiledLocals = (Var*)ckalloc(
1689
                (unsigned)(localCt * sizeof(Var))
1690
            );
1691
        }
1692
 
1693
        /*
1694
         * Initialize and resolve compiled variable references.
1695
         * Class variables will have special resolution rules.
1696
         * In that case, we call their "resolver" procs to get our
1697
         * hands on the variable, and we make the compiled local a
1698
         * link to the real variable.
1699
         */
1700
 
1701
        framePtr->procPtr = procPtr;
1702
        framePtr->numCompiledLocals = localCt;
1703
        framePtr->compiledLocals = contextPtr->compiledLocals;
1704
 
1705
        TclInitCompiledLocals(interp, framePtr,
1706
            (Namespace*)contextClass->namesp);
1707
    }
1708
    return result;
1709
}
1710
 
1711
 
1712
/*
1713
 * ------------------------------------------------------------------------
1714
 *  Itcl_PopContext()
1715
 *
1716
 *  Removes a class/object context previously set up by Itcl_PushContext.
1717
 *  Usually called after an [incr Tcl] code body has been executed,
1718
 *  to clean up.
1719
 * ------------------------------------------------------------------------
1720
 */
1721
void
1722
Itcl_PopContext(interp, contextPtr)
1723
    Tcl_Interp *interp;       /* interpreter managing this body of code */
1724
    ItclContext *contextPtr;  /* storage space for class/object context */
1725
{
1726
    Tcl_CallFrame *framePtr;
1727
    ItclObjectInfo *info;
1728
    ItclObject *contextObj;
1729
    Tcl_HashEntry *entry;
1730
 
1731
    /*
1732
     *  See if the current call frame has an object context
1733
     *  associated with it.  If so, release the claim on the
1734
     *  object info.
1735
     */
1736
    framePtr = _Tcl_GetCallFrame(interp, 0);
1737
    info = contextPtr->classDefn->info;
1738
 
1739
    entry = Tcl_FindHashEntry(&info->contextFrames, (char*)framePtr);
1740
    if (entry != NULL) {
1741
        contextObj = (ItclObject*)Tcl_GetHashValue(entry);
1742
        Itcl_ReleaseData((ClientData)contextObj);
1743
        Tcl_DeleteHashEntry(entry);
1744
    }
1745
 
1746
    /*
1747
     *  Remove the call frame.
1748
     */
1749
    Tcl_PopCallFrame(interp);
1750
 
1751
    /*
1752
     * Free the compiledLocals array if malloc'ed storage was used.
1753
     */
1754
    if (contextPtr->compiledLocals != &contextPtr->localStorage[0]) {
1755
        ckfree((char*)contextPtr->compiledLocals);
1756
    }
1757
}
1758
 
1759
 
1760
/*
1761
 * ------------------------------------------------------------------------
1762
 *  Itcl_GetContext()
1763
 *
1764
 *  Convenience routine for looking up the current object/class context.
1765
 *  Useful in implementing methods/procs to see what class, and perhaps
1766
 *  what object, is active.
1767
 *
1768
 *  Returns TCL_OK if the current namespace is a class namespace.
1769
 *  Also returns pointers to the class definition, and to object
1770
 *  data if an object context is active.  Returns TCL_ERROR (along
1771
 *  with an error message in the interpreter) if a class namespace
1772
 *  is not active.
1773
 * ------------------------------------------------------------------------
1774
 */
1775
int
1776
Itcl_GetContext(interp, cdefnPtr, odefnPtr)
1777
    Tcl_Interp *interp;           /* current interpreter */
1778
    ItclClass **cdefnPtr;         /* returns:  class definition or NULL */
1779
    ItclObject **odefnPtr;        /* returns:  object data or NULL */
1780
{
1781
    Tcl_Namespace *activeNs = Tcl_GetCurrentNamespace(interp);
1782
    ItclObjectInfo *info;
1783
    Tcl_CallFrame *framePtr;
1784
    Tcl_HashEntry *entry;
1785
 
1786
    /*
1787
     *  Return NULL for anything that cannot be found.
1788
     */
1789
    *cdefnPtr = NULL;
1790
    *odefnPtr = NULL;
1791
 
1792
    /*
1793
     *  If the active namespace is a class namespace, then return
1794
     *  all known info.  See if the current call frame is a known
1795
     *  object context, and if so, return that context.
1796
     */
1797
    if (Itcl_IsClassNamespace(activeNs)) {
1798
        *cdefnPtr = (ItclClass*)activeNs->clientData;
1799
 
1800
        framePtr = _Tcl_GetCallFrame(interp, 0);
1801
 
1802
        info = (*cdefnPtr)->info;
1803
        entry = Tcl_FindHashEntry(&info->contextFrames, (char*)framePtr);
1804
 
1805
        if (entry != NULL) {
1806
            *odefnPtr = (ItclObject*)Tcl_GetHashValue(entry);
1807
        }
1808
        return TCL_OK;
1809
    }
1810
 
1811
    /*
1812
     *  If there is no class/object context, return an error message.
1813
     */
1814
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1815
        "namespace \"", activeNs->fullName, "\" is not a class namespace",
1816
        (char*)NULL);
1817
 
1818
    return TCL_ERROR;
1819
}
1820
 
1821
 
1822
/*
1823
 * ------------------------------------------------------------------------
1824
 *  Itcl_AssignArgs()
1825
 *
1826
 *  Matches a list of arguments against a Tcl argument specification.
1827
 *  Supports all of the rules regarding arguments for Tcl procs, including
1828
 *  default arguments and variable-length argument lists.
1829
 *
1830
 *  Assumes that a local call frame is already installed.  As variables
1831
 *  are successfully matched, they are stored as variables in the call
1832
 *  frame.  Returns TCL_OK on success, or TCL_ERROR (along with an error
1833
 *  message in interp->result) on error.
1834
 * ------------------------------------------------------------------------
1835
 */
1836
int
1837
Itcl_AssignArgs(interp, objc, objv, mfunc)
1838
    Tcl_Interp *interp;        /* interpreter */
1839
    int objc;                  /* number of arguments */
1840
    Tcl_Obj *CONST objv[];     /* argument objects */
1841
    ItclMemberFunc *mfunc;     /* member function info (for error messages) */
1842
{
1843
    ItclMemberCode *mcode = mfunc->member->code;
1844
 
1845
    int result = TCL_OK;
1846
 
1847
    int defargc;
1848
    char **defargv = NULL;
1849
    Tcl_Obj **defobjv = NULL;
1850
    int configc = 0;
1851
    ItclVarDefn **configVars = NULL;
1852
    char **configVals = NULL;
1853
 
1854
    int vi, argsLeft;
1855
    ItclClass *contextClass;
1856
    ItclObject *contextObj;
1857
    CompiledLocal *argPtr;
1858
    CallFrame *framePtr;
1859
    Var *varPtr;
1860
    Tcl_Obj *objPtr, *listPtr;
1861
    char *value;
1862
 
1863
    framePtr = (CallFrame*) _Tcl_GetCallFrame(interp, 0);
1864
    framePtr->objc = objc;
1865
    framePtr->objv = objv;  /* ref counts for args are incremented below */
1866
 
1867
    /*
1868
     *  See if there is a current object context.  We may need
1869
     *  it later on.
1870
     */
1871
    (void) Itcl_GetContext(interp, &contextClass, &contextObj);
1872
    Tcl_ResetResult(interp);
1873
 
1874
    /*
1875
     *  Match the actual arguments against the procedure's formal
1876
     *  parameters to compute local variables.
1877
     */
1878
    varPtr = framePtr->compiledLocals;
1879
 
1880
    for (argsLeft=mcode->argcount, argPtr=mcode->arglist, objv++, objc--;
1881
         argsLeft > 0;
1882
         argPtr=argPtr->nextPtr, argsLeft--, varPtr++, objv++, objc--)
1883
    {
1884
        if (!TclIsVarArgument(argPtr)) {
1885
            panic("local variable %s is not argument but should be",
1886
                argPtr->name);
1887
            return TCL_ERROR;
1888
        }
1889
        if (TclIsVarTemporary(argPtr)) {
1890
            panic("local variable is temporary but should be an argument");
1891
            return TCL_ERROR;
1892
        }
1893
 
1894
        /*
1895
         *  Handle the special case of the last formal being "args".
1896
         *  When it occurs, assign it a list consisting of all the
1897
         *  remaining actual arguments.
1898
         */
1899
        if ((argsLeft == 1) && (strcmp(argPtr->name, "args") == 0)) {
1900
            if (objc < 0) objc = 0;
1901
 
1902
            listPtr = Tcl_NewListObj(objc, objv);
1903
            varPtr->value.objPtr = listPtr;
1904
            Tcl_IncrRefCount(listPtr); /* local var is a reference */
1905
            varPtr->flags &= ~VAR_UNDEFINED;
1906
            objc = 0;
1907
 
1908
            break;
1909
        }
1910
 
1911
        /*
1912
         *  Handle the special case of the last formal being "config".
1913
         *  When it occurs, treat all remaining arguments as public
1914
         *  variable assignments.  Set the local "config" variable
1915
         *  to the list of public variables assigned.
1916
         */
1917
        else if ( (argsLeft == 1) &&
1918
                  (strcmp(argPtr->name, "config") == 0) &&
1919
                  contextObj )
1920
        {
1921
            /*
1922
             *  If this is not an old-style method, discourage against
1923
             *  the use of the "config" argument.
1924
             */
1925
            if ((mfunc->member->flags & ITCL_OLD_STYLE) == 0) {
1926
                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1927
                    "\"config\" argument is an anachronism\n",
1928
                    "[incr Tcl] no longer supports the \"config\" argument.\n",
1929
                    "Instead, use the \"args\" argument and then use the\n",
1930
                    "built-in configure method to handle args like this:\n",
1931
                    "  eval configure $args",
1932
                    (char*)NULL);
1933
                result = TCL_ERROR;
1934
                goto argErrors;
1935
            }
1936
 
1937
            /*
1938
             *  Otherwise, handle the "config" argument in the usual way...
1939
             *   - parse all "-name value" assignments
1940
             *   - set "config" argument to the list of variable names
1941
             */
1942
            if (objc > 0) {  /* still have some arguments left? */
1943
 
1944
                result = ItclParseConfig(interp, objc, objv, contextObj,
1945
                    &configc, &configVars, &configVals);
1946
 
1947
                if (result != TCL_OK) {
1948
                    goto argErrors;
1949
                }
1950
 
1951
                listPtr = Tcl_NewListObj(0, (Tcl_Obj**)NULL);
1952
                for (vi=0; vi < configc; vi++) {
1953
                    objPtr = Tcl_NewStringObj(
1954
                        configVars[vi]->member->classDefn->name, -1);
1955
                    Tcl_AppendToObj(objPtr, "::", -1);
1956
                    Tcl_AppendToObj(objPtr, configVars[vi]->member->name, -1);
1957
 
1958
                    Tcl_ListObjAppendElement(interp, listPtr, objPtr);
1959
                }
1960
 
1961
                varPtr->value.objPtr = listPtr;
1962
                Tcl_IncrRefCount(listPtr); /* local var is a reference */
1963
                varPtr->flags &= ~VAR_UNDEFINED;
1964
 
1965
                objc = 0;  /* all remaining args handled */
1966
            }
1967
 
1968
            else if (argPtr->defValuePtr) {
1969
                value = Tcl_GetStringFromObj(argPtr->defValuePtr, (int*)NULL);
1970
 
1971
                result = Tcl_SplitList(interp, value, &defargc, &defargv);
1972
                if (result != TCL_OK) {
1973
                    goto argErrors;
1974
                }
1975
                defobjv = (Tcl_Obj**)ckalloc(
1976
                    (unsigned)(defargc*sizeof(Tcl_Obj*))
1977
                );
1978
                for (vi=0; vi < defargc; vi++) {
1979
                    objPtr = Tcl_NewStringObj(defargv[vi], -1);
1980
                    Tcl_IncrRefCount(objPtr);
1981
                    defobjv[vi] = objPtr;
1982
                }
1983
 
1984
                result = ItclParseConfig(interp, defargc, defobjv, contextObj,
1985
                    &configc, &configVars, &configVals);
1986
 
1987
                if (result != TCL_OK) {
1988
                    goto argErrors;
1989
                }
1990
 
1991
                listPtr = Tcl_NewListObj(0, (Tcl_Obj**)NULL);
1992
                for (vi=0; vi < configc; vi++) {
1993
                    objPtr = Tcl_NewStringObj(
1994
                        configVars[vi]->member->classDefn->name, -1);
1995
                    Tcl_AppendToObj(objPtr, "::", -1);
1996
                    Tcl_AppendToObj(objPtr, configVars[vi]->member->name, -1);
1997
 
1998
                    Tcl_ListObjAppendElement(interp, listPtr, objPtr);
1999
                }
2000
 
2001
                varPtr->value.objPtr = listPtr;
2002
                Tcl_IncrRefCount(listPtr); /* local var is a reference */
2003
                varPtr->flags &= ~VAR_UNDEFINED;
2004
            }
2005
            else {
2006
                objPtr = Tcl_NewStringObj("", 0);
2007
                varPtr->value.objPtr = objPtr;
2008
                Tcl_IncrRefCount(objPtr); /* local var is a reference */
2009
                varPtr->flags &= ~VAR_UNDEFINED;
2010
            }
2011
        }
2012
 
2013
        /*
2014
         *  Resume the usual processing of arguments...
2015
         */
2016
        else if (objc > 0) {          /* take next arg as value */
2017
            objPtr = *objv;
2018
            varPtr->value.objPtr = objPtr;
2019
            varPtr->flags &= ~VAR_UNDEFINED;
2020
            Tcl_IncrRefCount(objPtr);  /* local var is a reference */
2021
        }
2022
        else if (argPtr->defValuePtr) {    /* ...or use default value */
2023
            objPtr = argPtr->defValuePtr;
2024
            varPtr->value.objPtr = objPtr;
2025
            varPtr->flags &= ~VAR_UNDEFINED;
2026
            Tcl_IncrRefCount(objPtr);  /* local var is a reference */
2027
        }
2028
        else {
2029
            if (mfunc) {
2030
                objPtr = Tcl_GetObjResult(interp);
2031
                Tcl_AppendToObj(objPtr, "wrong # args: should be \"", -1);
2032
                Itcl_GetMemberFuncUsage(mfunc, contextObj, objPtr);
2033
                Tcl_AppendToObj(objPtr, "\"", -1);
2034
            } else {
2035
                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2036
                    "no value given for parameter \"", argPtr->name, "\"",
2037
                    (char*)NULL);
2038
            }
2039
            result = TCL_ERROR;
2040
            goto argErrors;
2041
        }
2042
    }
2043
 
2044
    if (objc > 0) {
2045
        if (mfunc) {
2046
            objPtr = Tcl_GetObjResult(interp);
2047
            Tcl_AppendToObj(objPtr, "wrong # args: should be \"", -1);
2048
            Itcl_GetMemberFuncUsage(mfunc, contextObj, objPtr);
2049
            Tcl_AppendToObj(objPtr, "\"", -1);
2050
        } else {
2051
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2052
                "too many arguments",
2053
                (char*)NULL);
2054
        }
2055
        result = TCL_ERROR;
2056
        goto argErrors;
2057
    }
2058
 
2059
    /*
2060
     *  Handle any "config" assignments.
2061
     */
2062
    if (configc > 0) {
2063
        if (ItclHandleConfig(interp, configc, configVars, configVals,
2064
                contextObj) != TCL_OK) {
2065
 
2066
            result = TCL_ERROR;
2067
            goto argErrors;
2068
        }
2069
    }
2070
 
2071
    /*
2072
     *  All arguments were successfully matched.
2073
     */
2074
    result = TCL_OK;
2075
 
2076
    /*
2077
     *  If any errors were found, clean up and return error status.
2078
     */
2079
argErrors:
2080
    if (defobjv) {
2081
        for (vi=0; vi < defargc; vi++) {
2082
            Tcl_DecrRefCount(defobjv[vi]);
2083
        }
2084
        ckfree((char*)defobjv);
2085
    }
2086
    if (defargv) {
2087
        ckfree((char*)defargv);
2088
    }
2089
    if (configVars) {
2090
        ckfree((char*)configVars);
2091
    }
2092
    if (configVals) {
2093
        ckfree((char*)configVals);
2094
    }
2095
    return result;
2096
}
2097
 
2098
 
2099
/*
2100
 * ------------------------------------------------------------------------
2101
 *  ItclParseConfig()
2102
 *
2103
 *  Parses a set of arguments as "-variable value" assignments.
2104
 *  Interprets all variable names in the most-specific class scope,
2105
 *  so that an inherited method with a "config" parameter will work
2106
 *  correctly.  Returns a list of public variable names and their
2107
 *  corresponding values; both lists should passed to ItclHandleConfig()
2108
 *  to perform assignments, and freed when no longer in use.  Returns a
2109
 *  status TCL_OK/TCL_ERROR and returns error messages in the interpreter.
2110
 * ------------------------------------------------------------------------
2111
 */
2112
static int
2113
ItclParseConfig(interp, objc, objv, contextObj, rargc, rvars, rvals)
2114
    Tcl_Interp *interp;      /* interpreter */
2115
    int objc;                /* number of arguments */
2116
    Tcl_Obj *CONST objv[];   /* argument objects */
2117
    ItclObject *contextObj;  /* object whose public vars are being config'd */
2118
    int *rargc;              /* return: number of variables accessed */
2119
    ItclVarDefn ***rvars;    /* return: list of variables */
2120
    char ***rvals;           /* return: list of values */
2121
{
2122
    int result = TCL_OK;
2123
    ItclVarLookup *vlookup;
2124
    Tcl_HashEntry *entry;
2125
    char *varName, *value;
2126
 
2127
    if (objc < 0) objc = 0;
2128
    *rargc = 0;
2129
    *rvars = (ItclVarDefn**)ckalloc((unsigned)(objc*sizeof(ItclVarDefn*)));
2130
    *rvals = (char**)ckalloc((unsigned)(objc*sizeof(char*)));
2131
 
2132
    while (objc-- > 0) {
2133
        /*
2134
         *  Next argument should be "-variable"
2135
         */
2136
        varName = Tcl_GetStringFromObj(*objv, (int*)NULL);
2137
        if (*varName != '-') {
2138
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2139
                "syntax error in config assignment \"",
2140
                varName, "\": should be \"-variable value\"",
2141
                (char*)NULL);
2142
            result = TCL_ERROR;
2143
            break;
2144
        }
2145
        else if (objc-- <= 0) {
2146
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2147
                "syntax error in config assignment \"",
2148
                varName, "\": should be \"-variable value\" (missing value)",
2149
                (char*)NULL);
2150
            result = TCL_ERROR;
2151
            break;
2152
        }
2153
 
2154
        entry = Tcl_FindHashEntry(&contextObj->classDefn->resolveVars,
2155
            varName+1);
2156
 
2157
        if (entry) {
2158
            vlookup = (ItclVarLookup*)Tcl_GetHashValue(entry);
2159
            value = Tcl_GetStringFromObj(*(objv+1), (int*)NULL);
2160
 
2161
            (*rvars)[*rargc] = vlookup->vdefn;  /* variable definition */
2162
            (*rvals)[*rargc] = value;           /* config value */
2163
            (*rargc)++;
2164
            objv += 2;
2165
        }
2166
        else {
2167
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2168
                "syntax error in config assignment \"",
2169
                varName, "\": unrecognized variable",
2170
                (char*)NULL);
2171
            result = TCL_ERROR;
2172
            break;
2173
        }
2174
    }
2175
    return result;
2176
}
2177
 
2178
/*
2179
 * ------------------------------------------------------------------------
2180
 *  ItclHandleConfig()
2181
 *
2182
 *  Handles the assignment of "config" values to public variables.
2183
 *  The list of assignments is parsed in ItclParseConfig(), but the
2184
 *  actual assignments are performed here.  If the variables have any
2185
 *  associated "config" code, it is invoked here as well.  If errors
2186
 *  are detected during assignment or "config" code execution, the
2187
 *  variable is set back to its previous value and an error is returned.
2188
 *
2189
 *  Returns a status TCL_OK/TCL_ERROR, and returns any error messages
2190
 *  in the given interpreter.
2191
 * ------------------------------------------------------------------------
2192
 */
2193
static int
2194
ItclHandleConfig(interp, argc, vars, vals, contextObj)
2195
    Tcl_Interp *interp;      /* interpreter currently in control */
2196
    int argc;                /* number of assignments */
2197
    ItclVarDefn **vars;      /* list of public variable definitions */
2198
    char **vals;             /* list of public variable values */
2199
    ItclObject *contextObj;  /* object whose public vars are being config'd */
2200
{
2201
    int result = TCL_OK;
2202
 
2203
    int i;
2204
    char *val;
2205
    Tcl_DString lastval;
2206
    ItclContext context;
2207
    Tcl_CallFrame *oldFramePtr, *uplevelFramePtr;
2208
 
2209
    Tcl_DStringInit(&lastval);
2210
 
2211
    /*
2212
     *  All "config" assignments are performed in the most-specific
2213
     *  class scope, so that inherited methods with "config" arguments
2214
     *  will work correctly.
2215
     */
2216
    result = Itcl_PushContext(interp, (ItclMember*)NULL,
2217
        contextObj->classDefn, contextObj, &context);
2218
 
2219
    if (result != TCL_OK) {
2220
        return TCL_ERROR;
2221
    }
2222
 
2223
    /*
2224
     *  Perform each assignment and execute the "config" code
2225
     *  associated with each variable.  If any errors are encountered,
2226
     *  set the variable back to its previous value, and return an error.
2227
     */
2228
    for (i=0; i < argc; i++) {
2229
        val = Tcl_GetVar2(interp, vars[i]->member->fullname, (char*)NULL, 0);
2230
        if (!val) {
2231
            val = "";
2232
        }
2233
        Tcl_DStringSetLength(&lastval, 0);
2234
        Tcl_DStringAppend(&lastval, val, -1);
2235
 
2236
        /*
2237
         *  Set the variable to the specified value.
2238
         */
2239
        if (!Tcl_SetVar2(interp, vars[i]->member->fullname, (char*)NULL,
2240
            vals[i], 0)) {
2241
 
2242
            char msg[256];
2243
            sprintf(msg, "\n    (while configuring public variable \"%.100s\")", vars[i]->member->fullname);
2244
            Tcl_AddErrorInfo(interp, msg);
2245
            result = TCL_ERROR;
2246
            break;
2247
        }
2248
 
2249
        /*
2250
         *  If the variable has a "config" condition, then execute it.
2251
         *  If it fails, put the variable back the way it was and return
2252
         *  an error.
2253
         *
2254
         *  TRICKY NOTE:  Be careful to evaluate the code one level
2255
         *    up in the call stack, so that it's executed in the
2256
         *    calling context, and not in the context that we've
2257
         *    set up for public variable access.
2258
         */
2259
        if (vars[i]->member->code) {
2260
 
2261
            uplevelFramePtr = _Tcl_GetCallFrame(interp, 1);
2262
            oldFramePtr = _Tcl_ActivateCallFrame(interp, uplevelFramePtr);
2263
 
2264
            result = Itcl_EvalMemberCode(interp, (ItclMemberFunc*)NULL,
2265
                vars[i]->member, contextObj, 0, (Tcl_Obj* CONST*)NULL);
2266
 
2267
            (void) _Tcl_ActivateCallFrame(interp, oldFramePtr);
2268
 
2269
            if (result != TCL_OK) {
2270
                char msg[256];
2271
                sprintf(msg, "\n    (while configuring public variable \"%.100s\")", vars[i]->member->fullname);
2272
                Tcl_AddErrorInfo(interp, msg);
2273
                Tcl_SetVar2(interp, vars[i]->member->fullname, (char*)NULL,
2274
                    Tcl_DStringValue(&lastval), 0);
2275
                break;
2276
            }
2277
        }
2278
    }
2279
 
2280
    /*
2281
     *  Clean up and return.
2282
     */
2283
    Itcl_PopContext(interp, &context);
2284
    Tcl_DStringFree(&lastval);
2285
 
2286
    return result;
2287
}
2288
 
2289
 
2290
/*
2291
 * ------------------------------------------------------------------------
2292
 *  Itcl_ConstructBase()
2293
 *
2294
 *  Usually invoked just before executing the body of a constructor
2295
 *  when an object is first created.  This procedure makes sure that
2296
 *  all base classes are properly constructed.  If an "initCode" fragment
2297
 *  was defined with the constructor for the class, then it is invoked.
2298
 *  After that, the list of base classes is checked for constructors
2299
 *  that are defined but have not yet been invoked.  Each of these is
2300
 *  invoked implicitly with no arguments.
2301
 *
2302
 *  Assumes that a local call frame is already installed, and that
2303
 *  constructor arguments have already been matched and are sitting in
2304
 *  this frame.  Returns TCL_OK on success; otherwise, this procedure
2305
 *  returns TCL_ERROR, along with an error message in the interpreter.
2306
 * ------------------------------------------------------------------------
2307
 */
2308
int
2309
Itcl_ConstructBase(interp, contextObj, contextClass)
2310
    Tcl_Interp *interp;       /* interpreter */
2311
    ItclObject *contextObj;   /* object being constructed */
2312
    ItclClass *contextClass;  /* current class being constructed */
2313
{
2314
    int result;
2315
    Itcl_ListElem *elem;
2316
    ItclClass *cdefn;
2317
    Tcl_HashEntry *entry;
2318
 
2319
    /*
2320
     *  If the class has an "initCode", invoke it in the current context.
2321
     *
2322
     *  TRICKY NOTE:
2323
     *    This context is the call frame containing the arguments
2324
     *    for the constructor.  The "initCode" makes sense right
2325
     *    now--just before the body of the constructor is executed.
2326
     */
2327
    if (contextClass->initCode) {
2328
      /* CYGNUS LOCAL - Fix for Tcl8.1 */
2329
#if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0
2330
      if (Tcl_EvalObj(interp, contextClass->initCode) != TCL_OK) {
2331
#else
2332
        if (Tcl_EvalObj(interp, contextClass->initCode, 0) != TCL_OK) {
2333
#endif
2334
          /* END CYGNUS LOCAL */
2335
          return TCL_ERROR;
2336
        }
2337
    }
2338
 
2339
    /*
2340
     *  Scan through the list of base classes and see if any of these
2341
     *  have not been constructed.  Invoke base class constructors
2342
     *  implicitly, as needed.  Go through the list of base classes
2343
     *  in reverse order, so that least-specific classes are constructed
2344
     *  first.
2345
     */
2346
    elem = Itcl_LastListElem(&contextClass->bases);
2347
    while (elem) {
2348
        cdefn = (ItclClass*)Itcl_GetListValue(elem);
2349
 
2350
        if (!Tcl_FindHashEntry(contextObj->constructed, cdefn->name)) {
2351
 
2352
            result = Itcl_InvokeMethodIfExists(interp, "constructor",
2353
                cdefn, contextObj, 0, (Tcl_Obj* CONST*)NULL);
2354
 
2355
            if (result != TCL_OK) {
2356
                return TCL_ERROR;
2357
            }
2358
 
2359
            /*
2360
             *  The base class may not have a constructor, but its
2361
             *  own base classes could have one.  If the constructor
2362
             *  wasn't found in the last step, then other base classes
2363
             *  weren't constructed either.  Make sure that all of its
2364
             *  base classes are properly constructed.
2365
             */
2366
            entry = Tcl_FindHashEntry(&cdefn->functions, "constructor");
2367
            if (entry == NULL) {
2368
                result = Itcl_ConstructBase(interp, contextObj, cdefn);
2369
                if (result != TCL_OK) {
2370
                    return TCL_ERROR;
2371
                }
2372
            }
2373
        }
2374
        elem = Itcl_PrevListElem(elem);
2375
    }
2376
    return TCL_OK;
2377
}
2378
 
2379
 
2380
/*
2381
 * ------------------------------------------------------------------------
2382
 *  Itcl_InvokeMethodIfExists()
2383
 *
2384
 *  Looks for a particular method in the specified class.  If the
2385
 *  method is found, it is invoked with the given arguments.  Any
2386
 *  protection level (protected/private) for the method is ignored.
2387
 *  If the method does not exist, this procedure does nothing.
2388
 *
2389
 *  This procedure is used primarily to invoke the constructor/destructor
2390
 *  when an object is created/destroyed.
2391
 *
2392
 *  Returns TCL_OK on success; otherwise, this procedure returns
2393
 *  TCL_ERROR along with an error message in the interpreter.
2394
 * ------------------------------------------------------------------------
2395
 */
2396
int
2397
Itcl_InvokeMethodIfExists(interp, name, contextClass, contextObj, objc, objv)
2398
    Tcl_Interp *interp;       /* interpreter */
2399
    char *name;               /* name of desired method */
2400
    ItclClass *contextClass;  /* current class being constructed */
2401
    ItclObject *contextObj;   /* object being constructed */
2402
    int objc;                 /* number of arguments */
2403
    Tcl_Obj *CONST objv[];    /* argument objects */
2404
{
2405
    int result = TCL_OK;
2406
 
2407
    ItclMemberFunc *mfunc;
2408
    ItclMember *member;
2409
    Tcl_HashEntry *entry;
2410
    Tcl_Obj *cmdlinePtr;
2411
    int cmdlinec;
2412
    Tcl_Obj **cmdlinev;
2413
 
2414
    /*
2415
     *  Scan through the list of base classes and see if any of these
2416
     *  have not been constructed.  Invoke base class constructors
2417
     *  implicitly, as needed.  Go through the list of base classes
2418
     *  in reverse order, so that least-specific classes are constructed
2419
     *  first.
2420
     */
2421
    entry = Tcl_FindHashEntry(&contextClass->functions, name);
2422
 
2423
    if (entry) {
2424
        mfunc  = (ItclMemberFunc*)Tcl_GetHashValue(entry);
2425
        member = mfunc->member;
2426
 
2427
        /*
2428
         *  Prepend the method name to the list of arguments.
2429
         */
2430
        cmdlinePtr = Itcl_CreateArgs(interp, name, objc, objv);
2431
 
2432
        (void) Tcl_ListObjGetElements((Tcl_Interp*)NULL, cmdlinePtr,
2433
            &cmdlinec, &cmdlinev);
2434
 
2435
        /*
2436
         *  Execute the code for the method.  Be careful to protect
2437
         *  the method in case it gets deleted during execution.
2438
         */
2439
        Itcl_PreserveData((ClientData)mfunc);
2440
 
2441
        result = Itcl_EvalMemberCode(interp, mfunc, member,
2442
            contextObj, cmdlinec, cmdlinev);
2443
 
2444
        result = Itcl_ReportFuncErrors(interp, mfunc,
2445
            contextObj, result);
2446
 
2447
        Itcl_ReleaseData((ClientData)mfunc);
2448
        Tcl_DecrRefCount(cmdlinePtr);
2449
    }
2450
    return result;
2451
}
2452
 
2453
 
2454
/*
2455
 * ------------------------------------------------------------------------
2456
 *  Itcl_ReportFuncErrors()
2457
 *
2458
 *  Used to interpret the status code returned when the body of a
2459
 *  Tcl-style proc is executed.  Handles the "errorInfo" and "errorCode"
2460
 *  variables properly, and adds error information into the interpreter
2461
 *  if anything went wrong.  Returns a new status code that should be
2462
 *  treated as the return status code for the command.
2463
 *
2464
 *  This same operation is usually buried in the Tcl InterpProc()
2465
 *  procedure.  It is defined here so that it can be reused more easily.
2466
 * ------------------------------------------------------------------------
2467
 */
2468
int
2469
Itcl_ReportFuncErrors(interp, mfunc, contextObj, result)
2470
    Tcl_Interp* interp;        /* interpreter being modified */
2471
    ItclMemberFunc *mfunc;     /* command member that was invoked */
2472
    ItclObject *contextObj;    /* object context for this command */
2473
    int result;                /* integer status code from proc body */
2474
{
2475
    Interp* iPtr = (Interp*)interp;
2476
    Tcl_Obj *objPtr;
2477
    char num[20];
2478
 
2479
    if (result != TCL_OK) {
2480
        if (result == TCL_RETURN) {
2481
            result = TclUpdateReturnInfo(iPtr);
2482
        }
2483
        else if (result == TCL_ERROR) {
2484
            objPtr = Tcl_NewStringObj("\n    ", -1);
2485
            Tcl_IncrRefCount(objPtr);
2486
 
2487
            if (mfunc->member->flags & ITCL_CONSTRUCTOR) {
2488
                Tcl_AppendToObj(objPtr, "while constructing object \"", -1);
2489
                Tcl_GetCommandFullName(contextObj->classDefn->interp,
2490
                    contextObj->accessCmd, objPtr);
2491
                Tcl_AppendToObj(objPtr, "\" in ", -1);
2492
                Tcl_AppendToObj(objPtr, mfunc->member->fullname, -1);
2493
                if ((mfunc->member->code->flags & ITCL_IMPLEMENT_TCL) != 0) {
2494
                    Tcl_AppendToObj(objPtr, " (", -1);
2495
                }
2496
            }
2497
 
2498
            else if (mfunc->member->flags & ITCL_DESTRUCTOR) {
2499
                Tcl_AppendToObj(objPtr, "while deleting object \"", -1);
2500
                Tcl_GetCommandFullName(contextObj->classDefn->interp,
2501
                    contextObj->accessCmd, objPtr);
2502
                Tcl_AppendToObj(objPtr, "\" in ", -1);
2503
                Tcl_AppendToObj(objPtr, mfunc->member->fullname, -1);
2504
                if ((mfunc->member->code->flags & ITCL_IMPLEMENT_TCL) != 0) {
2505
                    Tcl_AppendToObj(objPtr, " (", -1);
2506
                }
2507
            }
2508
 
2509
            else {
2510
                Tcl_AppendToObj(objPtr, "(", -1);
2511
 
2512
                if (contextObj && contextObj->accessCmd) {
2513
                    Tcl_AppendToObj(objPtr, "object \"", -1);
2514
                    Tcl_GetCommandFullName(contextObj->classDefn->interp,
2515
                        contextObj->accessCmd, objPtr);
2516
                    Tcl_AppendToObj(objPtr, "\" ", -1);
2517
                }
2518
 
2519
                if ((mfunc->member->flags & ITCL_COMMON) != 0) {
2520
                    Tcl_AppendToObj(objPtr, "procedure", -1);
2521
                } else {
2522
                    Tcl_AppendToObj(objPtr, "method", -1);
2523
                }
2524
                Tcl_AppendToObj(objPtr, " \"", -1);
2525
                Tcl_AppendToObj(objPtr, mfunc->member->fullname, -1);
2526
                Tcl_AppendToObj(objPtr, "\" ", -1);
2527
            }
2528
 
2529
            if ((mfunc->member->code->flags & ITCL_IMPLEMENT_TCL) != 0) {
2530
                Tcl_AppendToObj(objPtr, "body line ", -1);
2531
                sprintf(num, "%d", iPtr->errorLine);
2532
                Tcl_AppendToObj(objPtr, num, -1);
2533
                Tcl_AppendToObj(objPtr, ")", -1);
2534
            } else {
2535
                Tcl_AppendToObj(objPtr, ")", -1);
2536
            }
2537
 
2538
            Tcl_AddErrorInfo(interp, Tcl_GetStringFromObj(objPtr, (int*)NULL));
2539
            Tcl_DecrRefCount(objPtr);
2540
        }
2541
 
2542
        else if (result == TCL_BREAK) {
2543
            Tcl_ResetResult(interp);
2544
            Tcl_AppendToObj(Tcl_GetObjResult(interp),
2545
                    "invoked \"break\" outside of a loop", -1);
2546
            result = TCL_ERROR;
2547
        }
2548
 
2549
        else if (result == TCL_CONTINUE) {
2550
            Tcl_ResetResult(interp);
2551
            Tcl_AppendToObj(Tcl_GetObjResult(interp),
2552
                    "invoked \"continue\" outside of a loop", -1);
2553
            result = TCL_ERROR;
2554
        }
2555
    }
2556
    return result;
2557
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.