private void WriteBody(BlockSyntax body) { var origBlock = block; block = body; // write special pre block syntax if (BlockStartCallback != null) { BlockStartCallback(); BlockStartCallback = null; } // write statements if (blockStatementsOverride != null) { var statements = blockStatementsOverride; blockStatementsOverride = null; foreach (var statement in statements) { WriteStatement(statement); } } else { foreach (var statement in body.Statements) { WriteStatement(statement); } } block = origBlock; }
private void UsingStatement(UsingStatementSyntax statement) { if (statement.Expression != null) { throw new NotSupportedException("using statement expression not supported: " + statement.ToFullString()); } // add special locals StartTryBlock(out string jmpBuffLast, out string jmpBuff, out string isJmp); ++tryCatchNestingLevel; writer.WriteLinePrefix("/* using */"); var locals = WriteLocalDeclaration(statement.Declaration, ";" + Environment.NewLine, false, false); writer.WriteLinePrefix($"memcpy({jmpBuffLast}, CS2X_ThreadExceptionJmpBuff, sizeof(jmp_buf));"); writer.WriteLinePrefix($"{isJmp} = setjmp({jmpBuff});"); writer.WritePrefix($"if ({isJmp} == 0)"); if (statement.Statement is BlockSyntax) { writer.WriteLine(); void WriteTryStart() { writer.WriteLinePrefix($"memcpy(CS2X_ThreadExceptionJmpBuff, {jmpBuff}, sizeof(jmp_buf));"); } BlockStartCallback = WriteTryStart; } else { writer.WriteLine(); writer.WriteLinePrefix('{'); writer.AddTab(); writer.WriteLinePrefix($"memcpy(CS2X_ThreadExceptionJmpBuff, {jmpBuff}, sizeof(jmp_buf));"); } WriteStatement(statement.Statement); if (!(statement.Statement is BlockSyntax)) { writer.RemoveTab(); writer.WriteLinePrefix('}'); } writer.WriteLinePrefix($"memcpy(CS2X_ThreadExceptionJmpBuff, {jmpBuffLast}, sizeof(jmp_buf));"); foreach (var local in locals) { var disposeMethod = FindMethodByName(local.type, "Dispose"); if (local.type.IsReferenceType) { writer.WriteLinePrefix($"{GetMethodFullName(disposeMethod)}({local.name});"); } else { writer.WriteLinePrefix($"{GetMethodFullName(disposeMethod)}(&{local.name});"); } } writer.WriteLinePrefix($"if ({isJmp} != 0) longjmp(CS2X_ThreadExceptionJmpBuff, 1); /* throw caught exception */"); writer.WriteLinePrefix("/* end-using */"); --tryCatchNestingLevel; }
private void WriteStatement(StatementSyntax statement) { if (statement is BlockSyntax) { writer.WriteLinePrefix('{'); writer.AddTab(); using (var stream = new MemoryStream()) using (var subInstructionalBody = new InstructionalBody(stream, writer)) { subInstructionalBody.locals.AddRange(instructionalBody.locals); // copy parent block locals var origWriter = writer; var origInstructionalBody = instructionalBody; writer = subInstructionalBody; instructionalBody = subInstructionalBody; // write body WriteBody((BlockSyntax)statement); // write define locals writer = origWriter; instructionalBody = origInstructionalBody; foreach (var local in subInstructionalBody.locals) { if (local.block == (BlockSyntax)statement) // only write locals part of this block { writer.WriteLinePrefix($"{GetTypeFullNameRef(local.type)} {local.name};"); } } foreach (var local in subInstructionalBody.specialLocals) { if (local.block == (BlockSyntax)statement) // only write locals part of this block { writer.WriteLinePrefix($"{local.type} {local.name};"); } } } writer.RemoveTab(); writer.WriteLinePrefix('}'); } else { if (statement is ExpressionStatementSyntax) { ExpressionStatement((ExpressionStatementSyntax)statement); } else if (statement is LocalDeclarationStatementSyntax) { LocalDeclarationStatement((LocalDeclarationStatementSyntax)statement); } else if (statement is IfStatementSyntax) { IfStatement((IfStatementSyntax)statement); } else if (statement is WhileStatementSyntax) { WhileStatement((WhileStatementSyntax)statement); } else if (statement is DoStatementSyntax) { DoStatement((DoStatementSyntax)statement); } else if (statement is ForStatementSyntax) { ForStatement((ForStatementSyntax)statement); } else if (statement is ForEachStatementSyntax) { ForEachStatement((ForEachStatementSyntax)statement); } else if (statement is SwitchStatementSyntax) { SwitchStatement((SwitchStatementSyntax)statement); } else if (statement is BreakStatementSyntax) { BreakStatement((BreakStatementSyntax)statement); } else if (statement is ContinueStatementSyntax) { ContinueStatement((ContinueStatementSyntax)statement); } else if (statement is FixedStatementSyntax) { FixedStatement((FixedStatementSyntax)statement); } else if (statement is ReturnStatementSyntax) { WriteReturnStatement((ReturnStatementSyntax)statement); } else if (statement is TryStatementSyntax) { TryStatement((TryStatementSyntax)statement); } else if (statement is ThrowStatementSyntax) { ThrowStatement((ThrowStatementSyntax)statement); } else if (statement is UsingStatementSyntax) { UsingStatement((UsingStatementSyntax)statement); } else if (statement is EmptyStatementSyntax) { EmptyStatement((EmptyStatementSyntax)statement); } else { throw new NotSupportedException("Unsupported statement: " + statement.GetType()); } } BlockStartCallback = null; }
private void TryStatement(TryStatementSyntax statement) { if (statement.Finally != null) { throw new NotSupportedException("Finally blocks not supported: " + statement.ToFullString()); } if (statement.Catches == null || statement.Catches.Count == 0) { throw new NotSupportedException("No catach block after try: " + statement.ToFullString()); } // add special locals StartTryBlock(out string jmpBuffLast, out string jmpBuff, out string isJmp); ++tryCatchNestingLevel; // write try writer.WriteLinePrefix("/* try */"); writer.WriteLinePrefix($"memcpy({jmpBuffLast}, CS2X_ThreadExceptionJmpBuff, sizeof(jmp_buf));"); writer.WriteLinePrefix($"{isJmp} = setjmp({jmpBuff});"); writer.WriteLinePrefix($"if ({isJmp} == 0)"); void WriteTryStart() { writer.WriteLinePrefix($"memcpy(CS2X_ThreadExceptionJmpBuff, {jmpBuff}, sizeof(jmp_buf));"); } BlockStartCallback = WriteTryStart; WriteStatement(statement.Block); // write catches writer.WriteLinePrefix("else /* end try */"); writer.WriteLinePrefix('{'); writer.AddTab(); writer.WriteLinePrefix($"memcpy(CS2X_ThreadExceptionJmpBuff, {jmpBuffLast}, sizeof(jmp_buf));"); var first = statement.Catches.FirstOrDefault(); foreach (var c in statement.Catches) { if (c.Filter != null) { throw new NotSupportedException("Catch blocks do not support filters: " + c.ToFullString()); } if (c.Declaration != null) { var type = ResolveType(c.Declaration.Type); writer.WritePrefix(); if (c != first) { writer.Write("else "); } writer.WriteLine($"if (CS2X_IsType((({GetTypeFullName(specialTypes.objectType)}*)CS2X_ThreadExceptionObject)->CS2X_RuntimeType, &{GetRuntimeTypeObjFullName(type)})) /* catch */"); } else { writer.WriteLinePrefix("/* empty catch */"); } void WriteCatchStart() { if (c.Declaration != null) { var type = ResolveType(c.Declaration.Type); var local = TryAddLocal(c.Declaration.Identifier.ValueText, type); writer.WriteLinePrefix($"{local.name} = CS2X_ThreadExceptionObject;"); } writer.WriteLinePrefix("CS2X_ThreadExceptionObject = 0;"); } BlockStartCallback = WriteCatchStart; WriteStatement(c.Block); } writer.WriteLinePrefix("if (CS2X_ThreadExceptionObject != 0) longjmp(CS2X_ThreadExceptionJmpBuff, 1); /* throw unhandled exception */"); writer.RemoveTab(); writer.WriteLinePrefix('}'); writer.WriteLinePrefix($"memcpy(CS2X_ThreadExceptionJmpBuff, {jmpBuffLast}, sizeof(jmp_buf));"); writer.WriteLinePrefix("/* end catch */"); --tryCatchNestingLevel; }
private void ForEachStatement(ForEachStatementSyntax statement) { var collectionType = ResolveType(statement.Expression); if (collectionType.Kind == SymbolKind.ArrayType) { // get array object string identifierName = null; if (statement.Expression is IdentifierNameSyntax) { var syntax = (IdentifierNameSyntax)statement.Expression; var semanticModel = GetSemanticModel(syntax); var symbol = semanticModel.GetSymbolInfo(syntax).Symbol; if (symbol == null) { throw new Exception("Failed to get symbol for IdentifierNameSyntax: " + syntax.ToFullString()); } if (symbol.Kind == SymbolKind.Local) { var type = ResolveType(syntax); var localExpressionResult = instructionalBody.locals.First(x => x.Equals(syntax.Identifier.ValueText, type)); identifierName = localExpressionResult.name; } else if (symbol.Kind == SymbolKind.Parameter) { identifierName = GetParameterFullName((IParameterSymbol)symbol); } else if (symbol.Kind == SymbolKind.Field) { // check if caller is 'this' else handle as new local below var caller = GetCaller(statement.Expression); if (caller is ThisExpressionSyntax) { if (method.ContainingType.IsReferenceType) { identifierName = "self->"; } else { identifierName = "self."; } identifierName += GetFieldFullName((IFieldSymbol)symbol); } } else if (symbol.Kind == SymbolKind.Property) { // handle as new local below } else if (symbol.Kind == SymbolKind.Method) { // handle as new local below } else { throw new NotSupportedException("Array 'foreach' loop with identifier kind not supported: " + symbol.Kind); } } // if no existing variable can be used make one if (identifierName == null) { var localExpressionResult = TryAddLocal(statement.Identifier.ValueText + "_ex", collectionType); identifierName = localExpressionResult.name; writer.WritePrefix($"{identifierName} = "); WriteExpression(statement.Expression); writer.WriteLine(';'); } // get array length method var getLengthMethod = FindMethodByName(specialTypes.arrayType, "get_Length"); getLengthMethod = ResolveMethod(getLengthMethod, method); // write for statement writer.WritePrefix("for ("); var localIterator = TryAddLocal(statement.Identifier.ValueText + "_i", specialTypes.int32Type); writer.Write($"{localIterator.name} = 0; {localIterator.name} != {GetMethodFullName(getLengthMethod)}(({GetTypeFullName(specialTypes.arrayType)}*){identifierName}); ++{localIterator.name}"); // write statement void WriteLocal() { var type = ResolveType(statement.Type); var local = TryAddLocal(statement.Identifier.ValueText, type); void writeExpression() { writer.Write(identifierName); } writer.WritePrefix($"{local.name} = "); WriteArrayElementAccessOffset(writeExpression, collectionType); writer.WriteLine($"[{localIterator.name}];"); } if (statement.Statement is BlockSyntax) { BlockStartCallback = WriteLocal; WriteFlowControlStatement(statement.Statement, ")"); } else // force block style syntax { writer.WriteLine(')'); writer.WriteLinePrefix('{'); writer.AddTab(); WriteLocal(); WriteStatement(statement.Statement); writer.RemoveTab(); writer.WriteLinePrefix('}'); } } else if (collectionType.Kind == SymbolKind.NamedType) { // get special CS2X enumerator object var getEnumeratorMethod = FindMethodByName(collectionType, "GetEnumerator"); if (getEnumeratorMethod == null) { throw new Exception("No valid CS2X 'GetEnumerator' method found on: " + collectionType.FullName()); } if (getEnumeratorMethod.DeclaredAccessibility != Accessibility.Public) { throw new Exception("'GetEnumerator' must be public"); } getEnumeratorMethod = ResolveMethod(getEnumeratorMethod, method); var localExpressionResult = TryAddLocal(statement.Identifier.ValueText + "_en", getEnumeratorMethod.ReturnType); //write for statement var moveNextMethod = FindMethodByName(getEnumeratorMethod.ReturnType, "MoveNext"); moveNextMethod = ResolveMethod(moveNextMethod, method); writer.WritePrefix($"for/*each*/ ({localExpressionResult.name} = {GetMethodFullName(getEnumeratorMethod)}("); WriteExpression(statement.Expression); writer.Write($"); {GetMethodFullName(moveNextMethod)}(&{localExpressionResult.name});"); // write statement void WriteLocal() { var type = ResolveType(statement.Type); var local = TryAddLocal(statement.Identifier.ValueText, type); var getCurrentMethod = FindMethodByName(getEnumeratorMethod.ReturnType, "get_Current"); if (getCurrentMethod == null) { throw new Exception("No valid 'T Current' getter method found on: " + getEnumeratorMethod.ReturnType.FullName()); } if (getCurrentMethod.DeclaredAccessibility != Accessibility.Public) { throw new Exception("'T Current' must be public"); } getCurrentMethod = ResolveMethod(getCurrentMethod, method); writer.WriteLinePrefix($"{local.name} = {GetMethodFullName(getCurrentMethod)}(&{localExpressionResult.name});"); } if (statement.Statement is BlockSyntax) { BlockStartCallback = WriteLocal; WriteFlowControlStatement(statement.Statement, ")"); } else // force block style syntax { writer.WriteLine(')'); writer.WriteLinePrefix('{'); writer.AddTab(); WriteLocal(); WriteStatement(statement.Statement); writer.RemoveTab(); writer.WriteLinePrefix('}'); } } else { throw new NotSupportedException("Unsupported foreach collection kind: " + collectionType.Kind); } }