解决类似C#中的CodeDOM造成CSC.EXE堆栈溢出(CS1647)? [英] Work-around for C# CodeDom causing stack-overflow (CS1647) in csc.exe?

查看:557
本文介绍了解决类似C#中的CodeDOM造成CSC.EXE堆栈溢出(CS1647)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个情况我需要生成一个大的字符串常量的类。我的控制之外的代码导致我产生的CodeDOM树被发射到C#源且随后编译为一个较大大会的一部分。



不幸的是,我碰到一个情况,即如果该字符串的长度超过Win2K8 64 335440字符(926240中WIN2K3 86),C#编译器退出时一个致命的错误:




致命错误CS1647:表达式过长或复杂的编译近'诠释'




MSDN说CS1647是的编译器堆栈溢出(没有双关语意!)。仔细看我已经确定的CodeDOM很好,在80 chars.This包裹我的字符串常量会导致编译器来连接超过4193块的字符串,这显然是在64 NETFX C#编译器的堆栈深度。 。CSC.EXE必须在内部递归计算该表达式为水化我的单串



我最初的问题是:没有人知道一个变通更改代码生成器是怎样发出字符串?我无法控制的外部系统采用C#源作为中间的事实,我想这是一个常量(而不是字符串的运行时间串联)。



另外,我怎么能制定这个表达式使得一定数量的字符后,我仍然能够创造一个恒定的,但它是由多个中的的块



全部摄制是在这里:

  //这个字符串打破CSC:335440是Win2K8 64最大,926240是WIN2K3 86最大
串HugeString =新的String(X,926300);

CodeDomProvider供应商= CodeDomProvider.CreateProvider(C#);
CodeCompileUnit码=新CodeCompileUnit();

//命名空间的Foo {}
NS的CodeNamespace =新的CodeNamespace(富);
code.Namespaces.Add(NS);

//公共类酒吧{}
CodeTypeDeclaration类型=新CodeTypeDeclaration();
type.IsClass = TRUE;
type.Name =酒吧;
type.Attributes = MemberAttributes.Public;
ns.Types.Add(类型);

//公共常量字符串HugeString =XXXX ......;

CodeMemberField场=新CodeMemberField();
field.Name =HugeString;
field.Type =新CodeTypeReference(typeof运算(字符串));
field.Attributes = MemberAttributes.Public | MemberAttributes.Const;
field.InitExpression =新CodePrimitiveExpression(HugeString);
type.Members.Add(场);

//使用(TextWriter的作家= File.CreateText(FooBar.cs))
{
provider.GenerateCodeFromCompileUnit(代码生成作家类文件
,新CodeGeneratorOptions());
}

//编译的类文件
CompilerResults结果= provider.CompileAssemblyFromFile(新CompilerParameters(),FooBar.cs);

//输出reults
的foreach(在results.Output字符串味精)
{
Console.WriteLine(MSG);
}

//输出错误
的foreach(在results.Errors CompilerError错误)
{
Console.WriteLine(错误);
}


解决方案

使用CodeSnippetExpression和手动带引号的字符串,我是能够发出,我本来希望从Microsoft.CSharp.CSharpCodeGenerator看到的来源。



因此,要回答上面的问题,替换该行

  field.InitExpression =新CodePrimitiveExpression(HugeString); 



与此:

  field.InitExpression =新CodeSnippetExpression(QuoteSnippetStringCStyle(HugeString)); 

和最后修改私有字符串引用Microsoft.CSharp.CSharpCodeGenerator.QuoteSnippetStringCStyle方法不是80后的字符包

 私有静态字符串QuoteSnippetStringCStyle(字符串值)
{
// CS1647:表达式太长或复杂的编译附近'...'
//发生,如果行数包太多(335440最大是针对x64,926240是最大为86)

// CS1034 :编译超过极限:行不能超过16777214个字符
//理论上的每一个字符可以被转义的unicode(6个字符),加引号等

const int的LineWrapWidth =(6分之16777214) - 4;
StringBuilder的B =新的StringBuilder(value.Length + 5);

在b.append(\r\\\
\);
的for(int i = 0; I< value.Length;我++)
$ { b $ b开关(值[I])
{
案\\\
':
案\\\
':
{
INT CH =( INT)值[I];
在b.append(@\u);
在b.append(ch.ToString(X4,CultureInfo.InvariantCulture));
断点;
}
案\\':
{
在b.append(@\\);
中断;
}
案'\'':
{
在b.append(@\');
中断;
}
案 \t':
{
在b.append(@\t);
中断;
}
案\\\
':
{
在b.append(@\\\
);
中断;
}
案\r':
{
在b.append(@\r);
中断;
}
案':
{
在b.append(\\ \);
中断;
}
案\0':
{
在b.append(@\0);
中断;
}
默认:
{
在b.append(值[I]);
中断;
}
}

如果((ⅰ大于0)及及((ⅰ%LineWrapWidth)== 0))
{
如果((Char.IsHighSurrogate(值[I])及及(我≤(value.Length - 1)))及&放大器; Char.IsLowSurrogate(值[I + 1]))
{
在b.append(值[++ i]);
}
在b.append(\+ \r\\\
);
在b.append('');
}
}
在b.append(\);
返回b.ToString();
}


I've got a situation where I need to generate a class with a large string const. Code outside of my control causes my generated CodeDom tree to be emitted to C# source and then later compiled as part of a larger Assembly.

Unfortunately, I've run into a situation whereby if the length of this string exceeds 335440 chars in Win2K8 x64 (926240 in Win2K3 x86), the C# compiler exits with a fatal error:

fatal error CS1647: An expression is too long or complex to compile near 'int'

MSDN says CS1647 is "a stack overflow in the compiler" (no pun intended!). Looking more closely I've determined that the CodeDom "nicely" wraps my string const at 80 chars.This causes the compiler to concatenate over 4193 string chunks which apparently is the stack depth of the C# compiler in x64 NetFx. CSC.exe must internally recursively evaluate this expression to "rehydrate" my single string.

My initial question is this: "does anyone know of a work-around to change how the code generator emits strings?" I cannot control the fact that the external system uses C# source as an intermediate and I want this to be a constant (rather than a runtime concatenation of strings).

Alternatively, how can I formulate this expression such that after a certain number of chars, I am still able to create a constant but it is composed of multiple large chunks?

Full repro is here:

// this string breaks CSC: 335440 is Win2K8 x64 max, 926240 is Win2K3 x86 max
string HugeString = new String('X', 926300);

CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
CodeCompileUnit code = new CodeCompileUnit();

// namespace Foo {}
CodeNamespace ns = new CodeNamespace("Foo");
code.Namespaces.Add(ns);

// public class Bar {}
CodeTypeDeclaration type = new CodeTypeDeclaration();
type.IsClass = true;
type.Name = "Bar";
type.Attributes = MemberAttributes.Public;
ns.Types.Add(type);

// public const string HugeString = "XXXX...";

CodeMemberField field = new CodeMemberField();
field.Name = "HugeString";
field.Type = new CodeTypeReference(typeof(String));
field.Attributes = MemberAttributes.Public|MemberAttributes.Const;
field.InitExpression = new CodePrimitiveExpression(HugeString);
type.Members.Add(field);

// generate class file
using (TextWriter writer = File.CreateText("FooBar.cs"))
{
    provider.GenerateCodeFromCompileUnit(code, writer, new CodeGeneratorOptions());
}

// compile class file
CompilerResults results = provider.CompileAssemblyFromFile(new CompilerParameters(), "FooBar.cs");

// output reults
foreach (string msg in results.Output)
{
    Console.WriteLine(msg);
}

// output errors
foreach (CompilerError error in results.Errors)
{
    Console.WriteLine(error);
}

解决方案

Using a CodeSnippetExpression and a manually quoted string, I was able to emit the source that I would have liked to have seen from Microsoft.CSharp.CSharpCodeGenerator.

So to answer the question above, replace this line:

field.InitExpression = new CodePrimitiveExpression(HugeString);

with this:

field.InitExpression = new CodeSnippetExpression(QuoteSnippetStringCStyle(HugeString));

And finally modify the private string quoting Microsoft.CSharp.CSharpCodeGenerator.QuoteSnippetStringCStyle method to not wrap after 80 chars:

private static string QuoteSnippetStringCStyle(string value)
{
    // CS1647: An expression is too long or complex to compile near '...'
    // happens if number of line wraps is too many (335440 is max for x64, 926240 is max for x86)

    // CS1034: Compiler limit exceeded: Line cannot exceed 16777214 characters
    // theoretically every character could be escaped unicode (6 chars), plus quotes, etc.

    const int LineWrapWidth = (16777214/6) - 4;
    StringBuilder b = new StringBuilder(value.Length+5);

    b.Append("\r\n\"");
    for (int i=0; i<value.Length; i++)
    {
    	switch (value[i])
    	{
    		case '\u2028':
    		case '\u2029':
    		{
    			int ch = (int)value[i];
    			b.Append(@"\u");
    			b.Append(ch.ToString("X4", CultureInfo.InvariantCulture));
    			break;
    		}
    		case '\\':
    		{
    			b.Append(@"\\");
    			break;
    		}
    		case '\'':
    		{
    			b.Append(@"\'");
    			break;
    		}
    		case '\t':
    		{
    			b.Append(@"\t");
    			break;
    		}
    		case '\n':
    		{
    			b.Append(@"\n");
    			break;
    		}
    		case '\r':
    		{
    			b.Append(@"\r");
    			break;
    		}
    		case '"':
    		{
    			b.Append("\\\"");
    			break;
    		}
    		case '\0':
    		{
    			b.Append(@"\0");
    			break;
    		}
    		default:
    		{
    			b.Append(value[i]);
    			break;
    		}
    	}

    	if ((i > 0) && ((i % LineWrapWidth) == 0))
    	{
    		if ((Char.IsHighSurrogate(value[i]) && (i < (value.Length - 1))) && Char.IsLowSurrogate(value[i + 1]))
    		{
    			b.Append(value[++i]);
    		}
    		b.Append("\"+\r\n");
    		b.Append('"');
    	}
    }
    b.Append("\"");
    return b.ToString();
}

这篇关于解决类似C#中的CodeDOM造成CSC.EXE堆栈溢出(CS1647)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆