/* Little Smalltalk version 2 Written by Tim Budd, Oregon State University, July 1987 bytecode interpreter module execute bytecodes for a given method until one of six events occur 1. A message must be sent to another object 2. A message must be sent to super 3. A return from a method occurs 4. An explicit return from a block occurs (backs up the process chain) 5. A block must be created 6. A block must begin execution the global variable finalTask indicates which of the six events is to be performed. Various other global variables (described in process.h) give other information to be used in performing the called for task. Note that the interpreter is called as part of the main instruction sequence (single process) and (via a primitive call) as part of the multi-process scheduler loop (class Scheduler, Process, et al) */ # include # include "env.h" # include "memory.h" # include "names.h" # include "process.h" # include "interp.h" extern object unSyms[], binSyms[], keySyms[]; extern boolean primitive(INT X OBJP X INT); # define nextByte() byteToInt(bytecodes[byteCounter++]) # define ipush(x) incr(stack[stacktop++] = x) /* note that ipop leaves a ref count on the popped object */ # define ipop(x) x=stack[--stacktop]; stack[stacktop]=nilobj noreturn execute(method, byteCounter, stack, stacktop, arguments, temporaries) object method, *stack, *arguments, *temporaries; register int byteCounter; register int stacktop; { int i, j, low, high; object receiver, *instance, *literals; object newobj; byte *bytecodes; boolean done; double f; /* do initialization */ receiver = arguments[0]; if (isInteger(receiver)) instance = (object *) 0; else instance = memoryPtr(receiver); bytecodes = bytePtr(basicAt(method, bytecodesInMethod)); literals = memoryPtr(basicAt(method, literalsInMethod)); done = false; while( ! done ) { low = (high = nextByte()) & 0x0F; high >>= 4; if (high == 0) { high = low; low = nextByte(); } /*if (debugging) ignore fprintf(stderr,"executing %s %d %d %d\n", charPtr(basicAt(method, messageInMethod)), byteCounter, high, low);*/ switch(high) { case PushInstance: ipush(instance[low]); break; case PushArgument: ipush(arguments[low]); break; case PushTemporary: ipush(temporaries[low]); break; case PushLiteral: ipush(literals[low]); break; case PushConstant: if (low == 3) low = -1; if (low < 3) { ipush(newInteger(low)); } else switch(low) { case 4: ipush(nilobj); break; case 5: ipush(trueobj); break; case 6: ipush(falseobj); break; case 7: ipush(smallobj); break; case 8: ipush(globalNames); break; default: sysError("not done yet","pushConstant"); } break; case PushGlobal: newobj = nameTableLookup(globalNames, literals[low]); if (newobj == nilobj) { /* send message instead */ ipush(smallobj); ipush(literals[low]); argumentsOnStack = stacktop - 2; messageToSend = newSymbol("cantFindGlobal:"); finalTask = sendMessageTask; done = true; } else ipush(newobj); break; case PopInstance: decr(instance[low]); /* we transfer reference count to instance */ ipop(instance[low]); break; case PopTemporary: decr(temporaries[low]); /* we transfer reference count to temporaries */ ipop(temporaries[low]); break; case SendMessage: argumentsOnStack = stacktop - (low + 1); messageToSend = literals[nextByte()]; finalTask = sendMessageTask; done = true; break; case SendUnary: /* we optimize a couple common messages */ if (low == 0) { /* isNil */ ipop(newobj); if (newobj == nilobj) { ipush(trueobj); } else { decr(newobj); ipush(falseobj); } } else if (low == 1) { /* notNil */ ipop(newobj); if (newobj == nilobj) { ipush(falseobj); } else { decr(newobj); ipush(trueobj); } } else { argumentsOnStack = stacktop - 1; messageToSend = unSyms[low]; finalTask = sendMessageTask; done = true; } break; case SendBinary: /* optimize arithmetic as long as no */ /* conversions are necessary */ /* and overflow does not occur */ if (low <= 12) { if (isInteger(stack[stacktop-1]) && isInteger(stack[stacktop-2])) { ipop(newobj); i = intValue(newobj); ipop(newobj); j = intValue(newobj); ignore intBinary(low, j, i); if (returnedObject != nilobj) { ipush(returnedObject); break; } /* overflowed, go do it */ /* the old fashioned way */ ipush(newInteger(j)); ipush(newInteger(i)); } else if (isFloat(stack[stacktop-1]) && isFloat(stack[stacktop-2])) { ipop(newobj); f = floatValue(newobj); decr(newobj); ipop(newobj); ignore floatBinary(low, floatValue(newobj), f); decr(newobj); ipush(returnedObject); break; } } argumentsOnStack = stacktop - 2; messageToSend = binSyms[low]; finalTask = sendMessageTask; done = true; break; case SendKeyword: argumentsOnStack = stacktop - 3; messageToSend = keySyms[low]; finalTask = sendMessageTask; done = true; break; case DoPrimitive: i = nextByte(); done = primitive(i, &stack[stacktop - low], low); incr(returnedObject); /* pop off arguments */ for (i = low; i > 0; i--) { ipop(newobj); decr(newobj); } if (! done) { ipush(returnedObject); decr(returnedObject); } break; case CreateBlock: /* we do most of the work in making the block */ /* leaving it to the caller to fill in */ /* the context information */ newobj = allocObject(blockSize); setClass(newobj, blockclass); basicAtPut(newobj, argumentCountInBlock, newInteger(low)); i = (low > 0) ? nextByte() : 0; basicAtPut(newobj, argumentLocationInBlock, newInteger(i)); basicAtPut(newobj, bytecountPositionInBlock, newInteger(byteCounter + 1)); incr(returnedObject = newobj); /* avoid a subtle side effect here */ i = nextByte(); byteCounter = i; finalTask = BlockCreateTask; done = true; break; case DoSpecial: switch(low) { case SelfReturn: incr(returnedObject = receiver); finalTask = ReturnTask; done = true; break; case StackReturn: ipop(returnedObject); finalTask = ReturnTask; done = true; break; case BlockReturn: ipop(returnedObject); finalTask = BlockReturnTask; done = true; break; case Duplicate: ipop(newobj); ipush(newobj); ipush(newobj); decr(newobj); break; case PopTop: ipop(newobj); decr(newobj); break; case Branch: /* avoid a subtle bug here */ i = nextByte(); byteCounter = i; break; case BranchIfTrue: ipop(newobj); i = nextByte(); if (newobj == trueobj) { /* leave nil on stack */ ++stacktop; byteCounter = i; } decr(newobj); break; case BranchIfFalse: ipop(newobj); i = nextByte(); if (newobj == falseobj) { /* leave nil on stack */ ++stacktop; byteCounter = i; } decr(newobj); break; case AndBranch: ipop(newobj); i = nextByte(); if (newobj == falseobj) { ipush(newobj); byteCounter = i; } decr(newobj); break; case OrBranch: ipop(newobj); i = nextByte(); if (newobj == trueobj) { ipush(newobj); byteCounter = i; } decr(newobj); break; case SendToSuper: argumentsOnStack = stacktop - (nextByte() + 1); messageToSend = literals[nextByte()]; finalTask = sendSuperTask; done = true; break; default: sysError("invalid doSpecial",""); break; } break; default: sysError("invalid bytecode",""); break; } } /* when done, save stack top and bytecode counter */ /* before we exit */ finalStackTop = stacktop; finalByteCounter = byteCounter; } „bÅŒ( error: aString stderr print: 'Error: ',aString. errorRecoveryBlock value ÆÆ¢œš  ÇŒstdi