
    <<Debug.mesa>>
        <<Copyright © 1985 by Xerox Corporation.  All rights reserved.>>
        <<Satterthwaite, June 18, 1986 12:26:15 pm PDT>>
        <<Maxwell, August 2, 1983 2:39 pm>>
        <<Russ Atkinson (RRA) March 6, 1985 10:31:51 pm PST>>

    DIRECTORY
        Alloc USING [Base, Handle, Index, Notifier, AddNotify, DropNotify, Top],
        BcdDefs USING [Link, VersionStamp],
        CompilerUtil USING [AcquireStream, AcquireTable, ReleaseStream, ReleaseTable],
        ConvertUnsafe USING [SubString],
        DebugTable USING [CSRptr],
        IO USING [card, CR, int, Put, PutChar, PutF, rope, STREAM],
        Literals USING [Base, LitDescriptor, ltType],
        LiteralOps USING [DescriptorValue, MasterString, StringValue],
        Rope USING [ROPE],
        Symbols USING [Base, BitAddress, CTXRecord, TransferMode, TypeClass, Name, SEIndex, ISEIndex, CSEIndex, CTXIndex, BTIndex, 
        nullName, SENull, CTXFirst, CTXNull, lG, lZ, RootBti, typeTYPE, seType, ctxType, mdType, bodyType],
        SymbolOps USING [CtxLevel, DecodeBitAddr, DecodeCard, DecodeLink, DecodeType, EnumerateBodies, FindExtension, NameForSe, NextSe, 
        SubStringForName, TypeLink, XferMode],
        Tree USING [Base, Index, Link, NodeName, Scan, nullIndex, nullInfo, treeType],
        TreeOps USING [GetNode, GetTag, ScanSons];

    Debug: PROGRAM
        IMPORTS Alloc, CompilerUtil, IO, LiteralOps, SymbolOps, TreeOps
        EXPORTS CompilerUtil = {
        OPEN Symbols;

        tb: Tree.Base;
        seb: Symbols.Base;
        ctxb: Symbols.Base;
        mdb: Symbols.Base;
        bb: Symbols.Base;
        ltb: Literals.Base;

        definitionsOnly: BOOL;

        DebugNotify: Alloc.Notifier = {
            tb _ base[Tree.treeType];
            seb _ base[seType];  ctxb _ base[ctxType];  mdb _ base[mdType];
            bb _ base[bodyType];
            ltb _ base[Literals.ltType]};

        SubString: TYPE = ConvertUnsafe.SubString;

    <<basic io>>

        errorStream: IO.STREAM _ NIL;

        WriteChar: PROC [c: CHAR] = {IO.PutChar[errorStream, c]};

        WriteRope: PROC [s: Rope.ROPE] = {IO.Put[errorStream, IO.rope[s]]};

        WriteDecimal: PROC [n: INT] = {IO.Put[errorStream, IO.int[n]]};

        NewLine: PROC = INLINE {WriteChar[IO.CR]};

        Indent: PROC [n: CARDINAL] = {
            NewLine[];
            <<THROUGH [1..n/8] DO WriteChar[IO.TAB] ENDLOOP;>>
            THROUGH [1..n/8] DO WriteRope["        "] ENDLOOP;
            THROUGH [1..n MOD 8] DO WriteChar[' ] ENDLOOP};


        <<errorStream, csrP and desc.base are set by Enter>>

        csrP: DebugTable.CSRptr;
        ss: SubString;

        LockStringTable: PROC = INLINE {
            csrP _ CompilerUtil.AcquireTable[debug];
            ss.base _ @csrP[csrP.stringOffset]};

        UnlockStringTable: PROC = INLINE {CompilerUtil.ReleaseTable[debug]; csrP _ NIL};

        Enter: PROC [table: Alloc.Handle] = {
            table.AddNotify[DebugNotify];
            errorStream _ CompilerUtil.AcquireStream[log];  LockStringTable[]};

        Exit: PROC [table: Alloc.Handle] = {
            UnlockStringTable[];  CompilerUtil.ReleaseStream[log]; errorStream _ NIL;
            table.DropNotify[DebugNotify]};

        WriteSubString: PROC [ss: SubString] = {
            FOR i: CARDINAL IN [ss.offset..ss.offset+ss.length) DO
                WriteChar[ss.base[i]] ENDLOOP};


        <<tree printing>>

        PrintLiteral: PROC [t: Tree.Link.literal] = {
            desc: Literals.LitDescriptor = LiteralOps.DescriptorValue[t.index];
            v: WORD;
            IF desc.length # 1 THEN WriteChar['[];
            FOR i: CARDINAL IN [0 .. desc.length) DO
                IF (v _ ltb[desc.offset][i]) < 1000 THEN WriteDecimal[v]
                ELSE IO.PutF[errorStream, "%b", IO.int[v]]; -- octal
                IF i+1 # desc.length THEN WriteChar[',];
                ENDLOOP;
            IF desc.length # 1 THEN WriteChar[']]};

        PrintString: PROC [t: Tree.Link.string] = {
            s: LONG STRING = LiteralOps.StringValue[t.index];
            WriteChar['"];
            FOR i: CARDINAL IN [0..s.length) DO WriteChar[s[i]] ENDLOOP;
            WriteChar['"];
            IF t.index # LiteralOps.MasterString[t.index] THEN WriteChar['L]};

        WriteNodeName: PROC [n: Tree.NodeName] = {
            ss.offset _ csrP.NodePrintName[n].offset; ss.length _ csrP.NodePrintName[n].length;
            WriteSubString[ss]};

        PrintSubTree: PROC [t: Tree.Link, nBlanks: CARDINAL] = {
            OPEN Tree;

            Printer: Tree.Scan = {
                Indent[nBlanks];
                WITH s: t SELECT TreeOps.GetTag[t] FROM
                    hash => PrintName[s.index];
                    symbol => {PrintSei[s.index]; WriteChar['[]; PrintIndex[s.index]; WriteChar[']]};
                    literal => PrintLiteral[s];
                    subtree => {
                        node: Tree.Index = s.index;
                        IF node = Tree.nullIndex THEN WriteRope["<empty>"]
                        ELSE {
                            OPEN tb[node];
                            WriteNodeName[name];
                            WriteChar['[];  PrintIndex[node];  WriteRope["] "];
                            IF info # Tree.nullInfo THEN {WriteRope["  info="]; PrintIndex[LOOPHOLE[info]]};
                            IF attr1 OR attr2 OR attr3 THEN {
                                IF info = Tree.nullInfo THEN WriteChar[' ];
                                WriteChar['(];
                                IF attr1 THEN WriteChar['1];
                                IF attr2 THEN WriteChar['2];
                                IF attr3 THEN WriteChar['3];
                                WriteChar[')]};
                            nBlanks _ nBlanks + 2;
                            IF name # thread THEN TreeOps.ScanSons[s, Printer]
                            ELSE {
                                WriteRope[" link="]; PrintIndex[TreeOps.GetNode[son[2]]];
                                Printer[son[1]]};
                            nBlanks _ nBlanks - 2}};
                    ENDCASE};

            Printer[t]};


        PrintTree: PUBLIC PROC [table: Alloc.Handle, root: Tree.Link] = {
            Enter[table];
            PrintSubTree[root, 0];  NewLine[];  NewLine[];
            Exit[table]};

        PrintBodies: PUBLIC PROC [table: Alloc.Handle] = {
            Enter[table];
            [] _ SymbolOps.EnumerateBodies[RootBti, PrintBody];  NewLine[];
            Exit[table]};

        PrintBody: PROC [bti: BTIndex] RETURNS [BOOL] = {
            OPEN body: bb[bti];
            WriteRope["Body: "];
            WITH b: body SELECT FROM
                Callable => {
                    PrintSei[b.id];
                    IF b.inline THEN WriteRope[" [inline]"]
                    ELSE {
                        WriteRope[", ep: "];  WriteDecimal[b.entryIndex];
                        WITH b SELECT FROM
                            Inner => {WriteRope[", frame addr: "]; WriteDecimal[frameOffset]};
                            ENDCASE};
                    WriteRope[", attrs: "];
                    WriteChar[IF ~b.noXfers THEN 'x ELSE '-];
                    WriteChar[IF b.hints.safe THEN 's ELSE '-];
                    WriteChar[IF b.hints.nameSafe THEN 'n ELSE '-];
                    IF ~b.hints.noStrings THEN {Indent[2]; WriteRope["string literals"]}};
                ENDCASE => WriteRope["(anon)"];
            Indent[2];
            WriteRope["context: "];  PrintIndex[body.localCtx];
            WriteRope[", level: "];  WriteDecimal[body.level];
            WITH body.info SELECT FROM
                Internal => {
                    WriteRope[", frame size: "];  WriteDecimal[frameSize];
                    IF body.kind = Callable THEN PrintSubTree[[subtree[index: bodyTree]], 0]
                    ELSE {WriteRope[", tree root: "]; PrintIndex[bodyTree]}};
                ENDCASE;
            NewLine[];  NewLine[];  RETURN[FALSE]};


        PrintSymbols: PUBLIC PROC [table: Alloc.Handle, definitions: BOOL] = {
            ctx: CTXIndex;
            limit: CTXIndex;
            Enter[table];
            definitionsOnly _ definitions;
            limit _ table.Top[Symbols.ctxType];
            ctx _ CTXFirst + CTXRecord.nil.SIZE;
            UNTIL ctx = limit DO
                PrintContext[ctx];  NewLine[];  NewLine[];
                ctx _ ctx + (WITH ctxb[ctx] SELECT FROM
                    included => CTXRecord.included.SIZE,
                    imported => CTXRecord.imported.SIZE,
                    ENDCASE => CTXRecord.simple.SIZE);
                ENDLOOP;
            NewLine[];
            Exit[table]};

        PrintContext: PROC [ctx: CTXIndex] = {
            sei, root: ISEIndex;
            WriteRope["Context: "];  PrintIndex[ctx];
            IF SymbolOps.CtxLevel[ctx] # lZ THEN {
                WriteRope[", level: "]; WriteDecimal[SymbolOps.CtxLevel[ctx]]};
            WITH c: ctxb[ctx] SELECT FROM
                included => {
                    WriteRope[", copied from: "]; PrintName[mdb[c.module].moduleId];
                    WriteRope[" ["]; PrintName[mdb[c.module].fileId];
                    WriteRope[", "]; PrintVersion[mdb[c.module].stamp];
                    WriteRope["], context: "];  PrintIndex[c.map]};
                imported => {
                    WriteRope[", imported from: "]; PrintName[mdb[ctxb[c.includeLink].module].moduleId]};
                ENDCASE;
            root _ sei _ ctxb[ctx].seList;
            DO
                IF sei = SENull THEN EXIT;
                PrintSE[sei, 2];
                IF (sei _ SymbolOps.NextSe[sei]) = root THEN EXIT;
                ENDLOOP
            };


        PrintSE: PROC [sei: ISEIndex, nBlanks: CARDINAL] = {
            OPEN seb[sei];
            typeSei: SEIndex;
            Indent[nBlanks];
            PrintSei[sei];
            WriteRope[" ["];  PrintIndex[sei];  WriteChar[']];
            IF public THEN WriteRope[" [public]"];
            IF mark3 THEN {
                WriteRope[", type = "];
                IF idType = typeTYPE THEN {
                    typeSei _ SymbolOps.DecodeType[idInfo];
                    WriteRope["TYPE, equated to: "];
                    PrintType[typeSei];
                    IF SymbolOps.CtxLevel[idCtx] = lZ AND SymbolOps.TypeLink[sei] # SENull THEN {
                        WriteRope[", tag code: "]; WriteDecimal[SymbolOps.DecodeCard[idValue]]}}
                    ELSE {
                        typeSei _ idType;  PrintType[typeSei];
                        SELECT TRUE FROM
                            constant => WriteRope[" [const]"];
                            immutable => WriteRope[" [init only]"];
                            ENDCASE;
                        IF ~mark4 THEN {
                            WriteRope[", # refs: "]; WriteDecimal[SymbolOps.DecodeCard[idInfo]]}
                        ELSE
                            SELECT TRUE FROM
                                constant =>
                                    IF ~ extended THEN {
                                        WriteRope[", value: "];
                                        SELECT SymbolOps.XferMode[typeSei] FROM
                                            proc, program, signal, error => 
                                                PrintLink[SymbolOps.DecodeLink[idValue]];
                                            ENDCASE => {
                                                val: CARD _ SymbolOps.DecodeCard[idValue];
                                                IF val < 1000 THEN WriteDecimal[val]
                                                ELSE IO.PutF[errorStream, "%b", IO.card[val]]}; -- octal
                                        };
                                (definitionsOnly AND SymbolOps.CtxLevel[idCtx] = lG) => {
                                    WriteRope[", index: "]; WriteDecimal[SymbolOps.DecodeCard[idValue]]};
                                ENDCASE => {
                                    addr: BitAddress = SymbolOps.DecodeBitAddr[idValue];
                                    WriteRope[", address: "];
                                    WriteDecimal[addr];
                                    WriteChar['[];  WriteDecimal[SymbolOps.DecodeCard[idInfo]];  WriteChar[']];
                                    IF linkSpace THEN WriteChar['*]}};
                    PrintTypeInfo[typeSei, nBlanks+2];
                    IF extended THEN PrintSubTree[SymbolOps.FindExtension[sei].tree, nBlanks+4]}};


        PrintName: PROC [name: Name] = {
            s: SubString;
            IF name = nullName THEN WriteRope["(anon)"]
            ELSE {s _ SymbolOps.SubStringForName[name]; WriteSubString[s]}};

        PrintSei: PROC [sei: ISEIndex] = {PrintName[SymbolOps.NameForSe[sei]]};


        WriteTypeName: PROC [n: TypeClass] = {
            ss.offset _ csrP.TypePrintName[n].offset;  ss.length _ csrP.TypePrintName[n].length;
            WriteSubString[ss]};

        WriteModeName: PROC [n: TransferMode] = {
            ss.offset _ csrP.ModePrintName[n].offset;  ss.length _ csrP.ModePrintName[n].length;
            WriteSubString[ss]};

        PrintType: PROC [sei: SEIndex] = {
            tSei: SEIndex;
            IF sei = SENull THEN WriteChar['?]
            ELSE
                WITH t: seb[sei] SELECT FROM
                    cons =>
                        WITH t SELECT FROM
                            transfer => WriteModeName[mode];
                            ENDCASE => WriteTypeName[t.typeTag];
                    id =>
                        FOR tSei _ sei, SymbolOps.TypeLink[tSei] UNTIL tSei = SENull DO
                            WITH seb[tSei] SELECT FROM
                                id => {
                                    IF sei # tSei THEN WriteChar[' ];
                                    PrintSei[LOOPHOLE[tSei, ISEIndex]];
                                    IF ~mark3 OR SymbolOps.CtxLevel[idCtx] # lZ THEN EXIT};
                                ENDCASE;
                            ENDLOOP;
                    ENDCASE;
            WriteRope[" ["];  PrintIndex[sei];  WriteChar[']]};

        PrintTypeInfo: PROC [sei: SEIndex, nBlanks: CARDINAL] = {
            IF sei # SENull THEN
                WITH s: seb[sei] SELECT FROM
                    cons => {
                        Indent[nBlanks];
                        WriteChar['[];  PrintIndex[sei];  WriteRope["] "];
                        WITH s SELECT FROM
                            transfer => WriteModeName[mode];
                            ENDCASE => WriteTypeName[s.typeTag];
                        WITH t: s SELECT FROM
                            basic => NULL;
                            enumerated => {
                                IF t.machineDep THEN WriteRope[" (md)"]
                                ELSE IF ~t.painted THEN WriteRope[" (~painted)"];
                                WriteRope[", value ctx: "];  PrintIndex[t.valueCtx]};
                            record => {
                                IF t.machineDep THEN WriteRope[" (md)"];
                                IF t.monitored THEN WriteRope[" (monitored)"];
                                IF t.hints.variant THEN WriteRope[" (variant)"];
                                OutCtx[", field", t.fieldCtx];
                                WITH ctxb[t.fieldCtx] SELECT FROM
                                    included => IF ~complete THEN WriteRope[" [partial]"];
                                    imported => WriteRope[" [partial]"];
                                    ENDCASE;
                                WITH t SELECT FROM
                                    linked => {WriteRope[", link: "]; PrintType[linkType]};
                                    ENDCASE};
                            ref => {
                                SELECT TRUE FROM
                                    t.counted => WriteRope[" (counted)"];
                                    t.var => WriteRope[" (var)"];
                                    ENDCASE;
                                IF t.ordered THEN WriteRope[" (ordered)"];
                                IF t.basing THEN WriteRope[" (base)"];
                                WriteRope[", to: "]; PrintType[t.refType];
                                IF t.readOnly THEN WriteRope[" (readonly)"];
                                PrintTypeInfo[t.refType, nBlanks+2]};
                            array => {
                                IF t.packed THEN WriteRope[" (packed)"];
                                WriteRope[", index type: "]; PrintType[t.indexType];
                                WriteRope[", component type: "]; PrintType[t.componentType];
                                PrintTypeInfo[t.indexType, nBlanks+2];
                                PrintTypeInfo[t.componentType, nBlanks+2]};
                            arraydesc => {
                                WriteRope[", described type: "]; PrintType[t.describedType];
                                IF t.readOnly THEN WriteRope[" (readonly)"];
                                PrintTypeInfo[t.describedType, nBlanks+2]};
                            transfer => {
                                IF t.safe THEN WriteRope[" (safe)"];
                                OutArgType[", input", t.typeIn];
                                OutArgType[", output", t.typeOut]};
                            definition => {
                                WriteRope[", ctx: "];  PrintIndex[t.defCtx];
                                WriteRope[", ngfi: "];  WriteDecimal[t.nDummyGfi]};
                            union => {
                                IF t.overlaid THEN WriteRope[" (overlaid)"];
                                IF t.controlled THEN {WriteRope[", tag: "]; PrintSei[t.tagSei]};
                                WriteRope[", tag type: "];
                                PrintType[seb[t.tagSei].idType];
                                WriteRope[", case ctx: "];  PrintIndex[t.caseCtx];
                                IF t.controlled THEN PrintSE[t.tagSei, nBlanks+2]};
                            sequence => {
                                IF t.packed THEN WriteRope[" (packed)"];
                                IF t.controlled THEN {WriteRope[", tag: "]; PrintSei[t.tagSei]}
                                ELSE {WriteRope[", index type: "];  PrintType[seb[t.tagSei].idType]};
                                WriteRope[", component type: "]; PrintType[t.componentType];
                                IF t.controlled THEN PrintSE[t.tagSei, nBlanks+2]
                                ELSE PrintTypeInfo[seb[t.tagSei].idType, nBlanks+2];
                                PrintTypeInfo[t.componentType, nBlanks+2]};
                            relative => {
                                WriteRope[", base type: "]; PrintType[t.baseType];
                                WriteRope[", offset type: "]; PrintType[t.offsetType];
                                PrintTypeInfo[t.baseType, nBlanks+2];
                                PrintTypeInfo[t.offsetType, nBlanks+2]};
                            opaque => {
                                WriteRope[", id: "]; PrintSei[t.id];
                                IF t.lengthKnown THEN {WriteRope[", size: "]; WriteDecimal[t.length]}};
                            zone => {
                                IF t.counted THEN WriteRope[" (counted)"];
                                IF t.mds THEN WriteRope[" (mds)"]};
                            subrange => {
                                WriteRope[" of: "];  PrintType[t.rangeType];
                                IF t.filled THEN {
                                    WriteRope[" origin: "];  WriteDecimal[t.origin];
                                    WriteRope[", range: "];  WriteDecimal[t.range]};
                                PrintTypeInfo[t.rangeType, nBlanks+2]};
                            long, real => {
                                WriteRope[" of: "];  PrintType[t.rangeType];
                                PrintTypeInfo[t.rangeType, nBlanks+2]};
                            ENDCASE};
                    ENDCASE};

        OutCtx: PROC [message: Rope.ROPE, ctx: CTXIndex] = {
            WriteRope[message]; WriteRope[" ctx: "];
            IF ctx = CTXNull THEN WriteRope["NIL"] ELSE PrintIndex[ctx]};

        OutArgType: PROC [message: Rope.ROPE, sei: CSEIndex] = {
            IF sei = SENull THEN {WriteRope[message]; WriteRope[": NIL"]}
            ELSE
                WITH t: seb[sei] SELECT FROM
                    record => OutCtx[message, t.fieldCtx];
                    any => {WriteRope[message]; WriteRope[": ANY"]};
                    ENDCASE};

        PrintIndex: PROC[v: Alloc.Index] = LOOPHOLE[WriteDecimal];

        PrintLink: PROC [link: BcdDefs.Link] = {
            SELECT TRUE FROM
                link.proc => {
                    WriteRope["proc["]; WriteDecimal[link.gfi]; WriteChar[',]; WriteDecimal[link.ep]};
                link.type => {WriteRope["type["]; WriteDecimal[LOOPHOLE[link.typeID, CARDINAL]]};
                ENDCASE => {
                    WriteRope["var["]; WriteDecimal[link.gfi]; WriteChar[',]; WriteDecimal[link.var]};
            WriteChar[']]};

        PrintVersion: PROC [stamp: BcdDefs.VersionStamp] = {
            stampWords: CARDINAL = BcdDefs.VersionStamp.SIZE;
            str: PACKED ARRAY [0..4*stampWords) OF [0..16) = LOOPHOLE[stamp];
            digit: STRING = "0123456789abcdef"L;
            FOR i: NAT IN [0..4*stampWords) DO WriteChar[digit[str[i]]] ENDLOOP};

        }.

