Async await fix (#3146)

* Добавил комментарии для async/await

* source_context fix

* ReplaceVarNames fix

* Parametrs fix

* async_defs fix

---------

Co-authored-by: Aleksandr Kalinin <akalinin@sfedu.ru>
This commit is contained in:
Kalsash 2024-06-12 16:14:38 +03:00 committed by GitHub
parent 9fb7fba1e1
commit 6de1848ec9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 674 additions and 674 deletions

View file

@ -1,29 +1,26 @@
using PascalABCCompiler.SyntaxTree;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SyntaxVisitors.Async
{
// Билдер для построения StateMachine для каждого асинхронного метода
internal class AsyncBuilder
{
// корень дерева
private program_module program_Module { get; set; }
// нужен для перестановки defs, содержащихся в блоке program block
public List<declarations> declarationsList = new List<declarations>();
// список всех асинхронных методов
public List<procedure_definition> pdList = new List<procedure_definition>();
public List<declarations> declarations = new List<declarations>();
public List<procedure_definition> pdList = new List<procedure_definition>();
public HashSet<var_statement> VarsList = new HashSet<var_statement>();
// Словарик переменных, которые нужно будет переименовать
public Dictionary<string, string> RepVarsDict = new Dictionary<string, string>();
int k = 0;
int LabelNameCounter = 0;
private int StateCounter = 1;
private declarations Decls = new declarations();
// копирует блоки построенной StateMachine и передает их в declarationsList
private declarations Decls = new declarations();
public AsyncBuilder(program_module program_Module)
{
@ -82,10 +79,6 @@ namespace SyntaxVisitors.Async
var cd = new class_definition(ntr, classbodylist, defs.source_context);
var td = new type_declaration(new ident("StateMachine"), cd, program_Module.source_context);
//block bl = listNodes.FindLast(x => x is block) as block;
//var ld = new label_definitions("tbuilder");
//bl.defs.Add(ld);
var pd = new procedure_definition();
pd.proc_header = ph2;
var a = new assign(new ident("tbuilder"), new dot_node(new ident("AsyncVoidMethodBuilder"), new ident("Create")), Operators.Assignment, defs.source_context);
@ -125,10 +118,6 @@ namespace SyntaxVisitors.Async
Decls.Add(new type_declarations(td, defs.source_context), defs.source_context);
Decls.Add(pd, defs.source_context);
Decls.Add(pd2, defs.source_context);
//program_Module.program_block.defs.AddFirst(pd2);
//program_Module.program_block.defs.AddFirst(pd);
//program_Module.program_block.defs.AddFirst(td);
}
@ -139,7 +128,6 @@ namespace SyntaxVisitors.Async
BuildStateMachine();
var name = NewStateMachine();
//async_pm.program_block.defs
var t_name = Decls.list[0] as type_declarations;
t_name.types_decl[0].type_name = name;
@ -182,16 +170,16 @@ namespace SyntaxVisitors.Async
dc.Add(Decls.list[2], defs.source_context);
dc.Add(p, defs.source_context);
declarations.Add(dc);
declarationsList.Add(dc);
Decls.list.Clear();
}
public void ParseStateMachines()
// Для каждого асинхронного метода вызываем ParseStateMachine
public void ParseStateMachines()
{
//var async_pm = async_root as program_module;
var defs = program_Module.program_block.defs;
//pdList.Reverse();
var t = new List<procedure_definition>();
foreach (var item in pdList)
{
@ -204,16 +192,16 @@ namespace SyntaxVisitors.Async
{
ParseStateMachine(pd);
}
declarations.Reverse();
declarationsList.Reverse();
foreach (var item in declarations)
foreach (var item in declarationsList)
{
defs.AddFirst(item.list);
}
}
// Переставляем асинхронные методы в нужном порядке
public void SortBlocks()
{
var defs = program_Module.program_block.defs;
@ -266,7 +254,8 @@ namespace SyntaxVisitors.Async
{
pdList.Add(p);
}
public void ChangeBodies()
// Для каждого асинхронного метода вызываем ChangeBody
public void ChangeBodies()
{
StateCounter = 1;
pdList.Reverse();
@ -282,14 +271,6 @@ namespace SyntaxVisitors.Async
{
s = s.Substring(s.LastIndexOf('.') + 1);
}
// AddError нужно вместо SyntaxVisitorError
//if (s.StartsWith("Void") || s.StartsWith("Task"))
//{
// //BuilderType = "Async" + s + "MethodBuilder";
//}
//else
if (!s.Contains("Task"))
throw new SyntaxVisitorError("Возвращаемым типом асинхронного метода должен быть void, Task, Task<T> или аналогичный тип, IAsyncEnumerable<T> или IAsyncEnumerator<T>",
item.proc_header.source_context);
@ -306,8 +287,8 @@ namespace SyntaxVisitors.Async
}
public string newLabelName(string old)
{
k++;
return "@awvar@_" + k.ToString() + "_" + old;
LabelNameCounter++;
return "@awvar@_" + LabelNameCounter.ToString() + "_" + old;
}
// Изменяем тело асинхронной функции, добавляя AsyncBuilder для запуска StateMachine
@ -315,6 +296,7 @@ namespace SyntaxVisitors.Async
{
var state = "@awst@_";
var block = pd.proc_body as block;
if (pd.proc_header.IsAsync == false)
{
return;
@ -340,9 +322,14 @@ namespace SyntaxVisitors.Async
{
var nv = newLabelName(id.name);
RepVarsDict.Add(id.name, nv);
//id.name = nv;
parsList.Add(new assign(new dot_node(i, id), id, Operators.Assignment), block.source_context);
}
else
{
var nv = newLabelName(id.name);
RepVarsDict[id.name] = nv;
parsList.Add(new assign(new dot_node(i, id), id, Operators.Assignment), block.source_context);
}
}
}
}
@ -375,9 +362,32 @@ namespace SyntaxVisitors.Async
m.dereferencing_value = new dot_node(new dot_node(i, new ident("tbuilder", block.source_context)), new ident("Start", block.source_context), block.source_context);
var p = new procedure_call(m, block.source_context);//st.tbuilder.Start(st);
var stl = new statement_list((pd.proc_body as block).program_code, st, a, parsList, a2);
var temp_def = new declarations();
var defsCount = 0;
if (block.defs != null)
{
foreach (var dl in block.defs.list)
{
if (dl is variable_definitions)
{
defsCount++;
var ddl = dl as variable_definitions;
foreach (var item in ddl.list)
{
(pd.proc_body as block).program_code.AddFirst(new var_statement(item));
}
}
else
temp_def.Add(dl);
}
}
if (defsCount > 0)
{
block.defs = temp_def;
}
var stl = new statement_list((pd.proc_body as block).program_code, st, a, parsList, a2);
var class_name = pd.proc_header.name.class_name;
if (class_name != null)

View file

@ -1,18 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PascalABCCompiler.Errors;
using PascalABCCompiler;
using System.Collections.Generic;
using PascalABCCompiler.SyntaxTree;
using SyntaxVisitors.Async;
using PascalABCCompiler.Parsers;
using PascalABCCompiler.ParserTools;
using PascalABCCompiler.SyntaxTreeConverters;
using System.IO;
using System.Collections;
namespace SyntaxVisitors
{
@ -20,10 +8,14 @@ namespace SyntaxVisitors
{
private program_module program_Module { get; set; }
// текущий асинхронный метод, для которого будет построена StateMachine
private procedure_definition proc_def { get; set; }
// список всех асинхронных методов
private List<procedure_definition> proc_def_List = new List<procedure_definition>();
// Тип билдера, который нужно менять в зависимости от возвращаемого
// типа асинхронного типа
private string BuilderType = "AsyncVoidMethodBuilder";
private int AwaiterCounter = 1;
@ -35,23 +27,25 @@ namespace SyntaxVisitors
private bool IsFirstAwait = true;
private bool IsFirstAsync = true;
private int ChangeBuilderCounter = 0;
// Нужен, чтобы понимать с какой машиной
// состояний мы сейчас работаем
private int StateMachineNumber = 0;
public static AsyncVisitor New
{
get { return new AsyncVisitor(); }
}
public override void visit(procedure_definition pd)
{
if (pd.proc_header.IsAsync)
// Собираем все асинхронные методы в proc_def_List
if (pd.proc_header.IsAsync)
{
MainVisitor.flag = false;
MainVisitor.HasAwait = false;
MainVisitor.Accept(pd);
if (!MainVisitor.flag)
if (!MainVisitor.HasAwait)
{
var b = pd.proc_body as block;
if (b != null)
@ -77,7 +71,6 @@ namespace SyntaxVisitors
}
LoweringAsyncVisitor.Accept(pd);
AsyncBuilder.GetMethods(pd);
// DefaultVisit(pd);
}
else
{
@ -88,15 +81,16 @@ namespace SyntaxVisitors
{
if (procedure_code.list[i] is await_node_statement)
{
throw new SyntaxVisitorError("Ключевое слово 'await' может быть использовано только в асинхронных методах ", procedure_code.list[i].source_context);
throw new SyntaxVisitorError("Ключевое слово 'await' может быть использовано " +
"только в асинхронных методах ", procedure_code.list[i].source_context);
}
if (procedure_code.list[i] is var_statement)
{
var pp = procedure_code.list[i] as var_statement;
if (pp.var_def.inital_value is await_node)
{
// AddError нужно вместо SyntaxVisitorError
throw new SyntaxVisitorError("Ключевое слово 'await' может быть использовано только в асинхронных методах ", pp.var_def.inital_value.source_context);
throw new SyntaxVisitorError("Ключевое слово 'await' может быть использовано " +
"только в асинхронных методах ", pp.var_def.inital_value.source_context);
}
}
}
@ -117,29 +111,31 @@ namespace SyntaxVisitors
AwaitBuilder = new AwaitBuilder(program_Module, proc_def);
AwaitBuilder.VarsHelper.RepVarsDict = AsyncBuilder.RepVarsDict;
AwaitBuilder.GetCode();
AwaitBuilder.AddAwaiter("TaskAwaiter", true, a.ex, ChangeBuilderCounter);
AwaitBuilder.ChangeBuilder(BuilderType, ChangeBuilderCounter);
AwaitBuilder.AddAwaiter(true, a.ex, StateMachineNumber);
AwaitBuilder.ChangeBuilder(BuilderType, StateMachineNumber);
}
else
{
AwaitBuilder.ChangeBuilder(BuilderType, ChangeBuilderCounter);
AwaitBuilder.AddAwaiter("TaskAwaiter", false, a.ex, ChangeBuilderCounter);
AwaitBuilder.ChangeBuilder(BuilderType, StateMachineNumber);
AwaitBuilder.AddAwaiter(false, a.ex, StateMachineNumber);
}
if (AwaitBuilder.AwaiterCounter == AwaiterCounter - 1)
{
AwaitBuilder.GenAwait(ChangeBuilderCounter + 2, a.ex);
AwaitBuilder.GenAwait(StateMachineNumber + 2);
AwaitBuilder.DeleteBody();
}
}
public void GenMain(program_module pm)
{
if (MainVisitor.flag)
if (MainVisitor.HasAwait)
{
var d = pm.program_block.defs;
var p = new procedure_definition();
p.proc_header = new function_header(new template_type_reference(new named_type_reference(new ident("Task", d.source_context), d.source_context),
new template_param_list(new named_type_reference(new ident("integer", d.source_context), d.source_context), d.source_context), d.source_context), d.source_context);
p.proc_header = new function_header(new template_type_reference(new named_type_reference(new ident("Task",
d.source_context), d.source_context),
new template_param_list(new named_type_reference(new ident("integer", d.source_context),
d.source_context), d.source_context), d.source_context), d.source_context);
p.proc_header.name = new method_name();
p.proc_header.name.meth_name = new ident("@AsyncMain", d.source_context);
p.proc_header.IsAsync = true;
@ -151,7 +147,6 @@ namespace SyntaxVisitors
foreach (var item in pm.program_block.program_code.list)
{
b.program_code.list.Add(item);
}
p.proc_body = b;
pm.program_block.program_code.list.Clear();
@ -159,8 +154,10 @@ namespace SyntaxVisitors
var mc = new method_call();
mc.dereferencing_value = new ident("@AsyncMain");
mc.source_context = d.source_context;
var vst = new var_statement(new var_def_statement(new ident("@aw_main", d.source_context), mc, d.source_context), d.source_context);
var pc = new procedure_call(new dot_node(new dot_node(new ident("@aw_main", d.source_context), new ident("GetAwaiter")), new ident("GetResult"), d.source_context), d.source_context);
var vst = new var_statement(new var_def_statement(new ident("@aw_main",
d.source_context), mc, d.source_context), d.source_context);
var pc = new procedure_call(new dot_node(new dot_node(new ident("@aw_main", d.source_context),
new ident("GetAwaiter")), new ident("GetResult"), d.source_context), d.source_context);
pm.program_block.program_code.list.Add(vst);
pm.program_block.program_code.list.Add(pc);
@ -169,11 +166,10 @@ namespace SyntaxVisitors
public override void visit(program_module pm)
{
program_Module = pm;
MainVisitor.flag = false;
MainVisitor.HasAwait = false;
MainVisitor.Accept(pm.program_block.program_code);
GenMain(pm);
DefaultVisit(pm);
if (proc_def_List.Count > 0)
{
@ -211,7 +207,7 @@ namespace SyntaxVisitors
}
DefaultVisit(p);
ChangeBuilderCounter += 4;
StateMachineNumber += 4;
}
AsyncBuilder.SortBlocks();

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,12 @@
using PascalABCCompiler.SyntaxTree;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SyntaxVisitors.Async
{
internal class MainVisitor :BaseChangeVisitor
{
public static bool flag = false;
// Визитор, который проверяет, есть ли хоть
// один await в асинхронном методе
public static bool HasAwait = false;
public static MainVisitor New
{
get { return new MainVisitor(); }
@ -23,11 +21,11 @@ namespace SyntaxVisitors.Async
}
public override void visit(await_node_statement ans)
{
flag = true;
HasAwait = true;
}
public override void visit(await_node aw)
{
flag = true;
HasAwait = true;
}
}
}

View file

@ -1,9 +1,4 @@
using PascalABCCompiler.SyntaxTree;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SyntaxVisitors.Async
{
@ -22,12 +17,6 @@ namespace SyntaxVisitors.Async
public override void visit(ident id)
{
//if (id.name != _oldName)
//{
// return;
//}
//id.name = _newName;
if (id.name == _oldName)
{
id.name = _newName;

View file

@ -7,39 +7,46 @@ using System.Threading.Tasks;
namespace SyntaxVisitors.Async
{
// Класс для работы с переменными
public class VarsHelper
{
// Множество всех переменных, находящихся в данном асинхронном методе
public HashSet<var_statement> VarsList = new HashSet<var_statement>();
// Множество всех явно типизированных переменных
public HashSet<var_statement> TypedVarsList = new HashSet<var_statement>();
// Множество всех неявно типизированных переменных
public HashSet<var_statement> NoneTypedVarsList = new HashSet<var_statement>();
public HashSet<string> Parametrs = new HashSet<string>();
// Множество всех параметров асинхронного метода
public HashSet<string> Parametrs = new HashSet<string>();
public HashSet<string> ExprHash = new HashSet<string>();
// Множество переменных, находящихся внутри await
public HashSet<string> ExprHash = new HashSet<string>();
// Множество переменных, являющихся Task<T>
public HashSet<var_statement> TasksHash = new HashSet<var_statement>();
// Словарик переменных, которые нужно переименовать
public Dictionary<string,string> RepVarsDict = new Dictionary<string,string>();
// Список переменных класса
public HashSet<string> ClassIdentSet = new HashSet<string>();
public HashSet<string> ClassStaticSet = new HashSet<string>();
// Список статических переменных класса
public HashSet<string> ClassStaticSet = new HashSet<string>();
// Словарик переменных, являющихся Task<T>
public Dictionary<string, string> TaskIdents = new Dictionary<string, string>();
int k = 0;
public VarsHelper()
{
}
int LabelNameCounter = 0;
public string newLabelName(string old)
{
k++;
return "@awvar@_" + k.ToString() +"_" + old;
LabelNameCounter++;
return "@awvar@_" + LabelNameCounter.ToString() +"_" + old;
}
// Добавляем новую переменную, которую нужно будет переименовать
public void AddNewRep(var_statement var_Statement)
{
foreach (var v in var_Statement.var_def.vars.list)
@ -47,7 +54,6 @@ namespace SyntaxVisitors.Async
{
if (v.name.StartsWith("@"))
{
//RepVarsDict.Add(v.name, v.name);
continue;
}
var nv = newLabelName(v.name);
@ -56,7 +62,8 @@ namespace SyntaxVisitors.Async
v.name = nv;
}
}
// Помечаем какими являются переменные, чтобы знать
// что делать с ними дальше
public void MarkVars()
{
foreach (var var in VarsList)

View file

@ -10,7 +10,6 @@ unit ABCObjects;
//{$apptype windows}
{$reference '%GAC%\System.Windows.Forms.dll'}
{$reference '%GAC%\System.Drawing.dll'}
{$gendoc true}
interface

View file

@ -7,7 +7,6 @@
unit CRT;
{$apptype console}
{$gendoc true}
interface

View file

@ -8,7 +8,6 @@ unit GraphABC;
{$apptype windows}
{$reference '%GAC%\System.Windows.Forms.dll'}
{$reference '%GAC%\System.Drawing.dll'}
{$gendoc true}
interface

View file

@ -7,8 +7,6 @@ unit PABCSystem;
{$zerobasedstrings off}
{$gendoc true}
// Default Application type
{$apptype console}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>