home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 116.lha / SmallTalk / Sources / PROCESS.C < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-20  |  13.3 KB  |  441 lines

  1. /*
  2.     Little Smalltalk, version 2
  3.     Written by Tim Budd, Oregon State University, July 1987
  4.  
  5.     Process Manager
  6.  
  7.     This module manages the stack of pending processes.
  8.     SendMessage is called when it is desired to send a message to an
  9.     object.  It looks up the method associated with the class of
  10.     the receiver, then executes it.
  11.     A block context is created only when it is necessary, and when it
  12.     is required the routine executeFromContext is called instead of
  13.     sendMessage.
  14.     DoInterp is called by a primitive method to execute an interpreter,
  15.     it returns the interpreter to which execution should continue
  16.     following execution.
  17. */
  18. # include <stdio.h>
  19. # include "env.h"
  20. # include "memory.h"
  21. # include "names.h"
  22. # include "process.h"
  23.  
  24. # define ProcessStackMax 2000
  25.  
  26. extern noreturn execute(OBJ X INT X OBJP X INT X OBJP X OBJP);
  27.  
  28.     /* values set by interpreter when exiting */
  29. int finalStackTop;    /* stack top when finished with interpreter */
  30. int finalByteCounter;    /* bytecode counter when finished with interpreter */
  31. int argumentsOnStack;    /* position of arguments on stack for mess send */
  32. object messageToSend;    /* message to send */
  33. object returnedObject;    /* object returned from message */
  34. taskType finalTask;    /* next task to do (see below) */
  35. object creator;        /* creating interpreter for blocks */
  36.  
  37. static object blockReturnContext;
  38.  
  39. object processStack[ProcessStackMax];
  40. int processStackTop = 0;
  41.  
  42. /*
  43.     we cache recently used methods, in case we want them again
  44. */
  45.  
  46. # define ProcessCacheSize 101    /* a suitable prime number */
  47.  
  48. struct {
  49.     object startClass, messageSymbol, methodClass, theMethod;
  50.     } methodCache[ProcessCacheSize];
  51.  
  52. noreturn prpush(newobj)
  53. object newobj;
  54. {
  55.     incr(processStack[processStackTop++] = newobj);
  56.     if (processStackTop >= ProcessStackMax)
  57.         sysError("stack overflow","process stack");
  58. }
  59.  
  60. /* flush out cache so new methods can be read in */
  61. noreturn flushMessageCache()
  62. {    int i;
  63.  
  64.     for (i = 0; i < ProcessCacheSize; i++)
  65.         methodCache[i].messageSymbol = nilobj;
  66. }
  67.  
  68. static object findMethod(hash, message, startingClass)
  69. int hash;
  70. object message, startingClass;
  71. {    object method, class, methodtable;
  72.  
  73.     /* first examine cache */
  74.     if ((methodCache[hash].messageSymbol == message) &&
  75.         (methodCache[hash].startClass == startingClass)) {
  76.         /* found it in cache */
  77.         method = methodCache[hash].theMethod;
  78.         }
  79.     else {    /* must look in methods tables */
  80.         method = nilobj;
  81.         class = startingClass;
  82.         while ( class != nilobj ) {
  83.             methodtable = basicAt(class, methodsInClass);
  84.             if (methodtable != nilobj)
  85.                 method = nameTableLookup(methodtable, message);
  86.             if (method != nilobj) {
  87.                 /* fill in cache */
  88.                 methodCache[hash].messageSymbol = message;
  89.                 methodCache[hash].startClass = startingClass;
  90.                 methodCache[hash].methodClass = class;
  91.                 methodCache[hash].theMethod = method;
  92.                 class = nilobj;
  93.                 }
  94.             else
  95.                 class = basicAt(class, superClassInClass);
  96.             }
  97.         }
  98.  
  99.     return(method);
  100. }
  101.  
  102. /* newContext - create a new context.  Note this returns three values,
  103. via side effects
  104. */
  105. static newContext(method, methodClass, contextobj, argobj, tempobj)
  106. object method, methodClass, *contextobj, argobj, *tempobj;
  107. {    int temporarysize;
  108.  
  109.     *contextobj = allocObject(contextSize);
  110.     incr(*contextobj);
  111.     setClass(*contextobj, contextclass);
  112.     basicAtPut(*contextobj, methodInContext, method);
  113.     basicAtPut(*contextobj, methodClassInContext, methodClass);
  114.     basicAtPut(*contextobj, argumentsInContext, argobj);
  115.     temporarysize = intValue(basicAt(method, temporarySizeInMethod));
  116.     *tempobj = newArray(temporarysize);
  117.     basicAtPut(*contextobj, temporariesInContext, *tempobj);
  118. }
  119.  
  120. noreturn sendMessage(message, startingClass, argumentPosition)
  121. object message, startingClass;
  122. int argumentPosition;
  123. {    object method, methodClass, size;
  124.     object contextobj, tempobj, argobj, errMessage;
  125.     int i, hash, bytecounter, temporaryPosition, errloc;
  126.     int argumentsize, temporarySize;
  127.     boolean done;
  128.  
  129.     /* compute size of arguments part of stack */
  130.     argumentsize = processStackTop - argumentPosition;
  131.  
  132.     hash = (message + startingClass) % ProcessCacheSize;
  133.     method = findMethod(hash, message, startingClass);
  134. /*fprintf(stderr,"sending message %s class %s\n", charPtr(message), charPtr(basicAt(startingClass, nameInClass)));*/
  135.  
  136.     if (method == nilobj) {        /* didn't find it */
  137.         errMessage = newSymbol("class:doesNotRespond:");
  138.         if (message == errMessage)
  139.             /* better give up */
  140.             sysError("didn't find method", charPtr(message));
  141.         else {
  142.             errloc = processStackTop;
  143.             prpush(smallobj);
  144.             prpush(startingClass);
  145.             prpush(message);
  146.             sendMessage(errMessage, getClass(smallobj), errloc);
  147.             }
  148.         }
  149.     else {            /* found it, start execution */
  150.         /* initialize things for execution */
  151.         bytecounter = 0;
  152.         done = false;
  153.  
  154.         /* allocate temporaries */
  155.         temporaryPosition = processStackTop;
  156.         size = basicAt(method, temporarySizeInMethod);
  157.         if (! isInteger(size))
  158.             sysError("temp size not integer","in method");
  159.         else
  160.             for (i = temporarySize = intValue(size); i > 0; i--)
  161.                 prpush(nilobj);
  162.         methodClass = methodCache[hash].methodClass;
  163.  
  164.         while( ! done ) {
  165.             execute(method, bytecounter, 
  166.                 processStack, processStackTop,
  167.                 &processStack[argumentPosition],
  168.                 &processStack[temporaryPosition]);
  169.             bytecounter = finalByteCounter;
  170.             processStackTop = finalStackTop;
  171.  
  172.             switch(finalTask) {
  173.                 case sendMessageTask:
  174.                     sendMessage(messageToSend, 
  175.                         getClass(processStack[argumentsOnStack]),
  176.                         argumentsOnStack);
  177.                     if (finalTask == BlockReturnTask)
  178.                         done = true;
  179.                     break;
  180.  
  181.                 case sendSuperTask:
  182.                     sendMessage(messageToSend,
  183.                         basicAt(methodClass, superClassInClass),
  184.                         argumentsOnStack);
  185.                     if (finalTask == BlockReturnTask)
  186.                         done = true;
  187.                     break;
  188.  
  189.  
  190.                 case ContextExecuteTask:
  191.                     contextobj = messageToSend;
  192.                     executeFromContext(contextobj,
  193.                         argumentsOnStack);
  194.                     decr(contextobj);
  195.                     if (finalTask == ReturnTask)
  196.                         processStack[processStackTop++] = returnedObject;
  197.                     else
  198.                         done = true;
  199.                     break;
  200.  
  201.                 case BlockCreateTask:
  202.                     /* block is in returnedObject, we just add */
  203.                     /* context info  but first we must */
  204.                     /* create the context */
  205.                     argobj = newArray(argumentsize);
  206.                     newContext(method, methodClass, &contextobj, argobj, &tempobj);
  207.                     for (i = 1; i <= argumentsize; i++) {
  208.                         basicAtPut(argobj, i, processStack[argumentPosition + i - 1]);
  209.                         }
  210.                     for (i = 1; i <= temporarySize; i++) {
  211.                         basicAtPut(tempobj, i, processStack[temporaryPosition + i - 1]);
  212.                         }
  213.                     basicAtPut(returnedObject, contextInBlock, contextobj);
  214.                     processStack[processStackTop++] = returnedObject;
  215.                     /* we now execute using context - */
  216.                     /* so that changes to temp will be */
  217.                     /* recorded properly */
  218.                     executeFromContext(contextobj, bytecounter);
  219.                     while (processStackTop > argumentPosition) {
  220.                         decr(processStack[--processStackTop]);
  221.                         processStack[processStackTop] = nilobj;
  222.                         }
  223.  
  224.                     /* if it is a block return, */
  225.                     /* see if it is our context */
  226.                     /* if so, make into a simple return */
  227.                     /* otherwise pass back to caller */
  228.                     /* we can decr, since only nums are */
  229.                     /* important */
  230.                     decr(contextobj);
  231.                     if (finalTask == BlockReturnTask) {
  232.                         if (blockReturnContext != contextobj)
  233.                             return;
  234.                         }
  235.                     finalTask = ReturnTask;
  236.                     /* fall into return code */
  237.  
  238.                 case ReturnTask:
  239.                     while (processStackTop > argumentPosition) {
  240.                         decr(processStack[--processStackTop]);
  241.                         processStack[processStackTop] = nilobj;
  242.                         }
  243.                     /* note that ref count is picked up */
  244.                     /* from the interpreter */
  245.                     processStack[processStackTop++] = returnedObject;
  246.                     done = true;
  247.                     break;
  248.  
  249.                 default:
  250.                     sysError("unknown task","in sendMessage");
  251.                 }
  252.             }
  253.         }
  254. /*fprintf(stderr,"returning from message %s\n", charPtr(message));*/
  255. }
  256.  
  257. /*
  258.     execute from a context rather than from the process stack
  259. */
  260. static executeFromContext(context, bytecounter)
  261. object context;
  262. int bytecounter;
  263. {    object method, methodclass, arguments, temporaries;
  264.     boolean done = false;
  265.  
  266.     method = basicAt(context, methodInContext);
  267.     methodclass = basicAt(context, methodClassInContext);
  268.     arguments = basicAt(context, argumentsInContext);
  269.     temporaries = basicAt(context, temporariesInContext);
  270.  
  271.     while (! done) {
  272.         execute(method, bytecounter, processStack, processStackTop,
  273.             memoryPtr(arguments), memoryPtr(temporaries));
  274.         bytecounter = finalByteCounter;
  275.         processStackTop = finalStackTop;
  276.  
  277.         switch(finalTask) {
  278.             case sendMessageTask:
  279.                 sendMessage(messageToSend, 
  280.                     getClass(processStack[argumentsOnStack]),
  281.                     argumentsOnStack);
  282.                 if (finalTask == BlockReturnTask)
  283.                     done = true;
  284.                 break;
  285.  
  286.             case sendSuperTask:
  287.                 sendMessage(messageToSend,
  288.                     basicAt(methodclass, superClassInClass),
  289.                     argumentsOnStack);
  290.                 if (finalTask == BlockReturnTask)
  291.                     done = true;
  292.                 break;
  293.  
  294.             case BlockCreateTask:
  295.                 /* block is in returnedObject already */
  296.                 /* just add our context to it */
  297.                 basicAtPut(returnedObject, contextInBlock, context);
  298.                 processStack[processStackTop++] = returnedObject;
  299.                 break;
  300.  
  301.             case BlockReturnTask:
  302.                 blockReturnContext = context;
  303.                 /* fall into next case and return */
  304.  
  305.             case ReturnTask:
  306.                 /* exit and let caller handle it */
  307.                 done = true;
  308.                 break;
  309.     
  310.             default:
  311.                 sysError("unknown task","in context execute");
  312.         }
  313.     }
  314. }
  315.  
  316. flushstack()
  317. {
  318.     while (processStackTop > 0) {
  319.         decr(processStack[--processStackTop]);
  320.         processStack[processStackTop] = nilobj;
  321.         }
  322. }
  323.  
  324. static interpush(interp, value)
  325. object interp, value;
  326. {
  327.     int stacktop;
  328.     object stack;
  329.  
  330.     stacktop = 1 + intValue(basicAt(interp, stackTopInInterpreter));
  331.     stack = basicAt(interp, stackInInterpreter);
  332.     basicAtPut(stack, stacktop, value);
  333.     basicAtPut(interp, stackTopInInterpreter, newInteger(stacktop));
  334. }
  335.  
  336. object doInterp(interpreter)
  337. object interpreter;
  338. {    object context, method, arguments, temporaries, stack;
  339.     object prev, contextobj, obj, argobj, class, newinterp, tempobj;
  340.     int i, hash, argumentSize, bytecounter, stacktop;
  341.  
  342.     context = basicAt(interpreter, contextInInterpreter);
  343.     method = basicAt(context, methodInContext);
  344.     arguments = basicAt(context, argumentsInContext);
  345.     temporaries = basicAt(context, temporariesInContext);
  346.     stack = basicAt(interpreter, stackInInterpreter);
  347.     stacktop = intValue(basicAt(interpreter, stackTopInInterpreter));
  348.     bytecounter = intValue(basicAt(interpreter, byteCodePointerInInterpreter));
  349.  
  350.     execute(method, bytecounter, memoryPtr(stack), stacktop,
  351.         memoryPtr(arguments), memoryPtr(temporaries));
  352.     basicAtPut(interpreter, stackTopInInterpreter, newInteger(finalStackTop));
  353.     basicAtPut(interpreter, byteCodePointerInInterpreter, newInteger(finalByteCounter));
  354.  
  355.     switch(finalTask) {
  356.         case sendMessageTask:
  357.         case sendSuperTask:
  358.             /* first gather up arguments */
  359.             argumentSize = finalStackTop - argumentsOnStack;
  360.             argobj = newArray(argumentSize);
  361.             for (i = argumentSize; i >= 1; i--) {
  362.                 obj = basicAt(stack, finalStackTop);
  363.                 basicAtPut(argobj, i, obj);
  364.                 basicAtPut(stack, finalStackTop, nilobj);
  365.                 finalStackTop--;
  366.                 }
  367.  
  368.             /* now go look up method */
  369.             if (finalTask == sendMessageTask)
  370.                 class = getClass(basicAt(argobj, 1));
  371.             else 
  372.                 class = basicAt(basicAt(context, 
  373.                     methodClassInContext), superClassInClass);
  374.             hash = (messageToSend + class) % ProcessCacheSize;
  375.             method = findMethod(hash, messageToSend, class);
  376.  
  377.             if (method == nilobj) {
  378.                 /* didn't find it, change message */
  379.                 incr(argobj);    /* get rid of old args */
  380.                 decr(argobj);
  381.                 argobj = newArray(3);
  382.                 basicAtPut(argobj, 1, smallobj);
  383.                 basicAtPut(argobj, 2, class);
  384.                 basicAtPut(argobj, 3, messageToSend);
  385.                 class = getClass(smallobj);
  386.                 messageToSend = newSymbol("class:doesNotRespond:");
  387.                 hash = (messageToSend + class) % ProcessCacheSize;
  388.                 method = findMethod(hash, messageToSend, class);
  389.                 if (method == nilobj)    /* oh well */
  390.                     sysError("cant find method",charPtr(messageToSend));
  391.                 }
  392.             newContext(method, methodCache[hash].methodClass, &contextobj, argobj, &tempobj);
  393.             basicAtPut(interpreter, stackTopInInterpreter, newInteger(finalStackTop));
  394.             argumentsOnStack = 0;
  395.             /* fall into context execute */
  396.  
  397.         case ContextExecuteTask:
  398.             if (finalTask == ContextExecuteTask) {
  399.                 contextobj = messageToSend;
  400.                 }
  401.             newinterp = allocObject(InterpreterSize);
  402.             setClass(newinterp, intrclass);
  403.             basicAtPut(newinterp, contextInInterpreter, contextobj);
  404.             basicAtPut(newinterp, previousInterpreterInInterpreter, interpreter);
  405.             basicAtPut(newinterp, creatingInterpreterInInterpreter, creator);
  406.             /* this shouldn't be 15, but what should it be?*/
  407.             basicAtPut(newinterp, stackInInterpreter, newArray(15));
  408.             basicAtPut(newinterp, stackTopInInterpreter, newInteger(0));
  409.             basicAtPut(newinterp, byteCodePointerInInterpreter, newInteger(argumentsOnStack));
  410.             decr(contextobj);
  411.             return(newinterp);
  412.  
  413.         case BlockCreateTask:
  414.             basicAtPut(returnedObject, contextInBlock, context);
  415.             prev = basicAt(interpreter, creatingInterpreterInInterpreter);
  416.             if (prev == nilobj)
  417.                 prev = interpreter;
  418.             basicAtPut(returnedObject, creatingInterpreterInBlock, prev);
  419.             interpush(interpreter, returnedObject);
  420.             decr(returnedObject);
  421.             return(interpreter);
  422.  
  423.         case BlockReturnTask:
  424.             interpreter = basicAt(interpreter, creatingInterpreterInInterpreter);
  425.             /* fall into return task */
  426.  
  427.         case ReturnTask:
  428.             prev = basicAt(interpreter, previousInterpreterInInterpreter);
  429.             if (prev != nilobj) {
  430.                 interpush(prev, returnedObject);
  431.                 }
  432.             /* get rid of excess ref count */
  433.             decr(returnedObject);
  434.             return(prev);
  435.  
  436.         default:
  437.             sysError("unknown final task","doInterp");
  438.         }
  439.     return(nilobj);
  440. }
  441.