El método emitido no se llama en la salida generada

programación


Estoy creando un pequeño compilador de DFA que representa directamente en un ensamblado usando System.Reflection.Emit

Tenga en cuenta que a continuación hay una definición FALIB. Esto siempre se definirá cuando se utilice el compilador.

Se supone que mi código IL implementa una versión concreta de esto

#if FALIB
public
#endif
abstract partial class FARunner
{
		
	protected abstract int MatchImpl(LexContext lc);
	protected abstract FAMatch SearchImpl(LexContext lc);
	/// <summary>
	/// Searches through text for the next occurance of a pattern matchable by this machine
	/// </summary>
	/// <param name="lc">The <see cref="LexContext"/> with the text to search</param>
	/// <returns>A series of <see cref="FAMatch"/> instances with the matches</returns>
	public IEnumerable<FAMatch> Search(LexContext lc)
	{
		while(lc.Current!=LexContext.EndOfInput)
		{
			yield return SearchImpl(lc);
		}
	}
	/// <summary>
	/// Indicates whether this machine will match the indicated text
	/// </summary>
	/// <param name="lc">A <see cref="LexContext"/> containing the text</param>
	/// <returns>The accept symbol if matched, otherwise -1.</returns>
	public int Match(LexContext lc)
	{
		return MatchImpl(lc);
	}
	/// <summary>
	/// Searches through text for the next occurance of a pattern matchable by this machine
	/// </summary>
	/// <param name="@string">The <see cref="IEnumerable{char}"/> with the text to search</param>
	/// <returns>A series of <see cref="FAMatch"/> instances with the matches</returns>
	public IEnumerable<FAMatch> Search(IEnumerable<char> @string)
	{
		foreach (var match in Search(LexContext.Create(@string)))
		{
			yield return match;
		}
	}
	/// <summary>
	/// Searches through text for the next occurance of a pattern matchable by this machine
	/// </summary>
	/// <param name="reader">The <see cref="TextReader"/> with the text to search</param>
	/// <returns>A series of <see cref="FAMatch"/> instances with the matches</returns>
	public IEnumerable<FAMatch> Search(TextReader reader)
	{
		foreach (var match in Search(LexContext.CreateFrom(reader)))
		{
			yield return match;
		}
	}
	/// <summary>
	/// Indicates whether this machine will match the indicated text
	/// </summary>
	/// <param name="text">The text</param>
	/// <returns>True if the passed in text was a match, otherwise false.</returns>
	public int Match(IEnumerable<char> text)
	{
		return Match(LexContext.Create(text));
	}

}

Solo necesita completar SearchImpl y MatchImpl con métodos concretos, lo cual he hecho aunque actualmente arrojan no implementados.

El problema que tengo es el siguiente: el método MatchImpl funciona bien.

El método SearchImpl… bueno, es extraño. No sólo no es llamado, ni ninguno de los métodos que lo delega. No recibo ningún error, ni el compilador ni el tiempo de ejecución. Simplemente falla silenciosamente cuando intento llamarlo.

Lo único que se me ocurre es que mi tipo de retorno es una estructura. Esa es la única diferencia que puedo ver aquí… bueno, eso y el nombre.

Lo que he probado:

C#
public static partial class FACompiler
{
	public static FARunner Compile(this FA fa, IProgress<int> progress = null)
	{
		if (fa == null) throw new ArgumentNullException(nameof(fa));
		fa = fa.ToDfa(progress);
		var name = "FARunner" + fa.GetHashCode();
		var asmName = new AssemblyName(name);
		var asm = Thread.GetDomain().DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
		ModuleBuilder mod = asm.DefineDynamicModule("M"+name);
		TypeBuilder type = mod.DefineType(name, TypeAttributes.Public | TypeAttributes.Sealed, typeof(FARunner));
		Type[] paramTypes = new Type[] { typeof(LexContext) };
		Type searchReturnType = typeof(FAMatch);
		MethodBuilder searchImpl = type.DefineMethod("SearchImpl", MethodAttributes.Public | MethodAttributes.ReuseSlot |
			MethodAttributes.Virtual | MethodAttributes.HideBySig, searchReturnType, paramTypes);
		ILGenerator searchGen = searchImpl.GetILGenerator();
		searchGen.ThrowException(typeof(NotImplementedException));
			
		MethodInfo searchImplBase = typeof(FARunner).GetMethod("SearchImpl",BindingFlags.NonPublic | BindingFlags.Instance);
		type.DefineMethodOverride(searchImpl, searchImplBase);

		Type matchReturnType = typeof(int);
		MethodBuilder match = type.DefineMethod("MatchImpl", MethodAttributes.Public | MethodAttributes.ReuseSlot |
			MethodAttributes.Virtual | MethodAttributes.HideBySig, matchReturnType, paramTypes);
		ILGenerator matchGen = match.GetILGenerator();
		matchGen.ThrowException(typeof(NotImplementedException));
		MethodInfo matchBase = typeof(FARunner).GetMethod("MatchImpl", BindingFlags.NonPublic | BindingFlags.Instance);
		type.DefineMethodOverride(match, matchBase);

		Type newType = type.CreateType();

		return (FARunner)Activator.CreateInstance(newType);

	}
}

También intenté agregar estas líneas para ajustar la pila en caso de que necesitara llenarla incluso en caso de un lanzamiento:

C#
LocalBuilder famatch = searchGen.DeclareLocal(typeof(FAMatch));
searchGen.Emit(OpCodes.Ldloca_S, famatch);
searchGen.Emit(OpCodes.Initobj, typeof(FAMatch));

Obtuve los mismos resultados.

Solución 1

No entiendo por qué, pero cuando saqué los rendimientos de mi clase FARunner e implementé manualmente mi enumerador, todo funcionó bien.

Cierro esto como resuelto aunque estoy desconcertado.

コメント

タイトルとURLをコピーしました