-*-text-*- Inside Mutt ------ ---- Notes on what goes on under the hood of an imbedded langauge. Overview of Imbedded Languages -------- -- -------- --------- Why imbedded languages are cool Why you want one in your application - less code to debug, just need a core "machine" - easier to expand - same extension language in all your applications Typed (kinda), why: runtime error checking Compiled. why: static checking and speed Algol like Stack based procedure called based "Loosely" coupled with application: Standalone language Don't know much about the application General perpose - never will know much about application Has "library" (mmaux.c) that has functions used to interface with application. This makes it very easy to add Mutt to about any application. Drawbacks: Hard for Joe Average-Application-User to modify. Internal Objects -------- ------- Characteristics: Mutt knows everything there is to know about these objects. Should act like regular vars - globals live as long as the block, locals come and go with a stack frame. Since objects are [big] structs and might be used in outside libraries, they can be a pain: - MC has know to deal with them. This can be rude from the machine independent aspect. - It can be a pain to dig and reassemble the struct from a chunk of memory. Can't use C to just stuff it or retrieve because different machines require more/less space and different alignments. To do that would require MC to know the biggest the struct could ever be as well as the worst case alignment. Might be easier to add special instructions for each object and keep some kind of numeric id in var memory and have seprate areas and code to maintain them. Pro: - MC just thinks of objects as numbers. - Con: - Not as easy to control stack growth. More work to sync it with var stack. - Yet more stacks to control, more memory being used up. - More opcodes. How To: - Local: - (create-local-objects n): Create n objects for use in current stack frame. Have to remember where previous stack frame starts. This can be stored in the var stack frame. Have to remember where this frame starts. - (free-local-objects): Free all objects in the current stack frame. - (gc-local-objects): Free all local objects. - (set-local-object n): Set the nth object in the current stack frame. - (get-local-object n): Get the nth object in the current stack frame. - Global: - (create-global-object n offset-1 ... offset-n): Create n objects as global objects. Store their ids in offset-i. - (free-global-object offset): Free the object whos id is stored in offset. - (gc-global-objects block): Free all objects associated with block. This probably just entails calling the block prologue. - (set-global-object offset): Set the object whos id is stored in offset. - (get-global-object offset): Get the object whos id is stored in offset. EG: dStrings Constructers Destructers Local Objects garbage collection if program aborts proglogue epilogue Global garbage collection if block is removed prologue epilogue Wants: Want fast allocate/free for (at least) local objects Need to gc all local objects in event of abort, without knowing anything except that they are local. Need to be able to gc all objects if a block is freed. Do this by calling the block epilogue. External Objects -------- ------- Characteristics: Don't know what they are Don't know how to GC them Allocated by external source, I just have an id EG Buffers How created How freed How GCed in case of problems Global vs local Garbage Collection for Imbedded Systems ------- ---------- --- -------- ------- Need GC because: If a programs tanks, all the objects allocated so far won't be freed. Less for the programmer to worry about or forget. Programmers are bound to forget a few frees and never notice. Mutt strings. Domains: Internal Mutt: dStrings. OOP: Probably don't need gc - constructers and destructers can handle the normal cases. GC is needed for when the program is aborted. External Mutt: Objects can be allocated that Mutt will never know how to deal with. For example: Bags, Marks, Buffers. Constraints: Don't want to slow down programs. Solutions: Internal: Pool strings so can gc on abort or program termination. Handle like external objects (or OOP): { (string foo bar) (foo "this is a test") (done) (bar foo) } generates code: { (int foo bar) (foo (create-string)) (bar (create-string)) (copy-to-string foo "this is a test") ;; (foo "this is a test") (goto gc-strings) ;; (done) (copy-to-string bar (get-string foo)) ;; (bar foo) (label gc-strings) (free-string foo bar) (done) } Need new ops: (create-string [global]) ;; RV gets string id (copy-to-string string-id text) ;; RV gets texts, put in string (get-string string-id) ;; RV gets char * (free-string id) ;; Global strings: Need global strings so have 2 pools - Mortal and Imortal. No id overlap! External: When external objects are allocated, they are flaged Mortal or Immortal. When a program stops running or aborts, MMgc_foreign_objects() is called. The external "process" can then sweep the object lists and remove all objects marked Mortal. Pros: Easy. Doesn't slow program execution. Cons: Not real GC. If the programmer is lasy and doesn't free stuff, it could be while before the resources are freed (programs that repeatedly call a routine that creates objects). Mutt Machine ---- ------- Instructions Never ment to be compiled down to machine language Tradeoffs: small memory used, tagged, high level, portable. problems: reading/writing numbers: byte order, alignment