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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [itcl/] [itcl/] [generic/] [itcl_objects.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
 *  This segment handles "objects" which are instantiated from class
16
 *  definitions.  Objects contain public/protected/private data members
17
 *  from all classes in a derivation hierarchy.
18
 *
19
 * ========================================================================
20
 *  AUTHOR:  Michael J. McLennan
21
 *           Bell Labs Innovations for Lucent Technologies
22
 *           mmclennan@lucent.com
23
 *           http://www.tcltk.com/itcl
24
 *
25
 *     RCS:  $Id: itcl_objects.c,v 1.1.1.1 2002-01-16 10:24:46 markom Exp $
26
 * ========================================================================
27
 *           Copyright (c) 1993-1998  Lucent Technologies, Inc.
28
 * ------------------------------------------------------------------------
29
 * See the file "license.terms" for information on usage and redistribution
30
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
31
 */
32
#include "itclInt.h"
33
 
34
/*
35
 *  FORWARD DECLARATIONS
36
 */
37
static void ItclReportObjectUsage _ANSI_ARGS_((Tcl_Interp *interp,
38
    ItclObject* obj));
39
 
40
static char* ItclTraceThisVar _ANSI_ARGS_((ClientData cdata,
41
    Tcl_Interp *interp, char *name1, char *name2, int flags));
42
 
43
static void ItclDestroyObject _ANSI_ARGS_((ClientData cdata));
44
static void ItclFreeObject _ANSI_ARGS_((char* cdata));
45
 
46
static int ItclDestructBase _ANSI_ARGS_((Tcl_Interp *interp,
47
    ItclObject* obj, ItclClass* cdefn, int flags));
48
 
49
static void ItclCreateObjVar _ANSI_ARGS_((Tcl_Interp *interp,
50
    ItclVarDefn* vdefn, ItclObject* obj));
51
 
52
 
53
/*
54
 * ------------------------------------------------------------------------
55
 *  Itcl_CreateObject()
56
 *
57
 *  Creates a new object instance belonging to the given class.
58
 *  Supports complex object names like "namesp::namesp::name" by
59
 *  following the namespace path and creating the object in the
60
 *  desired namespace.
61
 *
62
 *  Automatically creates and initializes data members, including the
63
 *  built-in protected "this" variable containing the object name.
64
 *  Installs an access command in the current namespace, and invokes
65
 *  the constructor to initialize the object.
66
 *
67
 *  If any errors are encountered, the object is destroyed and this
68
 *  procedure returns TCL_ERROR (along with an error message in the
69
 *  interpreter).  Otherwise, it returns TCL_OK, along with a pointer
70
 *  to the new object data in roPtr.
71
 * ------------------------------------------------------------------------
72
 */
73
int
74
Itcl_CreateObject(interp, name, cdefn, objc, objv, roPtr)
75
    Tcl_Interp *interp;      /* interpreter mananging new object */
76
    char* name;              /* name of new object */
77
    ItclClass *cdefn;        /* class for new object */
78
    int objc;                /* number of arguments */
79
    Tcl_Obj *CONST objv[];   /* argument objects */
80
    ItclObject **roPtr;      /* returns: pointer to object data */
81
{
82
    ItclClass *cdefnPtr = (ItclClass*)cdefn;
83
    int result = TCL_OK;
84
 
85
    char *head, *tail;
86
    Tcl_DString buffer, objName;
87
    Tcl_Namespace *parentNs;
88
    ItclContext context;
89
    Tcl_Command cmd;
90
    ItclObject *newObj;
91
    ItclClass *cdPtr;
92
    ItclVarDefn *vdefn;
93
    ItclHierIter hier;
94
    Tcl_HashEntry *entry;
95
    Tcl_HashSearch place;
96
    int newEntry;
97
    Itcl_InterpState istate;
98
 
99
    /*
100
     *  If installing an object access command will clobber another
101
     *  command, signal an error.
102
     */
103
    cmd = Tcl_FindCommand(interp, name, (Tcl_Namespace*)NULL, /* flags */ 0);
104
    if (cmd != NULL && !Itcl_IsStub(cmd)) {
105
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
106
            "command \"", name, "\" already exists in namespace \"",
107
            Tcl_GetCurrentNamespace(interp)->fullName, "\"",
108
            (char*)NULL);
109
        return TCL_ERROR;
110
    }
111
 
112
    /*
113
     *  Extract the namespace context and the simple object
114
     *  name for the new object.
115
     */
116
    Itcl_ParseNamespPath(name, &buffer, &head, &tail);
117
    if (head) {
118
        parentNs = Itcl_FindClassNamespace(interp, head);
119
 
120
        if (!parentNs) {
121
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
122
                "namespace \"", head, "\" not found in context \"",
123
                Tcl_GetCurrentNamespace(interp)->fullName, "\"",
124
                (char*)NULL);
125
            Tcl_DStringFree(&buffer);
126
            return TCL_ERROR;
127
        }
128
    } else {
129
        parentNs = Tcl_GetCurrentNamespace(interp);
130
    }
131
 
132
    Tcl_DStringInit(&objName);
133
    if (parentNs != Tcl_GetGlobalNamespace(interp)) {
134
        Tcl_DStringAppend(&objName, parentNs->fullName, -1);
135
    }
136
    Tcl_DStringAppend(&objName, "::", -1);
137
    Tcl_DStringAppend(&objName, tail, -1);
138
 
139
    /*
140
     *  Create a new object and initialize it.
141
     */
142
    newObj = (ItclObject*)ckalloc(sizeof(ItclObject));
143
    newObj->classDefn = cdefnPtr;
144
    Itcl_PreserveData((ClientData)cdefnPtr);
145
 
146
    newObj->dataSize = cdefnPtr->numInstanceVars;
147
    newObj->data = (Var**)ckalloc((unsigned)(newObj->dataSize*sizeof(Var*)));
148
 
149
    newObj->constructed = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
150
    Tcl_InitHashTable(newObj->constructed, TCL_STRING_KEYS);
151
    newObj->destructed = NULL;
152
 
153
    /*
154
     *  Add a command to the current namespace with the object name.
155
     *  This is done before invoking the constructors so that the
156
     *  command can be used during construction to query info.
157
     */
158
    Itcl_PreserveData((ClientData)newObj);
159
    newObj->accessCmd = Tcl_CreateObjCommand(interp,
160
        Tcl_DStringValue(&objName), Itcl_HandleInstance,
161
        (ClientData)newObj, ItclDestroyObject);
162
 
163
    Itcl_PreserveData((ClientData)newObj);  /* while we're using this... */
164
    Itcl_EventuallyFree((ClientData)newObj, ItclFreeObject);
165
 
166
    Tcl_DStringFree(&buffer);
167
    Tcl_DStringFree(&objName);
168
 
169
    /*
170
     *  Install the class namespace and object context so that
171
     *  the object's data members can be initialized via simple
172
     *  "set" commands.
173
     */
174
    if (Itcl_PushContext(interp, (ItclMember*)NULL, cdefnPtr, newObj,
175
        &context) != TCL_OK) {
176
 
177
        return TCL_ERROR;
178
    }
179
 
180
    Itcl_InitHierIter(&hier, cdefn);
181
 
182
    cdPtr = Itcl_AdvanceHierIter(&hier);
183
    while (cdPtr != NULL) {
184
        entry = Tcl_FirstHashEntry(&cdPtr->variables, &place);
185
        while (entry) {
186
            vdefn = (ItclVarDefn*)Tcl_GetHashValue(entry);
187
            if ((vdefn->member->flags & ITCL_THIS_VAR) != 0) {
188
                if (cdPtr == cdefnPtr) {
189
                    ItclCreateObjVar(interp, vdefn, newObj);
190
                    Tcl_SetVar2(interp, "this", (char*)NULL, "", 0);
191
                    Tcl_TraceVar2(interp, "this", (char*)NULL,
192
                        TCL_TRACE_READS|TCL_TRACE_WRITES, ItclTraceThisVar,
193
                        (ClientData)newObj);
194
                }
195
            }
196
            else if ( (vdefn->member->flags & ITCL_COMMON) == 0) {
197
                ItclCreateObjVar(interp, vdefn, newObj);
198
            }
199
            entry = Tcl_NextHashEntry(&place);
200
        }
201
        cdPtr = Itcl_AdvanceHierIter(&hier);
202
    }
203
    Itcl_DeleteHierIter(&hier);
204
 
205
    Itcl_PopContext(interp, &context);  /* back to calling context */
206
 
207
    /*
208
     *  Now construct the object.  Look for a constructor in the
209
     *  most-specific class, and if there is one, invoke it.
210
     *  This will cause a chain reaction, making sure that all
211
     *  base classes constructors are invoked as well, in order
212
     *  from least- to most-specific.  Any constructors that are
213
     *  not called out explicitly in "initCode" code fragments are
214
     *  invoked implicitly without arguments.
215
     */
216
    result = Itcl_InvokeMethodIfExists(interp, "constructor",
217
        cdefn, newObj, objc, objv);
218
 
219
    /*
220
     *  If there is no constructor, construct the base classes
221
     *  in case they have constructors.  This will cause the
222
     *  same chain reaction.
223
     */
224
    if (!Tcl_FindHashEntry(&cdefn->functions, "constructor")) {
225
        result = Itcl_ConstructBase(interp, newObj, cdefn);
226
    }
227
 
228
    /*
229
     *  If construction failed, then delete the object access
230
     *  command.  This will destruct the object and delete the
231
     *  object data.  Be careful to save and restore the interpreter
232
     *  state, since the destructors may generate errors of their own.
233
     */
234
    if (result != TCL_OK) {
235
        istate = Itcl_SaveInterpState(interp, result);
236
        if (newObj->accessCmd != NULL) {
237
            Tcl_DeleteCommandFromToken(interp, newObj->accessCmd);
238
            newObj->accessCmd = NULL;
239
        }
240
        result = Itcl_RestoreInterpState(interp, istate);
241
    }
242
 
243
    /*
244
     *  At this point, the object is fully constructed.
245
     *  Destroy the "constructed" table in the object data, since
246
     *  it is no longer needed.
247
     */
248
    Tcl_DeleteHashTable(newObj->constructed);
249
    ckfree((char*)newObj->constructed);
250
    newObj->constructed = NULL;
251
 
252
    /*
253
     *  Add it to the list of all known objects. The only
254
     *  tricky thing to watch out for is the case where the
255
     *  object deleted itself inside its own constructor.
256
     *  In that case, we don't want to add the object to
257
     *  the list of valid objects. We can determine that
258
     *  the object deleted itself by checking to see if its
259
     *  accessCmd member is NULL.
260
     */
261
    if ((result == TCL_OK) && (newObj->accessCmd != NULL)) {
262
        entry = Tcl_CreateHashEntry(&cdefnPtr->info->objects,
263
            (char*)newObj->accessCmd, &newEntry);
264
 
265
        Tcl_SetHashValue(entry, (ClientData)newObj);
266
    }
267
 
268
    /*
269
     *  Release the object.  If it was destructed above, it will
270
     *  die at this point.
271
     */
272
    Itcl_ReleaseData((ClientData)newObj);
273
 
274
    *roPtr = newObj;
275
    return result;
276
}
277
 
278
 
279
/*
280
 * ------------------------------------------------------------------------
281
 *  Itcl_DeleteObject()
282
 *
283
 *  Attempts to delete an object by invoking its destructor.
284
 *
285
 *  If the destructor is successful, then the object is deleted by
286
 *  removing its access command, and this procedure returns TCL_OK.
287
 *  Otherwise, the object will remain alive, and this procedure
288
 *  returns TCL_ERROR (along with an error message in the interpreter).
289
 * ------------------------------------------------------------------------
290
 */
291
int
292
Itcl_DeleteObject(interp, contextObj)
293
    Tcl_Interp *interp;      /* interpreter mananging object */
294
    ItclObject *contextObj;  /* object to be deleted */
295
{
296
    ItclClass *cdefnPtr = (ItclClass*)contextObj->classDefn;
297
 
298
    Tcl_HashEntry *entry;
299
    Command *cmdPtr;
300
 
301
    Itcl_PreserveData((ClientData)contextObj);
302
 
303
    /*
304
     *  Invoke the object's destructors.
305
     */
306
    if (Itcl_DestructObject(interp, contextObj, 0) != TCL_OK) {
307
        Itcl_ReleaseData((ClientData)contextObj);
308
        return TCL_ERROR;
309
    }
310
 
311
    /*
312
     *  Remove the object from the global list.
313
     */
314
    entry = Tcl_FindHashEntry(&cdefnPtr->info->objects,
315
        (char*)contextObj->accessCmd);
316
 
317
    if (entry) {
318
        Tcl_DeleteHashEntry(entry);
319
    }
320
 
321
    /*
322
     *  Change the object's access command so that it can be
323
     *  safely deleted without attempting to destruct the object
324
     *  again.  Then delete the access command.  If this is
325
     *  the last use of the object data, the object will die here.
326
     */
327
    cmdPtr = (Command*)contextObj->accessCmd;
328
    cmdPtr->deleteProc = Itcl_ReleaseData;
329
 
330
    Tcl_DeleteCommandFromToken(interp, contextObj->accessCmd);
331
    contextObj->accessCmd = NULL;
332
 
333
    Itcl_ReleaseData((ClientData)contextObj);  /* object should die here */
334
 
335
    return TCL_OK;
336
}
337
 
338
 
339
/*
340
 * ------------------------------------------------------------------------
341
 *  Itcl_DestructObject()
342
 *
343
 *  Invokes the destructor for a particular object.  Usually invoked
344
 *  by Itcl_DeleteObject() or Itcl_DestroyObject() as a part of the
345
 *  object destruction process.  If the ITCL_IGNORE_ERRS flag is
346
 *  included, all destructors are invoked even if errors are
347
 *  encountered, and the result will always be TCL_OK.
348
 *
349
 *  Returns TCL_OK on success, or TCL_ERROR (along with an error
350
 *  message in the interpreter) if anything goes wrong.
351
 * ------------------------------------------------------------------------
352
 */
353
int
354
Itcl_DestructObject(interp, contextObj, flags)
355
    Tcl_Interp *interp;      /* interpreter mananging new object */
356
    ItclObject *contextObj;  /* object to be destructed */
357
    int flags;               /* flags: ITCL_IGNORE_ERRS */
358
{
359
    int result;
360
 
361
    /*
362
     *  If there is a "destructed" table, then this object is already
363
     *  being destructed.  Flag an error, unless errors are being
364
     *  ignored.
365
     */
366
    if (contextObj->destructed) {
367
        if ((flags & ITCL_IGNORE_ERRS) == 0) {
368
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
369
                "can't delete an object while it is being destructed",
370
                (char*)NULL);
371
            return TCL_ERROR;
372
        }
373
        return TCL_OK;
374
    }
375
 
376
    /*
377
     *  Create a "destructed" table to keep track of which destructors
378
     *  have been invoked.  This is used in ItclDestructBase to make
379
     *  sure that all base class destructors have been called,
380
     *  explicitly or implicitly.
381
     */
382
    contextObj->destructed = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
383
    Tcl_InitHashTable(contextObj->destructed, TCL_STRING_KEYS);
384
 
385
    /*
386
     *  Destruct the object starting from the most-specific class.
387
     *  If all goes well, return the null string as the result.
388
     */
389
    result = ItclDestructBase(interp, contextObj, contextObj->classDefn, flags);
390
 
391
    if (result == TCL_OK) {
392
        Tcl_ResetResult(interp);
393
    }
394
 
395
    Tcl_DeleteHashTable(contextObj->destructed);
396
    ckfree((char*)contextObj->destructed);
397
    contextObj->destructed = NULL;
398
 
399
    return result;
400
}
401
 
402
/*
403
 * ------------------------------------------------------------------------
404
 *  ItclDestructBase()
405
 *
406
 *  Invoked by Itcl_DestructObject() to recursively destruct an object
407
 *  from the specified class level.  Finds and invokes the destructor
408
 *  for the specified class, and then recursively destructs all base
409
 *  classes.  If the ITCL_IGNORE_ERRS flag is included, all destructors
410
 *  are invoked even if errors are encountered, and the result will
411
 *  always be TCL_OK.
412
 *
413
 *  Returns TCL_OK on success, or TCL_ERROR (along with an error message
414
 *  in interp->result) on error.
415
 * ------------------------------------------------------------------------
416
 */
417
static int
418
ItclDestructBase(interp, contextObj, contextClass, flags)
419
    Tcl_Interp *interp;       /* interpreter */
420
    ItclObject *contextObj;   /* object being destructed */
421
    ItclClass *contextClass;  /* current class being destructed */
422
    int flags;                /* flags: ITCL_IGNORE_ERRS */
423
{
424
    int result;
425
    Itcl_ListElem *elem;
426
    ItclClass *cdefn;
427
 
428
    /*
429
     *  Look for a destructor in this class, and if found,
430
     *  invoke it.
431
     */
432
    if (!Tcl_FindHashEntry(contextObj->destructed, contextClass->name)) {
433
 
434
        result = Itcl_InvokeMethodIfExists(interp, "destructor",
435
            contextClass, contextObj, 0, (Tcl_Obj* CONST*)NULL);
436
 
437
        if (result != TCL_OK) {
438
            return TCL_ERROR;
439
        }
440
    }
441
 
442
    /*
443
     *  Scan through the list of base classes recursively and destruct
444
     *  them.  Traverse the list in normal order, so that we destruct
445
     *  from most- to least-specific.
446
     */
447
    elem = Itcl_FirstListElem(&contextClass->bases);
448
    while (elem) {
449
        cdefn = (ItclClass*)Itcl_GetListValue(elem);
450
 
451
        if (ItclDestructBase(interp, contextObj, cdefn, flags) != TCL_OK) {
452
            return TCL_ERROR;
453
        }
454
        elem = Itcl_NextListElem(elem);
455
    }
456
 
457
    /*
458
     *  Throw away any result from the destructors and return.
459
     */
460
    Tcl_ResetResult(interp);
461
    return TCL_OK;
462
}
463
 
464
 
465
/*
466
 * ------------------------------------------------------------------------
467
 *  Itcl_FindObject()
468
 *
469
 *  Searches for an object with the specified name, which have
470
 *  namespace scope qualifiers like "namesp::namesp::name", or may
471
 *  be a scoped value such as "namespace inscope ::foo obj".
472
 *
473
 *  If an error is encountered, this procedure returns TCL_ERROR
474
 *  along with an error message in the interpreter.  Otherwise, it
475
 *  returns TCL_OK.  If an object was found, "roPtr" returns a
476
 *  pointer to the object data.  Otherwise, it returns NULL.
477
 * ------------------------------------------------------------------------
478
 */
479
int
480
Itcl_FindObject(interp, name, roPtr)
481
    Tcl_Interp *interp;      /* interpreter containing this object */
482
    char *name;              /* name of the object */
483
    ItclObject **roPtr;      /* returns: object data or NULL */
484
{
485
    Tcl_Namespace *contextNs = NULL;
486
 
487
    char *cmdName;
488
    Tcl_Command cmd;
489
    Command *cmdPtr;
490
 
491
    /*
492
     *  The object name may be a scoped value of the form
493
     *  "namespace inscope <namesp> <command>".  If it is,
494
     *  decode it.
495
     */
496
    if (Itcl_DecodeScopedCommand(interp, name, &contextNs, &cmdName)
497
        != TCL_OK) {
498
        return TCL_ERROR;
499
    }
500
 
501
    /*
502
     *  Look for the object's access command, and see if it has
503
     *  the appropriate command handler.
504
     */
505
    cmd = Tcl_FindCommand(interp, cmdName, contextNs, /* flags */ 0);
506
    if (cmd != NULL && Itcl_IsObject(cmd)) {
507
        cmdPtr = (Command*)cmd;
508
        *roPtr = (ItclObject*)cmdPtr->objClientData;
509
    }
510
    else {
511
        *roPtr = NULL;
512
    }
513
 
514
    if (cmdName != name) {
515
        ckfree(cmdName);
516
    }
517
    return TCL_OK;
518
}
519
 
520
 
521
/*
522
 * ------------------------------------------------------------------------
523
 *  Itcl_IsObject()
524
 *
525
 *  Checks the given Tcl command to see if it represents an itcl object.
526
 *  Returns non-zero if the command is associated with an object.
527
 * ------------------------------------------------------------------------
528
 */
529
int
530
Itcl_IsObject(cmd)
531
    Tcl_Command cmd;         /* command being tested */
532
{
533
    Command *cmdPtr = (Command*)cmd;
534
 
535
    if (cmdPtr->deleteProc == ItclDestroyObject) {
536
        return 1;
537
    }
538
 
539
    /*
540
     *  This may be an imported command.  Try to get the real
541
     *  command and see if it represents an object.
542
     */
543
    cmdPtr = (Command*)TclGetOriginalCommand(cmd);
544
    if (cmdPtr && cmdPtr->deleteProc == ItclDestroyObject) {
545
        return 1;
546
    }
547
    return 0;
548
}
549
 
550
 
551
/*
552
 * ------------------------------------------------------------------------
553
 *  Itcl_ObjectIsa()
554
 *
555
 *  Checks to see if an object belongs to the given class.  An object
556
 *  "is-a" member of the class if the class appears anywhere in its
557
 *  inheritance hierarchy.  Returns non-zero if the object belongs to
558
 *  the class, and zero otherwise.
559
 * ------------------------------------------------------------------------
560
 */
561
int
562
Itcl_ObjectIsa(contextObj, cdefn)
563
    ItclObject *contextObj;   /* object being tested */
564
    ItclClass *cdefn;         /* class to test for "is-a" relationship */
565
{
566
    Tcl_HashEntry *entry;
567
    entry = Tcl_FindHashEntry(&contextObj->classDefn->heritage, (char*)cdefn);
568
    return (entry != NULL);
569
}
570
 
571
 
572
/*
573
 * ------------------------------------------------------------------------
574
 *  Itcl_HandleInstance()
575
 *
576
 *  Invoked by Tcl whenever the user issues a command associated with
577
 *  an object instance.  Handles the following syntax:
578
 *
579
 *    <objName> <method> <args>...
580
 *
581
 * ------------------------------------------------------------------------
582
 */
583
int
584
Itcl_HandleInstance(clientData, interp, objc, objv)
585
    ClientData clientData;   /* object definition */
586
    Tcl_Interp *interp;      /* current interpreter */
587
    int objc;                /* number of arguments */
588
    Tcl_Obj *CONST objv[];   /* argument objects */
589
{
590
    ItclObject *contextObj = (ItclObject*)clientData;
591
 
592
    int result;
593
    char *token;
594
    Tcl_HashEntry *entry;
595
    ItclMemberFunc *mfunc;
596
    ItclObjectInfo *info;
597
    ItclContext context;
598
    CallFrame *framePtr;
599
 
600
    if (objc < 2) {
601
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
602
            "wrong # args: should be one of...",
603
            (char*)NULL);
604
        ItclReportObjectUsage(interp, contextObj);
605
        return TCL_ERROR;
606
    }
607
 
608
    /*
609
     *  Make sure that the specified operation is really an
610
     *  object method, and it is accessible.  If not, return usage
611
     *  information for the object.
612
     */
613
    token = Tcl_GetStringFromObj(objv[1], (int*)NULL);
614
    mfunc = NULL;
615
 
616
    entry = Tcl_FindHashEntry(&contextObj->classDefn->resolveCmds, token);
617
    if (entry) {
618
        mfunc = (ItclMemberFunc*)Tcl_GetHashValue(entry);
619
 
620
        if ((mfunc->member->flags & ITCL_COMMON) != 0) {
621
            mfunc = NULL;
622
        }
623
        else if (mfunc->member->protection != ITCL_PUBLIC) {
624
            Tcl_Namespace *contextNs = Itcl_GetTrueNamespace(interp,
625
                mfunc->member->classDefn->info);
626
 
627
            if (!Itcl_CanAccessFunc(mfunc, contextNs)) {
628
                mfunc = NULL;
629
            }
630
        }
631
    }
632
 
633
    if ( !mfunc && (*token != 'i' || strcmp(token,"info") != 0) ) {
634
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
635
            "bad option \"", token, "\": should be one of...",
636
            (char*)NULL);
637
        ItclReportObjectUsage(interp, contextObj);
638
        return TCL_ERROR;
639
    }
640
 
641
    /*
642
     *  Install an object context and invoke the method.
643
     *
644
     *  TRICKY NOTE:  We need to pass the object context into the
645
     *    method, but activating the context here puts us one level
646
     *    down, and when the method is called, it will activate its
647
     *    own context, putting us another level down.  If anyone
648
     *    were to execute an "uplevel" command in the method, they
649
     *    would notice the extra call frame.  So we mark this frame
650
     *    as "transparent" and Itcl_EvalMemberCode will automatically
651
     *    do an "uplevel" operation to correct the problem.
652
     */
653
    info = contextObj->classDefn->info;
654
 
655
    if (Itcl_PushContext(interp, (ItclMember*)NULL, contextObj->classDefn,
656
        contextObj, &context) != TCL_OK) {
657
 
658
        return TCL_ERROR;
659
    }
660
 
661
    framePtr = &context.frame;
662
    Itcl_PushStack((ClientData)framePtr, &info->transparentFrames);
663
 
664
    result = Itcl_EvalArgs(interp, objc-1, objv+1);
665
 
666
    Itcl_PopStack(&info->transparentFrames);
667
    Itcl_PopContext(interp, &context);
668
 
669
    return result;
670
}
671
 
672
 
673
/*
674
 * ------------------------------------------------------------------------
675
 *  Itcl_GetInstanceVar()
676
 *
677
 *  Returns the current value for an object data member.  The member
678
 *  name is interpreted with respect to the given class scope, which
679
 *  is usually the most-specific class for the object.
680
 *
681
 *  If successful, this procedure returns a pointer to a string value
682
 *  which remains alive until the variable changes it value.  If
683
 *  anything goes wrong, this returns NULL.
684
 * ------------------------------------------------------------------------
685
 */
686
char*
687
Itcl_GetInstanceVar(interp, name, contextObj, contextClass)
688
    Tcl_Interp *interp;       /* current interpreter */
689
    char *name;               /* name of desired instance variable */
690
    ItclObject *contextObj;   /* current object */
691
    ItclClass *contextClass;  /* name is interpreted in this scope */
692
{
693
    ItclContext context;
694
    char *val;
695
 
696
    /*
697
     *  Make sure that the current namespace context includes an
698
     *  object that is being manipulated.
699
     */
700
    if (contextObj == NULL) {
701
        Tcl_ResetResult(interp);
702
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
703
            "cannot access object-specific info without an object context",
704
            (char*)NULL);
705
        return NULL;
706
    }
707
 
708
    /*
709
     *  Install the object context and access the data member
710
     *  like any other variable.
711
     */
712
    if (Itcl_PushContext(interp, (ItclMember*)NULL, contextClass,
713
        contextObj, &context) != TCL_OK) {
714
 
715
        return NULL;
716
    }
717
 
718
    val = Tcl_GetVar2(interp, name, (char*)NULL, TCL_LEAVE_ERR_MSG);
719
    Itcl_PopContext(interp, &context);
720
 
721
    return val;
722
}
723
 
724
 
725
/*
726
 * ------------------------------------------------------------------------
727
 *  ItclReportObjectUsage()
728
 *
729
 *  Appends information to the given interp summarizing the usage
730
 *  for all of the methods available for this object.  Useful when
731
 *  reporting errors in Itcl_HandleInstance().
732
 * ------------------------------------------------------------------------
733
 */
734
static void
735
ItclReportObjectUsage(interp, contextObj)
736
    Tcl_Interp *interp;      /* current interpreter */
737
    ItclObject *contextObj;  /* current object */
738
{
739
    ItclClass *cdefnPtr = (ItclClass*)contextObj->classDefn;
740
    int ignore = ITCL_CONSTRUCTOR | ITCL_DESTRUCTOR | ITCL_COMMON;
741
 
742
    int cmp;
743
    char *name;
744
    Itcl_List cmdList;
745
    Itcl_ListElem *elem;
746
    Tcl_HashEntry *entry;
747
    Tcl_HashSearch place;
748
    ItclMemberFunc *mfunc, *cmpDefn;
749
    Tcl_Obj *resultPtr;
750
 
751
    /*
752
     *  Scan through all methods in the virtual table and sort
753
     *  them in alphabetical order.  Report only the methods
754
     *  that have simple names (no ::'s) and are accessible.
755
     */
756
    Itcl_InitList(&cmdList);
757
    entry = Tcl_FirstHashEntry(&cdefnPtr->resolveCmds, &place);
758
    while (entry) {
759
        name  = Tcl_GetHashKey(&cdefnPtr->resolveCmds, entry);
760
        mfunc = (ItclMemberFunc*)Tcl_GetHashValue(entry);
761
 
762
        if (strstr(name,"::") || (mfunc->member->flags & ignore) != 0) {
763
            mfunc = NULL;
764
        }
765
        else if (mfunc->member->protection != ITCL_PUBLIC) {
766
            Tcl_Namespace *contextNs = Itcl_GetTrueNamespace(interp,
767
                mfunc->member->classDefn->info);
768
 
769
            if (!Itcl_CanAccessFunc(mfunc, contextNs)) {
770
                mfunc = NULL;
771
            }
772
        }
773
 
774
        if (mfunc) {
775
            elem = Itcl_FirstListElem(&cmdList);
776
            while (elem) {
777
                cmpDefn = (ItclMemberFunc*)Itcl_GetListValue(elem);
778
                cmp = strcmp(mfunc->member->name, cmpDefn->member->name);
779
                if (cmp < 0) {
780
                    Itcl_InsertListElem(elem, (ClientData)mfunc);
781
                    mfunc = NULL;
782
                    break;
783
                }
784
                else if (cmp == 0) {
785
                    mfunc = NULL;
786
                    break;
787
                }
788
                elem = Itcl_NextListElem(elem);
789
            }
790
            if (mfunc) {
791
                Itcl_AppendList(&cmdList, (ClientData)mfunc);
792
            }
793
        }
794
        entry = Tcl_NextHashEntry(&place);
795
    }
796
 
797
    /*
798
     *  Add a series of statements showing usage info.
799
     */
800
    resultPtr = Tcl_GetObjResult(interp);
801
    elem = Itcl_FirstListElem(&cmdList);
802
    while (elem) {
803
        mfunc = (ItclMemberFunc*)Itcl_GetListValue(elem);
804
        Tcl_AppendToObj(resultPtr, "\n  ", -1);
805
        Itcl_GetMemberFuncUsage(mfunc, contextObj, resultPtr);
806
 
807
        elem = Itcl_NextListElem(elem);
808
    }
809
    Itcl_DeleteList(&cmdList);
810
}
811
 
812
 
813
/*
814
 * ------------------------------------------------------------------------
815
 *  ItclTraceThisVar()
816
 *
817
 *  Invoked to handle read/write traces on the "this" variable built
818
 *  into each object.
819
 *
820
 *  On read, this procedure updates the "this" variable to contain the
821
 *  current object name.  This is done dynamically, since an object's
822
 *  identity can change if its access command is renamed.
823
 *
824
 *  On write, this procedure returns an error string, warning that
825
 *  the "this" variable cannot be set.
826
 * ------------------------------------------------------------------------
827
 */
828
/* ARGSUSED */
829
static char*
830
ItclTraceThisVar(cdata, interp, name1, name2, flags)
831
    ClientData cdata;        /* object instance data */
832
    Tcl_Interp *interp;      /* interpreter managing this variable */
833
    char *name1;             /* variable name */
834
    char *name2;             /* unused */
835
    int flags;               /* flags indicating read/write */
836
{
837
    ItclObject *contextObj = (ItclObject*)cdata;
838
    char *objName;
839
    Tcl_Obj *objPtr;
840
 
841
    /*
842
     *  Handle read traces on "this"
843
     */
844
    if ((flags & TCL_TRACE_READS) != 0) {
845
        objPtr = Tcl_NewStringObj("", -1);
846
        Tcl_IncrRefCount(objPtr);
847
 
848
        if (contextObj->accessCmd) {
849
            Tcl_GetCommandFullName(contextObj->classDefn->interp,
850
                contextObj->accessCmd, objPtr);
851
        }
852
 
853
        objName = Tcl_GetStringFromObj(objPtr, (int*)NULL);
854
        Tcl_SetVar(interp, name1, objName, 0);
855
 
856
        Tcl_DecrRefCount(objPtr);
857
        return NULL;
858
    }
859
 
860
    /*
861
     *  Handle write traces on "this"
862
     */
863
    if ((flags & TCL_TRACE_WRITES) != 0) {
864
        return "variable \"this\" cannot be modified";
865
    }
866
    return NULL;
867
}
868
 
869
 
870
/*
871
 * ------------------------------------------------------------------------
872
 *  ItclDestroyObject()
873
 *
874
 *  Invoked when the object access command is deleted to implicitly
875
 *  destroy the object.  Invokes the object's destructors, ignoring
876
 *  any errors encountered along the way.  Removes the object from
877
 *  the list of all known objects and releases the access command's
878
 *  claim to the object data.
879
 *
880
 *  Note that the usual way to delete an object is via Itcl_DeleteObject().
881
 *  This procedure is provided as a back-up, to handle the case when
882
 *  an object is deleted by removing its access command.
883
 * ------------------------------------------------------------------------
884
 */
885
static void
886
ItclDestroyObject(cdata)
887
    ClientData cdata;  /* object instance data */
888
{
889
    ItclObject *contextObj = (ItclObject*)cdata;
890
    ItclClass *cdefnPtr = (ItclClass*)contextObj->classDefn;
891
    Tcl_HashEntry *entry;
892
    Itcl_InterpState istate;
893
 
894
    /*
895
     *  Attempt to destruct the object, but ignore any errors.
896
     */
897
    istate = Itcl_SaveInterpState(cdefnPtr->interp, 0);
898
    Itcl_DestructObject(cdefnPtr->interp, contextObj, ITCL_IGNORE_ERRS);
899
    Itcl_RestoreInterpState(cdefnPtr->interp, istate);
900
 
901
    /*
902
     *  Now, remove the object from the global object list.
903
     *  We're careful to do this here, after calling the destructors.
904
     *  Once the access command is nulled out, the "this" variable
905
     *  won't work properly.
906
     */
907
    if (contextObj->accessCmd) {
908
        entry = Tcl_FindHashEntry(&cdefnPtr->info->objects,
909
            (char*)contextObj->accessCmd);
910
 
911
        if (entry) {
912
            Tcl_DeleteHashEntry(entry);
913
        }
914
        contextObj->accessCmd = NULL;
915
    }
916
 
917
    Itcl_ReleaseData((ClientData)contextObj);
918
}
919
 
920
 
921
/*
922
 * ------------------------------------------------------------------------
923
 *  ItclFreeObject()
924
 *
925
 *  Deletes all instance variables and frees all memory associated with
926
 *  the given object instance.  This is usually invoked automatically
927
 *  by Itcl_ReleaseData(), when an object's data is no longer being used.
928
 * ------------------------------------------------------------------------
929
 */
930
static void
931
ItclFreeObject(cdata)
932
    char* cdata;  /* object instance data */
933
{
934
    ItclObject *contextObj = (ItclObject*)cdata;
935
    Tcl_Interp *interp = contextObj->classDefn->interp;
936
 
937
    int i;
938
    ItclClass *cdPtr;
939
    ItclHierIter hier;
940
    Tcl_HashSearch place;
941
    Tcl_HashEntry *entry;
942
    ItclVarDefn *vdefn;
943
    ItclContext context;
944
    Itcl_InterpState istate;
945
 
946
    /*
947
     *  Install the class namespace and object context so that
948
     *  the object's data members can be destroyed via simple
949
     *  "unset" commands.  This makes sure that traces work properly
950
     *  and all memory gets cleaned up.
951
     *
952
     *  NOTE:  Be careful to save and restore the interpreter state.
953
     *    Data can get freed in the middle of any operation, and
954
     *    we can't affort to clobber the interpreter with any errors
955
     *    from below.
956
     */
957
    istate = Itcl_SaveInterpState(interp, 0);
958
 
959
    /*
960
     *  Scan through all object-specific data members and destroy the
961
     *  actual variables that maintain the object state.  Do this
962
     *  by unsetting each variable, so that traces are fired off
963
     *  correctly.  Make sure that the built-in "this" variable is
964
     *  only destroyed once.  Also, be careful to activate the
965
     *  namespace for each class, so that private variables can
966
     *  be accessed.
967
     */
968
    Itcl_InitHierIter(&hier, contextObj->classDefn);
969
    cdPtr = Itcl_AdvanceHierIter(&hier);
970
    while (cdPtr != NULL) {
971
 
972
        if (Itcl_PushContext(interp, (ItclMember*)NULL, cdPtr,
973
            contextObj, &context) == TCL_OK) {
974
 
975
            entry = Tcl_FirstHashEntry(&cdPtr->variables, &place);
976
            while (entry) {
977
                vdefn = (ItclVarDefn*)Tcl_GetHashValue(entry);
978
                if ((vdefn->member->flags & ITCL_THIS_VAR) != 0) {
979
                    if (cdPtr == contextObj->classDefn) {
980
                        Tcl_UnsetVar2(interp, vdefn->member->fullname,
981
                            (char*)NULL, 0);
982
                    }
983
                }
984
                else if ((vdefn->member->flags & ITCL_COMMON) == 0) {
985
                    Tcl_UnsetVar2(interp, vdefn->member->fullname,
986
                        (char*)NULL, 0);
987
                }
988
                entry = Tcl_NextHashEntry(&place);
989
            }
990
            Itcl_PopContext(interp, &context);
991
        }
992
 
993
        cdPtr = Itcl_AdvanceHierIter(&hier);
994
    }
995
    Itcl_DeleteHierIter(&hier);
996
 
997
    /*
998
     *  Free the memory associated with object-specific variables.
999
     *  For normal variables this would be done automatically by
1000
     *  CleanupVar() when the variable is unset.  But object-specific
1001
     *  variables are protected by an extra reference count, and they
1002
     *  must be deleted explicitly here.
1003
     */
1004
    for (i=0; i < contextObj->dataSize; i++) {
1005
        if (contextObj->data[i]) {
1006
            ckfree((char*)contextObj->data[i]);
1007
        }
1008
    }
1009
 
1010
    Itcl_RestoreInterpState(interp, istate);
1011
 
1012
    /*
1013
     *  Free any remaining memory associated with the object.
1014
     */
1015
    ckfree((char*)contextObj->data);
1016
 
1017
    if (contextObj->constructed) {
1018
        Tcl_DeleteHashTable(contextObj->constructed);
1019
        ckfree((char*)contextObj->constructed);
1020
    }
1021
    if (contextObj->destructed) {
1022
        Tcl_DeleteHashTable(contextObj->destructed);
1023
        ckfree((char*)contextObj->destructed);
1024
    }
1025
    Itcl_ReleaseData((ClientData)contextObj->classDefn);
1026
 
1027
    ckfree((char*)contextObj);
1028
}
1029
 
1030
 
1031
/*
1032
 * ------------------------------------------------------------------------
1033
 *  ItclCreateObjVar()
1034
 *
1035
 *  Creates one variable acting as a data member for a specific
1036
 *  object.  Initializes the variable according to its definition,
1037
 *  and sets up its reference count so that it cannot be deleted
1038
 *  by ordinary means.  Installs the new variable directly into
1039
 *  the data array for the specified object.
1040
 * ------------------------------------------------------------------------
1041
 */
1042
static void
1043
ItclCreateObjVar(interp, vdefn, contextObj)
1044
    Tcl_Interp* interp;       /* interpreter managing this object */
1045
    ItclVarDefn* vdefn;       /* variable definition */
1046
    ItclObject* contextObj;   /* object being updated */
1047
{
1048
    Var *varPtr;
1049
    Tcl_HashEntry *entry;
1050
    ItclVarLookup *vlookup;
1051
    ItclContext context;
1052
 
1053
    varPtr = _TclNewVar();
1054
    varPtr->name = vdefn->member->name;
1055
    varPtr->nsPtr = (Namespace*)vdefn->member->classDefn->namesp;
1056
 
1057
    /*
1058
     *  NOTE:  Tcl reports a "dangling upvar" error for variables
1059
     *         with a null "hPtr" field.  Put something non-zero
1060
     *         in here to keep Tcl_SetVar2() happy.  The only time
1061
     *         this field is really used is it remove a variable
1062
     *         from the hash table that contains it in CleanupVar,
1063
     *         but since these variables are protected by their
1064
     *         higher refCount, they will not be deleted by CleanupVar
1065
     *         anyway.  These variables are unset and removed in
1066
     *         ItclFreeObject().
1067
     */
1068
    varPtr->hPtr = (Tcl_HashEntry*)0x1;
1069
    varPtr->refCount = 1;  /* protect from being deleted */
1070
 
1071
    /*
1072
     *  Install the new variable in the object's data array.
1073
     *  Look up the appropriate index for the object using
1074
     *  the data table in the class definition.
1075
     */
1076
    entry = Tcl_FindHashEntry(&contextObj->classDefn->resolveVars,
1077
        vdefn->member->fullname);
1078
 
1079
    if (entry) {
1080
        vlookup = (ItclVarLookup*)Tcl_GetHashValue(entry);
1081
        contextObj->data[vlookup->var.index] = varPtr;
1082
    }
1083
 
1084
    /*
1085
     *  If this variable has an initial value, initialize it
1086
     *  here using a "set" command.
1087
     *
1088
     *  TRICKY NOTE:  We push an object context for the class that
1089
     *    owns the variable, so that we don't have any trouble
1090
     *    accessing it.
1091
     */
1092
    if (vdefn->init) {
1093
        if (Itcl_PushContext(interp, (ItclMember*)NULL,
1094
            vdefn->member->classDefn, contextObj, &context) == TCL_OK) {
1095
 
1096
            Tcl_SetVar2(interp, vdefn->member->fullname,
1097
                (char*)NULL, vdefn->init, 0);
1098
            Itcl_PopContext(interp, &context);
1099
        }
1100
    }
1101
}
1102
 
1103
 
1104
/*
1105
 * ------------------------------------------------------------------------
1106
 *  Itcl_ScopedVarResolver()
1107
 *
1108
 *  This procedure is installed to handle variable resolution throughout
1109
 *  an entire interpreter.  It looks for scoped variable references of
1110
 *  the form:
1111
 *
1112
 *    @itcl ::namesp::namesp::object variable
1113
 *
1114
 *  If a reference like this is recognized, this procedure finds the
1115
 *  desired variable in the object and returns the variable, along with
1116
 *  the status code TCL_OK.  If the variable does not start with
1117
 *  "@itcl", this procedure returns TCL_CONTINUE, and variable
1118
 *  resolution continues using the normal rules.  If anything goes
1119
 *  wrong, this procedure returns TCL_ERROR, and access to the
1120
 *  variable is denied.
1121
 * ------------------------------------------------------------------------
1122
 */
1123
int
1124
Itcl_ScopedVarResolver(interp, name, contextNs, flags, rPtr)
1125
    Tcl_Interp *interp;        /* current interpreter */
1126
    char *name;                /* variable name being resolved */
1127
    Tcl_Namespace *contextNs;  /* current namespace context */
1128
    int flags;                 /* TCL_LEAVE_ERR_MSG => leave error message */
1129
    Tcl_Var *rPtr;             /* returns: resolved variable */
1130
{
1131
    int namec;
1132
    char **namev;
1133
    Tcl_Interp *errs;
1134
    Tcl_CmdInfo cmdInfo;
1135
    ItclObject *contextObj;
1136
    ItclVarLookup *vlookup;
1137
    Tcl_HashEntry *entry;
1138
 
1139
    /*
1140
     *  See if the variable starts with "@itcl".  If not, then
1141
     *  let the variable resolution process continue.
1142
     */
1143
    if (*name != '@' || strncmp(name, "@itcl", 5) != 0) {
1144
        return TCL_CONTINUE;
1145
    }
1146
 
1147
    /*
1148
     *  Break the variable name into parts and extract the object
1149
     *  name and the variable name.
1150
     */
1151
    if (flags & TCL_LEAVE_ERR_MSG) {
1152
        errs = interp;
1153
    } else {
1154
        errs = NULL;
1155
    }
1156
 
1157
    if (Tcl_SplitList(errs, name, &namec, &namev) != TCL_OK) {
1158
        return TCL_ERROR;
1159
    }
1160
    if (namec != 3) {
1161
        if (errs) {
1162
            Tcl_AppendStringsToObj(Tcl_GetObjResult(errs),
1163
                "scoped variable \"", name, "\" is malformed: ",
1164
                "should be: @itcl object variable",
1165
                (char*)NULL);
1166
        }
1167
        ckfree((char*)namev);
1168
        return TCL_ERROR;
1169
    }
1170
 
1171
    /*
1172
     *  Look for the command representing the object and extract
1173
     *  the object context.
1174
     */
1175
    if (!Tcl_GetCommandInfo(interp, namev[1], &cmdInfo)) {
1176
        if (errs) {
1177
            Tcl_AppendStringsToObj(Tcl_GetObjResult(errs),
1178
                "can't resolve scoped variable \"", name, "\": ",
1179
                "can't find object ", namev[1],
1180
                (char*)NULL);
1181
        }
1182
        ckfree((char*)namev);
1183
        return TCL_ERROR;
1184
    }
1185
    contextObj = (ItclObject*)cmdInfo.objClientData;
1186
 
1187
    /*
1188
     *  Resolve the variable with respect to the most-specific
1189
     *  class definition.
1190
     */
1191
    entry = Tcl_FindHashEntry(&contextObj->classDefn->resolveVars, namev[2]);
1192
    if (!entry) {
1193
        if (errs) {
1194
            Tcl_AppendStringsToObj(Tcl_GetObjResult(errs),
1195
                "can't resolve scoped variable \"", name, "\": ",
1196
                "no such data member ", namev[2],
1197
                (char*)NULL);
1198
        }
1199
        ckfree((char*)namev);
1200
        return TCL_ERROR;
1201
    }
1202
 
1203
    vlookup = (ItclVarLookup*)Tcl_GetHashValue(entry);
1204
    *rPtr = (Tcl_Var) contextObj->data[vlookup->var.index];
1205
 
1206
    ckfree((char*)namev);
1207
    return TCL_OK;
1208
}

powered by: WebSVN 2.1.0

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