작업에 대한 해 C#CodeDom 일으키는 스택-오버플로우(CS1647)에 csc.exe?
-
12-09-2019 - |
문제
나는 상황이 필요를 생성하는 등 큰 문자열 const.코드의 원인은 내 생성 CodeDom 나무를 수출하는 C#소스 그리고 나중에 컴파일된 부분으로 큰 어셈블리입니다.
불행하게도,내가 실행하는 상황으로 그것에 의하여하는 경우 이 문자열의 길이를 초과하 335440 문서 Win2K8 64(926240 에 Win2K3x86),C#컴파일러는 종료와 함께 치명적인 오류가:
MSDN 말 CS1647 은"스택에서 오버플로우 컴파일러"(웃기려는 의도는 없습니다!).더 자세히 보면 내가 결정하는 CodeDom"잘"랩 내 문자열 const80 습니다.이 컴파일러 연결을 통해 4193 문자열 덩어리를 분명히는 스 깊이의 C#컴파일러에서 64NetFx.CSC.exe 해 내부적으로 재귀적으로 평가하는 이 표현을"수분은"내 하나의 문자열입니다.
내 초기 질문은 이것입니다:"가 누구나 알고 작품의 주위를 변경하는 코드는 어떻게 발전기 방출한 문자열?"내가 통제할 수 없는 사실을 외부 시스템을 사용하는 C#소스로는 중간고 나는 이 일(보다는 런타임 문자열을 연결).
또는 할 수 있는 방법을 공식화하는 이 표현한 후 그의 특정 번호 문자,나는 아직도를 만들 수 있지만 지속적인으로 구성되어 여러 크 청?
전체 재현은 여기:
// 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);
}
해결책
CodesNipTexpression과 수동으로 인용 된 문자열을 사용하여 Microsoft.csharp.csharpcodegenerator에서보고 싶었던 소스를 방출 할 수있었습니다.
위의 질문에 답하기 위해이 줄을 바꾸십시오.
field.InitExpression = new CodePrimitiveExpression(HugeString);
이것으로 :
field.InitExpression = new CodeSnippetExpression(QuoteSnippetStringCStyle(HugeString));
마지막으로 Microsoft.csharp.csharpcodegenerator.quotesnippetstringcstyle 메소드를 80 숯으로 랩핑하지 않도록 개인 문자열을 수정하십시오.
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# 소스 파일을 가지고 있다고 말하는 것입니다.
public const HugeString = "xxxxxxxxxxxx...." +
"yyyyy....." +
"zzzzz.....";
당신은요 그 다음에 그것을 컴파일하려고?
그렇다면 컴파일하기 전에 텍스트 파일을 코드로 편집하려고합니다. 아마도 엄격하게 정의 된 패턴 (인간 생성 소스 코드와 비교)을 따를 것이므로 비교적 간단해야합니다. 각 상수에 대해 단일 거대한 선이 있도록 변환하십시오. 이것을 시도하기 위해 샘플 코드를 원하는지 알려주세요.
그건 그렇고, 당신의 재현은 내 상자에 오류없이 성공합니다 - 어떤 버전의 프레임 워크를 사용하고 있습니까? (내 상자에는 베타가 4.0 켜져있어 사물에 영향을 줄 수 있습니다.)
편집 : 문자열 상수가 아닌 것을 변경하는 것은 어떻습니까? 당신은 그것을 스스로 분해하고 다음과 같은 공개 정적 독창적 인 필드로 방출해야합니다.
public static readonly HugeString = "xxxxxxxxxxxxxxxx" + string.Empty +
"yyyyyyyyyyyyyyyyyyy" + string.Empty +
"zzzzzzzzzzzzzzzzzzz";
결정적으로, string.Empty
a public static readonly
필드, ~ 아니다 상수. 즉, C# 컴파일러가 string.Concat
괜찮을 수도 있습니다. 물론 실행 시간에 한 번만 발생합니다. 컴파일 타임에서 수행하는 것보다 느리지 만 다른 어떤 것보다 더 쉬운 인수 일 수 있습니다.
문자열을 const로 선언하면 복사 코드 에서이 문자열을 사용하는 각 어셈블리에서.
정적으로 읽기가 더 좋을 수도 있습니다.
또 다른 방법은 문자열을 반환하는 준비된 속성을 선언하는 것입니다.
코드 생성기의 동작을 변경하는 방법을 모르겠지만 컴파일러가 사용하는 스택 크기를 변경할 수 있습니다. /스택 옵션 editbin.exe.
예시:
editbin /stack:100000,1000 csc.exe <options>
다음은 사용의 예입니다.
class App
{
private static long _Depth = 0;
// recursive function to blow stack
private static void GoDeep()
{
if ((++_Depth % 10000) == 0) System.Console.WriteLine("Depth is " +
_Depth.ToString());
GoDeep();
return;
}
public static void Main() {
try
{
GoDeep();
}
finally
{
}
return;
}
}
editbin /stack:100000,1000 q.exe
Depth is 10000
Depth is 20000
Unhandled Exception: StackOverflowException.
editbin /stack:1000000,1000 q.exe
Depth is 10000
Depth is 20000
Depth is 30000
Depth is 40000
Depth is 50000
Depth is 60000
Depth is 70000
Depth is 80000
Unhandled Exception: StackOverflowException.
는지 확인 응용 프로그램이 수영장에서 IIS 이 32 비트 응용 프로그램을 사용합니다.의 모든것들은 나를 위해 이것을 치료하는 문제를 컴파일하려고 하는 32 비트 응용 프로그램에서 Win7 64 비트입니다.이상(또는지),Microsoft 공급할 수 없게 되는 이 대답이다.의 하루 후기 내용을 발견하려면 이 링크를 수정에는 철도 디자이너 포럼:
http://darrell.mozingo.net/2009/01/17/running-iis-7-in-32-bit-mode/