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:
parent
9fb7fba1e1
commit
6de1848ec9
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ unit ABCObjects;
|
|||
//{$apptype windows}
|
||||
{$reference '%GAC%\System.Windows.Forms.dll'}
|
||||
{$reference '%GAC%\System.Drawing.dll'}
|
||||
{$gendoc true}
|
||||
|
||||
interface
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
unit CRT;
|
||||
|
||||
{$apptype console}
|
||||
{$gendoc true}
|
||||
|
||||
interface
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ unit GraphABC;
|
|||
{$apptype windows}
|
||||
{$reference '%GAC%\System.Windows.Forms.dll'}
|
||||
{$reference '%GAC%\System.Drawing.dll'}
|
||||
{$gendoc true}
|
||||
|
||||
interface
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ unit PABCSystem;
|
|||
|
||||
{$zerobasedstrings off}
|
||||
|
||||
{$gendoc true}
|
||||
|
||||
// Default Application type
|
||||
{$apptype console}
|
||||
|
||||
|
|
|
|||
11
TestSuite/exe/eto.exe.config
Normal file
11
TestSuite/exe/eto.exe.config
Normal 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>
|
||||
Loading…
Reference in a new issue