(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* File: Module.m3 *) (* Last modified on Mon Oct 12 09:20:09 PDT 1992 by kalsow *) (* modified on Thu Dec 5 17:20:57 PST 1991 by muller *) UNSAFE MODULE Module; IMPORT Value, ValueRep, String, Scope, Stmt, Error, ESet, Frame, External; IMPORT Variable, Type, Constant, Procedure, Ident, MBuf, Rd, ETimer, M3; IMPORT BlockStmt, Tracer; IMPORT Host, Emit, IntRefTbl, Token, Revelation, Coverage, Decl, Scanner; FROM Scanner IMPORT GetToken, Fail, Match, Match1, MatchID, MatchID1, cur; CONST LinkerMagic = "M3 v2.1\n"; (* NOTE: must be kept in sync with M3Linker.LinkerMagic *) REVEAL T = Value.T BRANDED "Module.T" OBJECT safe : BOOLEAN; interface : BOOLEAN; external : BOOLEAN; genericBase : String.T; externals : External.Set; importScope : Scope.T; localScope : Scope.T; revelations : Revelation.Set; body : Stmt.T; depth : INTEGER; counter : ARRAY [0..4] OF CHAR; fails : ESet.T; trace : Tracer.T; OVERRIDES typeCheck := TypeCheckMethod; class := MyClass; fingerprint := FPrinter; load := ValueRep.NoLoader; write := ValueRep.NoWriter; declare0 := ValueRep.Never; declare1 := ObjCompile; toExpr := ValueRep.NoExpr; toType := ValueRep.NoType; typeOf := ValueRep.TypeVoid; END; TYPE TK = Token.T; VAR defns := IntRefTbl.New (); curModule : T := NIL; parseStack : ARRAY [0..200] OF String.T; parseDepth := 0; parse_timer : ETimer.T := NIL; check_timer : ETimer.T := NIL; emit_timer : ETimer.T := NIL; arrow : String.T := NIL; (* " -> " used in error messages *) PROCEDURE Initialize () = BEGIN IF Host.do_timing THEN emit_timer := ETimer.New ("emitting C"); check_timer := ETimer.New ("typechecking modules"); parse_timer := ETimer.New ("parsing modules"); END; END Initialize; PROCEDURE Reset () = BEGIN depth := 0; curModule := NIL; parseDepth := 0; END Reset; PROCEDURE Create (name: String.T): T = VAR t: T; BEGIN t := NEW (T); ValueRep.Init (t, name); t.readonly := TRUE; t.safe := TRUE; t.interface := TRUE; t.external := FALSE; t.genericBase := NIL; t.externals := External.NewSet (); t.importScope := NIL; t.localScope := NIL; t.body := NIL; t.revelations := Revelation.NewSet (); t.depth := depth; t.fails := NIL; t.trace := NIL; t.counter[0] := '_'; FOR i := 1 TO LAST (t.counter) DO t.counter[i] := '0' END; IF (name # NIL) THEN Revelation.SetName (t.revelations, name) END; RETURN t; END Create; PROCEDURE NewDefn (name: TEXT; safe: BOOLEAN): T = VAR t: T; zz: Scope.T; yy: Revelation.Set; BEGIN t := Create (String.Add (name)); t.safe := safe; yy := Revelation.Push (t.revelations); zz := Scope.Push (Scope.Initial); t.importScope := Scope.PushNew (TRUE, NIL, module := TRUE); t.localScope := Scope.PushNew (TRUE, t.name, module := TRUE); Scope.PopNew (); Scope.PopNew (); Scope.Pop (zz); Revelation.Pop (yy); RecordInterface (t); RETURN t; END NewDefn; PROCEDURE Parse (interfaceOnly : BOOLEAN := FALSE): T = CONST TailStart = Token.Set {TK.tEND, TK.tEOF}; ImportStart = Token.Set {TK.tIMPORT, TK.tFROM}; ImportTail = Token.Set {TK.tBEGIN} +TailStart+ImportStart+Token.DeclStart; TIDStart = Token.Set {TK.tSEMI} + ImportTail; ModuleStart = Token.Set {TK.tIDENT, TK.tEXPORTS} + TIDStart; UnitStart = Token.Set {TK.tUNSAFE, TK.tGENERIC, TK.tINTERFACE, TK.tMODULE} + ModuleStart; VAR t, save: T; id: String.T; n: INTEGER; genericReader: Rd.T; yy: Revelation.Set; BEGIN ETimer.Push (parse_timer); INC (depth); t := Create (NIL); yy := Revelation.Push (t.revelations); save := curModule; curModule := t; IF (cur.token = TK.tEXTERNAL) THEN Decl.ParseExternalPragma (UnitStart, id); IF (id # NIL) THEN Error.Str (id, "external module name ignored") END; t.external := TRUE; END; IF (cur.token = TK.tUNSAFE) THEN t.safe := FALSE; GetToken (); END; t.interface := (cur.token = TK.tINTERFACE); IF interfaceOnly THEN IF (cur.token = TK.tINTERFACE) THEN GetToken (); ELSE Fail ("missing INTERFACE keyword", ModuleStart); END; t.interface := TRUE; ELSIF (cur.token = TK.tINTERFACE) OR (cur.token = TK.tMODULE) THEN GetToken (); ELSE Fail ("missing INTERFACE or MODULE keyword", ModuleStart); END; IF t.external AND NOT t.interface THEN Error.Msg ("Only interfaces can be <*EXTERNAL*>"); END; id := MatchID (Token.Set {TK.tEXPORTS}, TIDStart); t.name := id; Revelation.SetName (t.revelations, id); IF (t.interface) THEN RecordInterface (t); IF (depth = 1) THEN EVAL PushInterface (id); INC (parseDepth) END; END; IF (cur.token = TK.tEXPORTS) THEN IF (t.interface) THEN Error.Msg ("EXPORTS clause not allowed in an interface"); t.interface := FALSE; END; GetToken (); n := Ident.ParseList (TIDStart); FOR i := 0 TO n - 1 DO External.NoteExport (t.externals, Ident.stack[Ident.top - n + i]); END; DEC (Ident.top, n); ELSIF (NOT t.interface) THEN External.NoteExport (t.externals, t.name); END; IF (cur.token = TK.tSEMI) THEN (* this is a simple module/interface, just fall through *) GetToken (); (* ; *) ELSIF (cur.token = TK.tEQUAL) THEN (* this is an instantiation of a generic module/interface *) GetToken (); (* = *) t.genericBase := PushGeneric (t, genericReader); ELSE Fail ("missing \';\' or \'=\', assuming \';\'", TIDStart); END; (* parse the imports *) External.ParseImports (t.externals, t); (* build my scopes and fill them! *) t.importScope := Scope.PushNew (TRUE, NIL, module := TRUE); (* this scope must be created after the imports & exports are parsed so that their module scopes aren't nested inside this one. *) (* copy the imports and exports into my scope *) External.LoadImports (t.externals, t); (* open my private, local scope *) t.localScope := Scope.PushNew (TRUE, id, module := TRUE); WHILE (cur.token IN Token.DeclStart) DO Decl.Parse (Token.Set {TK.tBEGIN} + TailStart + Token.DeclStart, t.interface, TRUE, t.fails); END; IF (NOT t.interface) THEN Match (TK.tBEGIN, TailStart, Token.StmtStart); t.trace := BlockStmt.ParseTrace (TailStart + Token.StmtStart); t.body := Stmt.Parse (TailStart + Token.StmtStart); END; IF (t.genericBase # NIL) THEN ParseFinalEndID (t.genericBase); Scanner.Pop (); END; Host.CloseRd (genericReader); ParseFinalEndID (t.name); Scope.PopNew (); (* localScope *) Scope.PopNew (); (* importScope *) Revelation.Pop (yy); curModule := save; DEC (depth); ETimer.Pop (); RETURN t; END Parse; PROCEDURE PushGeneric (t: T; VAR rd: Rd.T): String.T = (* instantiate a call on a generic interface or module *) CONST TailStart = Token.Set {TK.tEND, TK.tEOF}; ImportStart = Token.Set {TK.tIMPORT, TK.tFROM}; ImportTail = Token.Set {TK.tBEGIN} + TailStart + ImportStart; VAR genericName, id : String.T; nActuals, aBase : INTEGER; nFormals, fBase : INTEGER; formal, actual : String.T; filename: String.T; im: T; old_file := Scanner.offset; save: INTEGER; BEGIN genericName := MatchID1 (Token.Set {TK.tLPAREN, TK.tRPAREN,TK.tSEMI}); IF (genericName = NIL) THEN RETURN genericName END; (* parse the list of actuals *) nActuals := ParseGenericArgs (ImportTail); (* open the external file *) rd := Host.OpenUnit (genericName, t.interface, TRUE, filename); Scanner.Push (filename, rd); (* make sure we got what we wanted *) Match1 (TK.tGENERIC, Token.Set {TK.tINTERFACE, TK.tMODULE,TK.tIDENT}); IF (t.interface) THEN Match1 (TK.tINTERFACE, Token.Set {TK.tMODULE, TK.tIDENT}); ELSE Match1 (TK.tMODULE, Token.Set {TK.tINTERFACE, TK.tIDENT}); END; (* get the generic's name *) id := MatchID1 (Token.Set {TK.tLPAREN, TK.tRPAREN, TK.tSEMI}); IF (id # NIL) THEN IF (id # genericName) THEN Error.Str (id, "imported module has wrong name"); genericName := id; END; END; (* parse the list of formals *) nFormals := ParseGenericArgs (ImportTail); Match1 (TK.tSEMI, ImportTail); (* finally, generate the rewriting *) IF (nActuals # nFormals) THEN save := Scanner.offset; Scanner.offset := old_file; Error.Msg ("number of actuals doesn\'t match number of generic formals"); Scanner.offset := save; END; fBase := Ident.top - nFormals; aBase := fBase - nActuals; FOR i := 0 TO MAX (nActuals, nFormals)-1 DO IF (i < nFormals) THEN formal := Ident.stack[fBase + i]; ELSE formal := Ident.stack[aBase + i]; (* use the actual instead *) END; IF (i < nActuals) THEN actual := Ident.stack[aBase + i]; ELSE actual := formal; (* use the actual instead *) END; im := LookUp (actual); External.NoteImport (t.externals, im, formal); END; DEC (Ident.top, nActuals + nFormals); RETURN genericName; END PushGeneric; PROCEDURE ParseGenericArgs (READONLY fail: Token.Set): INTEGER = VAR n := 0; BEGIN Match (TK.tLPAREN, fail, Token.Set {TK.tRPAREN, TK.tIDENT, TK.tSEMI}); IF (cur.token = TK.tIDENT) THEN n := Ident.ParseList (Token.Set {TK.tRPAREN, TK.tSEMI}); END; Match (TK.tRPAREN, fail, Token.Set {TK.tRPAREN, TK.tIDENT, TK.tSEMI}); RETURN n; END ParseGenericArgs; PROCEDURE ParseFinalEndID (goal: String.T) = VAR id: String.T; BEGIN Match1 (TK.tEND, Token.Set{TK.tIDENT, TK.tDOT, TK.tEOF}); id := MatchID1 (Token.Set {TK.tDOT, TK.tEOF}); IF (goal # id) THEN Error.Str (id, "Initial module name doesn\'t match final name"); END; Match1 (TK.tDOT, Token.Set {TK.tEOF}); IF (cur.token # TK.tEOF) THEN Fail ("extra tokens ignored", Token.Set {TK.tEOF}); END; END ParseFinalEndID; TYPE RefT = REF T; PROCEDURE PushInterface (name: String.T): BOOLEAN = VAR i: INTEGER; path: String.T; BEGIN (* check for a cycle in the active imports *) parseStack [parseDepth] := name; i := 0; WHILE (parseStack[i] # name) DO INC (i) END; IF (i = parseDepth) THEN RETURN TRUE END; IF (arrow = NIL) THEN arrow := String.Add (" -> ") END; path := name; FOR j := i+1 TO parseDepth DO path := String.Concat (path, arrow); path := String.Concat (path, parseStack [j]); END; Error.Str (path, "circular imports"); RETURN FALSE; END PushInterface; PROCEDURE LookUp (name: String.T): T = (* find and return the named interface module *) VAR t: T; i, save: INTEGER; ref: REFANY; filename: String.T; cs := M3.OuterCheckState; BEGIN IF NOT PushInterface (name) THEN RETURN NIL END; i := LOOPHOLE (name, INTEGER); IF defns.in (i, ref) THEN t := NARROW (ref, RefT)^; ELSE (* open the external file & parse the interface*) WITH rd = Host.OpenUnit (name, TRUE, FALSE, filename) DO Scanner.Push (filename, rd); INC (parseDepth); t := Parse (TRUE); DEC (parseDepth); Scanner.Pop (); Host.CloseRd (rd); END; (* make sure we got what we wanted *) IF (t = NIL) THEN Error.Str (name, "imported object is not an interface"); RETURN NIL; END; IF (t.name # name) THEN save := Scanner.offset; Scanner.offset := t.origin; Error.Str (name, "imported interface has wrong name"); Scanner.offset := save; RETURN NIL; END; IF (NOT t.interface) THEN save := Scanner.offset; Scanner.offset := t.origin; Error.Str (name, "imported unit is not an interface"); Scanner.offset := save; RETURN NIL; END; RecordInterface (t); Value.TypeCheck (t, cs); END; IF (curModule # NIL) AND (curModule.safe) AND (NOT t.safe) THEN Error.Str (name, "cannot import an unsafe interface in a safe module"); END; RETURN t; END LookUp; PROCEDURE RecordInterface (t: T) = VAR r: RefT; BEGIN IF (t = NIL) OR (t.name = NIL) THEN RETURN END; r := NEW (RefT); r^ := t; EVAL defns.put (LOOPHOLE (t.name, INTEGER), r); END RecordInterface; PROCEDURE ImportRevelations (t: T; source: Value.T) = BEGIN Revelation.Inherit (t.revelations, source); END ImportRevelations; PROCEDURE TypeCheckMethod (t: T; VAR cs: Value.CheckState) = BEGIN TypeCheck (t, FALSE, cs); END TypeCheckMethod; PROCEDURE TypeCheck (t: T; main: BOOLEAN; VAR cs: Value.CheckState) = VAR save: T; saveDepth: INTEGER; yy: Revelation.Set; z1, z2: Scope.T; BEGIN ETimer.Push (check_timer); saveDepth := depth; depth := t.depth; save := curModule; curModule := t; yy := Revelation.Push (t.revelations); SoftPush (t.importScope, z1); Scope.TypeCheck (t.importScope, cs); SoftPush (t.localScope, z2); ESet.TypeCheck (t.fails); ESet.Push (cs, NIL, t.fails, stop := TRUE); Revelation.TypeCheck (t.revelations); Scope.TypeCheck (t.localScope, cs); IF (NOT t.interface) THEN BlockStmt.CheckTrace (t.trace, cs); Stmt.TypeCheck (t.body, cs); END; ESet.Pop (cs, NIL, t.fails, stop := TRUE); SoftPop (t.localScope, z2); SoftPop (t.importScope, z1); Revelation.Pop (yy); CheckDuplicates (t); IF (main) THEN NoteVisibility (t); Scope.WarnUnused (t.importScope); Scope.WarnUnused (t.localScope); END; curModule := save; depth := saveDepth; ETimer.Pop (); END TypeCheck; PROCEDURE SoftPush (s: Scope.T; VAR tmp: Scope.T) = (* the scopes may be NIL when there's illegal cycles in the import graph *) BEGIN IF (s # NIL) THEN tmp := Scope.Push (s) END; END SoftPush; PROCEDURE SoftPop (s: Scope.T; tmp: Scope.T) = BEGIN IF (s # NIL) THEN Scope.Pop (tmp) END; END SoftPop; PROCEDURE CheckDuplicates (t: T) = VAR n1, n2: INTEGER; objs1, objs2: Scope.ValueList; names1, names2: Scope.NameList; o1, o2: Value.T; name: String.T; save: INTEGER; BEGIN save := Scanner.offset; Scope.ToListWithAliases (t.importScope, objs1, n1, names1); Scope.ToListWithAliases (t.localScope, objs2, n2, names2); IF (n1 < n2) THEN FOR i := 0 TO n1-1 DO o1 := objs1[i]; name := o1.name; IF (names1 # NIL) THEN name := names1[i] END; o2 := Scope.LookUpX (t.localScope, name); IF (o2 # NIL) THEN (* possible duplicate *) IF (NOT External.IsExportable (o1)) OR (Value.ClassOf (o1) # Value.Class.Procedure) OR (Value.ClassOf (o2) # Value.Class.Procedure) THEN Scanner.offset := o2.origin; Error.Str (name, "symbol redefined"); ELSE Procedure.NoteExport (o2, o1); External.Redirect (o1, o2); END; END; END; ELSE (* n1 >= n2 *) FOR i := 0 TO n2-1 DO o2 := objs2[i]; name := o2.name; IF (names2 # NIL) THEN name := names2[i] END; o1 := Scope.LookUpX (t.importScope, name); IF (o1 # NIL) THEN (* possible duplicate *) IF (NOT External.IsExportable (o1)) OR (Value.ClassOf (o1) # Value.Class.Procedure) OR (Value.ClassOf (o2) # Value.Class.Procedure) THEN Scanner.offset := o2.origin; Error.Str (name, "symbol redefined"); ELSE Procedure.NoteExport (o2, o1); External.Redirect (o1, o2); END; END; END; END; Scanner.offset := save; END CheckDuplicates; PROCEDURE NoteVisibility (t: T) = VAR v: Value.T; n: INTEGER; objs: Scope.ValueList; BEGIN Scope.ToList (t.localScope, objs, n); FOR i := 0 TO n - 1 DO v := objs[i]; CASE Value.ClassOf (v) OF | Value.Class.Module, Value.Class.Error => (* no change of import/export status *) | Value.Class.Expr, Value.Class.Var, Value.Class.Type, Value.Class.Exception => IF (t.interface) THEN <*ASSERT NOT v.imported*> v.exported := TRUE; v.exportable := TRUE; (* ELSE no change of import/export status *) END; | Value.Class.Procedure => <*ASSERT NOT v.imported*> IF (t.interface) THEN v.used := TRUE; (* force a version stamp *) v.exported := v.external; v.imported := NOT v.exported; v.exportable := TRUE; END; | Value.Class.Field, Value.Class.Method, Value.Class.Formal => <* ASSERT FALSE *> END; END; END NoteVisibility; PROCEDURE IsSafe (): BOOLEAN = BEGIN RETURN (curModule = NIL) OR (curModule.safe); END IsSafe; PROCEDURE IsInterface (): BOOLEAN = BEGIN RETURN (curModule = NIL) OR (curModule.interface); END IsInterface; PROCEDURE IsExternal (): BOOLEAN = BEGIN RETURN (curModule # NIL) AND (curModule.external); END IsExternal; PROCEDURE ExportScope (t: T): Scope.T = BEGIN IF (t = NIL) THEN RETURN NIL; ELSE RETURN t.localScope; END; END ExportScope; PROCEDURE ObjCompile (<*UNUSED*> t: T) = BEGIN END ObjCompile; PROCEDURE MyClass (<*UNUSED*> t: T): Value.Class = BEGIN RETURN Value.Class.Module; END MyClass; PROCEDURE Compile (t: T) = VAR save: T; zz: Scope.T; yy: Revelation.Set; BEGIN ETimer.Push (emit_timer); save := curModule; curModule := t; Scanner.offset := t.origin; yy := Revelation.Push (t.revelations); zz := Scope.Push (t.localScope); IF (t.interface) THEN CompileInterface (t); ELSE CompileModule (t); END; Scope.Pop (zz); Revelation.Pop (yy); curModule := save; ETimer.Pop (); END Compile; PROCEDURE CompileInterface (t: T) = VAR hasMapProc: BOOLEAN; frame: Frame.T; BEGIN EVAL Emit.Switch (Emit.Stream.LinkHeader); Emit.Op (LinkerMagic); Emit.OpS ("I@\n", t.name); (* declare the modules that I import & export *) Emit.OpS ("A@\n", t.name); IF (t.genericBase # NIL) THEN Emit.OpS ("g@\n", t.genericBase) END; External.GenLinkInfo (t.externals); EVAL Emit.Switch (Emit.Stream.Code); (* declare my imports, exports and local variables *) External.GenImports (t.externals); Scope.Enter (t.importScope); Scope.Enter (t.localScope); (** GenInterfaceRecord (t); **) (* declare any visible revelations *) Revelation.Declare (t.revelations); (* generate any internal procedures *) Procedure.GenBodies (); (* generate my initialization procedure *) Frame.Push (frame, 0); Emit.Op ("_LOCAL_PROC _VOID _init_ ()\n"); Emit.Op ("{\n\001"); EVAL Emit.SwitchToBody (); Emit.Op ("\001"); Scope.InitValues (t.importScope); Scope.InitValues (t.localScope); External.InitGlobals (t.externals); Emit.Op ("return;\n"); Frame.Pop (frame); Scanner.offset := 0; (* we don't care about line numbers any more *) Emit.Op ("\n"); hasMapProc := Variable.GenGlobalMap (t.localScope); Constant.DeclareAllStructuredConstants (); Type.GenLinkerInfo (); GenLinkerInfo (t, hasMapProc); END CompileInterface; PROCEDURE CompileModule (t: T) = VAR oc : Stmt.Outcomes; zz : Scope.T; hasMapProc : BOOLEAN; frame : Frame.T; BEGIN EVAL Emit.Switch (Emit.Stream.LinkHeader); Emit.Op (LinkerMagic); Emit.OpS ("M@\n", t.name); (* declare the modules that I import & export *) IF (t.genericBase # NIL) THEN Emit.OpS ("g@\n", t.genericBase) END; External.GenLinkInfo (t.externals); EVAL Emit.Switch (Emit.Stream.Code); (* declare my imports, exports and local variables *) (**** moved below **** External.GenImports (t.externals); *) Scope.Enter (t.importScope); Scope.Enter (t.localScope); (** GenInterfaceRecord (t); **) (* declare any visible revelations *) Revelation.Declare (t.revelations); (* generate the frame types *) Scope.GenFrameTypes (); (* generate the tables for coverage *) Coverage.GenerateTables (); (* generate any internal procedures *) Procedure.GenBodies (); (* restore my environment *) zz := Scope.Push (t.localScope); (* generate my initialization procedure *) Frame.Push (frame, 0, TRUE); Emit.Op ("\n_LOCAL_PROC _VOID _init_ ()\n"); Emit.Op ("{\n\001"); EVAL Emit.SwitchToBody (); Emit.Op ("\001"); Scope.InitValues (t.importScope); Scope.InitValues (t.localScope); (* initialize my exported variables *) External.InitGlobals (t.externals); (* perform the main body *) Tracer.Push (t.trace); oc := Stmt.Compile (t.body); Tracer.Pop (t.trace); (* IF (Stmt.Outcome.FallThrough IN oc) THEN Emit.Op ("return;\n") END; *) Frame.Pop (frame); Scanner.offset := 0; (* we don't care about line numbers any more *) Emit.Op ("\n"); hasMapProc := Variable.GenGlobalMap (t.localScope); (* declare my imports *) EVAL Emit.Switch (Emit.Stream.Constants); External.GenImports (t.externals); (* we deferred the import declarations until all the code has been generated to pick up imports that are used via "Value.Load", but not "Scope.LookUp". *) Constant.DeclareAllStructuredConstants (); Type.GenLinkerInfo (); GenLinkerInfo (t, hasMapProc); Scope.Pop (zz); END CompileModule; (**************************************** PROCEDURE GenInterfaceRecord (t: T) = VAR n: INTEGER; elts: Scope.ValueList; BEGIN Emit.Op ("\n"); Scope.ToList (t.importScope, elts, n); FOR i := 0 TO n - 1 DO Value.Declare0 (elts[i]); END; Scope.ToList (t.localScope, elts, n); FOR i := 0 TO n - 1 DO Value.Declare0 (elts[i]); END; Emit.Op ("\n"); END GenInterfaceRecord; ******************************************) PROCEDURE GenLinkerInfo (t: T; hasMapProc: BOOLEAN) = CONST tag = ARRAY BOOLEAN OF TEXT {"_M_", "_I_"}; VAR file: String.T; line: INTEGER; save: Emit.Stream; BEGIN save := Emit.Switch (Emit.Stream.LinkTables); Scanner.offset := t.origin; Scanner.Here (file, line); Emit.OpX ("_EXPORT _VOLATILE _LINK_INFO @", tag [t.interface]); Emit.OpS ("@ = {\n", t.name); Emit.OpS (" \"@\",\n", file); Emit.Op (" _type_info,\n"); Emit.Op (" _fp_data,\n"); Emit.Op (" _type_cells,\n"); Emit.Op (" _exported_full_revelations,\n"); Emit.Op (" _exported_partial_revelations,\n"); Emit.Op (" _imported_full_revelations,\n"); Emit.Op (" _imported_partial_revelations,\n"); Emit.Op (" _proc_info,\n"); Emit.Op (" _init_,\n"); IF (hasMapProc) THEN Emit.Op (" _map_\n"); ELSE Emit.Op (" 0\n"); END; Emit.Op ("};\n"); EVAL Emit.Switch (save); END GenLinkerInfo; PROCEDURE FPrinter (t: T; <*UNUSED*> map: Type.FPMap; wr: MBuf.T) = BEGIN String.Put (wr, t.name); MBuf.PutText (wr, "."); END FPrinter; PROCEDURE CurrentName (): String.T = BEGIN IF curModule = NIL THEN RETURN NIL; END; RETURN curModule.name; END CurrentName; PROCEDURE CurrentCounter (): ARRAY [0..4] OF CHAR = BEGIN <* ASSERT curModule # NIL *> RETURN curModule.counter; END CurrentCounter; PROCEDURE SetCurrentCounter (c: ARRAY [0..4] OF CHAR) = BEGIN <* ASSERT curModule # NIL *> curModule.counter := c; END SetCurrentCounter; BEGIN END Module.