CompiledMethod CompileBlock(Class host, Undo undo, Report Report)
{
#if STATIC
throw new NotSupportedException();
#else
string current_debug_name = "eval-" + count + ".dll";
++count;
AssemblyDefinitionDynamic assembly;
AssemblyBuilderAccess access;
if (Environment.GetEnvironmentVariable("SAVE") != null)
{
access = AssemblyBuilderAccess.RunAndSave;
assembly = new AssemblyDefinitionDynamic(module, current_debug_name, current_debug_name);
assembly.Importer = importer;
}
else
{
#if !NET_2_0
access = AssemblyBuilderAccess.RunAndCollect;
#else
access = AssemblyBuilderAccess.Run;
#endif
assembly = new AssemblyDefinitionDynamic(module, current_debug_name);
}
assembly.Create(AppDomain.CurrentDomain, access);
Method expression_method;
if (host != null)
{
var base_class_imported = importer.ImportType(prev_host ?? base_class);
var baseclass_list = new List <FullNamedExpression> (1)
{
new TypeExpression(base_class_imported, host.Location)
};
host.SetBaseTypes(baseclass_list);
expression_method = (Method)host.Members[0];
if ((expression_method.ModFlags & Modifiers.ASYNC) != 0)
{
//
// Host method is async. When WaitOnTask is set we wrap it with wait
//
// void AsyncWait (ref object $retval) {
// $retval = Host();
// ((Task)$retval).Wait(); // When WaitOnTask is set
// }
//
var p = new ParametersCompiled(
new Parameter(new TypeExpression(module.Compiler.BuiltinTypes.Object, Location.Null), "$retval", Parameter.Modifier.REF, null, Location.Null)
);
var method = new Method(host, new TypeExpression(module.Compiler.BuiltinTypes.Void, Location.Null),
Modifiers.PUBLIC | Modifiers.STATIC, new MemberName("AsyncWait"), p, null);
method.Block = new ToplevelBlock(method.Compiler, p, Location.Null);
method.Block.AddStatement(new StatementExpression(new SimpleAssign(
new SimpleName(p [0].Name, Location.Null),
new Invocation(new SimpleName(expression_method.MemberName.Name, Location.Null), new Arguments(0)),
Location.Null), Location.Null));
if (WaitOnTask)
{
var task = new Cast(expression_method.TypeExpression, new SimpleName(p [0].Name, Location.Null), Location.Null);
method.Block.AddStatement(new StatementExpression(new Invocation(
new MemberAccess(task, "Wait", Location.Null),
new Arguments(0)), Location.Null));
}
host.AddMember(method);
expression_method = method;
}
host.CreateContainer();
host.DefineContainer();
host.Define();
}
else
{
expression_method = null;
}
module.CreateContainer();
// Disable module and source file re-definition checks
module.EnableRedefinition();
source_file.EnableRedefinition();
module.Define();
if (Report.Errors != 0)
{
if (undo != null)
{
undo.ExecuteUndo();
}
return(null);
}
if (host != null)
{
host.PrepareEmit();
host.EmitContainer();
}
module.EmitContainer();
if (Report.Errors != 0)
{
if (undo != null)
{
undo.ExecuteUndo();
}
return(null);
}
module.CloseContainerEarlyForReflectionEmit();
module.CloseContainer();
if (host != null)
{
host.CloseContainer();
}
if (access == AssemblyBuilderAccess.RunAndSave)
{
assembly.Save();
}
if (host == null)
{
return(null);
}
//
// Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
// work from MethodBuilders. Retarded, I know.
//
var tt = assembly.Builder.GetType(host.TypeBuilder.Name);
prev_host = tt;
var mi = tt.GetMethod(expression_method.MemberName.Name);
//
// We need to then go from FieldBuilder to FieldInfo
// or reflection gets confused (it basically gets confused, and variables override each
// other).
//
foreach (var member in host.Members)
{
var field = member as Field;
if (field == null)
{
continue;
}
var fi = tt.GetField(field.Name);
Tuple <FieldSpec, FieldInfo> old;
// If a previous value was set, nullify it, so that we do
// not leak memory
if (fields.TryGetValue(field.Name, out old))
{
if (old.Item1.MemberType.IsStruct)
{
//
// TODO: Clear fields for structs
//
}
else
{
try {
old.Item2.SetValue(null, null);
} catch {
}
}
}
Console.WriteLine("replacing fieldspec for " + field.Name);
fields[field.Name] = Tuple.Create(field.Spec, fi);
}
return((CompiledMethod)System.Delegate.CreateDelegate(typeof(CompiledMethod), mi));
#endif
}