1+
2+ /**
3+ * Copyright (c) Rich Hickey. All rights reserved.
4+ * The use and distribution terms for this software are covered by the
5+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
6+ * which can be found in the file epl-v10.html at the root of this distribution.
7+ * By using this software in any fashion, you are agreeing to be bound by
8+ * the terms of this license.
9+ * You must not remove this notice, or any other, from this software.
10+ **/
11+
12+ #if NET11_0_OR_GREATER
13+
14+ using System . Reflection ;
15+ using System . Reflection . Emit ;
16+
17+ using System ;
18+ using System . Threading . Tasks ;
19+
20+ namespace clojure . lang . CljCompiler . Ast ;
21+
22+ public class AwaitExpr : Expr
23+ {
24+ #region Data
25+
26+ readonly Expr _taskExpr ;
27+ public Expr TaskExpr => _taskExpr ;
28+
29+ readonly Type _resultType ;
30+ readonly MethodInfo _awaitMethod ;
31+
32+ #endregion
33+
34+ #region Ctors
35+
36+ public AwaitExpr ( Expr taskExpr , Type resultType , MethodInfo awaitMethod )
37+ {
38+ _taskExpr = taskExpr ;
39+ _resultType = resultType ;
40+ _awaitMethod = awaitMethod ;
41+ }
42+
43+ #endregion
44+
45+ #region Type mangling
46+
47+ public bool HasClrType => true ;
48+
49+ public Type ClrType => _resultType == typeof ( void ) ? typeof ( object ) : _resultType ;
50+
51+ #endregion
52+
53+ #region Parsing
54+
55+ public sealed class Parser : IParser
56+ {
57+ public Expr Parse ( ParserContext pcon , object frm )
58+ {
59+ ISeq form = ( ISeq ) frm ;
60+
61+ if ( ! Compiler . RuntimeAsyncAvailable )
62+ throw new ParseException (
63+ "(await* ...) requires runtime async support" ) ;
64+
65+ if ( RT . count ( form ) != 2 )
66+ throw new ParseException (
67+ "Wrong number of arguments to await*, expected: (await* expr)" ) ;
68+
69+ ObjMethod method = ( ObjMethod ) Compiler . MethodVar . deref ( ) ;
70+
71+ if ( method is null )
72+ throw new ParseException (
73+ "(await* ...) must appear inside a function body" ) ;
74+
75+ if ( Compiler . InCatchFinallyVar . deref ( ) is not null )
76+ throw new ParseException (
77+ "(await* ...) cannot appear inside a catch, finally, or fault handler" ) ;
78+
79+ if ( ! method . IsAsync )
80+ throw new ParseException (
81+ "(await* ...) can only be used inside a ^:async function or (async ...) block" ) ;
82+
83+ //if (pcon.Rhc == RHC.Eval)
84+ // return Compiler.Analyze(pcon,
85+ // RT.list(RT.list(Compiler.FnOnceSym, PersistentVector.EMPTY, form)),
86+ // "await__" + RT.nextID());
87+
88+ Expr taskExpr = Compiler . Analyze (
89+ pcon . SetRhc ( RHC . Expression ) . SetAssign ( false ) ,
90+ RT . second ( form ) ) ;
91+
92+ Type taskType ;
93+ if ( taskExpr . HasClrType )
94+ {
95+ taskType = taskExpr . ClrType ;
96+
97+ bool isTaskType =
98+ taskType == typeof ( Task )
99+ || taskType == typeof ( ValueTask )
100+ || ( taskType . IsGenericType &&
101+ ( taskType . GetGenericTypeDefinition ( ) == typeof ( Task < > )
102+ || taskType . GetGenericTypeDefinition ( ) == typeof ( ValueTask < > ) ) ) ;
103+
104+ if ( ! isTaskType )
105+ {
106+ if ( taskType == typeof ( object ) )
107+ taskType = typeof ( Task < object > ) ;
108+ else
109+ throw new ParseException (
110+ $ "(await* ...) requires a Task, Task<T>, ValueTask, or ValueTask<T>, got: { taskType . FullName } ") ;
111+ }
112+ }
113+ else
114+ {
115+ taskType = typeof ( Task < object > ) ;
116+ }
117+
118+ MethodInfo awaitMethod =
119+ Compiler . AsyncMethodCache . ResolveAwaitMethod ( taskType , out Type resultType ) ;
120+
121+ if ( awaitMethod is null )
122+ throw new ParseException (
123+ "Failed to resolve AsyncHelpers.Await method for type: " + taskType . FullName ) ;
124+
125+ //method.HasAwait = true;
126+
127+ return new AwaitExpr ( taskExpr , resultType , awaitMethod ) ;
128+ }
129+ }
130+
131+ #endregion
132+
133+ #region eval
134+
135+ public object Eval ( )
136+ {
137+ throw new InvalidOperationException ( "Can't eval await*" ) ;
138+ }
139+
140+ #endregion
141+
142+ #region Code generation
143+
144+ public void Emit ( RHC rhc , ObjExpr objx , CljILGen ilg )
145+ {
146+ _taskExpr . Emit ( RHC . Expression , objx , ilg ) ;
147+
148+ if ( _taskExpr . HasClrType && _taskExpr . ClrType == typeof ( object ) )
149+ {
150+ ilg . Emit ( OpCodes . Castclass , typeof ( Task < object > ) ) ;
151+ }
152+
153+ ilg . Emit ( OpCodes . Call , _awaitMethod ) ;
154+
155+ if ( _resultType != typeof ( void ) )
156+ {
157+ if ( rhc == RHC . Statement )
158+ ilg . Emit ( OpCodes . Pop ) ;
159+ else if ( _resultType . IsValueType )
160+ ilg . Emit ( OpCodes . Box , _resultType ) ;
161+ }
162+ else
163+ {
164+ if ( rhc != RHC . Statement )
165+ ilg . Emit ( OpCodes . Ldnull ) ;
166+ }
167+ }
168+
169+ public bool HasNormalExit ( ) => true ;
170+
171+ #endregion
172+
173+ }
174+
175+
176+ #endif
0 commit comments