Solución temporal para C#CodeDom que causa el desbordamiento de pila(CS1647) en csc.exe?
compiler-construction (5)
Así que estoy en lo cierto al decir que tienes el archivo fuente C # con algo como:
public const HugeString = "xxxxxxxxxxxx...." +
"yyyyy....." +
"zzzzz.....";
y luego intentas compilarlo?
Si es así, trataría de editar el archivo de texto (en código, por supuesto) antes de compilar. Eso debería ser relativamente sencillo de hacer, ya que presumiblemente seguirán un patrón rígidamente definido (en comparación con el código fuente generado por el ser humano). Convierta para tener una sola línea masiva para cada constante. Avíseme si desea algún código de muestra para probar esto.
Por cierto, tu repro tiene éxito sin errores en mi caja, ¿qué versión del framework estás usando? (Mi caja tiene la versión beta de 4.0 en, lo que puede afectar las cosas).
EDITAR: ¿Qué hay de cambiarlo para que no sea una cadena constante? Tendría que dividirlo usted mismo y emitirlo como un campo público de solo lectura estático como este:
public static readonly HugeString = "xxxxxxxxxxxxxxxx" + string.Empty +
"yyyyyyyyyyyyyyyyyyy" + string.Empty +
"zzzzzzzzzzzzzzzzzzz";
Fundamentalmente, string.Empty
es un campo public static readonly
, no una constante. Eso significa que el compilador de C # simplemente emitirá una llamada a string.Concat
que bien puede estar bien. Solo ocurrirá una vez en el tiempo de ejecución, por supuesto, más lento que hacerlo en tiempo de compilación, pero puede ser una solución más fácil que cualquier otra cosa.
Tengo una situación en la que necesito generar una clase con una gran const de cuerda. El código fuera de mi control hace que mi árbol CodeDom generado se emita a la fuente C # y luego se compile como parte de un ensamblaje más grande.
Desafortunadamente, me he encontrado con una situación en la que si la longitud de esta cadena supera los 335440 caracteres en Win2K8 x64 (926240 en Win2K3 x86), el compilador C # sale con un error fatal:
error fatal CS1647: una expresión es demasiado larga o compleja de compilar cerca de ''int''
MSDN dice que CS1647 es "un desbordamiento de pila en el compilador" (¡sin juego de palabras!). Mirando más de cerca, he determinado que el CodeDom "muy bien" envuelve mi const de cadena a 80 caracteres. Esto hace que el compilador concatene más de 4193 fragmentos de cadena que aparentemente es la profundidad de la pila del compilador de C # en x64 NetFx. CSC.exe debe evaluar internamente recursivamente esta expresión para "rehidratar" mi cadena única.
Mi pregunta inicial es esta: " ¿alguien sabe de una solución alternativa para cambiar la forma en que el generador de códigos emite cadenas? " No puedo controlar el hecho de que el sistema externo usa la fuente C # como intermedio y quiero que sea una constante (en lugar que una concatenación de cadenas en tiempo de ejecución).
Alternativamente, ¿cómo puedo formular esta expresión de tal manera que después de un cierto número de caracteres, todavía puedo crear una constante pero está compuesta de múltiples fragmentos grandes ?
La reproducción completa está aquí:
// 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);
}
No tengo idea de cómo cambiar el comportamiento del generador de código, pero puede cambiar el tamaño de la pila que usa el compilador con la opción / stack de EditBin.EXE .
Ejemplo:
editbin /stack:100000,1000 csc.exe <options>
A continuación se muestra un ejemplo de su uso:
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: Exception.
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: Exception.
Usando CodeSnippetExpression y una cadena entre comillas, pude emitir la fuente que me hubiera gustado ver desde Microsoft.CSharp.CSharpCodeGenerator.
Entonces, para responder a la pregunta anterior, reemplace esta línea:
field.InitExpression = new CodePrimitiveExpression(HugeString);
con este:
field.InitExpression = new CodeSnippetExpression(QuoteSnippetStringCStyle(HugeString));
Y finalmente modifique la cadena privada que cita el método Microsoft.CSharp.CSharpCodeGenerator.QuoteSnippetStringCStyle para que no se ajuste después de 80 caracteres:
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();
}
Tenga en cuenta que si declara la cadena como const, se copiará en cada conjunto que use esta cadena en su código.
Es posible que esté mejor con static readonly.
Otra forma sería declarar una propiedad de solo lectura que devuelve la cadena.
Asegúrese de que los grupos de aplicaciones en IIS tengan aplicaciones de 32 bits habilitadas. Eso fue todo lo que necesité para resolver este problema al intentar compilar una aplicación de 32 bits en Win7 de 64 bits. Curiosamente (o no), Microsoft no pudo proporcionar esta respuesta. Después de un día completo de búsqueda, encontré este enlace al arreglo en un foro de Iron Speed Designer:
http://darrell.mozingo.net/2009/01/17/running-iis-7-in-32-bit-mode/