Skip to content

Commit

Permalink
Add await support in to evaluator and REPL
Browse files Browse the repository at this point in the history
  • Loading branch information
marek-safar committed Jul 10, 2013
1 parent 5219618 commit 5f87707
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 24 deletions.
11 changes: 11 additions & 0 deletions mcs/class/Mono.CSharp/Test/Evaluator/ExpressionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,16 @@ public void DynamicStatement ()
Evaluator.Run ("d.GetType ();");
}
#endif

#if NET_4_5
[Test]
public void AwaitExpression ()
{
Evaluator.WaitOnTask = true;
var res = Evaluator.Evaluate("var res = await System.Threading.Tasks.Task.FromResult (1) + await System.Threading.Tasks.Task.FromResult (2);");
res = Evaluator.Evaluate ("res;");
Assert.AreEqual (3, res, "#1");
}
#endif
}
}
4 changes: 0 additions & 4 deletions mcs/mcs/class.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,17 +356,13 @@ public override string GetSignatureForError ()

public string GetSignatureForMetadata ()
{
#if STATIC
if (Parent is TypeDefinition) {
return Parent.GetSignatureForMetadata () + "+" + TypeNameParser.Escape (MemberName.Basename);
}

var sb = new StringBuilder ();
CreateMetadataName (sb);
return sb.ToString ();
#else
throw new NotImplementedException ();
#endif
}

public virtual void RemoveContainer (TypeContainer cont)
Expand Down
27 changes: 16 additions & 11 deletions mcs/mcs/cs-parser.jay
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ namespace Mono.CSharp
// Keeps track of global data changes to undo on parser error
//
public Undo undo;

bool? interactive_async;

Stack<Linq.QueryBlock> linq_clause_blocks;

Expand Down Expand Up @@ -3926,6 +3928,9 @@ unary_expression
} else if (current_anonymous_method is AnonymousMethodExpression) {
report.Error (4035, GetLocation ($1),
"The `await' operator can only be used when its containing anonymous method is marked with the `async' modifier");
} else if (interactive_async != null) {
current_block.Explicit.RegisterAsyncAwait ();
interactive_async = true;
} else {
report.Error (4033, GetLocation ($1),
"The `await' operator can only be used when its containing method is marked with the `async' modifier");
Expand Down Expand Up @@ -5377,10 +5382,7 @@ interactive_statement_expression
: expression
{
Expression expr = (Expression) $1;
ExpressionStatement s;

s = new OptionalAssign (new SimpleName ("$retval", lexer.Location), expr, lexer.Location);
$$ = new StatementExpression (s);
$$ = new StatementExpression (new OptionalAssign (expr, lexer.Location));
}
| error
{
Expand Down Expand Up @@ -6653,27 +6655,30 @@ interactive_parsing
mods |= Modifiers.UNSAFE;

current_local_parameters = pars;
Method method = new Method (
var method = new InteractiveMethod (
current_type,
new TypeExpression (compiler.BuiltinTypes.Void, Location.Null),
mods,
new MemberName ("Host"),
pars,
null /* attributes */);
pars);

current_type.AddMember (method);

oob_stack.Push (method);

interactive_async = false;

++lexer.parsing_block;
start_block (lexer.Location);
}
interactive_statement_list opt_COMPLETE_COMPLETION
{
--lexer.parsing_block;
Method method = (Method) oob_stack.Pop ();

var method = (InteractiveMethod) oob_stack.Pop ();
method.Block = (ToplevelBlock) end_block(lexer.Location);

if (interactive_async == true) {
method.ChangeToAsync ();
}

InteractiveResult = (Class) pop_current_class ();
current_local_parameters = null;
}
Expand Down
106 changes: 98 additions & 8 deletions mcs/mcs/eval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ void Reset ()
Location.Initialize (ctx.SourceFiles);
}

/// <summary>
/// When set evaluator will automatically wait on Task of async methods. When not
/// set it's called responsibility to handle Task execution
/// </summary>
public bool WaitOnTask { get; set; }

/// <summary>
/// If true, turns type expressions into valid expressions
/// and calls the describe method on it
Expand Down Expand Up @@ -429,7 +435,7 @@ public object Evaluate (string input)
throw new ArgumentException ("Syntax error on input: partial input");

if (result_set == false)
throw new ArgumentException ("The expression did not set a result");
throw new ArgumentException ("The expression failed to resolve");

return result;
}
Expand Down Expand Up @@ -657,11 +663,47 @@ CompiledMethod CompileBlock (Class host, Undo undo, Report Report)

host.SetBaseTypes (baseclass_list);

host.CreateContainer ();
host.DefineContainer ();
host.Define ();

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;
}
Expand Down Expand Up @@ -1056,6 +1098,27 @@ static public string Describe (object x)
#endif
}

class InteractiveMethod : Method
{
public InteractiveMethod(TypeDefinition parent, FullNamedExpression returnType, Modifiers mod, ParametersCompiled parameters)
: base(parent, returnType, mod, new MemberName("Host"), parameters, null)
{
}

public void ChangeToAsync ()
{
ModFlags |= Modifiers.ASYNC;
ModFlags &= ~Modifiers.UNSAFE;
type_expr = new TypeExpression(Module.PredefinedTypes.Task.TypeSpec, Location);
parameters = ParametersCompiled.EmptyReadOnlyParameters;
}

public override string GetSignatureForError()
{
return "InteractiveHost";
}
}

class HoistedEvaluatorVariable : HoistedVariable
{
public HoistedEvaluatorVariable (Field field)
Expand All @@ -1076,11 +1139,17 @@ protected override FieldExpr GetFieldExpression (EmitContext ec)
/// the return value for an invocation.
/// </summary>
class OptionalAssign : SimpleAssign {
public OptionalAssign (Expression t, Expression s, Location loc)
: base (t, s, loc)
public OptionalAssign (Expression s, Location loc)
: base (null, s, loc)
{
}

public override Location StartLocation {
get {
return Location.Null;
}
}

protected override Expression DoResolve (ResolveContext ec)
{
Expression clone = source.Clone (new CloneContext ());
Expand All @@ -1093,7 +1162,7 @@ protected override Expression DoResolve (ResolveContext ec)
// A useful feature for the REPL: if we can resolve the expression
// as a type, Describe the type;
//
if (ec.Module.Evaluator.DescribeTypeExpressions){
if (ec.Module.Evaluator.DescribeTypeExpressions && !(ec.CurrentAnonymousMethod is AsyncInitializer)) {
var old_printer = ec.Report.SetPrinter (new SessionReportPrinter ());
Expression tclone;
try {
Expand All @@ -1119,8 +1188,29 @@ protected override Expression DoResolve (ResolveContext ec)
}

source = clone;

var host = (Method) ec.MemberContext.CurrentMemberDefinition;

if (host.ParameterInfo.IsEmpty) {
eclass = ExprClass.Value;
type = InternalType.FakeInternalType;
return this;
}

target = new SimpleName (host.ParameterInfo[0].Name, Location);

return base.DoResolve (ec);
}

public override void EmitStatement(EmitContext ec)
{
if (target == null) {
source.Emit (ec);
return;
}

base.EmitStatement(ec);
}
}

public class Undo
Expand Down
2 changes: 1 addition & 1 deletion mcs/mcs/flowanalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,7 @@ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
}
}

public class FlowBranchingAsync : FlowBranchingBlock
public class FlowBranchingAsync : FlowBranchingBlock
{
readonly AsyncInitializer async_init;

Expand Down
34 changes: 34 additions & 0 deletions mcs/mcs/support.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,4 +322,38 @@ public string BaseText {
}
}
}

struct TypeNameParser
{
internal static string Escape(string name)
{
if (name == null) {
return null;
}
StringBuilder sb = null;
for (int pos = 0; pos < name.Length; pos++) {
char c = name[pos];
switch (c) {
case '\\':
case '+':
case ',':
case '[':
case ']':
case '*':
case '&':
if (sb == null) {
sb = new StringBuilder(name, 0, pos, name.Length + 3);
}
sb.Append("\\").Append(c);
break;
default:
if (sb != null) {
sb.Append(c);
}
break;
}
}
return sb != null ? sb.ToString() : name;
}
}
}
1 change: 1 addition & 0 deletions mcs/tools/csharp/repl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ static int Main (string [] args)

eval.InteractiveBaseClass = typeof (InteractiveBaseShell);
eval.DescribeTypeExpressions = true;
eval.WaitOnTask = true;

CSharpShell shell;
#if !ON_DOTNET
Expand Down

0 comments on commit 5f87707

Please sign in to comment.