protected HoistedThis hoisted_this;
// Local variable which holds this storey instance
- public LocalTemporary Instance;
+ public Expression Instance;
public AnonymousMethodStorey (Block block, TypeContainer parent, MemberBase host, TypeParameter[] tparams, string name)
: base (parent, MakeMemberName (host, name, unique_id, tparams, block.StartLocation),
protected Field AddCompilerGeneratedField (string name, FullNamedExpression type)
{
- const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED;
+ return AddCompilerGeneratedField (name, type, false);
+ }
+
+ protected Field AddCompilerGeneratedField (string name, FullNamedExpression type, bool privateAccess)
+ {
+ Modifiers mod = Modifiers.COMPILER_GENERATED | (privateAccess ? Modifiers.PRIVATE : Modifiers.INTERNAL);
Field f = new Field (this, type, mod, new MemberName (name, Location), null);
AddField (f);
return f;
SymbolWriter.OpenCompilerGeneratedBlock (ec);
//
- // Create an instance of a storey
+ // Create an instance of this storey
//
- var storey_type_expr = CreateStoreyTypeExpression (ec);
-
ResolveContext rc = new ResolveContext (ec.MemberContext);
rc.CurrentBlock = block;
- Expression e = new New (storey_type_expr, null, Location).Resolve (rc);
- e.Emit (ec);
- Instance = new LocalTemporary (storey_type_expr.Type);
- Instance.Store (ec);
+ var storey_type_expr = CreateStoreyTypeExpression (ec);
+ var source = new New (storey_type_expr, null, Location).Resolve (rc);
+
+ //
+ // When the current context is async (or iterator) lift local storey
+ // instantiation to the currect storey
+ //
+ if (ec.CurrentAnonymousMethod is StateMachineInitializer) {
+ //
+ // Unfortunately, normal capture mechanism could not be used because we are
+ // too late in the pipeline and standart assign cannot be used either due to
+ // recursive nature of GetStoreyInstanceExpression
+ //
+ var field = ec.CurrentAnonymousMethod.Storey.AddCompilerGeneratedField (
+ LocalVariable.GetCompilerGeneratedName (block), storey_type_expr, true);
+
+ field.Define ();
+ field.Emit ();
+
+ var fexpr = new FieldExpr (field, Location);
+ fexpr.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location);
+ fexpr.EmitAssign (ec, source, false, false);
+
+ Instance = fexpr;
+ } else {
+ var local = TemporaryVariableReference.Create (source.Type, block, Location);
+ local.EmitAssign (ec, source);
+
+ Instance = local;
+ }
EmitHoistedFieldsInitialization (rc, ec);
- SymbolWriter.DefineScopeVariable (ID, Instance.Builder);
+ // TODO: Implement properly
+ //SymbolWriter.DefineScopeVariable (ID, Instance.Builder);
+
SymbolWriter.CloseCompilerGeneratedBlock (ec);
}
if (f == null) {
if (am.Storey == this) {
//
- // Access inside of same storey (S -> S)
+ // Access from inside of same storey (S -> S)
//
return new CompilerGeneratedThis (CurrentType, Location);
}
+
//
// External field access
//
{
readonly string name;
- public HoistedLocalVariable (AnonymousMethodStorey scope, LocalVariable local, string name)
- : base (scope, name, local.Type)
+ public HoistedLocalVariable (AnonymousMethodStorey storey, LocalVariable local, string name)
+ : base (storey, name, local.Type)
{
this.name = local.Name;
}
+ //
+ // For compiler generated local variables
+ //
+ public HoistedLocalVariable (AnonymousMethodStorey storey, Field field)
+ : base (storey, field)
+ {
+ }
+
public override void EmitSymbolInfo ()
{
SymbolWriter.DefineCapturedLocal (storey.ID, name, field.Name);
public Field AddAwaiter (TypeSpec type, Location loc)
{
- return AddCompilerGeneratedField ("$awaiter" + awaiters++.ToString ("X"), new TypeExpression (type, loc));
+ return AddCompilerGeneratedField ("$awaiter" + awaiters++.ToString ("X"), new TypeExpression (type, loc), true);
}
public Field AddCapturedLocalVariable (TypeSpec type)
if (mutator != null)
type = mutator.Mutate (type);
- var field = AddCompilerGeneratedField ("<s>$" + locals_captured++.ToString ("X"), new TypeExpression (type, Location));
+ var field = AddCompilerGeneratedField ("<s>$" + locals_captured++.ToString ("X"), new TypeExpression (type, Location), true);
field.Define ();
return field;
{
var action = Module.PredefinedTypes.Action.Resolve ();
if (action != null) {
- continuation = AddCompilerGeneratedField ("$continuation", new TypeExpression (action, Location));
+ continuation = AddCompilerGeneratedField ("$continuation", new TypeExpression (action, Location), true);
continuation.ModFlags |= Modifiers.READONLY;
}
// FIXME: IsIterator is too aggressive, we should capture only if child
// block contains yield
- if (CurrentAnonymousMethod.IsIterator)
+ if (CurrentAnonymousMethod.IsIterator || CurrentAnonymousMethod is AsyncInitializer)
return true;
return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
return new TemporaryVariableReference (li, loc);
}
- public override Expression CreateExpressionTree (ResolveContext ec)
- {
- throw new NotSupportedException ("ET");
- }
-
protected override Expression DoResolve (ResolveContext ec)
{
eclass = ExprClass.Variable;
//
// Don't capture temporary variables except when using
- // iterator redirection
+ // state machine redirection
//
- if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.IsIterator && ec.IsVariableCapturingRequired) {
+ if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod is StateMachineInitializer && ec.IsVariableCapturingRequired) {
AnonymousMethodStorey storey = li.Block.Explicit.CreateAnonymousMethodStorey (ec);
storey.CaptureLocalVariable (ec, li);
}
}
public override VariableInfo VariableInfo {
- get { throw new NotImplementedException (); }
+ get { return null; }
}
}
return false;
}
+ public override Expression CreateExpressionTree (ResolveContext ec)
+ {
+ HoistedVariable hv = GetHoistedVariable (ec);
+ if (hv != null)
+ return hv.CreateExpressionTree ();
+
+ Arguments arg = new Arguments (1);
+ arg.Add (new Argument (this));
+ return CreateExpressionFactoryCall (ec, "Constant", arg);
+ }
+
public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
{
if (IsLockedByStatement) {
local_info.AddressTaken = true;
}
- public override Expression CreateExpressionTree (ResolveContext ec)
- {
- HoistedVariable hv = GetHoistedVariable (ec);
- if (hv != null)
- return hv.CreateExpressionTree ();
-
- Arguments arg = new Arguments (1);
- arg.Add (new Argument (this));
- return CreateExpressionFactoryCall (ec, "Constant", arg);
- }
-
void DoResolveBase (ResolveContext ec)
{
VerifyAssigned (ec);
}
}
- public override Expression CreateExpressionTree (ResolveContext ec)
- {
- Arguments args = new Arguments (1);
- args.Add (new Argument (this));
-
- // Use typeless constant for ldarg.0 to save some
- // space and avoid problems with anonymous stories
- return CreateExpressionFactoryCall (ec, "Constant", args);
- }
-
protected override Expression DoResolve (ResolveContext ec)
{
ResolveBase (ec);
protected override string GetVariableMangledName (LocalVariable local_info)
{
- return "<" + local_info.Name + ">__" + local_name_idx++.ToString ();
+ return "<" + local_info.Name + ">__" + local_name_idx++.ToString ("X");
}
}
public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
{
- LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
+ LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
li.Type = type;
return li;
}
ec.Emit (OpCodes.Ldloca, builder);
}
+ public static string GetCompilerGeneratedName (Block block)
+ {
+ return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
+ }
+
public string GetReadOnlyContext ()
{
switch (flags & Flags.ReadonlyMask) {
}
}
+ public int TemporaryLocalsCount { get; set; }
+
#endregion
// <summary>
// Have to keep original lock value around to unlock same location
// in the case the original has changed or is null
//
- expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock.Parent, loc);
+ expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
expr_copy.Resolve (ec);
//
// Ensure Monitor methods are available
//
if (ResolvePredefinedMethods (ec) > 1) {
- lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock.Parent, loc);
+ lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
lock_taken.Resolve (ec);
}
--- /dev/null
+// Compiler options: -langversion:future
+
+using System;
+using System.Threading.Tasks;
+using System.Threading;
+using System.Reflection;
+using System.Linq;
+
+class Base : IDisposable
+{
+ protected object ovalue;
+
+ protected static int dispose_counter;
+
+ public void Dispose ()
+ {
+ ++dispose_counter;
+ }
+}
+
+class Tester : Base
+{
+ async Task<int> SwitchTest_1 ()
+ {
+ switch (await Task.Factory.StartNew (() => "X")) {
+ case "A":
+ return 1;
+ case "B":
+ return 2;
+ case "C":
+ return 3;
+ case "D":
+ return 4;
+ case "X":
+ return 0;
+ }
+
+ return 5;
+ }
+
+ async Task<int> Using_1 ()
+ {
+ using (Base a = await Task.Factory.StartNew (() => new Base ()),
+ b = await Task.Factory.StartNew (() => new Tester ()),
+ c = await Task.Factory.StartNew (() => new Base ()),
+ d = await Task.Factory.StartNew (() => new Base ()))
+ {
+ }
+
+ if (dispose_counter != 4)
+ return 1;
+
+ return 0;
+ }
+
+ static bool RunTest (MethodInfo test)
+ {
+ Console.Write ("Running test {0, -25}", test.Name);
+ try {
+ Task t = test.Invoke (new Tester (), null) as Task;
+ if (!Task.WaitAll (new[] { t }, 1000)) {
+ Console.WriteLine ("FAILED (Timeout)");
+ return false;
+ }
+
+ var ti = t as Task<int>;
+ if (ti != null) {
+ if (ti.Result != 0) {
+ Console.WriteLine ("FAILED (Result={0})", ti.Result);
+ return false;
+ }
+ } else {
+ var tb = t as Task<bool>;
+ if (tb != null) {
+ if (!tb.Result) {
+ Console.WriteLine ("FAILED (Result={0})", tb.Result);
+ return false;
+ }
+ }
+ }
+
+ Console.WriteLine ("OK");
+ return true;
+ } catch (Exception e) {
+ Console.WriteLine ("FAILED");
+ Console.WriteLine (e.ToString ());
+ return false;
+ }
+ }
+
+ public static int Main ()
+ {
+ var tests = from test in typeof (Tester).GetMethods (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)
+ where test.GetParameters ().Length == 0
+ orderby test.Name
+ select RunTest (test);
+
+ int failures = tests.Count (a => !a);
+ Console.WriteLine (failures + " tests failed");
+ return failures;
+ }
+}
--- /dev/null
+// Compiler options: -langversion:future
+using System;
+using System.Threading.Tasks;
+using System.Threading;
+
+class Tester
+{
+ async Task<int> Lambda_1 ()
+ {
+ int res = 1;
+ {
+ int a = 8;
+ Func<int> f = () => a;
+ res = await Task.Factory.StartNew (f);
+ res += f ();
+ }
+
+ return res - 16;
+ }
+
+ async Task<int> Lambda_2 ()
+ {
+ int res = 1;
+ {
+ int a = 8;
+ Func<int> f = () => a + res;
+ res = await Task.Factory.StartNew (f);
+ res += f ();
+ }
+
+ return res - 26;
+ }
+
+ async Task<int> Lambda_3<T> ()
+ {
+ int res = 1;
+ {
+ int a = 8;
+ Func<int> f = () => a;
+ res = await Task.Factory.StartNew (f);
+ res += f ();
+ }
+
+ return res - 16;
+ }
+
+ public static int Main ()
+ {
+ var t = new Tester ().Lambda_1 ();
+ if (!Task.WaitAll (new [] { t }, 1000))
+ return 1;
+
+ if (t.Result != 0)
+ return 2;
+
+ t = new Tester ().Lambda_2 ();
+ if (!Task.WaitAll (new [] { t }, 1000))
+ return 3;
+
+ if (t.Result != 0)
+ return 4;
+
+ t = new Tester ().Lambda_3<ulong>();
+ if (!Task.WaitAll (new [] { t }, 1000))
+ return 5;
+
+ if (t.Result != 0)
+ return 6;
+
+ Console.WriteLine ("ok");
+ return 0;
+ }
+}
</method>
</type>
</test>
+ <test name="test-async-16.cs">
+ <type name="Base">
+ <method name="Void Dispose()">
+ <size>13</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="Tester">
+ <method name="System.Threading.Tasks.Task`1[System.Int32] SwitchTest_1()">
+ <size>27</size>
+ </method>
+ <method name="System.Threading.Tasks.Task`1[System.Int32] Using_1()">
+ <size>27</size>
+ </method>
+ <method name="Boolean RunTest(System.Reflection.MethodInfo)">
+ <size>235</size>
+ </method>
+ <method name="Int32 Main()">
+ <size>179</size>
+ </method>
+ <method name="Boolean <Main>m__0(System.Reflection.MethodInfo)">
+ <size>12</size>
+ </method>
+ <method name="System.String <Main>m__1(System.Reflection.MethodInfo)">
+ <size>7</size>
+ </method>
+ <method name="Boolean <Main>m__2(System.Reflection.MethodInfo)">
+ <size>7</size>
+ </method>
+ <method name="Boolean <Main>m__3(Boolean)">
+ <size>5</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="Tester+<SwitchTest_1>c__async0">
+ <method name="Void MoveNext()">
+ <size>380</size>
+ </method>
+ <method name="System.String <>m__4()">
+ <size>6</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>36</size>
+ </method>
+ </type>
+ <type name="Tester+<Using_1>c__async1">
+ <method name="Void MoveNext()">
+ <size>831</size>
+ </method>
+ <method name="Base <>m__5()">
+ <size>6</size>
+ </method>
+ <method name="Tester <>m__6()">
+ <size>6</size>
+ </method>
+ <method name="Base <>m__7()">
+ <size>6</size>
+ </method>
+ <method name="Base <>m__8()">
+ <size>6</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>36</size>
+ </method>
+ </type>
+ <type name="Tester+<Using_1>c__async1+<Using_1>c__AnonStorey2">
+ <method name="Void .ctor()">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
<test name="test-async-17.cs">
<type name="Tester">
<method name="System.Threading.Tasks.Task`1[System.Int32] TestException_1()">
</method>
</type>
</test>
+ <test name="test-async-18.cs">
+ <type name="Tester">
+ <method name="System.Threading.Tasks.Task`1[System.Int32] Lambda_1()">
+ <size>27</size>
+ </method>
+ <method name="System.Threading.Tasks.Task`1[System.Int32] Lambda_2()">
+ <size>27</size>
+ </method>
+ <method name="System.Threading.Tasks.Task`1[System.Int32] Lambda_3[T]()">
+ <size>27</size>
+ </method>
+ <method name="Int32 Main()">
+ <size>165</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="Tester+<Lambda_1>c__async0">
+ <method name="Void MoveNext()">
+ <size>290</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>36</size>
+ </method>
+ </type>
+ <type name="Tester+<Lambda_2>c__async1">
+ <method name="Void MoveNext()">
+ <size>290</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>36</size>
+ </method>
+ </type>
+ <type name="Tester+<Lambda_3>c__async2`1[T]">
+ <method name="Void MoveNext()">
+ <size>290</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>36</size>
+ </method>
+ </type>
+ <type name="Tester+<Lambda_1>c__async0+<Lambda_1>c__AnonStorey3">
+ <method name="Int32 <>m__0()">
+ <size>7</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="Tester+<Lambda_2>c__async1+<Lambda_2>c__AnonStorey4">
+ <method name="Int32 <>m__1()">
+ <size>19</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="Tester+<Lambda_3>c__async2`1+<Lambda_3>c__AnonStorey5`1[T]">
+ <method name="Int32 <>m__2()">
+ <size>7</size>
+ </method>
+ <method name="Void .ctor()">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
<test name="test-cls-00.cs">
<type name="CLSCLass_6">
<method name="Void .ctor()">