Annotation of researchv10no/cmd/sml/src/sepcomp/importer.sml, revision 1.1.1.1

1.1       root        1: (* Copyright 1989 by AT&T Bell Laboratories *)
                      2: (* importer.sml    608567950   46    20    100444  17742     `*)
                      3: (* Importer: Isolation of Mads' original code from Interact() into a separate
                      4:    functor. Numerous extensions, Make system, etc. etc. (NICK) *)
                      5: 
                      6: functor Importer(structure FilePaths: FILEPATHS
                      7:                 val fileExtension: string
                      8:                 structure ModuleComp: MODULE_COMPILER
                      9:                    sharing ModuleComp.Lambda = Lambda
                     10:                        and ModuleComp.Absyn = BareAbsyn
                     11:                        and type ModuleComp.lvar = Access.lvar)
                     12:                : IMPORTER =
                     13: struct
                     14: 
                     15:   open PrintUtil Access Basics Stampset Env
                     16: 
                     17:   val gcmessages = System.Control.Runtime.gcmessages 
                     18:        (* The message "Major collection... abandoned" is
                     19:          annoying me, so I'm hosing it. NICK *)
                     20: 
                     21:   fun all pred list =
                     22:       fold (fn (this, res) => pred this andalso res) list true
                     23: 
                     24:   val DEBUG = false
                     25:   val debug = 
                     26:       if DEBUG then fn str => output std_out ("<" ^ str ^ ">\n")
                     27:       else fn _ => ()
                     28: 
                     29:   val TRACE_IO = false
                     30:   val open_in =
                     31:       if TRACE_IO
                     32:       then fn name => (debug("open_in \\" ^ name ^ "\\"); open_in name)
                     33:       else open_in
                     34: 
                     35:   val open_out =
                     36:       if TRACE_IO
                     37:       then (fn name => (debug("open_out \\" ^ name ^ "\\"); open_out name))
                     38:       else open_out
                     39: 
                     40:   exception Import of string
                     41:      (* A single exception for any failure to
                     42:        import (barring compiler bugs). compSource
                     43:        requires a protective coating so that it
                     44:        doesn't leave the global static environment
                     45:        in a funny state. *)
                     46: 
                     47: 
                     48:   (* Feedback messages. If anybody's interested, files which may
                     49:      cause failures, or may cause nested reads, are done as:
                     50: 
                     51:        [reading fred.sml]
                     52:        [closing fred.sml]
                     53: 
                     54:      Ones which shouldn't (eg. reading from a binary) produce:
                     55: 
                     56:        [reading fred.bin... done]
                     57:    *)
                     58: 
                     59:   fun reading(file, indent) =
                     60:       (tab indent; print("[reading " ^ file ^ "]\n"))
                     61:   fun reading1(file, indent) =
                     62:       (tab indent; print("[reading " ^ file ^ "... "); flush_out std_out)
                     63:   fun writing(file, indent) =
                     64:       (tab indent; print("[writing " ^ file ^ "]\n"))
                     65:   fun writing1(file, indent) =
                     66:       (tab indent; print("[writing " ^ file ^ "... "); flush_out std_out)
                     67:   fun closing(file, indent) =
                     68:       (tab indent; print("[closing " ^ file ^ "]\n"))
                     69:   fun done() = print "done]\n"
                     70: 
                     71:   fun fail(msg, verdict) =
                     72:      (print("import: " ^ msg ^ "\n"); raise Import verdict)
                     73: 
                     74:  (* impliedPath: derived from FilePaths.impliedPath, but catches
                     75:     ImpliedPath if a "~"-filename fails to translate. *)
                     76: 
                     77:   fun impliedPath(oldPath, oldName) =
                     78:       FilePaths.impliedPath(oldPath, oldName)
                     79:       handle FilePaths.ImpliedPath =>
                     80:        fail("couldn't translate path in: " ^ oldName, "open")
                     81: 
                     82:   type LambDynModule = ModuleComp.LambDynModule
                     83:   type CodeDynModule = ModuleComp.CodeDynModule
                     84:   type lvar = Access.lvar
                     85: 
                     86:   datatype ToplevelFns =
                     87:      TOPLEVEL_FNS of
                     88:        {bind: lvar * System.Unsafe.object -> unit,
                     89:        lookup: lvar -> System.Unsafe.object,
                     90:        parse: Lex.lexer -> BareAbsyn.dec,
                     91:        getvars: BareAbsyn.dec -> lvar list,
                     92:        opt: Lambda.lexp -> Lambda.lexp}
                     93: 
                     94:   datatype statModule =
                     95:       STATmodule of {table: symtable, lvars: Basics.Access.lvar list}
                     96: 
                     97: 
                     98:     (* Rename the lvars, and shift the stamps, of the static module.
                     99:        Only signature and functor bindings are accepted.
                    100:        For each functor binding, a fresh lvar will be chosen; hence
                    101:        at run-time, several imports of the same functor will presumably
                    102:        lead to a new copy of the code of that functor *)
                    103: 
                    104:   fun importModule(STATmodule{table,lvars}) : lvar list = 
                    105:       let val newlvars = map (fn _ => mkLvar()) lvars
                    106:          fun lookup x =
                    107:              let fun f(a::ar, b::br) = if a=x then b else f(ar,br)
                    108:                    | f _ = ErrorMsg.impossible "importModule 1"
                    109:               in f(lvars,newlvars)
                    110:              end
                    111:          fun renBinding(SIGbind(SIGvar{name,binding})) =
                    112:                SIGbind(SIGvar
                    113:                        {name=name, 
                    114:                         binding=ModUtil.shiftSigStamps(newStampsets(),binding)})
                    115:            | renBinding(FCTbind(FCTvar{name,access=LVAR lvar, binding})) =
                    116:                FCTbind(FCTvar{name = name, access= LVAR(lookup lvar),
                    117:                         binding = ModUtil.shiftFctStamps binding})
                    118:             | renBinding _ = ErrorMsg.impossible "importModule 2"
                    119:        in IntStrMap.app (fn (i,s,b) => add(i,s,renBinding b)) table;
                    120:          newlvars
                    121:       end
                    122: 
                    123:  (* New code (NICK) - I store the static information (StatModule) and
                    124:     dynamic information (CodeDynModule) in one object, so that I can blast
                    125:     out the entire thing as a single object into a file. Foo.sml now gets
                    126:     compiled into Foo.vax/Foo.m68/..., which contains
                    127:     both. The object stored in the file is a pair: the first element is a
                    128:     "version number" for the data structures, the second is whatever needs
                    129:     storing (currently a record of {statModule, dynModule, imports}).
                    130:     If this version number changes, I have to recompile. *)
                    131: 
                    132:   type BinFormat = {statModule: statModule,
                    133:                    dynModule: CodeDynModule,
                    134:                    imports: string list}
                    135: 
                    136:   val blastRead: instream -> BinFormat = System.Unsafe.blast_read
                    137:   val blastWrite: (outstream * BinFormat) -> unit = System.Unsafe.blast_write
                    138: 
                    139:   val blastWrite =    (* Silent version. *)
                    140:       fn (stream, obj) =>
                    141:         let val oldmsgs = !gcmessages
                    142:          in gcmessages := 0;
                    143:             blastWrite(stream, obj);
                    144:             gcmessages := oldmsgs
                    145:         end
                    146: 
                    147:   val BIN_VERSION = System.version ^ " - LAMBDA v0 " ^ fileExtension^ "\n"
                    148:        (* This is stored as the first line of the
                    149:          binary file. Be sure to increment it whenever the structure
                    150:          of any of the stored data objects changes. It cannot
                    151:          contain any \n characters, except at the end where
                    152:          one is required.  *)
                    153: 
                    154:   fun tryOpenIn filename: instream option =
                    155:       SOME(open_in filename) handle Io _ => NONE
                    156: 
                    157:   fun createBinary(indent, filename,
                    158:                   statModule: statModule,
                    159:                   dynModule: CodeDynModule,
                    160:                   imports: string list) : unit =
                    161:       let val fullName = filename ^ ".bin"
                    162:          val outstream =
                    163:               open_out fullName
                    164:               handle Io _ => fail("couldn't open " ^ fullName ^ " for output",
                    165:                                   "open")
                    166:        in writing1(fullName, indent);
                    167:          output outstream BIN_VERSION;
                    168:          blastWrite(outstream,
                    169:                     {statModule=statModule,
                    170:                      dynModule=dynModule,
                    171:                      imports=imports});
                    172:          close_out outstream;
                    173:          done()
                    174:       end
                    175: 
                    176:   val statPrinter: statModule -> string = (fn _ => "")
                    177:    (* this definition causes createTextual to have no effect *)
                    178: 
                    179:   fun createTextual(indent, filename, statModule): unit =
                    180:       case statPrinter statModule  (* currently always "" *)
                    181:        of "" => ()   (* Do NOTHING if the print function is a dummy *)
                    182:         | text =>
                    183:           let val fullName = filename ^ ".lstat"
                    184:               val outstream =
                    185:                    open_out fullName
                    186:                    handle Io _ =>
                    187:                      fail("couldn't open " ^ fullName ^ " for output", "open")
                    188:            in writing1(fullName, indent);
                    189:               output outstream text;
                    190:               close_out outstream;
                    191:               done()
                    192:           end
                    193: 
                    194:  (* We must do a syntactic check that the source declarations in a module
                    195:     are just functor and signature declarations (or sequences thereof),
                    196:     otherwise the renaming routines will fall over later. Importer is the
                    197:     place to do it, where we still have a fighting chance of a putting
                    198:     out a decent diagnostic message. We don't allow IMPORT - that should
                    199:     have been dealt with earlier. *)
                    200: 
                    201:   fun kosherModuleDecl dec =
                    202:       case dec
                    203:        of BareAbsyn.FCTdec _ => true
                    204:         | BareAbsyn.SIGdec _ => true
                    205:         | BareAbsyn.SEQdec decs =>     (* ALL must be kosher. *)
                    206:             all kosherModuleDecl decs
                    207:         | _ => false
                    208: 
                    209:   fun badModuleDecl() = ErrorMsg.condemn "expecting SIGNATURE/FUNCTOR/IMPORT"
                    210: 
                    211:  (* uptodate should be memo'd sometime, since it's quite expensive. *)
                    212:   fun uptodate (path, myBinTime) name =
                    213:       let val {newPath, validName} = impliedPath(path, name)
                    214:          val _ = debug("uptodate(quotedName=" ^ name
                    215:                        ^ ", validName=" ^ name ^ ")?")
                    216:          val trySml = tryOpenIn(validName ^ ".sml")
                    217:          val tryBin = tryOpenIn(validName ^ ".bin")
                    218:        in case (trySml, tryBin)
                    219:           of (SOME source, SOME binary) =>
                    220:                let val srcTime = mtime source
                    221:                    val binTime = mtime binary
                    222:                    val _ = debug("uptodate(" ^ validName ^ "):\
                    223:                                  \ src time = " ^ makestring srcTime
                    224:                                  ^ ", bin time = " ^ makestring binTime)
                    225:                 in if srcTime >= binTime       (* binary out of date *)
                    226:                       orelse binTime >= myBinTime
                    227:                               (* Some other branch of the Make
                    228:                                  task compiled this under me...? *)
                    229:                    then (close_in source; close_in binary; false)
                    230:                    else   (* source is older; check imports *)
                    231:                      let val _ = close_in source
                    232:                          val fullName = validName ^ ".bin"
                    233:                          val binVersion = input_line binary
                    234:                       in if binVersion <> BIN_VERSION
                    235:                          then (close_in binary; false)
                    236:                           (* can't trust "imports" : chicken out *)
                    237:                          else let val {imports, ...} = 
                    238:                                       blastRead binary before close_in binary
                    239:                                in all (uptodate (newPath, myBinTime)) imports
                    240:                               end
                    241:                      end
                    242:                 end
                    243: 
                    244:            | (SOME source, NONE) =>    (* No bin: force recompile *)
                    245:                (close_in source; false)
                    246: 
                    247:            | (NONE, SOME binary) =>    (* No source: trust for now... *)
                    248:                (close_in binary; true)
                    249: 
                    250:            | (NONE, NONE) =>
                    251:                fail("cannot find source or binary\
                    252:                     \ of required module " ^ validName,
                    253:                     "open")
                    254:       end (* uptodate *)
                    255: 
                    256: fun getModule(name,pervasives,TOPLEVEL_FNS{bind,lookup,parse,getvars,opt})
                    257:       : statModule * CodeDynModule =
                    258:     let fun getModule'(parents, indent, path, name) =
                    259:         (* "parents" is a depth-first list of filenames used for
                    260:            a circularity check. "indent" is for cosmetic purposes. *)
                    261:        let val {validName as filename, newPath as path} = impliedPath(path, name)
                    262:            val _ = if exists (fn x  => x = filename) parents
                    263:                    then fail("self-referential import of " ^ validName, "open")
                    264:                    else ()
                    265:            val parents = filename :: parents
                    266:            val _ = debug("getModule'(name=" ^ name ^ ")")
                    267: 
                    268:            fun compSource0(source: instream) : statModule * CodeDynModule =
                    269:                let val lex = Lex.mkLex{stream=source,interactive=false}
                    270:                    fun loop(dynModule, lvars, imports)
                    271:                        : LambDynModule * lvar list * string list =
                    272:                        (case parse lex (*  (Lex.toplevel := true; parse()) *)
                    273:                          of BareAbsyn.IMPORTdec names => 
                    274:                              let fun loop'([], dynMod, lvars, imports) =
                    275:                                        (dynMod, lvars, imports)
                    276:                                    | loop'(name::rest, dynMod, lvars, imports)=
                    277:                                        let val {newPath, ...} = impliedPath(path, name)
                    278:                                            val (stat, codeDyn) =
                    279:                                                getModule'(parents, indent+2,
                    280:                                                          newPath, name)
                    281:                                            val newLvars = importModule stat
                    282:                                            val lambDyn = ModuleComp.abstractDynModule
                    283:                                                            (codeDyn, newLvars)
                    284:                                            val dynMod' = ModuleComp.importDynModule
                    285:                                                            (lambDyn, dynMod)
                    286:                                         in loop'(rest, dynMod', lvars @ newLvars,
                    287:                                                  name :: imports)
                    288:                                        end
                    289:                               in loop(loop'(names, dynModule, lvars, imports))
                    290:                              end
                    291: 
                    292:                           | absyn => (* normal program *)
                    293:                               if kosherModuleDecl absyn
                    294:                               then let val newLvars = getvars absyn
                    295:                                        val newMod = ModuleComp.addDeclaration
                    296:                                                       (absyn, newLvars, dynModule)
                    297:                                                     handle ModuleComp.AddDeclaration =>
                    298:                                                     fail("error during translate",
                    299:                                                          "translate")
                    300:                                     in loop(newMod, lvars @ newLvars, imports)
                    301:                                    end
                    302:                               else badModuleDecl())
                    303: 
                    304:                        handle Parse.Eof => (dynModule, lvars, imports)
                    305:                             | Import x  => raise Import x
                    306:                                         (* Resignal nested Import probs. *)
                    307:                             | Io x => raise Import("unexpected: Io(" ^ x ^ ")")
                    308:                             | exn => raise Import("compile-time exception: "
                    309:                                                   ^ System.exn_name exn)
                    310: 
                    311:                    val (lambDynModule, lvars, imports) =
                    312:                          loop(ModuleComp.emptyDynModule, [], [])
                    313:                    val statModule= STATmodule{table=Env.popModule(pervasives),
                    314:                                               lvars=lvars}
                    315:                    val dynModule = ModuleComp.compileDynModule opt lambDynModule
                    316:                                    handle ModuleComp.CompileDynModule =>
                    317:                                      fail("code generation failed", "codegen")
                    318:                 in createBinary(indent, filename, statModule,
                    319:                                 dynModule, imports)
                    320:                      handle Import _ => (); (* make failed writes nonfatal... *)
                    321:                    createTextual(indent, filename, statModule) (* no-op *)
                    322:                      handle Import _ => ();
                    323:                    (statModule, dynModule)
                    324:                end  (* fun compSource *)
                    325: 
                    326:            fun compSource(source) =
                    327:                let val _ = debug(filename ^ ": source only")
                    328:                    val fullName = filename ^ ".sml"
                    329:                    val _ = reading(fullName, indent)
                    330: 
                    331:                    val oldfile = !ErrorMsg.fileName
                    332:                    val oldlinenum = !ErrorMsg.lineNum
                    333:                    val oldinteractive = !System.interactive
                    334:                    val savedEnv = Env.current()
                    335:                    fun cleanup () =
                    336:                       (closing(fullName, indent);
                    337:                        close_in source;
                    338:                        ErrorMsg.fileName := oldfile;
                    339:                        ErrorMsg.lineNum := oldlinenum;
                    340:                        System.interactive := oldinteractive;
                    341:                        Env.resetEnv savedEnv)
                    342:                 in ErrorMsg.fileName := fullName;
                    343:                    ErrorMsg.lineNum := 1;
                    344:                    System.interactive := false;
                    345:                    Env.resetEnv pervasives;
                    346:                    (compSource0(source) before cleanup())
                    347:                    handle exp => (cleanup(); raise exp)
                    348:                end
                    349: 
                    350:         in case (tryOpenIn(filename ^ ".sml"), tryOpenIn(filename ^ ".bin"))
                    351:              of (SOME source, NONE) =>  (* Source only: Compile! *)
                    352:                   compSource(source)
                    353:               | (SOME source, SOME binary) =>
                    354:                   let val srcTime = mtime source
                    355:                       val binTime = mtime binary
                    356:                       val _ = debug(filename ^ ": src dated " ^ makestring srcTime
                    357:                                     ^ ", bin dated " ^ makestring binTime)
                    358:                    in if srcTime >= binTime   (* (">=" for safety) *)
                    359:                       then (* binary out of date? *)
                    360:                         (tab indent;
                    361:                          print("[" ^ filename ^ ".bin is out of date;\
                    362:                                \ recompiling]\n");
                    363:                          close_in binary;
                    364:                          compSource(source))
                    365:                       else (* bin is newer: what about the things imported? *)
                    366:                         let val _ = debug(filename ^ ": checking imports")
                    367:                             val fullName = filename ^ ".bin"
                    368:                             val _ = reading1(fullName, indent)
                    369:                             val binVersion = input_line binary
                    370:                          in if (binVersion <> BIN_VERSION)
                    371:                             then (print "]\n";
                    372:                                   tab indent;
                    373:                                   print("[" ^ fullName ^ " is the wrong format;\
                    374:                                     \ recompiling]\n");
                    375:                                   closing(fullName, indent);
                    376:                                   close_in binary;
                    377:                                   compSource(source))
                    378:                             else let val {statModule, dynModule, imports} =
                    379:                                            blastRead binary
                    380:                                      fun allOk imports =
                    381:                                          all (uptodate (path, binTime)) imports
                    382:                                          handle exn =>
                    383:                                            (print "]\n";
                    384:                                             closing(fullName, indent);
                    385:                                             close_in binary;
                    386:                                             close_in source;
                    387:                                             raise exn)
                    388:                                   in if not(allOk imports)
                    389:                                      then (print "]\n";
                    390:                                            tab indent;
                    391:                                            print("[import(s) of " ^ filename
                    392:                                              ^ " are out of date; recompiling]\n");
                    393:                                            closing(fullName, indent);
                    394:                                            close_in binary;
                    395:                                            compSource(source))
                    396:                                       else (* All OK: use the binary. *)
                    397:                                            (debug(filename ^ ": all up to date");
                    398:                                             close_in source;
                    399:                                             close_in binary;
                    400:                                             done();
                    401:                                             (statModule, dynModule))
                    402:                                  end
                    403:                         end
                    404:                   end
                    405: 
                    406:               | (NONE, SOME binary) =>
                    407:                   let val _ = debug(filename ^ ": binary only")
                    408:                       val fullName = filename ^ ".bin"
                    409:                       val _ = reading1(fullName, indent)
                    410:                       val binVersion = input_line binary
                    411:                    in if binVersion = BIN_VERSION
                    412:                       then let val {statModule, dynModule, ...} = blastRead binary
                    413:                             in close_in binary;
                    414:                                done();
                    415:                                (statModule, dynModule)
                    416:                            end
                    417:                       else (print "]\n";       (* Outstanding message... *)
                    418:                             tab indent;
                    419:                             print("[" ^ fullName ^ " is the wrong format;\
                    420:                                    \ recompiling]\n");
                    421:                             closing(fullName, indent);
                    422:                             close_in binary;
                    423:                             case tryOpenIn(filename ^ ".sml")
                    424:                               of SOME source =>
                    425:                                    compSource(source)
                    426:                                | NONE =>
                    427:                                  fail(fullName ^ " is out of date, and can't\
                    428:                                       \ open " ^ filename ^ ".sml",
                    429:                                       "open"))
                    430:                   end
                    431: 
                    432:               | (NONE, NONE) => fail("cannot open " ^ filename ^ ".sml", "open")
                    433:        end (* getModule' *)
                    434:      in getModule'([],0,FilePaths.defaultPath,name)
                    435:     end  (* getModule *)
                    436: 
                    437:   fun getAndExecModule(filename, pervasives,
                    438:                       toplevelFns as TOPLEVEL_FNS{bind, lookup, ...}) : unit = 
                    439:       let val (statModule as STATmodule{table, ...}, compDynModule) =
                    440:                getModule(filename, pervasives, toplevelFns)
                    441:        in let val newlvars = importModule statModule 
                    442:              (* adds the static bindings of the module to the
                    443:                 static environment*)
                    444: 
                    445:              val result =
                    446:                  ModuleComp.executeDynModule compDynModule lookup
                    447:                  handle exn => (* Local handle for module execution (NICK). *)
                    448:                    fail("execution of module raised exception "
                    449:                         ^ System.exn_name exn
                    450:                         ^ "\n\t(static bindings of module take no effect)\n",
                    451:                         "uncaught exception")
                    452: 
                    453:              fun bindlvars (i,v::r) =
                    454:                    (bind(v,result sub i);
                    455:                     bindlvars (i+1,r))
                    456:                | bindlvars (_,nil) = ()
                    457: 
                    458:           in bindlvars(0,newlvars);    (* add new runtime bindings *)
                    459:              Env.commit();                     (* accept static bindings *)
                    460:              PrintDec.printBindingTbl table
                    461:          end
                    462:          handle  (* Exceptions other than ones raised through module execution. *)
                    463:                Interrupt => raise Interrupt
                    464:              | exn => ErrorMsg.impossible("addAndExecModule: exn ("
                    465:                                           ^ System.exn_name exn ^ ")??")
                    466:       end
                    467: 
                    468: end (* functor Importer *)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.