cfad47cfa3/t3compiler/tads3/lib/_main.t

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#charset "us-ascii"
2
3
/* 
4
 *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved.
5
 *   
6
 *   This module defines a number of low-level functions and classes that
7
 *   most TADS 3 programs will need, whether based on the adv3 library or
8
 *   not.  This module includes the main program entrypoint, the basic
9
 *   Exception classes, and the modular initialization framework.
10
 *   
11
 *   The compiler automatically links this module into every program by
12
 *   default, but you can override this by specifying the "-nodef" option
13
 *   to t3make.  If you remove this module, you'll have to provide your own
14
 *   implementations for many of the functions and classes defined here.  
15
 */
16
17
#include "tads.h"
18
#include "reflect.h"
19
20
21
/* ------------------------------------------------------------------------ */
22
/*
23
 *   Main program entrypoint.  The VM invokes this function at program
24
 *   startup.  
25
 */
26
_main(args)
27
{
28
    /* call the common main entrypoint, with no startup file specified */
29
    _mainCommon(args, nil);
30
}
31
32
/*
33
 *   Main program entrypoint for restoring a saved state.  The VM invokes
34
 *   this function at startup instead of _main() when the user explicitly
35
 *   specifies a saved state file to restore when starting the program.
36
 *   (On a command-line interpreter, this would involve using a special
37
 *   option on the T3 interpreter command line; for a GUI shell, this
38
 *   might simply involve double-clicking on the desktop icon for a saved
39
 *   state file.)
40
 *   
41
 *   Note that we must export this as the 'mainRestore' symbol so that the
42
 *   interpreter knows how to find it.  
43
 */
44
export _mainRestore 'mainRestore';
45
_mainRestore(args, restoreFile)
46
{
47
    /* call the common main entrypoint */
48
    _mainCommon(args, restoreFile);
49
}
50
51
/*
52
 *   Common main entrypoint.  This function can be called with or without
53
 *   a saved state file to restore.  
54
 */
55
_mainCommon(args, restoreFile)
56
{    
57
    /* keep going as long as we keep restarting */
58
    for (;;)
59
    {
60
        try
61
        {
62
            /* perform load-time initialization */
63
            initAfterLoad();
64
            
65
            /* if we're not in preinit-only mode, run the program */
66
            if (!t3GetVMPreinitMode())
67
            {
68
                /* 
69
                 *   If there's a saved state file to restore, call our
70
                 *   mainRestore() function instead of main().  If
71
                 *   mainRestore() isn't defined, show a message to this
72
                 *   effect but keep going. 
73
                 */
74
                if (restoreFile != nil)
75
                {
76
                    /* check for a mainRestore function */
77
                    if (dataType(mainGlobal.mainRestoreFunc) == TypeFuncPtr)
78
                    {
79
                        try
80
                        {
81
                            /* 
82
                             *   Call the user's main startup-and-restore
83
                             *   entrypoint function.  Note that we call
84
                             *   indirectly through our function pointer
85
                             *   so that we don't force a function called
86
                             *   mainRestore() to be linked with the
87
                             *   program.  
88
                             */
89
                            (mainGlobal.mainRestoreFunc)(args, restoreFile);
90
                        }
91
                        finally
92
                        {
93
                            /* 
94
                             *   whatever happens, forget the saved state
95
                             *   file, since we do not want to restore it
96
                             *   again after restarting or anything else
97
                             *   that takes us back through the main
98
                             *   entrypoint again 
99
                             */
100
                            restoreFile = nil;
101
                        }
102
                    }
103
                    else
104
                    {
105
                        /* 
106
                         *   there's no mainRestore, so we can't restore
107
                         *   the saved state - note the problem 
108
                         */
109
                        "\n[This program cannot restore the saved position
110
                        file automatically.  Please try restoring the
111
                        saved position file again using a command within
112
                        the program.]\b";
113
114
                        /* call the ordinary main */
115
                        main(args);
116
                    }
117
                }
118
                else
119
                {
120
                    /* call the user's main program entrypoint */
121
                    main(args);
122
                }
123
            }
124
125
            /* we're done - break out of the restart loop */
126
            break;
127
        }
128
        catch (RestartSignal rsig)
129
        {
130
            /* 
131
             *   call the intrinsic restartGame function to reset all of
132
             *   the static objects to their initial state 
133
             */
134
            restartGame();
135
136
            /* 
137
             *   Now that we've reset the VM, update the restart ID in the
138
             *   main globals.  Note that we waited until now to do this,
139
             *   because this change would have been lost in the reset if
140
             *   we'd made the change before the reset.  Note also that the
141
             *   'rsig' object itself will survive the reset because the
142
             *   thrower presumably allocated it dynamically, hence it's
143
             *   not a static object subject to reset.  
144
             */
145
            mainGlobal.restartID = rsig.restartID;
146
147
            /*
148
             *   Now we can just continue on to the next iteration of the
149
             *   restart loop.  This will take us back to the
150
             *   initialization and enter the game as though we'd just
151
             *   started the program again. 
152
             */
153
        }
154
        catch (ProgramException exc)
155
        {
156
            /* 
157
             *   just re-throw these out to the VM, so that the VM exits to
158
             *   the operating system with an error indication 
159
             */
160
            throw exc;
161
        }
162
        catch (Exception exc)
163
        {
164
            /* display the unhandled exception */
165
            "\n<<exc.displayException()>>\n";
166
167
            /* we can't go on - break out of the restart loop */
168
            break;
169
        }
170
    }
171
}
172
173
/* ------------------------------------------------------------------------ */
174
/*
175
 *   Restart signal.  This can be used to restart from the main
176
 *   entrypoint.  The caller should create one of these objects, then use
177
 *   restartGame() (or an equivalent from a different function set, if
178
 *   appropriate) to reset static object state to the initial program load
179
 *   conditions, then throw the signal object.  
180
 */
181
class RestartSignal: Exception
182
    construct()
183
    {
184
        /* 
185
         *   use the next restart ID, so we can tell that we're on a fresh
186
         *   run on this session 
187
         */
188
        restartID = mainGlobal.restartID + 1;
189
    }
190
;
191
192
/* ------------------------------------------------------------------------ */
193
/*
194
 *   General post-load initialization.  The main program entrypoint
195
 *   _main() calls this routine to set up the default display function,
196
 *   run pre-initialization if necessary, and run initialization.  This
197
 *   routine is also useful for the target of a restartGame() routine, to
198
 *   perform all of the basic load-time initialization again after a
199
 *   restart.  
200
 */
201
initAfterLoad()
202
{
203
    /* establish the default display function */
204
    t3SetSay(_default_display_fn);
205
206
    /* if we haven't run preinit, do so now */
207
    if (!mainGlobal.preinited_)
208
    {
209
        /* 
210
         *   Explicitly run garbage collection prior to preinit.  This is
211
         *   usually harmless but unnecessary, since it usually doesn't
212
         *   much matter whether unreachable objects are still sitting in
213
         *   memory or not; but under certain conditions it's important.
214
         *   
215
         *   In particular, object loops (over all objects, or over all
216
         *   instances of a given class) can still see otherwise
217
         *   unreachable objects.  It's common to do these kinds of loops
218
         *   in preinit to set up static data caches and tables and so on.
219
         *   So, if there were any garbage objects lying around, preinit
220
         *   might find them and register them into tables or what not.
221
         *   
222
         *   Even considering the preinit object loop, doing a garbage
223
         *   collection sweep would *still* be redundant in most cases,
224
         *   since preinit is normally done right after compilation, when
225
         *   the program wouldn't yet have had a chance to create any
226
         *   garbage objects to be worried about in object loops.  However,
227
         *   there's still one more case to consider, and that's RESTART:
228
         *   in a debug build, or even in some release builds, we'd have to
229
         *   re-run preinit after a RESTART, and there certainly could be
230
         *   garbage objects left around from before the RESTART.
231
         *   
232
         *   To ensure that we deal gracefully with this combination of
233
         *   conditions - garbage objects, RESTART, and object loops in
234
         *   preinit - simply perform an explicit garbage collection cycle
235
         *   before invoking preinit.  
236
         */
237
        t3RunGC();
238
239
        /* run our internal preinit */
240
        _preinit();
241
        
242
        /* remember that we've run preinit */
243
        mainGlobal.preinited_ = true;
244
    }
245
246
    /* if we're not in preinit-only mode, run internal initialization */
247
    if (!t3GetVMPreinitMode())
248
        _init();
249
}
250
251
252
253
/* ------------------------------------------------------------------------ */
254
/*
255
 *   Module Execution Object.  This is an abstract base class for various
256
 *   classes that provide modular execution hooks.  This class and its
257
 *   subclasses are mix-in classes - they can be multiply inherited by any
258
 *   object (as long as it's not already some other kind of module
259
 *   execution object).
260
 *   
261
 *   The point of the Module Execution Object and its subclasses is to
262
 *   allow libraries and user code to define execution hooks, without
263
 *   having to worry about what other libraries and user code bits are
264
 *   defining the same hook.  When we need to execute a hook defined via
265
 *   this object, we iterate over all of the instances of the appropriate
266
 *   subclass and invoke its execute() method.
267
 *   
268
 *   By default, the order of execution is arbitrary.  In some cases,
269
 *   though, dependencies will exist, so that one object cannot be invoked
270
 *   until another object has already been invoked.  In these cases, you
271
 *   must set the execBeforeMe property to contain a list of the objects
272
 *   whose execute() methods must be invoked before this object's
273
 *   execute() method is invoked.  The library will check this list before
274
 *   calling execute() on this object, and ensure that each object in the
275
 *   list has been invoked before calling this object's execute().  
276
 */
277
class ModuleExecObject: object
278
    /* 
279
     *   List of objects that must be executed before me - by default, the
280
     *   order doesn't matter, so we'll set this to an empty list.
281
     *   Instances can override this if it is necessary to execute other
282
     *   objects before this object can be executed.  
283
     */
284
    execBeforeMe = []
285
286
    /*
287
     *   List of objects that must be executed after me - this is
288
     *   analogous to execBeforeMe, but we make sure we run before these. 
289
     */
290
    execAfterMe = []
291
292
    /* 
293
     *   Subclass-specific execution method.  Each subclass should
294
     *   override this method to provide its execution code.  
295
     */
296
    execute() { }
297
298
299
    /* 
300
     *   PRIVATE METHODS AND PROPERTIES.  Subclasses and instances should
301
     *   not need to override or invoke these.  
302
     */
303
304
    /* flag - true if we've been executed on this round */
305
    isExecuted_ = nil
306
307
    /* flag - true if we're in the process of executing */
308
    isDoingExec_ = nil
309
310
    /* execute - internal method: checks dependency order */
311
    _execute()
312
    {
313
        /*
314
         *   If I've already been executed, there's nothing more that I
315
         *   need to do.  We might be called by the arbitrarily-ordered
316
         *   iteration over all objects after we've already been executed,
317
         *   because we might be executed explicitly by an object that
318
         *   depends upon us if it's reached before we are.  
319
         */
320
        if (isExecuted_)
321
            return;
322
323
        /* 
324
         *   If we're in the process of executing any of the objects we
325
         *   depend upon, and a dependent calls us, we have a circular
326
         *   dependency.  
327
         */
328
        if (isDoingExec_)
329
            throw new CircularExecException(self);
330
331
        /*
332
         *   Mark ourselves as being in the process of executing.  If
333
         *   there are any circular dependencies (i.e., if we depend on an
334
         *   object, which in turn depends on us), it's clearly an error,
335
         *   in that both objects can't be executed before the other.
336
         *   This flag allows us to detect circular dependencies by
337
         *   noticing if we're called by a dependent while we're in the
338
         *   process of calling the things we depend upon.  
339
         */
340
        isDoingExec_ = true;
341
        
342
        /*
343
         *   Check each entry in my 'before' list to ensure that they've
344
         *   all been executed already.  Invoke execute() now for any that
345
         *   haven't.  
346
         */
347
        for (local i = 1, local cnt = execBeforeMe.length() ;
348
             i <= cnt ; ++i)
349
        {
350
            local cur;
351
352
            /* get this object */
353
            cur = execBeforeMe[i];
354
            
355
            /* if this one hasn't been executed yet, do so now */
356
            if (!cur.isExecuted_)
357
            {
358
                /* 
359
                 *   This one hasn't been executed yet - explicitly
360
                 *   execute it now.  Note that we do this recursively
361
                 *   through the internal execution method, so that 'cur'
362
                 *   has a chance to execute any objects that it depends
363
                 *   upon.  
364
                 */
365
                cur._execute();
366
            }
367
        }
368
369
        /* 
370
         *   we've resolved all of our dependencies, so we're good to go -
371
         *   run the user's execution code 
372
         */
373
        execute();
374
375
        /* 
376
         *   mark ourselves as having been executed, so we don't run the
377
         *   user's code again should we be called again by a dependent or
378
         *   by the global iteration loop later in the scan 
379
         */
380
        isExecuted_ = true;
381
        isDoingExec_ = nil;
382
    }
383
384
    /* flag to indicate that this is the first time running classExec */
385
    hasInitialized_ = nil
386
387
    /*
388
     *   Class execution.  Call this method on the particular class of
389
     *   modules to execute.  We'll iterate over all instances of that
390
     *   class and invoke each instance's _execute() method. 
391
     */
392
    classExec()
393
    {
394
        /*
395
         *   If this is the first time running this classExec, turn
396
         *   execAfterMe dependencies into appropriate execBeforeMe
397
         *   dependencies.  
398
         */
399
        if (!hasInitialized_)
400
        {
401
            /* 
402
             *   Go through all instances of this type of initializer, and
403
             *   re-cast the execAfterMe lists as execBeforeMe lists.  
404
             */
405
            forEachInstance(self,
406
                new function(obj)
407
                {
408
                    foreach(local dependent in obj.execAfterMe)
409
                        dependent.execBeforeMe += obj;
410
                });
411
412
            /* remember that we're now initialized */
413
            hasInitialized_ = true;
414
        }
415
        
416
        /* 
417
         *   since we're starting a new round, clear all of the 'executed'
418
         *   flags in all of the objects, to ensure that we execute all
419
         *   objects on this round (this cleans up the flag settings from
420
         *   any previous rounds) 
421
         */
422
        forEachInstance(self,
423
            { obj: obj.isExecuted_ = obj.isDoingExec_ = nil });
424
425
        /* execute all objects */
426
        forEachInstance(self, { obj: obj._execute() });
427
    }
428
;
429
430
/*
431
 *   Pre-Initialization object.  During pre-initialization, we'll invoke
432
 *   the execute() method on each instance of this class.  
433
 */
434
class PreinitObject: ModuleExecObject
435
    /*
436
     *   Each instance of this object MUST override execute() with the
437
     *   specific pre-initialization code that the instance wants to
438
     *   perform.
439
     *   
440
     *   In addition, each instance can optionally set the property
441
     *   execBeforeMe to a list of the other PreinitObject's that must be
442
     *   invoked before this object is.  If this property is not set, this
443
     *   object's place in the preinit execution order will be arbitrary.  
444
     */
445
;
446
447
/*
448
 *   Initialization object.  During initialization, just before calling
449
 *   the user's main(args) function, we'll invoke the execute() method on
450
 *   each instance of this class. 
451
 */
452
class InitObject: ModuleExecObject
453
    /*
454
     *   Each instance of this object MUST override execute() with the
455
     *   specific initialization code that the instance wants to perform.
456
     *   
457
     *   In addition, each instance can optionally set the property
458
     *   execBeforeMe to a list of the other InitObject's that must be
459
     *   invoked before this object is.  If this property is not set, this
460
     *   object's place in the initialization execution order will be
461
     *   arbitrary.  
462
     */
463
;
464
465
466
/*
467
 *   Exception: circular execution dependency in ModuleExecObject
468
 */
469
class CircularExecException: Exception
470
    construct(obj) { obj_ = obj; }
471
    displayException()
472
    {
473
        "circular module dependency detected (refer to
474
        ModuleExecObject._execute() in _main.t)";
475
    }
476
477
    /* 
478
     *   The object that detected the circular dependency.  We can't use
479
     *   this for much ourselves, but it might be useful to store this
480
     *   information so that it's available to the programmer from within
481
     *   the debugger.  
482
     */
483
    obj_ = nil
484
;
485
486
/*
487
 *   Library pre-initialization.  This is called immediately after
488
 *   compilation to pre-initialize the program.  Any changes made here to
489
 *   object states become part of the initial state stored in the image
490
 *   file, so this establishes the static initial state of the program.
491
 *   
492
 *   The advantage of doing work during pre-initialization is that this
493
 *   work is done once, during compilation, and is thus not repeated each
494
 *   time a user starts the program.  Time-consuming initialization work
495
 *   can thus be made invisible to the user.
496
 *   
497
 *   Note that the pre-initialization code should never do anything that
498
 *   involves the user interface, since this code runs during compilation
499
 *   and does not run again when users start the program.  So, anything
500
 *   that you want a user to see must be done during normal initialization
501
 *   (such as in the main() routine), not here.  
502
 */
503
_preinit()
504
{
505
    local symtab;
506
507
    /* try getting the mainRestore() function from the global symbol table */
508
    if ((symtab = t3GetGlobalSymbols()) != nil)
509
        mainGlobal.mainRestoreFunc = symtab['mainRestore'];
510
511
    /* execute all preinit objects */
512
    PreinitObject.classExec();
513
}
514
515
/*
516
 *   Library initialization.  This is called during each program start-up
517
 *   to initialize the program.  Since this is run each time the user
518
 *   starts the program, this can display any introductory messages, set
519
 *   up the user interface, and so on.  
520
 */
521
_init()
522
{
523
    /* execute all init objects */
524
    InitObject.classExec();
525
}
526
527
/* ------------------------------------------------------------------------ */
528
/*
529
 *   For convenience, a simple object iterator function.  This function
530
 *   invokes a callback function for each instance of the given class, in
531
 *   arbitrary order.
532
 *   
533
 *   The callback is invoked with one argument, which gives the current
534
 *   instance.  The callback can "break" out of the loop by throwing a
535
 *   BreakLoopSignal, which can be done conveniently using the breakLoop
536
 *   macro.  
537
 */
538
forEachInstance(cls, func)
539
{
540
    try
541
    {
542
        /* loop over all objects of the given class */
543
        for (local obj = firstObj(cls) ; obj != nil ; obj = nextObj(obj, cls))
544
            func(obj);
545
    }
546
    catch (BreakLoopSignal sig)
547
    {
548
        /* 
549
         *   ignore the signal - it simply means we want to terminate the
550
         *   loop and return to the caller 
551
         */
552
    }
553
}
554
555
/*
556
 *   Find an instance of the given class for which the given function
557
 *   returns true.  We iterate over objects of the given class in
558
 *   arbitrary order, and return the first instance for which the function
559
 *   returns true.  Retursn nil if there is no such instance.  
560
 */
561
instanceWhich(cls, func)
562
{
563
    /* loop over all objects of the given class */
564
    for (local obj = firstObj(cls) ; obj != nil ; obj = nextObj(obj, cls))
565
    {
566
        /* if the callback returns true for this object, return the object */
567
        if (func(obj))
568
            return obj;
569
    }
570
571
    /* 
572
     *   we didn't find any instances for which the callback returns true;
573
     *   indicate this by returning nil 
574
     */
575
    return nil;
576
}
577
578
/*
579
 *   An exception object for breaking out of a callback loop, such as
580
 *   forEachInstance. 
581
 */
582
class BreakLoopSignal: Exception
583
    displayException() { "loop break signal"; }
584
;
585
586
587
/* ------------------------------------------------------------------------ */
588
/*
589
 *   Get the "translated" datatype of a value.  This is essentially the
590
 *   same as dataType(), except that anonymous function objects are
591
 *   indicated as ordinary function pointer (TypeFuncPtr).  
592
 */
593
dataTypeXlat(val)
594
{
595
    local t;
596
597
    /* get the base type */
598
    t = dataType(val);
599
    
600
    /* if it's an anonymous function, return TypeFuncPtr */
601
    if (t == TypeObject && val.ofKind(AnonFuncPtr))
602
        return TypeFuncPtr;
603
604
    /* otherwise, just return the base type */
605
    return t;
606
}
607
608
/* ------------------------------------------------------------------------ */
609
/*
610
 *   Base class for all exception objects.  We derive all exceptions from
611
 *   this base class so that we can write 'catch' blocks that catch all
612
 *   exceptions by catching 'Exception'.
613
 *   
614
 *   The displayException() method displays a message describing the
615
 *   exception.  Subclasses should override this method.  
616
 */
617
class Exception: object
618
    /* display the exception - should always be overridden */
619
    displayException()
620
    {
621
        "Unknown exception";
622
    }
623
624
    /* 
625
     *   Display a stack trace, given a list of T3StackInfo objects.  Note
626
     *   that, for efficiency, we do not by default cache a stack trace
627
     *   when an exception occurs; individual subclasses can obtain a
628
     *   stack trace if desired at construction and use the information to
629
     *   show a stack trace for the exception. 
630
     */
631
    showStackTrace(stackList)
632
    {
633
        local haveSrc;
634
635
        /* check to see if there's any source info in the stack trace */
636
        haveSrc = nil;
637
        foreach (local cur in stackList)
638
        {
639
            /* note if we have source info here */
640
            if (cur.srcInfo_ != nil)
641
            {
642
                /* 
643
                 *   we have source information - note it and stop
644
                 *   searching, since even one bit of source info is
645
                 *   enough to show the stack 
646
                 */
647
                haveSrc = true;
648
                break;
649
            }
650
        }
651
652
        /* 
653
         *   if we have any source information at all, or we have
654
         *   reflection services available to decode the stack trace
655
         *   symbolically, show the stack 
656
         */
657
        if (haveSrc || mainGlobal.reflectionObj != nil)
658
        {
659
            "\nStack trace:\n";
660
            for (local i = 1, local cnt = stackList.length() ; i <= cnt ; ++i)
661
            {
662
                local cur = stackList[i];
663
                
664
                /* show a mark next to level 1, spaces elsewhere */
665
                if (i == 1)
666
                    "--&gt;";
667
                else
668
                    "\ \ ";
669
670
                /* 
671
                 *   if there's a system reflection object, show symbolic
672
                 *   information on the current function call; otherwise,
673
                 *   simply show the source location 
674
                 */
675
                if (mainGlobal.reflectionObj != nil)
676
                {
677
                    /* reflection is available - show full symbolic info */
678
                    _tads_io_say(mainGlobal.reflectionObj.
679
                                 formatStackFrame(cur, true));
680
                }
681
                else
682
                {
683
                    /* no reflection information - show source only */
684
                    if (cur.srcInfo_ != nil)
685
                        "<<cur.srcInfo_[1]>>, line <<cur.srcInfo_[2]>>";
686
                    else if (cur.isSystem())
687
                        "&lt;System&gt;";
688
                    else
689
                        "???";
690
                }
691
692
                /* end the line */
693
                "\n";
694
            }
695
        }
696
    }
697
;
698
699
700
/* ------------------------------------------------------------------------ */
701
/*
702
 *   RuntimeError exception class.  The VM creates and throws an instance
703
 *   of this class when any run-time error occurs.  The VM explicitly sets
704
 *   the exceptionMessage property to a string giving the VM error message
705
 *   for the run-time error that occurred.  
706
 */
707
class RuntimeError: Exception
708
    construct(errno, ...)
709
    {
710
        /* remember the VM error number */
711
        errno_ = errno;
712
713
        /* 
714
         *   Store a stack trace for the current location.  Always discard
715
         *   the first element of the result, since this will reflect
716
         *   RuntimeError.construct, which is obviously not interesting.  
717
         */
718
        stack_ = t3GetStackTrace().sublist(2);
719
720
        /* 
721
         *   The next element of the stack trace is usually a native code
722
         *   frame, because the VM itself invokes our constructor in
723
         *   response to a runtime exception; this is not an interesting
724
         *   frame, so if it's present, remove it.  
725
         */
726
        if (stack_.length() > 0 && stack_[1].isSystem())
727
            stack_ = stack_.sublist(2);
728
    }
729
730
    /* display the exception */
731
    displayException()
732
    {
733
        /* show the exception message */
734
        "Runtime error: <<exceptionMessage>>";
735
736
        /* show a stack trace if possible */
737
        showStackTrace(stack_);
738
    }
739
740
    /* check to see if it's a debugger signal of some kind */
741
    isDebuggerSignal()
742
    {
743
        switch(errno_)
744
        {
745
        case 2391:                       /* debugger 'abort command' signal */
746
        case 2392:                             /* debugger 'restart' signal */
747
            /* it's a debugger signal */
748
            return true;
749
750
        default:
751
            /* not a debugger signal */
752
            return nil;
753
        }
754
    }
755
756
    /* the VM error number of the exception */
757
    errno_ = 0
758
759
    /* the exception message, provided to us by the VM after creation */
760
    exceptionMessage = ''
761
762
    /* the stack trace, which we store at the time we're created */
763
    stack_ = nil
764
;
765
766
/*
767
 *   Export our RuntimeError class so that the VM knows about it and can
768
 *   create instances of it.  Also export our exceptionMessage property,
769
 *   so the VM can store its explanatory text there.
770
 */
771
export RuntimeError;
772
export exceptionMessage;
773
774
/*
775
 *   Unknown character set exception - this is thrown from any routine that
776
 *   needs a local character set mapping when no mapping exists on the local
777
 *   platform. 
778
 */
779
class UnknownCharSetException: Exception
780
;
781
782
/* 
783
 *   this exception object must be exported for use by the CharacterSet
784
 *   intrinsic class 
785
 */
786
export UnknownCharSetException 'CharacterSet.UnknownCharSetException';
787
788
789
/*
790
 *   A Program Exception terminates the entire program, passing an error
791
 *   indication to the operating system.  The VM doesn't provide a way to
792
 *   specify the *particular* error code to return to the OS, as there's no
793
 *   portable set of error codes; rather, the VM simply returns a code to
794
 *   the OS that means generically that an error occurred, if there's any
795
 *   such concept on the local operating system.  The VM will normally
796
 *   display this message just before it terminates the program, possibly
797
 *   with some additional text mentioning that a program error occurred
798
 *   (such as "unhandled exception: <your message>").
799
 */
800
class ProgramException: Exception
801
    construct(msg) { exceptionMessage = msg.htmlify(); }
802
    displayException() { "<<exceptionMessage>> "; }
803
;
804
805
/* ------------------------------------------------------------------------ */
806
/*
807
 *   Default string display function.  Our main entrypoint code
808
 *   establishes this function as the default output function.  
809
 */
810
_default_display_fn(str) { _tads_io_say(str); }
811
812
813
/* ------------------------------------------------------------------------ */
814
/*
815
 *   The stack information object.  The intrinsic function
816
 *   t3GetStackTrace() in the 't3vm' function set returns a list of these
817
 *   objects; each object represents a level in the stack trace.  
818
 */
819
class T3StackInfo: object
820
    /*
821
     *   Construct a stack level object.  The system invokes this
822
     *   constructor with information on the stack level.
823
     */
824
    construct(func, obj, prop, selfObj, argList, srcInfo)
825
    {
826
        /* remember the values */
827
        func_ = func;
828
        obj_ = obj;
829
        prop_ = prop;
830
        self_ = selfObj;
831
        argList_ = argList;
832
        srcInfo_ = srcInfo;
833
    }
834
835
    /*
836
     *   Is this a system routine?  This returns true if an intrinsic
837
     *   function or an intrinsic class method is running at this level. 
838
     */
839
    isSystem()
840
    {
841
        /* 
842
         *   if it's a system routine, we won't have a function OR an
843
         *   object method 
844
         */
845
        return func_ == nil && obj_ == nil;
846
    }
847
848
    /* 
849
     *   the function running at this stack level - this is nil if an
850
     *   object property is running instead of a function 
851
     */
852
    func_ = nil
853
854
    /* 
855
     *   The object and property running at this stack level - these are
856
     *   nil if a function is running instead of an object method.  The
857
     *   object is the object where the method is actually defined - this
858
     *   might not be the same as self, because the object might have
859
     *   inherited the method from a base class.  
860
     */
861
    obj_ = nil
862
    prop_ = nil
863
864
    /* 
865
     *   the 'self' object at this level - this is nil if a function is
866
     *   running at this level instead of an object method 
867
     */
868
    self_ = nil
869
870
    /* the list of arguments to the function or method */
871
    argList_ = []
872
873
    /*
874
     *   The source location of the next code to be executed in the
875
     *   function or method in this frame.  If debugging records are
876
     *   available for the current execution point in this frame, this
877
     *   will contain a list of two values:
878
     *   
879
     *   srcInfo_[1] = string giving the name of the source file
880
     *.  srcInfo_[2] = integer giving the line number in the source file
881
     *   
882
     *   If the program wasn't compiled with debugging records, or the
883
     *   current code location in the frame doesn't have any source
884
     *   information, this will be set to nil.
885
     *   
886
     *   Note that this gives the location of the *next* statement to be
887
     *   executed in this frame, when control returns to the frame.  This
888
     *   means that the location is frequently the next statement after
889
     *   the one that called the next inner frame, because this is where
890
     *   execution will resume when control returns to the frame.  
891
     */
892
    srcInfo_ = nil
893
;
894
895
/* export T3StackInfo for use by the system */
896
export T3StackInfo;
897
898
899
/* ------------------------------------------------------------------------ */
900
/*
901
 *   global data object for this module
902
 */
903
mainGlobal: object
904
    /* flag: we've run pre-initialization */
905
    preinited_ = nil
906
907
    /* 
908
     *   The global reflection object - if the program is compiled with
909
     *   the standard reflection module, that module will set this
910
     *   property to point to the reflection object.
911
     *   
912
     *   We use this so that we don't require the reflection module to be
913
     *   included.  If the module isn't included, this will be nil, so
914
     *   we'll know not to use reflection.  If this is not nil, we'll know
915
     *   we can use reflection services.  
916
     */
917
    reflectionObj = nil
918
919
    /*
920
     *   Restart ID.  This is an integer that indicates how the main
921
     *   entrypoint was last reached.  This is initially zero; each time
922
     *   we restart the game, this is incremented.
923
     *   
924
     *   The restart ID is the only information that survives across a
925
     *   restart boundary.  Other than this, entering via a restart is
926
     *   exactly like loading the program from scratch; all other
927
     *   information about the program state before the restart is lost in
928
     *   the restart operation.  
929
     */
930
    restartID = 0
931
932
    /* pointer to mainRestore function, if defined */
933
    mainRestoreFunc = nil
934
;
935