private static void InternalConvertSetupExecutionFrame( CodeTextWriter tw, IExtractContextHost extractContext, PreparedMethodInformation preparedMethod, ILocalVariableInformation[] objRefEntries, ILocalVariableInformation[] valueEntries, DebugInformationWriteController debugInformationController) { tw.WriteLine("//-------------------"); tw.WriteLine("// [3-5] Setup execution frame:"); tw.SplitLine(); // Important NULL assigner (p = NULL): // Because these variables are pointer (of object reference 'O' type). // So GC will traverse these variables just setup the stack frame. debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "{0}_EXECUTION_FRAME__ frame__ =", preparedMethod.Method.CLanguageFunctionName); using (var __ = tw.Shift()) { if (valueEntries.Length >= 1) { tw.WriteLine( "{{ NULL, {0}, {1}, {2} }};", objRefEntries.Length, valueEntries.Length, string.Join( ", ", objRefEntries.Select(___ => "NULL"). Concat(valueEntries. Select(valueEntry => string.Format( "il2c_typeof({0}), NULL", valueEntry.TargetType.MangledUniqueName))))); } else { // Make short initializer expression if value type not included, // maybe C compiler makes better code. tw.WriteLine( "{{ NULL, {0} }};", objRefEntries.Length); } } foreach (var valueEntry in valueEntries) { debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "frame__.{0}_value_ptr__ = &{0};", extractContext.GetSymbolName(valueEntry)); } debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("il2c_link_execution_frame(&frame__);"); tw.SplitLine(); }
private static void InternalConvertExecutionFrame( CodeTextWriter tw, IExtractContextHost extractContext, PreparedMethodInformation preparedMethod, ILocalVariableInformation[] objRefEntries, ILocalVariableInformation[] valueEntries) { tw.WriteLine("//-------------------"); tw.WriteLine("// [3-7] Declare execution frame:"); tw.SplitLine(); tw.WriteLine( "typedef struct {0}_EXECUTION_FRAME_DECL", preparedMethod.Method.CLanguageFunctionName); tw.WriteLine("{"); using (var _ = tw.Shift()) { tw.WriteLine("const IL2C_EXECUTION_FRAME* pNext__;"); tw.WriteLine("const uint16_t objRefCount__;"); tw.WriteLine("const uint16_t valueCount__;"); if (objRefEntries.Length >= 1) { tw.WriteLine("//-------------------- objref"); foreach (var objRefEntry in objRefEntries) { tw.WriteLine( "{0} {1};", objRefEntry.TargetType.CLanguageTypeName, extractContext.GetSymbolName(objRefEntry)); } } if (valueEntries.Length >= 1) { tw.WriteLine("//-------------------- value type"); foreach (var valueEntry in valueEntries) { var name = extractContext.GetSymbolName(valueEntry); tw.WriteLine( "const IL2C_RUNTIME_TYPE {0}_type__;", name); tw.WriteLine( "const {0}* {1}_value_ptr__;", valueEntry.TargetType.CLanguageTypeName, name); } } } tw.WriteLine( "}} {0}_EXECUTION_FRAME__;", preparedMethod.Method.CLanguageFunctionName); tw.SplitLine(); }
public static string[] WriteSourceCodes( CodeTextStorage storage, TranslateContext translateContext, PreparedInformations prepared, DebugInformationOptions debugInformationOption) { IExtractContextHost extractContext = translateContext; var assemblyName = extractContext.Assembly.Name; var typesByDeclaring = prepared.Types. GroupBy(type => type.DeclaringType ?? type). ToDictionary( g => g.Key, g => g.OrderByDependant(translateContext.Assembly).ToArray()); var sourceFiles = new List <string>(); foreach (var targetType in prepared.Types. Where(type => type.DeclaringType == null)) { using (var _ = storage.EnterScope(targetType.ScopeName)) { using (var twSource = storage.CreateSourceCodeWriter(targetType.Name)) { twSource.WriteLine( "// [15-2] This is {0} native code translated by IL2C, do not edit.", assemblyName); twSource.SplitLine(); twSource.WriteLine( "#include <{0}.h>", assemblyName); twSource.WriteLine( "#include <{0}_internal.h>", assemblyName); twSource.SplitLine(); // Write assembly references at the file scope. InternalWriteAssemblyReferences( twSource, translateContext, extractContext, targetType); twSource.WriteLine("#ifdef __cplusplus"); twSource.WriteLine("extern \"C\" {"); twSource.WriteLine("#endif"); twSource.SplitLine(); InternalWriteSourceCode( twSource, extractContext, prepared, targetType, debugInformationOption, typesByDeclaring); twSource.WriteLine("#ifdef __cplusplus"); twSource.WriteLine("}"); twSource.WriteLine("#endif"); twSource.SplitLine(); twSource.Flush(); sourceFiles.Add(twSource.RelatedPath); } } } return(sourceFiles.ToArray()); }
private static void InternalWriteSourceCode( CodeTextWriter twSource, IExtractContextHost extractContext, PreparedInformations prepared, ITypeInformation targetType, DebugInformationOptions debugInformationOption, IReadOnlyDictionary <ITypeInformation, ITypeInformation[]> typesByDeclaring) { // TODO: invalid sequence for multiple nested types. if (typesByDeclaring.TryGetValue(targetType, out var types)) { foreach (var type in types) { // The nested types have to declare before outer types. if (!type.Equals(targetType)) { InternalWriteSourceCode( twSource, extractContext, prepared, type, debugInformationOption, typesByDeclaring); } twSource.WriteLine("//////////////////////////////////////////////////////////////////////////////////"); twSource.WriteLine( "// [9-1] Type: {0}", targetType.FriendlyName); twSource.SplitLine(); twSource.WriteLine("//////////////////////////////////////////////////////////////////////////////////"); twSource.WriteLine("// [9-2] File scope prototypes:"); twSource.SplitLine(); // Write type members. TypeWriter.WriteMemberDefinitions( twSource, type, field => field.CLanguageMemberScope == MemberScopes.File, method => (method.CLanguageMemberScope == MemberScopes.File) && prepared.Functions.ContainsKey(method)); // All static fields except enum and native value. if (!type.IsEnum) { var staticFields = type.Fields. Where(field => field.IsStatic && (field.NativeValue == null)). ToArray(); if (staticFields.Length >= 1) { twSource.WriteLine("//////////////////////////////////////////////////////////////////////////////////"); twSource.WriteLine("// [9-3] Static field handlers:"); twSource.SplitLine(); var staticFieldsName = type.MangledUniqueName + "_STATIC_FIELDS"; twSource.WriteLine( "static volatile uintptr_t {0}_initializerCount__ = 0;", staticFieldsName); twSource.SplitLine(); twSource.WriteLine( "static struct {0}_DECL__ /* IL2C_STATIC_FIELDS */", staticFieldsName); twSource.WriteLine("{"); var objrefStaticFields = staticFields. Where(field => field.FieldType.IsReferenceType). ToArray(); var valueTypeStaticFields = staticFields. Where(field => field.FieldType.IsValueType && field.FieldType.IsRequiredTraverse). ToArray(); var otherStaticFields = new HashSet <IFieldInformation>(staticFields. Except(objrefStaticFields). Except(valueTypeStaticFields)); using (var _ = twSource.Shift()) { twSource.WriteLine("IL2C_STATIC_FIELDS* pNext__;"); twSource.WriteLine("const uint16_t objRefCount__;"); twSource.WriteLine("const uint16_t valueCount__;"); if (objrefStaticFields.Length >= 1) { twSource.WriteLine("//-------------------- objref"); foreach (var field in objrefStaticFields) { twSource.WriteLine( "{0} {1};", field.FieldType.CLanguageTypeName, field.MangledName); } } if (valueTypeStaticFields.Length >= 1) { twSource.WriteLine("//-------------------- value type"); foreach (var field in valueTypeStaticFields) { twSource.WriteLine( "{0} {1};", field.FieldType.CLanguageTypeName, field.MangledName); } } } twSource.WriteLine( "}} {0}__ = {{ NULL, {1}, {2} }};", staticFieldsName, objrefStaticFields.Length, valueTypeStaticFields.Length); twSource.SplitLine(); foreach (var field in otherStaticFields) { twSource.WriteLine( "static {0} {1};", field.FieldType.CLanguageTypeName, field.MangledUniqueName); } twSource.SplitLine(); foreach (var field in staticFields) { twSource.WriteLine( "{0}* {1}_HANDLER__(void)", field.FieldType.CLanguageTypeName, field.MangledUniqueName); twSource.WriteLine("{"); using (var _ = twSource.Shift()) { // TODO: Have to guard race condition for the multi threading feature. twSource.WriteLine( "if (il2c_unlikely__({0}_initializerCount__ != *il2c_initializer_count))", staticFieldsName); twSource.WriteLine("{"); using (var __ = twSource.Shift()) { twSource.WriteLine( "{0}_initializerCount__ = *il2c_initializer_count;", staticFieldsName); twSource.WriteLine( "il2c_register_static_fields(&{0}__);", staticFieldsName); var typeInitializer = type.DeclaredMethods. FirstOrDefault(method => method.IsConstructor && method.IsStatic); if (typeInitializer != null) { twSource.WriteLine( "{0}();", typeInitializer.CLanguageFunctionFullName); } } twSource.WriteLine("}"); if (otherStaticFields.Contains(field)) { twSource.WriteLine( "return &{0};", field.MangledUniqueName); } else { twSource.WriteLine( "return &{0}__.{1};", staticFieldsName, field.MangledName); } } twSource.WriteLine("}"); twSource.SplitLine(); } } } twSource.WriteLine("//////////////////////////////////////////////////////////////////////////////////"); twSource.WriteLine("// [9-4] Type: {0}", type.FriendlyName); twSource.SplitLine(); // All methods and constructor exclude type initializer foreach (var method in type.DeclaredMethods. Where(method => prepared.Functions.ContainsKey(method))) { FunctionWriter.InternalConvertFromMethod( twSource, extractContext, prepared, method, debugInformationOption); } if (type.IsClass || type.IsValueType) { TypeHelperWriter.InternalConvertTypeHelper( twSource, type); } else if (type.IsInterface) { TypeHelperWriter.InternalConvertTypeHelperForInterface( twSource, type); } twSource.SplitLine(); } } }
public static void InternalConvertFromMethod( CodeTextWriter tw, IExtractContextHost extractContext, PreparedInformations preparedFunctions, IMethodInformation method, DebugInformationOptions debugInformationOption = DebugInformationOptions.None) { if (method.IsVirtual) { if (method.IsAbstract) { if (!method.DeclaringType.IsInterface) { InternalConvertFromAbstractFunction( tw, method); } return; } } // internalcall or DllImport if (method.IsExtern) { // DllImport var pinvokeInfo = method.PInvokeInfo; if (pinvokeInfo != null) { InternalConvertFromPInvokeFunction( tw, method, pinvokeInfo); return; } // Specialize delegate type methods: if (method.DeclaringType.IsDelegate && !method.DeclaringType.IsAbstract) { // Delegate constructor if (method.IsConstructor) { // Ignore. We have to use the "il2c_new_delegate()" instead this constructor translated body. return; } // Delegate "Invoke" if (method.Name == "Invoke") { InternalConvertFromDelegateInvoker( tw, extractContext, method); return; } } throw new InvalidProgramSequenceException( "Unknown internallcall method declaration. Name={0}", method.FriendlyName); } if (!preparedFunctions.Functions.TryGetValue(method, out var preparedMethod)) { return; } InternalConvertFromFunction( tw, extractContext, preparedMethod, debugInformationOption); }
private static void InternalConvertFromFunction( CodeTextWriter tw, IExtractContextHost extractContext, PreparedMethodInformation preparedMethod, DebugInformationOptions debugInformationOption) { var locals = preparedMethod.Method.LocalVariables; tw.SplitLine(); tw.WriteLine("///////////////////////////////////////"); tw.WriteLine( "// [3] {0}{1}", preparedMethod.Method.IsVirtual ? "Virtual: " : string.Empty, preparedMethod.Method.FriendlyName); var codeStream = preparedMethod.Method.CodeStream; // Write exception filters: if (codeStream.ExceptionHandlers.Length >= 1) { tw.SplitLine(); tw.WriteLine("//-------------------"); tw.WriteLine("// [3-1] Exception filters:"); tw.SplitLine(); for (var handlerIndex = 0; handlerIndex < codeStream.ExceptionHandlers.Length; handlerIndex++) { var handler = codeStream.ExceptionHandlers[handlerIndex]; var filterName = string.Format( "{0}_ExceptionFilter{1}__", preparedMethod.Method.CLanguageFunctionName, handlerIndex); tw.WriteLine( "static int16_t {0}(System_Exception* ex)", filterName); tw.WriteLine("{"); using (var _ = tw.Shift()) { tw.WriteLine("il2c_assert(ex != NULL);"); for (var catchHandlerIndex = 0; catchHandlerIndex < handler.CatchHandlers.Length; catchHandlerIndex++) { var catchHandler = handler.CatchHandlers[catchHandlerIndex]; if (catchHandler.CatchHandlerType == ExceptionCatchHandlerTypes.Catch) { tw.WriteLine( "if (il2c_isinst__(ex, il2c_typeof({0}))) return {1};", catchHandler.CatchType.MangledName, catchHandlerIndex + 1); } } // Write finally block index if contains. var finallyHandler = handler.CatchHandlers. Select((catchHandler, index) => new { catchHandler, index }). FirstOrDefault(entry => entry.catchHandler.CatchHandlerType == ExceptionCatchHandlerTypes.Finally); if (finallyHandler != null) { tw.WriteLine("return IL2C_FILTER_FINALLY; // Not matched (will go to finally)"); } else { tw.WriteLine("return IL2C_FILTER_NOMATCH; // Not matched"); } } tw.WriteLine("}"); } } // Start function: tw.SplitLine(); tw.WriteLine("//-------------------"); tw.WriteLine("// [3-2] Function body:"); tw.SplitLine(); tw.WriteLine(preparedMethod.Method.CLanguageFunctionPrototype); tw.WriteLine("{"); using (var _ = tw.Shift()) { if (!preparedMethod.Method.IsStatic) { tw.WriteLine("il2c_assert(this__ != NULL);"); tw.SplitLine(); } var localDefinitions = preparedMethod.Method.LocalVariables. Where(local => !local.TargetType.IsReferenceType). ToArray(); if (localDefinitions.Length >= 1) { tw.WriteLine("//-------------------"); tw.WriteLine("// [3-3] Local variables (not objref):"); tw.SplitLine(); foreach (var local in localDefinitions) { // HACK: The local variables mark to "volatile." // Because the gcc misread these variables calculated statically (or maybe assigned to the registers) // at compile time with optimization. // It will cause the strange results for exception handling (with sjlj.) tw.WriteLine( "{0}{1} {2};", (codeStream.ExceptionHandlers.Length >= 1) ? "volatile " : string.Empty, local.TargetType.CLanguageTypeName, extractContext.GetSymbolName(local)); } tw.SplitLine(); } var stackDefinitions = preparedMethod.Stacks. Where(stack => !stack.TargetType.IsReferenceType). ToArray(); if (stackDefinitions.Length >= 1) { tw.WriteLine("//-------------------"); tw.WriteLine("// [3-4] Evaluation stacks (not objref):"); tw.SplitLine(); foreach (var stack in stackDefinitions) { tw.WriteLine( "{0} {1};", stack.TargetType.CLanguageTypeName, extractContext.GetSymbolName(stack)); } tw.SplitLine(); } var objRefEntries = locals. Concat(preparedMethod.Stacks). Where(v => v.TargetType.IsReferenceType). // Only objref ToArray(); if (objRefEntries.Length >= 1) { tw.WriteLine("//-------------------"); tw.WriteLine("// [3-5] Setup execution frame:"); tw.SplitLine(); tw.WriteLine( "struct {0}_EXECUTION_FRAME__", preparedMethod.Method.CLanguageFunctionName); tw.WriteLine("{"); using (var __ = tw.Shift()) { tw.WriteLine("uint8_t objRefCount__;"); tw.WriteLine("uint8_t objRefRefCount__;"); tw.WriteLine("IL2C_EXECUTION_FRAME* pNext__;"); foreach (var objRefEntry in objRefEntries) { tw.WriteLine( "{0} {1};", objRefEntry.TargetType.CLanguageTypeName, extractContext.GetSymbolName(objRefEntry)); } } // Important NULL assigner (p = NULL): // Because these variables are pointer (of object reference 'O' type). // So GC will traverse these variables just setup the stack frame. // TODO: https://github.com/kekyo/IL2C/issues/12 tw.WriteLine("}} frame__ = {{ {0}, 0 }};", objRefEntries.Length); tw.WriteLine("il2c_link_execution_frame(&frame__);"); tw.SplitLine(); } tw.WriteLine("//-------------------"); tw.WriteLine("// [3-6] IL body:"); tw.SplitLine(); // Set symbol prefix to make valid access variables. using (var __ = extractContext.BeginLocalVariablePrefix( local => local.TargetType.IsReferenceType ? "frame__." : null)) { // Construct exception handler controller. var exceptionHandlerController = new ExceptionHandlerController( codeStream.ExceptionHandlers, (handler, handlerIndex, nestedIndex) => { var nestedIndexName = string.Format("nest{0}", nestedIndex); extractContext.SetNestedExceptionFrameIndexName(nestedIndexName); // Reached try block: var filterName = string.Format( "{0}_ExceptionFilter{1}__", preparedMethod.Method.CLanguageFunctionName, handlerIndex); tw.WriteLine("il2c_try({0}, {1})", nestedIndexName, filterName); tw.WriteLine("{"); tw.Shift(); }, (handler, handlerIndex, nestedIndex) => { // Reached try end block: tw.Shift(-1); tw.WriteLine("}"); }, (handler, handlerIndex, nestedIndex, catchHandler, catchHandlerIndex) => { var nestedIndexName = extractContext.GetExceptionNestedFrameIndexName(); switch (catchHandler.CatchHandlerType) { case ExceptionCatchHandlerTypes.Catch: // Reached catch block: tw.WriteLine( "il2c_catch({0}, {1}, {2}) // catch ({3})", nestedIndexName, catchHandlerIndex + 1, extractContext.GetSymbolName(preparedMethod.CatchVariables[catchHandler.CatchStart]), catchHandler.CatchType.MangledName); break; case ExceptionCatchHandlerTypes.Finally: // Reached finally block: tw.WriteLine("il2c_finally({0})", nestedIndexName); break; } tw.WriteLine("{"); tw.Shift(); }, (handler, handlerIndex, nestedIndex, catchHandler, catchHandlerIndex) => { // Reached catch end block: tw.Shift(-1); tw.WriteLine("}"); }, (handler, handlerIndex, nestedIndex, parentHandler, parentHandlerIndex, parentNestedIndex) => { var nestedIndexName = extractContext.GetExceptionNestedFrameIndexName(); var parentNestedIndexName = (parentNestedIndex >= 0) ? string.Format("nest{0}", parentNestedIndex) : null; // Write leave bind expressions if needed. // Extract the continuation fromOffset inside at mostly inner exception handler. var bindEntries = preparedMethod.LeaveContinuations. SelectMany(entry => codeStream.ExceptionHandlers. // nested exception handlers: inner --> outer Reverse(). // Is this handler contains leave continuation target? Where(h => entry.Value.fromOffsets.Any(offset => h.ContainsOffset(offset))). // Found. Select(h => new { handler = h, continuationIndex = entry.Key, entry.Value.targetOffset })). // ... is current handler? Where(entry => entry.handler.Equals(handler)). ToArray(); if (bindEntries.Length >= 1) { tw.WriteLine("il2c_leave_to({0})", nestedIndexName); tw.WriteLine("{"); using (var ___ = tw.Shift()) { foreach (var bind in bindEntries) { if ((parentNestedIndex < 0) || codeStream.ExceptionHandlers[parentNestedIndex].ContainsOffset(bind.targetOffset)) { var labelName = preparedMethod.LabelNames[bind.targetOffset]; tw.WriteLine( "il2c_leave_bind({0}, {1}, {2});", nestedIndexName, bind.continuationIndex, labelName); } else { tw.WriteLine( "il2c_leave_through({0}, {1}, {2});", nestedIndexName, bind.continuationIndex, parentNestedIndexName); } } } tw.WriteLine("}"); } // Reached end of entire try block. tw.WriteLine("il2c_end_try({0});", nestedIndexName); extractContext.SetNestedExceptionFrameIndexName(parentNestedIndexName); }); // Traverse code fragments. var canWriteSequencePoint = true; foreach (var ci in codeStream) { // 1: Update the exception handler controller. // (Will write exception related sentences.) exceptionHandlerController.Update(ci); // 2: Write label if available and used. if (preparedMethod.LabelNames.TryGetValue(ci.Offset, out var labelName)) { using (var ___ = tw.Shift(-1)) { tw.WriteLine("{0}:", labelName); } } // 3: Write the line preprocessor directive if available. if (canWriteSequencePoint && ci.Debug.Any()) { var sp = ci.Debug.First(); switch (debugInformationOption) { case DebugInformationOptions.Full: tw.Parent.WriteLine( "#line {0} \"{1}\"", sp.Line, sp.Path.Replace("\\", "\\\\")); break; case DebugInformationOptions.CommentOnly: tw.Parent.WriteLine( "/* {0}({1}): */", sp.Path.Replace("\\", "\\\\"), sp.Line); break; } canWriteSequencePoint = false; } // 4: Generate source code fragments and write. if (debugInformationOption != DebugInformationOptions.None) { // Write debugging information. tw.WriteLine( "/* {0} */", ci); } var sourceCodes = preparedMethod.Generators[ci.Offset](extractContext); foreach (var sourceCode in sourceCodes) { // Dirty hack: // Write unlink execution frame code if cause exiting method. if (sourceCode.StartsWith("return") && (objRefEntries.Length >= 1)) { tw.WriteLine( "il2c_unlink_execution_frame(&frame__);"); } tw.WriteLine( "{0};", sourceCode); canWriteSequencePoint = true; } } if (!exceptionHandlerController.IsFinished) { throw new InvalidProgramSequenceException( "Invalid exception handler range. MethodName={0}, ExceptionHandlers=[{1}]", preparedMethod.Method.FriendlyName, string.Join( ",", codeStream.ExceptionHandlers. Select(handler => string.Format("[{0}]", handler)))); } } } tw.WriteLine("}"); tw.SplitLine(); }
internal static void InternalWriteSourceCode( CodeTextWriter twSource, TranslateContext translateContext, PreparedInformations prepared, DebugInformationOptions debugInformationOption, bool includeAssemblyHeader) { IExtractContextHost extractContext = translateContext; if (includeAssemblyHeader) { foreach (var fileName in extractContext.EnumerateRequiredPrivateIncludeFileNames()) { twSource.WriteLine("#include \"{0}\"", fileName); } twSource.WriteLine("#include \"{0}.h\"", extractContext.Assembly.Name); twSource.SplitLine(); } WriteConstStrings(twSource, translateContext); WriteDeclaredValues(twSource, translateContext); twSource.WriteLine("//////////////////////////////////////////////////////////////////////////////////"); twSource.WriteLine("// [9-2] File scope prototypes:"); twSource.SplitLine(); // All types exclude publics and internals (for file scope prototypes) PrototypeWriter.InternalConvertToPrototypes( twSource, prepared.Types, type => !type.IsCLanguagePublicScope, field => !(field.IsPublic || field.IsFamily || field.IsFamilyOrAssembly), method => (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly) && prepared.Functions.ContainsKey(method)); twSource.WriteLine("//////////////////////////////////////////////////////////////////////////////////"); twSource.WriteLine("// [9-3] Declare static fields:"); twSource.SplitLine(); foreach (var type in prepared.Types. Where(type => !type.IsEnum)) { // All static fields foreach (var field in type.Fields. Where(field => field.IsStatic)) { twSource.WriteLine( "{0};", field.GetCLanguageStaticPrototype(true)); } twSource.SplitLine(); } foreach (var type in prepared.Types) { twSource.WriteLine("//////////////////////////////////////////////////////////////////////////////////"); twSource.WriteLine("// [9-4] Type: {0}", type.FriendlyName); twSource.SplitLine(); // All methods and constructor exclude type initializer foreach (var method in type.DeclaredMethods) { FunctionWriter.InternalConvertFromMethod( twSource, extractContext, prepared, method, debugInformationOption); } if (type.IsClass || type.IsValueType) { TypeHelperWriter.InternalConvertTypeHelper( twSource, type); } else if (type.IsInterface) { TypeHelperWriter.InternalConvertTypeHelperForInterface( twSource, type); } } twSource.SplitLine(); }
public static string[] WriteSourceCodes( CodeTextStorage storage, TranslateContext translateContext, PreparedInformations prepared, DebugInformationOptions debugInformationOption) { IExtractContextHost extractContext = translateContext; var assemblyName = extractContext.Assembly.Name; var typesByDeclaring = prepared.Types. GroupBy(type => type.DeclaringType ?? type). ToDictionary( g => g.Key, g => g.OrderByDependant(translateContext.Assembly).ToArray()); var sourceFiles = new List <string>(); foreach (var targetType in prepared.Types. Where(type => type.DeclaringType == null)) { using (var _ = storage.EnterScope(targetType.ScopeName)) { using (var twSource = storage.CreateSourceCodeWriter(targetType.Name)) { // HACK: Unreal Engine 4 needs include directive with same file name as header extension (ex: foo.c --> foo.h) at first line. if (extractContext.TargetPlatform == TargetPlatforms.UE4) { twSource.WriteLine( "#include \"{0}.h\" // [16-1] Needs for Unreal Engine 4.", targetType.Name); twSource.SplitLine(); } twSource.WriteLine( "// [15-2] This is {0} native code translated by IL2C, do not edit.", assemblyName); twSource.SplitLine(); twSource.WriteLine( "#include <{0}.h>", assemblyName); twSource.WriteLine( "#include <{0}_internal.h>", assemblyName); twSource.SplitLine(); // Write assembly references at the file scope. InternalWriteAssemblyReferences( twSource, translateContext, extractContext, targetType); twSource.WriteLine("#ifdef __cplusplus"); twSource.WriteLine("extern \"C\" {"); twSource.WriteLine("#endif"); twSource.SplitLine(); InternalWriteSourceCode( twSource, extractContext, prepared, targetType, debugInformationOption, typesByDeclaring); twSource.WriteLine("#ifdef __cplusplus"); twSource.WriteLine("}"); twSource.WriteLine("#endif"); twSource.SplitLine(); twSource.Flush(); sourceFiles.Add(twSource.RelatedPath); } // HACK: Unreal Engine 4 needs include directive with same file name as header extension (ex: foo.c --> foo.h) at first line. if (extractContext.TargetPlatform == TargetPlatforms.UE4) { using (var twUE4Header = storage.CreateHeaderWriter(targetType.Name)) { twUE4Header.WriteLine( "// [16-2] This is {0} native code translated by IL2C, do not edit.", assemblyName); twUE4Header.WriteLine( "// It's a dummy header file for helping and using only Unreal Engine 4.", assemblyName); twUE4Header.Flush(); } } } } return(sourceFiles.ToArray()); }
public static void InternalConvertFromMethod( CodeTextWriter tw, IExtractContextHost extractContext, PreparedInformations preparedFunctions, IMethodInformation method, DebugInformationOptions debugInformationOption = DebugInformationOptions.None) { if (method.IsVirtual) { if (method.IsAbstract) { if (!method.DeclaringType.IsInterface) { InternalConvertFromAbstractFunction( tw, method); } return; } } // internalcall or DllImport if (method.IsExtern) { // Specialize delegate type methods: if (method.DeclaringType.IsDelegate && !method.DeclaringType.IsAbstract) { // Delegate constructor if (method.IsConstructor) { // Ignore. We have to use the "il2c_new_delegate()" instead this constructor translated body. return; } // Delegate "Invoke" if (method.Name == "Invoke") { InternalConvertFromDelegateInvoker( tw, extractContext, method); return; } } // InternalCall or DllImport InternalConvertFromInternalCallFunction( tw, method); return; } if (!preparedFunctions.Functions.TryGetValue(method, out var preparedMethod)) { return; } InternalConvertFromFunction( tw, extractContext, preparedMethod, debugInformationOption); }
private static void InternalConvertFromFunction( CodeTextWriter tw, IExtractContextHost extractContext, PreparedMethodInformation preparedMethod, DebugInformationOptions debugInformationOption) { var locals = preparedMethod.Method.LocalVariables; tw.WriteLine("///////////////////////////////////////"); tw.WriteLine( "// [3] {0}{1}", preparedMethod.Method.IsVirtual ? "Virtual: " : string.Empty, preparedMethod.Method.FriendlyName); tw.SplitLine(); var codeStream = preparedMethod.Method.CodeStream; var objRefEntries = locals. Concat(preparedMethod.Stacks). Where(v => v.TargetType.IsReferenceType). // Only objref ToArray(); var valueEntries = locals. Concat(preparedMethod.Stacks). Where(v => v.TargetType.IsValueType && v.TargetType.IsRequiredTraverse). ToArray(); // Write declaring exception handlers if (codeStream.ExceptionHandlers.Length >= 1) { InternalConvertExceptionFilter( tw, extractContext, preparedMethod, codeStream); } // Write declaring execution frame if ((objRefEntries.Length >= 1) || (valueEntries.Length >= 1)) { InternalConvertExecutionFrame( tw, extractContext, preparedMethod, objRefEntries, valueEntries); } // Make readable debugging comment, it was splitting and reducing same informations. var debugInformationController = new DebugInformationWriteController( preparedMethod.Method, debugInformationOption); // Start function: tw.WriteLine("//-------------------"); tw.WriteLine("// [3-2] Function body:"); tw.SplitLine(); tw.WriteLine(preparedMethod.Method.CLanguageFunctionPrototype); tw.WriteLine("{"); using (var _ = tw.Shift()) { if (!preparedMethod.Method.IsStatic) { debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("il2c_assert(this__ != NULL);"); tw.SplitLine(); } var localDefinitions = preparedMethod.Method.LocalVariables. Where(local => !local.TargetType.IsReferenceType). ToArray(); if (localDefinitions.Length >= 1) { tw.WriteLine("//-------------------"); tw.WriteLine("// [3-3] Local variables (!objref):"); tw.SplitLine(); foreach (var local in localDefinitions) { var name = extractContext.GetSymbolName(local); // HACK: The local variables mark to "volatile." // Because the gcc misread these variables calculated statically (or maybe assigned to the registers) // at compile time with optimization. // It will cause the strange results for exception handling (with sjlj.) // We have to initialize the local variables. if (local.TargetType.IsPrimitive || local.TargetType.IsPointer || local.TargetType.IsByReference) { debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "{0}{1} {2} = {3};", (codeStream.ExceptionHandlers.Length >= 1) ? "volatile " : string.Empty, local.TargetType.CLanguageTypeName, name, Utilities.GetCLanguageExpression(local.TargetType.InternalStaticEmptyValue)); } else { Debug.Assert(local.TargetType.IsValueType); debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "{0}{1} {2};", (codeStream.ExceptionHandlers.Length >= 1) ? "volatile " : string.Empty, local.TargetType.CLanguageTypeName, name); debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "memset({0}&{1}, 0x00, sizeof {1});", (codeStream.ExceptionHandlers.Length >= 1) ? "(void*)" : string.Empty, name); } } tw.SplitLine(); } var stackDefinitions = preparedMethod.Stacks. Where(stack => !stack.TargetType.IsReferenceType). ToArray(); if (stackDefinitions.Length >= 1) { tw.WriteLine("//-------------------"); tw.WriteLine("// [3-4] Evaluation stacks (!objref):"); tw.SplitLine(); foreach (var stack in stackDefinitions) { var name = extractContext.GetSymbolName(stack); debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "{0} {1};", stack.TargetType.CLanguageTypeName, name); // Note: We often don't have to initalize the evaluation stack variables. // Because these variables push value at first usage. // But the value type may contains objref field, // so we have to initialize for value type. if (stack.TargetType.IsRequiredTraverse) { debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "memset(&{0}, 0, sizeof {0});", name); } } tw.SplitLine(); } // Write doing setup execution frame if ((objRefEntries.Length >= 1) || (valueEntries.Length >= 1)) { InternalConvertSetupExecutionFrame( tw, extractContext, preparedMethod, objRefEntries, valueEntries, debugInformationController); } tw.WriteLine("//-------------------"); tw.WriteLine("// [3-6] IL body:"); tw.SplitLine(); // Set symbol prefix to make valid access variables. using (var __ = extractContext.BeginLocalVariablePrefix( local => local.TargetType.IsReferenceType ? "frame__." : null)) { // Construct exception handler controller. var exceptionHandlerController = new ExceptionHandlerController( codeStream.ExceptionHandlers, (handler, handlerIndex, nestedIndex) => { var nestedIndexName = string.Format("nest{0}", nestedIndex); extractContext.SetNestedExceptionFrameIndexName(nestedIndexName); // Reached try block: var filterName = string.Format( "{0}_ExceptionFilter{1}__", preparedMethod.Method.CLanguageFunctionName, handlerIndex); debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("il2c_try({0}, {1})", nestedIndexName, filterName); debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("{"); tw.Shift(); }, (handler, handlerIndex, nestedIndex) => { // Reached try end block: debugInformationController.WriteInformationBeforeCode(tw); tw.Shift(-1); tw.WriteLine("}"); }, (handler, handlerIndex, nestedIndex, catchHandler, catchHandlerIndex) => { var nestedIndexName = extractContext.GetExceptionNestedFrameIndexName(); switch (catchHandler.CatchHandlerType) { case ExceptionCatchHandlerTypes.Catch: // Reached catch block: debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "il2c_catch({0}, {1}, {2}) // catch ({3})", nestedIndexName, catchHandlerIndex + 1, extractContext.GetSymbolName(preparedMethod.CatchVariables[catchHandler.CatchStart]), catchHandler.CatchType.MangledUniqueName); break; case ExceptionCatchHandlerTypes.Finally: // Reached finally block: debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("il2c_finally({0})", nestedIndexName); break; } debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("{"); tw.Shift(); }, (handler, handlerIndex, nestedIndex, catchHandler, catchHandlerIndex) => { // Reached catch end block: debugInformationController.WriteInformationBeforeCode(tw); tw.Shift(-1); tw.WriteLine("}"); }, (handler, handlerIndex, nestedIndex, parentHandler, parentHandlerIndex, parentNestedIndex) => { var nestedIndexName = extractContext.GetExceptionNestedFrameIndexName(); var parentNestedIndexName = (parentNestedIndex >= 0) ? string.Format("nest{0}", parentNestedIndex) : null; // Write leave bind expressions if needed. // Extract the continuation fromOffset inside at mostly inner exception handler. var bindEntries = preparedMethod.LeaveContinuations. SelectMany(entry => codeStream.ExceptionHandlers. // nested exception handlers: inner --> outer Reverse(). // Is this handler contains leave continuation target? Where(h => entry.Value.fromOffsets.Any(offset => h.ContainsOffset(offset))). // Found. Select(h => new { handler = h, continuationIndex = entry.Key, entry.Value.targetOffset })). // ... is current handler? Where(entry => entry.handler.Equals(handler)). ToArray(); if (bindEntries.Length >= 1) { debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("il2c_leave_to({0})", nestedIndexName); debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("{"); using (var ___ = tw.Shift()) { foreach (var bind in bindEntries) { if ((parentNestedIndex < 0) || codeStream.ExceptionHandlers[parentNestedIndex].ContainsOffset(bind.targetOffset)) { var labelName = preparedMethod.LabelNames[bind.targetOffset]; debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "il2c_leave_bind({0}, {1}, {2});", nestedIndexName, bind.continuationIndex, labelName); } else { debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "il2c_leave_through({0}, {1}, {2});", nestedIndexName, bind.continuationIndex, parentNestedIndexName); } } } debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("}"); } // Reached end of entire try block. debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("il2c_end_try({0});", nestedIndexName); extractContext.SetNestedExceptionFrameIndexName(parentNestedIndexName); }); // Traverse code fragments. foreach (var ci in codeStream) { debugInformationController.SetNextCode(ci); // 1: Update the exception handler controller. // (Will write exception related sentences.) exceptionHandlerController.Update(ci); // 2: Write label if available and used. if (preparedMethod.LabelNames.TryGetValue(ci.Offset, out var labelName)) { using (var ___ = tw.Shift(-1)) { tw.WriteLine("{0}:", labelName); } } // 3: Write source code comment. debugInformationController.WriteCodeComment(tw); // 4: Generate source code fragments and write. var sourceCodes = preparedMethod.Generators[ci.Offset](extractContext); foreach (var sourceCode in sourceCodes) { // DIRTY HACK: // Write unlink execution frame code if cause exiting method. if (sourceCode.StartsWith("return") && ((objRefEntries.Length >= 1) || (valueEntries.Length >= 1))) { debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "il2c_unlink_execution_frame(&frame__);"); } debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine( "{0};", sourceCode); } } // If last opcode is 'endfinally' and not emitted 'ret', // can't finished for exceptionHandlerController. // We can check and force update the TryFinish method for this situation. exceptionHandlerController.TryFinish(); if (!exceptionHandlerController.IsFinished) { throw new InvalidProgramSequenceException( "Invalid exception handler range. MethodName={0}, ExceptionHandlers=[{1}]", preparedMethod.Method.FriendlyName, string.Join( ",", codeStream.ExceptionHandlers. Select(handler => string.Format("[{0}]", handler)))); } } } debugInformationController.WriteInformationBeforeCode(tw); tw.WriteLine("}"); tw.SplitLine(); }
private static void InternalConvertExceptionFilter( CodeTextWriter tw, IExtractContextHost extractContext, PreparedMethodInformation preparedMethod, ICodeStream codeStream) { tw.WriteLine("//-------------------"); tw.WriteLine("// [3-1] Exception filters:"); tw.SplitLine(); for (var handlerIndex = 0; handlerIndex < codeStream.ExceptionHandlers.Length; handlerIndex++) { var handler = codeStream.ExceptionHandlers[handlerIndex]; var filterName = string.Format( "{0}_ExceptionFilter{1}__", preparedMethod.Method.CLanguageFunctionName, handlerIndex); tw.WriteLine( "static int16_t {0}(System_Exception* ex)", filterName); tw.WriteLine("{"); using (var _ = tw.Shift()) { tw.WriteLine("il2c_assert(ex != NULL);"); for (var catchHandlerIndex = 0; catchHandlerIndex < handler.CatchHandlers.Length; catchHandlerIndex++) { var catchHandler = handler.CatchHandlers[catchHandlerIndex]; if (catchHandler.CatchHandlerType == ExceptionCatchHandlerTypes.Catch) { tw.WriteLine( "if (il2c_unlikely__(il2c_isinst__(ex, il2c_typeof({0})))) return {1};", catchHandler.CatchType.MangledUniqueName, catchHandlerIndex + 1); } } // Write finally block index if contains. var finallyHandler = handler.CatchHandlers. Select((catchHandler, index) => new { catchHandler, index }). FirstOrDefault(entry => entry.catchHandler.CatchHandlerType == ExceptionCatchHandlerTypes.Finally); if (finallyHandler != null) { tw.WriteLine("return IL2C_FILTER_FINALLY; // Not matched (will go to finally)"); } else { tw.WriteLine("return IL2C_FILTER_NOMATCH; // Not matched"); } } tw.WriteLine("}"); tw.SplitLine(); } }