/** STCompiler.m Bytecode compiler. Generates STExecutableCode from source code. 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 "STCompiler.h" #import "STMessage.h" #import "STLiterals.h" #import "STBytecodes.h" #import "STCompiledScript.h" #import "STCompiledCode.h" #import "STCompiledMethod.h" #import "STCompilerUtils.h" #import "STSourceReader.h" #import "Externs.h" #import #import #import #import #import #import #import #import #import #import #import extern int STCparse(void *context); /* FIXME: rewrite parser We need three kinds of grammars: 1. = | 2. = '[|' ']' = | '!' = | 3. = = | Parser 1. are 2. are for scripts. Parser 3. is for script object methods. Because majority of the grammar is reused in all three parsers we use only one grammar definition. There is no problem in haveng 1. and 2. in one file. To be able to have 3. in the same file we do a hack: we add a prefix '!!' for method source. Then we diferentiate it in grammar file by changin the rule: 3. = '!' '!' See STGrammar.y */ @interface STCompiler(STCompilerPrivate) - (void)compile; - (void)initializeContext; - (void)destroyCompilationContext; - (unsigned)tempVariableIndex:(NSString *)varName; - (unsigned)externalVariableIndex:(NSString *)varName; - (void) initializeCompilationContext; - (NSDictionary *)exceptionInfo; - (unsigned)addSelectorLiteral:(NSString*)selector; - (unsigned)addLiteral:literal; - (void)compileMethod:(STCMethod *)method; - (STCompiledCode *) compileStatements:(STCStatements *)statements; - (void)compileStatements:(STCStatements *)statements blockFlag:(BOOL)blockFlag; - (void)compilePrimary:(STCPrimary *)primary; - (void)compileExpression:(STCExpression *)expr; - (void)emitPushReceiverVariable:(unsigned)index; - (void)emitPushSelf; - (void)emitPopAndStoreReceiverVariable:(unsigned)index; - (void)emitReturn; - (void)emitReturnFromBlock; - (void)emitPushTemporary:(unsigned)index; - (void)emitPushLiteral:(unsigned)index; - (void)emitPushVariable:(unsigned)index; - (void)emitPopAndStoreTemporary:(unsigned)index; - (void)emitPopAndStoreVariable:(unsigned)index ; - (void)emitPopStack; - (void)emitSendSelector:(unsigned)index argCount:(unsigned)argCount; - (void)emitBlockCopy; - (void)emitDuplicateStackTop; - (void)emitJump:(short)offset; - (void)emitLongJump:(short)offset; - (void)emitPushNil; - (void)emitPushTrue; - (void)emitPushFalse; - (void)fixupLongJumpAt:(unsigned)index with:(short)offset; - (unsigned)currentBytecode; @end #define MAX(a,b) (((a)>(b))?(a):(b)) @implementation STCompiler + compilerWithEnvironment:(STEnvironment *)env { return AUTORELEASE([[self alloc] initWithEnvironment:env]); } - initWithEnvironment:(STEnvironment *)env { self = [self init]; [self setEnvironment:env]; return self; } - init { [super init]; arrayLiteralClass = [NSMutableArray class]; stringLiteralClass = [NSMutableString class]; /* bytesLiteralClass = [NSMutableData class]; */ intNumberLiteralClass = [NSNumber class]; realNumberLiteralClass = [NSNumber class]; symbolLiteralClass = [STSelector class]; characterLiteralClass = [NSString class]; return self; } - (void)dealloc { [super dealloc]; } - (BOOL)beginScript { if(isSingleMethod) { [NSException raise:@"STCompilerException" format:@"Script source given for single method"]; return NO; } else { return YES; } } /* --------------------------------------------------------------------------- * Compilation * --------------------------------------------------------------------------- */ - (void)setEnvironment:(STEnvironment *)env { ASSIGN(environment,env); } - (STCompiledMethod *)compileMethodFromSource:(NSString *)aString forReceiver:(id )receiver { STCompiledMethod *result; NSString *hackedSource; NSString *exceptionFmt = @"Syntax error at line %i near '%@', " @"reason: %@."; int parsRetval = 0; NSDebugLLog(@"STCompiler", @"Compile method", aString); if(!environment) { [NSException raise:STCompilerGenericException format:@"Compilation environment is not initialized"]; return nil; } hackedSource = [@"!!" stringByAppendingString:aString]; reader = [[STSourceReader alloc] initWithString:hackedSource]; receiverVars = [[NSArray alloc] initWithArray:[receiver instanceVariableNames]]; isSingleMethod = YES; STParserContextInit(&context,self,reader); NS_DURING { // extern int STCdebug; // STCdebug = 1; parsRetval = STCparse(&context); } NS_HANDLER { if ([[localException name] isEqualToString: STCompilerSyntaxException]) { NSString *tokenString; int line; tokenString = [reader tokenString]; line = [reader currentLine]; RELEASE(reader); RELEASE(receiverVars); receiverVars = nil; reader = nil; [NSException raise:STCompilerSyntaxException format:exceptionFmt, line, tokenString, [localException reason]]; } [localException raise]; } NS_ENDHANDLER RELEASE(receiverVars); RELEASE(reader); result = AUTORELEASE(resultMethod); resultMethod = nil; return result; } - (STCompiledScript *)compileString:(NSString *)aString { NSAutoreleasePool *pool = [NSAutoreleasePool new]; STCompiledScript *result; NSString *exceptionFmt = @"Syntax error at line %i near '%@', " @"reason: %@."; int parsRetval = 0; NSDebugLLog(@"STCompiler", @"Compile string", aString); isSingleMethod = NO; if(!environment) { [NSException raise:STCompilerGenericException format:@"Compilation environment is not initialized"]; return nil; } reader = [[STSourceReader alloc] initWithString:aString]; receiverVars = [[NSMutableArray alloc] init]; STParserContextInit(&context,self,reader); NS_DURING { // extern int STCdebug; // STCdebug = 1; parsRetval = STCparse(&context); } NS_HANDLER { if ([[localException name] isEqualToString: STCompilerSyntaxException]) { NSString *tokenString; int line; tokenString = [reader tokenString]; line = [reader currentLine]; RELEASE(reader); RELEASE(receiverVars); receiverVars = nil; reader = nil; [NSException raise:STCompilerSyntaxException format:exceptionFmt, line, tokenString, [localException reason]]; } [localException raise]; } NS_ENDHANDLER RELEASE(receiverVars); RELEASE(reader); [pool release]; result = AUTORELEASE(resultScript); resultScript = nil; return result; } - (void)compileMethod:(STCMethod *)method { STCompiledMethod *compiledMethod; STCompiledCode *code; STMessage *messagePattern; /* FIXME: unite STCMessage and STMessage */ messagePattern = (STMessage *)[method messagePattern]; if(!messagePattern) { messagePattern = [STMessage messageWithSelector:@"_unnamed_method" arguments:nil]; } NSDebugLLog(@"STCompiler", @"Compile method %@", [messagePattern selector]); tempVars = [NSMutableArray arrayWithArray:[messagePattern arguments]]; code = [self compileStatements:[method statements]]; compiledMethod = [STCompiledMethod methodWithCode:AUTORELEASE(code) messagePattern:messagePattern]; if(!isSingleMethod) { if(resultMethod) { [NSException raise:@"STCompilerException" format:@"Method is present when compiling a script"]; return; } if(!resultScript) { NSDebugLLog(@"STCompiler", @"Creating script with %i variables",[receiverVars count]); resultScript = [[STCompiledScript alloc] initWithVariableCount: [receiverVars count]]; } [resultScript addMethod:compiledMethod]; } else { if(resultMethod) { [NSException raise:@"STCompilerException" format:@"More than one method compiled for single method request"]; return; } if(resultScript) { [NSException raise:@"STCompilerException" format:@"Compiled script is present when compiling single method"]; return; } resultMethod = RETAIN(compiledMethod); } } - (STCompiledCode *)compileStatements:(STCStatements *)statements { STCompiledCode *compiledCode; #ifdef DEBUG int count; int i; #endif /* FIXME: create another class */ [self initializeCompilationContext]; NSDebugLLog(@"STCompiler", @"compiling statements"); tempsSize = tempsCount = [tempVars count]; [self compileStatements:statements blockFlag:NO]; NSDebugLLog(@"STCompiler", @" temporaries %i stack %i", tempsSize,stackSize); #ifdef DEBUG count = [literals count]; NSDebugLLog(@"STCompiler", @" literals count %i", count); for(i=0;i 0; index--) { [self emitPopAndStoreTemporary:tempsSave + index - 1]; } } array = [statements expressions]; if(array) { BOOL first = YES; enumerator = [array objectEnumerator]; while((expr = [enumerator nextObject])) { [self compileExpression:expr]; /* Mateu Batle: ignore returned value on the stack ? */ /* FIXME: please check this !!! */ if (!first) { [self emitPopStack]; } else { first = NO; } } } expr = [statements returnExpression]; if(expr) { [self compileExpression:expr]; } if(blockFlag) { [blockInfo setStackSize:stackSize]; AUTORELEASE(blockInfo); stackSize = stackSizeSave; stackPos = stackPosSave; [self emitReturnFromBlock]; } else { [self emitReturn]; } /* fixup jump (if block) */ if(blockFlag) { [self fixupLongJumpAt:jumpIP with:[self currentBytecode] - jumpIP]; } /* cleanup unneeded temp variables */ [tempVars removeObjectsInRange:NSMakeRange(tempsSave, [tempVars count]-tempsSave)]; tempsCount = tempsSave; } - (void)compilePrimary:(STCPrimary *)primary { id object = [primary object]; int index; NSDebugLLog(@"STCompiler-misc",@" compile primary"); switch([primary type]) { case STCVariablePrimaryType: if([object isEqualToString:@"YES"] || [object isEqualToString:@"true"]) { [self emitPushTrue]; } else if([object isEqualToString:@"NO"] || [object isEqualToString:@"false"]) { [self emitPushFalse]; } else if([object isEqualToString:@"nil"]) { [self emitPushNil]; } else { index = [self tempVariableIndex:object]; if(index != NSNotFound) { [self emitPushTemporary:index]; break; } else if( (index = [self receiverVariableIndex:object])!= NSNotFound) { [self emitPushReceiverVariable:index]; break; } else if( [object isEqual:@"self"] ) { [self emitPushSelf]; break; } index = [self externalVariableIndex:object]; [self emitPushVariable:index]; } break; case STCLiteralPrimaryType: index = [self addLiteral:object]; [self emitPushLiteral:index]; break; case STCBlockPrimaryType: [self compileStatements:object blockFlag:YES]; break; case STCExpressionPrimaryType: [self compileExpression:object]; break; default: [NSException raise:STCompilerInconsistencyException format:@"Unknown primary type %i", [primary type]]; break; } } - (void)compileMessage:(STCMessage *)message { NSEnumerator *enumerator; NSArray *args; id obj; int index; args = [message arguments]; if(args && ([args count]>0)) { enumerator = [args objectEnumerator]; while((obj = [enumerator nextObject])) { if([obj isKindOfClass:[STCPrimary class]]) [self compilePrimary:obj]; else [self compileExpression:obj]; } } index = [self addSelectorLiteral:[message selector]]; [self emitSendSelector:index argCount:[args count]]; } - (void)compileExpression:(STCExpression *)expr { NSEnumerator *enumerator; NSArray *cascade; NSString *varName; NSArray *array; unsigned count; unsigned index,i; id obj; NSDebugLLog(@"STCompiler-misc",@" compile expression"); if([expr isPrimary]) { [self compilePrimary:[expr object]]; } else /* message expression */ { obj = [expr target]; /* target */ if([obj isKindOfClass:[STCPrimary class]]) [self compilePrimary:obj]; else [self compileExpression:obj]; cascade = [expr cascade]; if(cascade) { count = [cascade count]; for(i=0;i0) { for(i = 0; i>8)&0xff);\ bc[2] = (unsigned char)(bc2&0xff); \ [byteCodes appendBytes:bc length:3];\ bcpos+=3;\ } while(0) #define EMIT_TRIPPLE(bc1,bc2,bc3) \ do { \ unsigned char bc[5] = {bc1,0,0,0,0}; \ bc[1] = (unsigned char)((((unsigned short)bc2)>>8)&0xff);\ bc[2] = (unsigned char)(bc2&0xff); \ bc[3] = (unsigned char)((((unsigned short)bc3)>>8)&0xff);\ bc[4] = (unsigned char)(bc3&0xff); \ [byteCodes appendBytes:bc length:5];\ bcpos+=5;\ } while(0) #define STACK_PUSH \ do {\ stackPos++; \ stackSize = MAX(stackPos,stackSize);\ /* NSDebugLLog(@"STCompiler",@"stack pointer %i/%i",stackPos,stackSize); */\ } while(0) #define STACK_PUSH_COUNT(count) \ do {\ stackPos+=count; \ stackSize = MAX(stackPos,stackSize);\ /* NSDebugLLog(@"STCompiler",@"stack pointer %i/%i",stackPos,stackSize);*/ \ } while(0) #define STACK_POP \ stackPos--; \ /* NSDebugLLog(@"STCompiler",@"stack pointer %i/%i",stackPos,stackSize) */; #define STACK_POP_COUNT(count) \ stackPos-=count; \ /* NSDebugLLog(@"STCompiler",@"stack pointer %i/%i",stackPos,stackSize) */; - (void)emitPushSelf { NSDebugLLog(@"STCompiler-emit", @"#%04x push self", bcpos); EMIT_SINGLE(STPushReceiverBytecode); STACK_PUSH; } - (void)emitPushNil { NSDebugLLog(@"STCompiler-emit", @"#%04x push nil", bcpos); EMIT_SINGLE(STPushNilBytecode); STACK_PUSH; } - (void)emitPushTrue { NSDebugLLog(@"STCompiler-emit", @"#%04x push true", bcpos); EMIT_SINGLE(STPushTrueBytecode); STACK_PUSH; } - (void)emitPushFalse { NSDebugLLog(@"STCompiler-emit", @"#%04x push false", bcpos); EMIT_SINGLE(STPushFalseBytecode); STACK_PUSH; } - (void)emitPushReceiverVariable:(unsigned)index { NSDebugLLog(@"STCompiler-emit", @"#%04x push receiver variable %i (%@)", bcpos,index,[receiverVars objectAtIndex:index]); EMIT_DOUBLE(STPushRecVarBytecode,index); STACK_PUSH; } - (void)emitPushTemporary:(unsigned)index { NSDebugLLog(@"STCompiler-emit", @"#%04x push temporary %i (%@)", bcpos,index,[tempVars objectAtIndex:index]); EMIT_DOUBLE(STPushTemporaryBytecode,index); STACK_PUSH; } - (void)emitPushLiteral:(unsigned)index { NSDebugLLog(@"STCompiler-emit", @"#%04x push literal %i (%@)", bcpos,index,[literals objectAtIndex:index]); EMIT_DOUBLE(STPushLiteralBytecode,index); STACK_PUSH; stackSize++; } - (void)emitPushVariable:(unsigned)index { NSDebugLLog(@"STCompiler-emit", @"#%04x push external variable %i (%@)", bcpos,index,[externVars objectAtIndex:index]); EMIT_DOUBLE(STPushExternBytecode,index); STACK_PUSH; } - (void)emitPopAndStoreTemporary:(unsigned)index { NSDebugLLog(@"STCompiler-emit", @"#%04x pop and store temp %i (%@)", bcpos,index,[tempVars objectAtIndex:index]); EMIT_DOUBLE(STPopAndStoreTempBytecode,index); STACK_POP; } - (void)emitPopAndStoreVariable:(unsigned)index { NSDebugLLog(@"STCompiler-emit", @"#%04x pop and store ext variable %i (%@)", bcpos,index,[externVars objectAtIndex:index]); EMIT_DOUBLE(STPopAndStoreExternBytecode,index); STACK_POP; } - (void)emitPopAndStoreReceiverVariable:(unsigned)index { NSDebugLLog(@"STCompiler-emit", @"#%04x pop and store rec variable %i (%@)", bcpos,index,[receiverVars objectAtIndex:index]); EMIT_DOUBLE(STPopAndStoreRecVarBytecode,index); STACK_POP; } - (void)emitSendSelector:(unsigned)index argCount:(unsigned)argCount { NSDebugLLog(@"STCompiler-emit", @"#%04x send selector %i (%@) with %i args", bcpos,index,[literals objectAtIndex:index],argCount); EMIT_TRIPPLE(STSendSelectorBytecode,index,argCount); STACK_PUSH_COUNT(argCount); STACK_POP_COUNT(argCount); } - (void)emitDuplicateStackTop { NSDebugLLog(@"STCompiler-emit", @"#%04x dup",bcpos); EMIT_SINGLE(STDupBytecode); STACK_PUSH; } - (void)emitPopStack { NSDebugLLog(@"STCompiler-emit", @"#%04x pop stack",bcpos); EMIT_SINGLE(STPopStackBytecode); STACK_POP; } - (void)emitReturn { NSDebugLLog(@"STCompiler-emit", @"#%04x return",bcpos); EMIT_SINGLE(STReturnBytecode); } - (void)emitReturnFromBlock { NSDebugLLog(@"STCompiler-emit", @"#%04x return from block",bcpos); EMIT_SINGLE(STReturnBlockBytecode); } - (void)emitBlockCopy { NSDebugLLog(@"STCompiler-emit", @"#%04x create block",bcpos); EMIT_SINGLE(STBlockCopyBytecode); } - (void)emitLongJump:(short)offset { NSDebugLLog(@"STCompiler-emit", @"#%04x long jump %i (0x%04x)",bcpos,offset,bcpos+offset); /* EMIT_TRIPPLE(STLongJumpBytecode,STLongJumpFirstByte(offset), STLongJumpSecondByte(offset)); */ EMIT_DOUBLE(STLongJumpBytecode, offset); } - (void)fixupLongJumpAt:(unsigned)index with:(short)offset { //unsigned char bytes[4] = {0,STLongJumpFirstByte(offset),0,STLongJumpSecondByte(offset)}; unsigned char bytes[2] = { (offset >> 8) & 0xff, offset & 0xff }; NSDebugLLog(@"STCompiler-emit", @"# fixup long jump at 0x%04x to 0x%04x",index, index + offset); [byteCodes replaceBytesInRange:NSMakeRange(index+1,2) withBytes:bytes]; } - (unsigned)currentBytecode { return [byteCodes length]; } @end