1 (* M2RTS.mod implements access to the exception handlers.
3 Copyright (C) 2010-2024 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. *)
27 IMPLEMENTATION MODULE M2RTS
;
30 FROM libc
IMPORT abort
, exit
, write
, getenv
, printf
, strlen
;
31 (* FROM Builtins IMPORT strncmp, strcmp ; not available during bootstrap. *)
32 FROM NumberIO
IMPORT CardToStr
;
33 FROM StrLib
IMPORT StrCopy
, StrLen
, StrEqual
;
34 FROM SYSTEM
IMPORT ADR
;
35 FROM ASCII
IMPORT nl
, nul
;
36 FROM Storage
IMPORT ALLOCATE
;
46 PtrToChar
= POINTER TO CHAR ;
48 ProcedureChain
= POINTER TO RECORD
51 next
: ProcedureChain
;
54 ProcedureList
= RECORD
55 head
, tail
: ProcedureChain
61 TerminateProc
: ProcedureList
;
70 ConstructModules - resolve dependencies and then call each
71 module constructor in turn.
74 PROCEDURE ConstructModules (applicationmodule
, libname
: ADDRESS
;
75 overrideliborder
: ADDRESS
;
76 argc
: INTEGER; argv
, envp
: ADDRESS
) ;
78 M2Dependent.
ConstructModules (applicationmodule
, libname
,
81 END ConstructModules
;
85 DeconstructModules - resolve dependencies and then call each
86 module constructor in turn.
89 PROCEDURE DeconstructModules (applicationmodule
, libname
: ADDRESS
;
90 argc
: INTEGER; argv
, envp
: ADDRESS
) ;
92 M2Dependent.
DeconstructModules (applicationmodule
, libname
,
94 END DeconstructModules
;
98 RegisterModule - adds module name to the list of outstanding
99 modules which need to have their dependencies
100 explored to determine initialization order.
103 PROCEDURE RegisterModule (name
, libname
: ADDRESS
;
104 init
, fini
: ArgCVEnvP
;
105 dependencies
: PROC) ;
107 M2Dependent.
RegisterModule (name
, libname
, init
, fini
, dependencies
)
112 RequestDependant - used to specify that modulename is dependant upon
113 module dependantmodule.
116 PROCEDURE RequestDependant (modulename
, libname
,
117 dependantmodule
, dependantlibname
: ADDRESS
) ;
119 M2Dependent.
RequestDependant (modulename
, libname
,
120 dependantmodule
, dependantlibname
)
121 END RequestDependant
;
125 ExecuteReverse - execute the procedure associated with procptr
126 and then proceed to try and execute all previous
127 procedures in the chain.
130 PROCEDURE ExecuteReverse (procptr
: ProcedureChain
) ;
132 WHILE procptr #
NIL DO
133 procptr^.p
; (* Invoke the procedure. *)
134 procptr
:= procptr^.prev
140 ExecuteTerminationProcedures - calls each installed termination procedure
144 PROCEDURE ExecuteTerminationProcedures
;
146 ExecuteReverse (TerminateProc.tail
)
147 END ExecuteTerminationProcedures
;
151 ExecuteInitialProcedures - executes the initial procedures installed by
152 InstallInitialProcedure.
155 PROCEDURE ExecuteInitialProcedures
;
157 ExecuteReverse (InitialProc.tail
)
158 END ExecuteInitialProcedures
;
162 AppendProc - append proc to the end of the procedure list
166 PROCEDURE AppendProc (VAR proclist
: ProcedureList
; proc
: PROC) : BOOLEAN ;
168 pdes
: ProcedureChain
;
173 prev
:= proclist.tail
;
176 IF proclist.head
= NIL
178 proclist.head
:= pdes
180 proclist.tail
:= pdes
;
186 InstallTerminationProcedure - installs a procedure, p, which will
187 be called when the procedure
188 ExecuteTerminationProcedures
189 is invoked. It returns TRUE if the
190 procedure is installed.
193 PROCEDURE InstallTerminationProcedure (p
: PROC) : BOOLEAN ;
195 RETURN AppendProc (TerminateProc
, p
)
196 END InstallTerminationProcedure
;
200 InstallInitialProcedure - installs a procedure to be executed just
201 before the BEGIN code section of the
205 PROCEDURE InstallInitialProcedure (p
: PROC) : BOOLEAN ;
207 RETURN AppendProc (InitialProc
, p
)
208 END InstallInitialProcedure
;
212 HALT - terminate the current program. The procedure
213 ExecuteTerminationProcedures
214 is called before the program is stopped. The parameter
215 exitcode is optional. If the parameter is not supplied
216 HALT will call libc 'abort', otherwise it will exit with
217 the code supplied. Supplying a parameter to HALT has the
218 same effect as calling ExitOnHalt with the same code and
219 then calling HALT with no parameter.
222 PROCEDURE HALT ([exitcode
: INTEGER = -1]) <* noreturn
*> ;
227 ExitValue
:= exitcode
231 (* double HALT found *)
235 ExecuteTerminationProcedures
;
247 Terminate - provides compatibility for pim. It call exit with
248 the exitcode provided in a prior call to ExitOnHalt
249 (or zero if ExitOnHalt was never called). It does
250 not call ExecuteTerminationProcedures.
253 PROCEDURE Terminate
<* noreturn
*> ;
260 ErrorString - writes a string to stderr.
263 PROCEDURE ErrorString (a
: ARRAY OF CHAR) ;
267 n
:= write (stderrFd
, ADR (a
), StrLen (a
))
272 ErrorStringC - writes a string to stderr.
275 PROCEDURE ErrorStringC (str
: ADDRESS
) ;
279 len
:= write (stderrFd
, str
, strlen (str
))
284 ErrorMessage - emits an error message to stderr and then calls exit (1).
287 PROCEDURE ErrorMessage (message
: ARRAY OF CHAR;
288 filename
: ARRAY OF CHAR;
290 function
: ARRAY OF CHAR) <* noreturn
*> ;
292 buffer
: ARRAY [0.
.10] OF CHAR ;
294 ErrorString (filename
) ; ErrorString(':') ;
295 CardToStr (line
, 0, buffer
) ;
296 ErrorString (buffer
) ; ErrorString(':') ;
297 IF NOT StrEqual (function
, '')
299 ErrorString ('in ') ;
300 ErrorString (function
) ;
301 ErrorString (' has caused ') ;
303 ErrorString (message
) ;
304 buffer
[0] := nl
; buffer
[1] := nul
;
305 ErrorString (buffer
) ;
311 ErrorMessageC - emits an error message to stderr and then calls exit (1).
314 PROCEDURE ErrorMessageC (message
, filename
: ADDRESS
;
316 function
: ADDRESS
) <* noreturn
*> ;
318 buffer
: ARRAY [0.
.10] OF CHAR ;
320 ErrorStringC (filename
) ; ErrorString (':') ;
321 CardToStr (line
, 0, buffer
) ;
322 ErrorString (buffer
) ; ErrorString(':') ;
323 IF strlen (function
) > 0
325 ErrorString ('in ') ;
326 ErrorStringC (function
) ;
327 ErrorString (' has caused ') ;
329 ErrorStringC (message
) ;
330 buffer
[0] := nl
; buffer
[1] := nul
;
331 ErrorString (buffer
) ;
337 HaltC - provides a more user friendly version of HALT, which takes
338 four parameters to aid debugging. It writes an error message
339 to stderr and calls exit (1).
342 PROCEDURE HaltC (description
, filename
, function
: ADDRESS
; line
: CARDINAL) ;
344 ErrorMessageC (description
, filename
, line
, function
)
349 Halt - provides a more user friendly version of HALT, which takes
350 four parameters to aid debugging. It writes an error message
351 to stderr and calls exit (1).
354 PROCEDURE Halt (description
, filename
, function
: ARRAY OF CHAR; line
: CARDINAL) ;
356 ErrorMessage (description
, filename
, line
, function
)
361 IsTerminating - Returns true if any coroutine has started program termination
365 PROCEDURE IsTerminating () : BOOLEAN ;
372 HasHalted - Returns true if a call to HALT has been made and false
376 PROCEDURE HasHalted () : BOOLEAN ;
386 PROCEDURE ErrorCharStar (a
: ADDRESS
) ;
393 WHILE (p#
NIL) AND (p^#nul
) DO
405 ErrorMessageColumn - emits an error message to the stderr
408 PROCEDURE ErrorMessageColumn (filename
, scope
, message
: ADDRESS
;
409 line
, column
: CARDINAL) ;
411 LineNo
: ARRAY [0.
.10] OF CHAR ;
413 ErrorCharStar(filename
) ; ErrorString(':') ;
414 CardToStr(line
, 0, LineNo
) ;
415 ErrorString(LineNo
) ; ErrorString(':') ;
416 CardToStr(column
, 0, LineNo
) ;
417 ErrorString(LineNo
) ; ErrorString(':') ;
418 ErrorCharStar(scope
) ; ErrorString(':') ;
419 ErrorCharStar(message
) ;
420 LineNo
[0] := nl
; LineNo
[1] := nul
;
421 ErrorString(LineNo
) ;
423 END ErrorMessageColumn
;
427 ExitOnHalt - if HALT is executed then call exit with the exit code, e.
430 PROCEDURE ExitOnHalt (e
: INTEGER) ;
438 Length - returns the length of a string, a. This is called whenever
439 the user calls LENGTH and the parameter cannot be calculated
443 PROCEDURE Length (a
: ARRAY OF CHAR) : CARDINAL ;
449 WHILE (l
<=h
) AND (a
[l
]#nul
) DO
457 The following are the runtime exception handler routines.
460 PROCEDURE AssignmentException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
462 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
463 filename
, line
, column
, scope
, message
)
464 END AssignmentException
;
467 PROCEDURE ReturnException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
469 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
470 filename
, line
, column
, scope
, message
)
471 END ReturnException
;
474 PROCEDURE IncException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
476 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
477 filename
, line
, column
, scope
, message
)
481 PROCEDURE DecException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
483 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
484 filename
, line
, column
, scope
, message
)
488 PROCEDURE InclException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
490 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
491 filename
, line
, column
, scope
, message
)
495 PROCEDURE ExclException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
497 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
498 filename
, line
, column
, scope
, message
)
502 PROCEDURE ShiftException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
504 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
505 filename
, line
, column
, scope
, message
)
509 PROCEDURE RotateException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
511 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
512 filename
, line
, column
, scope
, message
)
513 END RotateException
;
516 PROCEDURE StaticArraySubscriptException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
518 RTExceptions.
Raise(ORD (M2EXCEPTION.indexException
),
519 filename
, line
, column
, scope
, message
)
520 END StaticArraySubscriptException
;
523 PROCEDURE DynamicArraySubscriptException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
525 RTExceptions.
Raise (ORD (M2EXCEPTION.indexException
),
526 filename
, line
, column
, scope
, message
)
527 END DynamicArraySubscriptException
;
530 PROCEDURE ForLoopBeginException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
532 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
533 filename
, line
, column
, scope
, message
)
534 END ForLoopBeginException
;
537 PROCEDURE ForLoopToException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
539 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
540 filename
, line
, column
, scope
, message
)
541 END ForLoopToException
;
544 PROCEDURE ForLoopEndException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
546 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
547 filename
, line
, column
, scope
, message
)
548 END ForLoopEndException
;
551 PROCEDURE PointerNilException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
553 RTExceptions.
Raise (ORD (M2EXCEPTION.invalidLocation
),
554 filename
, line
, column
, scope
, message
)
555 END PointerNilException
;
558 PROCEDURE NoReturnException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
560 RTExceptions.
Raise (ORD (M2EXCEPTION.functionException
),
561 filename
, line
, column
, scope
, message
)
562 END NoReturnException
;
565 PROCEDURE CaseException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
567 RTExceptions.
Raise (ORD (M2EXCEPTION.caseSelectException
),
568 filename
, line
, column
, scope
, message
)
572 PROCEDURE WholeNonPosDivException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
574 RTExceptions.
Raise (ORD (M2EXCEPTION.wholeDivException
),
575 filename
, line
, column
, scope
, message
)
576 END WholeNonPosDivException
;
579 PROCEDURE WholeNonPosModException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
581 RTExceptions.
Raise (ORD (M2EXCEPTION.wholeDivException
),
582 filename
, line
, column
, scope
, message
)
583 END WholeNonPosModException
;
586 PROCEDURE WholeZeroDivException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
588 RTExceptions.
Raise (ORD (M2EXCEPTION.wholeDivException
),
589 filename
, line
, column
, scope
, message
)
590 END WholeZeroDivException
;
593 PROCEDURE WholeZeroRemException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
595 RTExceptions.
Raise (ORD (M2EXCEPTION.wholeDivException
),
596 filename
, line
, column
, scope
, message
)
597 END WholeZeroRemException
;
600 PROCEDURE WholeValueException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
602 RTExceptions.
Raise (ORD (M2EXCEPTION.wholeValueException
),
603 filename
, line
, column
, scope
, message
)
604 END WholeValueException
;
607 PROCEDURE RealValueException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
609 RTExceptions.
Raise (ORD (M2EXCEPTION.realValueException
),
610 filename
, line
, column
, scope
, message
)
611 END RealValueException
;
614 PROCEDURE ParameterException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
616 RTExceptions.
Raise (ORD (M2EXCEPTION.rangeException
),
617 filename
, line
, column
, scope
, message
)
618 END ParameterException
;
621 PROCEDURE NoException (filename
: ADDRESS
; line
, column
: CARDINAL; scope
, message
: ADDRESS
) ;
623 RTExceptions.
Raise (ORD (M2EXCEPTION.exException
),
624 filename
, line
, column
, scope
, message
)
629 InitProcList - initialize the head and tail pointers to NIL.
632 PROCEDURE InitProcList (VAR p
: ProcedureList
) ;
645 InitProcList (InitialProc
) ;
646 InitProcList (TerminateProc
) ;
649 CallExit
:= FALSE ; (* default by calling abort *)
650 isTerminating
:= FALSE
655 CheckInitialized - checks to see if this module has been initialized
656 and if it has not it calls Init. We need this
657 approach as this module is called by module ctors
658 before we reach main.
661 PROCEDURE CheckInitialized
;
665 Initialized
:= TRUE ;
668 END CheckInitialized
;
672 (* Initialized := FALSE ; is achieved though setting the bss section to zero. *)