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 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(); }