/** STBytecodeInterpreter.m Copyright (c) 2002 Free Software Foundation This file is part of the StepTalk project. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #import "STBytecodeInterpreter.h" #import "Externs.h" #import "STBlock.h" #import "STBlockContext.h" #import "STBytecodes.h" #import "STCompiledMethod.h" #import "STExecutionContext.h" #import "STLiterals.h" #import "STMessage.h" #import "STMethodContext.h" #import "STSmalltalkScriptObject.h" #import "STStack.h" #import #import #import #import #import #import #import #import #import #import #import #import #import @interface STBytecodeInterpreter(STPrivateMethods) - (short)fetchBytecode; - (BOOL)dispatchBytecode:(STBytecode)bytecode; - (void)invalidBytecode:(STBytecode)bytecode; - (void)setInstructionPointer:(unsigned)newIP; - (unsigned)instructionPointer; - (void)sendSelectorAtIndex:(unsigned)index withArgs:(unsigned)argCount; - (id)interpret; - (void)returnValue:(id)value; @end static STBytecodeInterpreter *sharedInterpreter = nil; static SEL sendSelectorAtIndexSel; static IMP sendSelectorAtIndexImp; static SEL pushSel; static IMP pushImp; static SEL popSel; static IMP popImp; static Class NSInvocation_class = nil; @implementation STBytecodeInterpreter + (void)initialize { sendSelectorAtIndexSel = @selector(sendSelectorAtIndex:withArgCount:); pushSel = @selector(push:); popSel = @selector(pop); sendSelectorAtIndexImp = [STBytecodeInterpreter instanceMethodForSelector:sendSelectorAtIndexSel]; pushImp = [STStack instanceMethodForSelector:pushSel]; popImp = [STStack instanceMethodForSelector:popSel]; NSInvocation_class = [NSInvocation class]; } + (STBytecodeInterpreter *)sharedInterpreter { if(!sharedInterpreter) { sharedInterpreter = [[self alloc] init]; } return sharedInterpreter; } - (void)setEnvironment:(STEnvironment *)env { ASSIGN(environment,env); } - (id)interpretMethod:(STCompiledMethod *)method forReceiver:(id)anObject arguments:(NSArray*)args { STExecutionContext *oldContext; STMethodContext *newContext; id retval; if(!environment) { [NSException raise:STInterpreterGenericException format:@"No execution environment set"]; return nil; } NSDebugLLog(@"STBytecodeInterpreter",@"Executing method %@ with %i args", [method selector],[args count]); if(!method) { return nil; } if([args count] != [method argumentCount]) { [NSException raise:STInterpreterGenericException format:@"Invalid argument count %i (should be %i)" @" for method %@ ", [args count],[method argumentCount], [method selector]]; } newContext = [STMethodContext methodContextWithMethod:method environment:environment]; [newContext setArgumentsFromArray:args]; [newContext setReceiver:anObject]; oldContext = activeContext; [self setContext:newContext]; retval = [self interpret]; [self setContext:oldContext]; return retval; } /* --------------------------------------------------------------------------- * Interpret * --------------------------------------------------------------------------- */ - (void)setContext:(STExecutionContext *)newContext { NSDebugLLog(@"STExecutionContext", @"Switch from context %@ to context %@", activeContext, newContext); if(!newContext) { RELEASE(activeContext); activeContext = nil; stack = nil; bytecodes = nil; instructionPointer = 0; receiver = nil; return; } if( ![newContext isValid]) { [NSException raise:STInterpreterGenericException format:@"Trying to set an invalid context"]; } [activeContext setInstructionPointer:instructionPointer]; ASSIGN(activeContext,newContext); stack = [activeContext stack]; receiver = [activeContext receiver]; if(!stack) { [NSException raise:STInternalInconsistencyException format:@"No execution stack"]; } instructionPointer = [activeContext instructionPointer]; bytecodes = [activeContext bytecodes]; } - (STExecutionContext *)context { return activeContext; } - (id)interpret { STBytecode bytecode; id retval; entry++; NSDebugLLog(@"STBytecodeInterpreter", @"Interpreter entry %i", entry); NSDebugLLog(@"STBytecodeInterpreter", @"IP %x %x", instructionPointer, [bytecodes length]); if(!bytecodes) { NSLog(@"Smalltalk: No bytecodes."); return nil; } stopRequested = NO; do { bytecode = [bytecodes fetchNextBytecodeAtPointer:&instructionPointer]; if(stopRequested) { break; } } while( [self dispatchBytecode:bytecode] ); if(!stopRequested) { retval = [stack pop]; } else { NSDebugLLog(@"STBytecode interpreter",@"Stop requested"); retval = nil; } NSDebugLLog(@"STBytecodeInterpreter", @"Returning '%@' from interpreter (entry %i)",retval,entry); entry --; return retval; } - (void)halt { NSDebugLLog(@"STBytecode interpreter",@"Halt!"); stopRequested = YES; } /* --------------------------------------------------------------------------- * Return * --------------------------------------------------------------------------- */ - (void)returnValue:(id)value { NSDebugLLog(@"STExecutionContext", @"%@ return value '%@' from method", activeContext,value); [activeContext invalidate]; [stack push:value]; } - (unsigned)instructionPointer { return instructionPointer; } /* --------------------------------------------------------------------------- * Block manipulation * --------------------------------------------------------------------------- */ - (void)createBlockWithArgumentCount:(int)argCount stackSize:(int)stackSize { unsigned ptr; STBlock *block; ptr = instructionPointer + STLongJumpBytecodeSize; NSDebugLLog(@"STExecutionContext", @"%@ Create block: argc:%i stack:%i ip:0x%04x", activeContext, argCount, stackSize, ptr); block = [STBlock alloc]; [block initWithInterpreter:self homeContext:[activeContext homeContext] initialIP:ptr argumentCount:argCount stackSize:stackSize]; [stack push:block]; } /* --------------------------------------------------------------------------- send selector (see also STEnvironment class) * --------------------------------------------------------------------------- */ - (void)sendSelectorAtIndex:(unsigned)selIndex withArgCount:(unsigned)argCount { NSString *selector; NSInvocation *invocation; id target; int index; id object; NSDebugLLog(@"STSending", @"send selector '%@' with %i args'", [activeContext literalObjectAtIndex:selIndex],argCount); target = [stack valueFromTop:argCount]; /* FIXME */ if(!target) { target = STNil; } selector = [activeContext literalObjectAtIndex:selIndex]; NSDebugLLog(@"STSending", @" %s receiver:%@ (%@) selector:%@", [receiver isProxy] ? "proxy for" : "", target, NSStringFromClass([target class]), selector); /* FIXME: this is too slow */ selector = [environment translateSelector:selector forReceiver:target]; invocation = [NSInvocation_class invocationWithTarget:target selectorName:selector]; if(!invocation) { [NSException raise:STInternalInconsistencyException format:@"Should not send selector '%@' to " @"receiver of type %@ (could not create invocation)", selector,[target className]]; return; } for(index = argCount + 1; index > 1; index--) { object = [stack pop]; if(object == STNil) { object = nil; } NSDebugLLog(@"STSending", @" argument %2i: '%@'",index - 2, object); [invocation setArgumentAsObject:object atIndex:index]; } /* ---------------------------- * invoke it! * ---------------------------- */ NSDebugLLog(@"STSending",@" invoking... (%@ %s)",invocation, [[invocation methodSignature] methodReturnType]); [invocation invoke]; NSDebugLLog(@"STSending",@" done invoking.",invocation); /* FIXME */ if(!stopRequested) { /* pop the receiver from the stack */ [stack pop]; [stack push: [invocation returnValueAsObject]]; } } /* --------------------------------------------------------------------------- Bytecode manipulation * --------------------------------------------------------------------------- */ #define STDebugBytecode(bc) \ NSDebugLLog(@"STBytecodeInterpreter", \ @"#%04x %@", \ (bc).pointer, \ STDissasembleBytecode(bc)) #define STDebugBytecodeWith(bc,object) \ NSDebugLLog(@"STBytecodeInterpreter", \ @"#%04x %@ (%@)", \ (bc).pointer, \ STDissasembleBytecode(bc), \ (object)) #define STPush(s, v) (*pushImp)(s, pushSel, v) // #define STPush(s, v) [s push:v] #define STPop(s) (*popImp)(s, popSel) // #define STPop(s) [s pop] - (BOOL)dispatchBytecode:(STBytecode)bytecode { id object; switch(bytecode.code) { case STLongJumpBytecode: { int offset = STLongJumpOffset(bytecode.arg1,bytecode.arg2) - STLongJumpBytecodeSize; STDebugBytecode(bytecode); instructionPointer+=offset; break; } case STPushReceiverBytecode: STDebugBytecodeWith(bytecode,receiver); STPush(stack, receiver); break; case STPushNilBytecode: STDebugBytecodeWith(bytecode,receiver); STPush(stack,nil); break; case STPushTrueBytecode: STDebugBytecodeWith(bytecode,receiver); STPush(stack,[NSNumber numberWithInt:YES]); break; case STPushFalseBytecode: STDebugBytecodeWith(bytecode,receiver); STPush(stack,[NSNumber numberWithInt:NO]); break; case STPushRecVarBytecode: object = [(STSmalltalkScriptObject *)receiver instanceVariableAtIndex: bytecode.arg1]; STDebugBytecodeWith(bytecode,object); STPush(stack,object); break; case STPushExternBytecode: object = [activeContext externAtIndex:bytecode.arg1]; STDebugBytecodeWith(bytecode,object); STPush(stack,object); break; case STPushTemporaryBytecode: object = [activeContext temporaryAtIndex:bytecode.arg1]; STPush(stack,object); STDebugBytecodeWith(bytecode,object); break; case STPushLiteralBytecode: object = [activeContext literalObjectAtIndex:bytecode.arg1]; STDebugBytecodeWith(bytecode,object); STPush(stack,object); break; case STPopAndStoreRecVarBytecode: STDebugBytecode(bytecode); [(STSmalltalkScriptObject *)receiver setInstanceVariable:STPop(stack) atIndex:bytecode.arg1]; break; case STPopAndStoreExternBytecode: STDebugBytecode(bytecode); [activeContext setExtern:STPop(stack) atIndex:bytecode.arg1]; break; case STPopAndStoreTempBytecode: STDebugBytecode(bytecode); [activeContext setTemporary:STPop(stack) atIndex:bytecode.arg1]; break; case STSendSelectorBytecode: STDebugBytecodeWith(bytecode, [activeContext literalObjectAtIndex:bytecode.arg1]); (*sendSelectorAtIndexImp)(self, sendSelectorAtIndexSel, bytecode.arg1,bytecode.arg2); /* [self sendSelectorAtIndex:bytecode.arg1 withArgCount:bytecode.arg2]; */ break; case STSuperSendSelectorBytecode: /* FIXME: not implemented */ [self invalidBytecode:bytecode]; break; case STDupBytecode: STDebugBytecode(bytecode); [stack duplicateTop]; break; case STPopStackBytecode: STDebugBytecode(bytecode); [stack pop]; break; case STReturnBytecode: case STReturnBlockBytecode: STDebugBytecode(bytecode); [self returnValue:[stack pop]]; return NO; case STBlockCopyBytecode: STDebugBytecode(bytecode); { STBlockLiteral *info = [stack pop]; [self createBlockWithArgumentCount:[info argumentCount] stackSize:[info stackSize]]; } break; default: [self invalidBytecode:bytecode]; break; }; return YES; } - (void)invalidBytecode:(STBytecode)bytecode { [NSException raise:STInternalInconsistencyException format:@"invalid bytecode (0x%02x) at 0x%06x", bytecode.code,bytecode.pointer]; } @end