From 3a6e470309f7564f884470007ca7e7ef49b197e1 Mon Sep 17 00:00:00 2001 From: Yuri Goncharuk Date: Tue, 24 Feb 2026 09:23:52 +0200 Subject: [PATCH 01/65] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82=D1=80?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=B2=D0=B5=D1=80=D1=88=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=B4=D0=BE=D1=87=D0=B5=D1=80=D0=BD=D0=B8=D1=85=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=BE=D0=B2=20=D0=B2=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20=D0=97=D0=B0=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=88=D0=B8=D1=82=D1=8C()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Processes/ProcessContext.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/OneScript.StandardLibrary/Processes/ProcessContext.cs b/src/OneScript.StandardLibrary/Processes/ProcessContext.cs index e4d2b6267..a1ee50e77 100644 --- a/src/OneScript.StandardLibrary/Processes/ProcessContext.cs +++ b/src/OneScript.StandardLibrary/Processes/ProcessContext.cs @@ -196,10 +196,15 @@ public int ProcessId [ContextProperty("Имя", "Name")] public string Name => _p.ProcessName; - [ContextMethod("Завершить","Stop")] - public void Stop() + /// + /// Завершить процесс и, опционально, все его дочерние процессы. + /// + /// Булево. Завершить все дочерние процессы. + /// Не все дочерние процессы удалось завершить (только при entireProcessTree = true). + [ContextMethod("Завершить", "Stop")] + public void Stop(bool entireProcessTree = false) { - _p.Kill(); + _p.Kill(entireProcessTree); } public void Dispose() From 82af84a50dbd8b5a8b5124528673c43f300ec493 Mon Sep 17 00:00:00 2001 From: Diversus23 Date: Wed, 8 Apr 2026 14:00:26 +0300 Subject: [PATCH 02/65] =?UTF-8?q?fix=20#1468=20#6=20#1329:=20=D0=A0=D0=B5?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=20=D0=BE?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=B0=20=D0=9F=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B9=D1=82=D0=B8=20/=20Goto=20=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=B8=D0=BB=D1=8F=D1=82=D0=BE=D1=80=D0=B5=20?= =?UTF-8?q?=D1=81=D1=82=D0=B5=D0=BA=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=BC=D0=B0?= =?UTF-8?q?=D1=88=D0=B8=D0=BD=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Compiler/CodeGeneratorPrivateTypes.cs | 18 + src/ScriptEngine/Compiler/CompilerErrors.cs | 12 + .../Compiler/StackMachineCodeGenerator.cs | 258 ++++++++++- .../GotoCodeGenerationTests.cs | 403 ++++++++++++++++++ tests/goto.os | 208 +++++++++ 5 files changed, 876 insertions(+), 23 deletions(-) create mode 100644 src/Tests/OneScript.Core.Tests/GotoCodeGenerationTests.cs create mode 100644 tests/goto.os diff --git a/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs b/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs index 28b7b4af7..945e91a23 100644 --- a/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs +++ b/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs @@ -42,5 +42,23 @@ public static NestedLoopInfo New() public List breakStatements; public int tryNesting; } + + private class LabelInfo + { + public int codeIndex = DUMMY_ADDRESS; + public List<(string type, int id)> blockStack; + public int tryNesting; + } + + private struct PendingGoto + { + public int commandIndex; + public int exitTryIndex; + public string labelName; + public List<(string type, int id)> blockStack; + public List<(int commandIndex, string loopType, int blockId)> loopCleanupSlots; + public CodeRange location; + public int tryNesting; + } } } \ No newline at end of file diff --git a/src/ScriptEngine/Compiler/CompilerErrors.cs b/src/ScriptEngine/Compiler/CompilerErrors.cs index 9651e20ad..983ce5b99 100644 --- a/src/ScriptEngine/Compiler/CompilerErrors.cs +++ b/src/ScriptEngine/Compiler/CompilerErrors.cs @@ -29,6 +29,18 @@ public static CodeError MissedImport(string symbol, string libName) => Create($"Свойство {symbol} принадлежит пакету {libName}, который не импортирован в данном модуле", $"Property {symbol} belongs to package {libName} which is not imported in this module"); + public static CodeError DuplicateLabelDefinition(string name) => + Create($"Дублирование определения метки ~{name}", + $"Duplicate label definition ~{name}"); + + public static CodeError UndefinedLabel(string name) => + Create($"Метка не определена ~{name}", + $"Undefined label ~{name}"); + + public static CodeError InvalidGotoTarget(string name) => + Create($"На метку с указанным именем имеется недопустимый переход (~{name})", + $"Invalid goto target (~{name})"); + private static CodeError Create(string ru, string en, [CallerMemberName] string errorId = default) { return new CodeError diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs index 3edae66b7..6d774216d 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -41,6 +41,20 @@ public partial class StackMachineCodeGenerator : BslSyntaxWalker private readonly List _forwardedMethods = new List(); private readonly Stack _nestedLoops = new Stack(); + private readonly Dictionary _labels = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly List _pendingGotos = new List(); + private readonly List<(string type, int id)> _blockStack = new List<(string type, int id)>(); + private int _tryNestingCount; + private int _blockIdCounter; + + private const string BlockWhile = "while"; + private const string BlockForEach = "foreach"; + private const string BlockFor = "for"; + private const string BlockIf = "if"; + private const string BlockElseIf = "elseif"; + private const string BlockElse = "else"; + private const string BlockTry = "try"; + private const string BlockExcept = "except"; private IBslProcess _compilerProcess; @@ -197,6 +211,7 @@ protected override void VisitModuleBody(BslSyntaxNode child) if (child.Children.Count == 0) return; + ResetLabelState(); var entry = _module.Code.Count; var localCtx = new SymbolScope(); _ctx.PushScope(localCtx, ScopeBindingDescriptor.ThisScope()); @@ -211,6 +226,7 @@ protected override void VisitModuleBody(BslSyntaxNode child) throw; } + FinalizePendingGotos(); _ctx.PopScope(); var topIdx = _ctx.ScopeCount - 1; @@ -249,12 +265,118 @@ private static string[] GetVariableNames(SymbolScope localCtx) protected override void VisitGotoNode(NonTerminalNode node) { - throw new NotSupportedException(); + var labelNode = (LabelNode)node.Children[0]; + var labelName = labelNode.LabelName; + + if (_labels.TryGetValue(labelName, out var labelInfo) && labelInfo.codeIndex != DUMMY_ADDRESS) + { + // Backward goto — label already known + var currentStack = SnapshotBlockStack(); + if (!IsValidGotoTarget(currentStack, labelInfo.blockStack)) + { + AddError(CompilerErrors.InvalidGotoTarget(labelName), node.Location); + return; + } + + GenerateLoopCleanup(currentStack, labelInfo.blockStack); + + var tryDiff = _tryNestingCount - labelInfo.tryNesting; + if (tryDiff > 0) + AddCommand(OperationCode.ExitTry, tryDiff); + + AddCommand(OperationCode.Jmp, labelInfo.codeIndex); + } + else + { + // Forward goto — label not yet known + if (!_labels.ContainsKey(labelName)) + _labels[labelName] = new LabelInfo(); + + var currentStack = SnapshotBlockStack(); + + // Reserve cleanup slots for loops (innermost to outermost) + var cleanupSlots = new List<(int commandIndex, string loopType, int blockId)>(); + for (int i = currentStack.Count - 1; i >= 0; i--) + { + var block = currentStack[i]; + if (block.type == BlockForEach || block.type == BlockFor) + { + var idx = AddCommand(OperationCode.Nop); + cleanupSlots.Add((idx, block.type, block.id)); + } + } + + var exitTryIndex = _tryNestingCount > 0 + ? AddCommand(OperationCode.ExitTry, 0) + : -1; + var jmpIndex = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); + + _pendingGotos.Add(new PendingGoto + { + commandIndex = jmpIndex, + exitTryIndex = exitTryIndex, + labelName = labelName, + blockStack = currentStack, + loopCleanupSlots = cleanupSlots, + location = node.Location, + tryNesting = _tryNestingCount + }); + } } protected override void VisitLabelNode(LabelNode node) { - throw new NotSupportedException(); + var labelName = node.LabelName; + + if (_labels.TryGetValue(labelName, out var existing) && existing.codeIndex != DUMMY_ADDRESS) + { + AddError(CompilerErrors.DuplicateLabelDefinition(labelName), node.Location); + return; + } + + var labelInfo = existing ?? new LabelInfo(); + labelInfo.codeIndex = _module.Code.Count; + labelInfo.blockStack = SnapshotBlockStack(); + labelInfo.tryNesting = _tryNestingCount; + _labels[labelName] = labelInfo; + + // Resolve pending forward gotos targeting this label + for (int i = _pendingGotos.Count - 1; i >= 0; i--) + { + var pending = _pendingGotos[i]; + if (!string.Equals(pending.labelName, labelName, StringComparison.OrdinalIgnoreCase)) + continue; + + if (!IsValidGotoTarget(pending.blockStack, labelInfo.blockStack)) + { + AddError(CompilerErrors.InvalidGotoTarget(labelName), pending.location); + } + else + { + // Patch loop cleanup slots + var exitedBlockIds = new HashSet(); + for (int j = labelInfo.blockStack.Count; j < pending.blockStack.Count; j++) + exitedBlockIds.Add(pending.blockStack[j].id); + + foreach (var slot in pending.loopCleanupSlots) + { + if (exitedBlockIds.Contains(slot.blockId)) + { + if (slot.loopType == BlockForEach) + CorrectCommand(slot.commandIndex, OperationCode.StopIterator, 0); + else if (slot.loopType == BlockFor) + CorrectCommand(slot.commandIndex, OperationCode.PopTmp, 1); + } + } + + var tryDiff = pending.tryNesting - labelInfo.tryNesting; + if (tryDiff > 0 && pending.exitTryIndex != -1) + CorrectCommandArgument(pending.exitTryIndex, tryDiff); + CorrectCommandArgument(pending.commandIndex, labelInfo.codeIndex); + } + + _pendingGotos.RemoveAt(i); + } } protected override void VisitMethod(MethodNode methodNode) @@ -332,23 +454,25 @@ protected override void VisitMethod(MethodNode methodNode) protected override void VisitMethodBody(MethodNode methodNode) { + ResetLabelState(); var codeStart = _module.Code.Count; - + foreach (var variableDefinition in methodNode.VariableDefinitions()) { VisitMethodVariable(methodNode, variableDefinition); } VisitCodeBlock(methodNode.MethodBody); - + if (methodNode.Signature.IsFunction) { // неявный возврат Undefined AddCommand(OperationCode.PushUndef); } - + var codeEnd = _module.Code.Count; - + FinalizePendingGotos(); + VisitBlockEnd(methodNode.EndLocation); // debug last line num AddCommand(OperationCode.Return); @@ -383,15 +507,17 @@ protected override void VisitWhileNode(WhileLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = conditionIndex; _nestedLoops.Push(loopRecord); + PushBlock(BlockWhile); base.VisitExpression(node.Children[0]); var jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); - + VisitCodeBlock(node.Children[1]); VisitBlockEnd(node.EndLocation); - + AddCommand(OperationCode.Jmp, conditionIndex); var endLoop = AddCommand(OperationCode.Nop); CorrectCommandArgument(jumpFalseIndex, endLoop); + PopBlock(); CorrectBreakStatements(_nestedLoops.Pop(), endLoop); } @@ -409,14 +535,16 @@ protected override void VisitForEachLoopNode(ForEachLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = loopBegin; _nestedLoops.Push(loopRecord); - + PushBlock(BlockForEach); + VisitIteratorLoopBody(node.LoopBody); VisitBlockEnd(node.EndLocation); - + AddCommand(OperationCode.Jmp, loopBegin); - + var indexLoopEnd = AddCommand(OperationCode.StopIterator); CorrectCommandArgument(condition, indexLoopEnd); + PopBlock(); CorrectBreakStatements(_nestedLoops.Pop(), indexLoopEnd); } @@ -447,6 +575,7 @@ protected override void VisitForLoopNode(ForLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = indexLoopBegin; _nestedLoops.Push(loopRecord); + PushBlock(BlockFor); VisitCodeBlock(node.LoopBody); VisitBlockEnd(node.EndLocation); @@ -456,6 +585,7 @@ protected override void VisitForLoopNode(ForLoopNode node) var indexLoopEnd = AddCommand(OperationCode.PopTmp, 1); CorrectCommandArgument(conditionIndex, indexLoopEnd); + PopBlock(); CorrectBreakStatements(_nestedLoops.Pop(), indexLoopEnd); } @@ -504,11 +634,13 @@ protected override void VisitIfNode(ConditionNode node) var jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); + PushBlock(BlockIf); VisitIfTruePart(node.TruePart); + PopBlock(); exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); bool hasAlternativeBranches = false; - + foreach (var alternative in node.GetAlternatives()) { CorrectCommandArgument(jumpFalseIndex, _module.Code.Count); @@ -517,7 +649,9 @@ protected override void VisitIfNode(ConditionNode node) AddLineNumber(alternative.Location.LineNumber); VisitIfExpression(elif.Expression); jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); + PushBlock(BlockElseIf); VisitIfTruePart(elif.TruePart); + PopBlock(); exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); } else @@ -525,7 +659,9 @@ protected override void VisitIfNode(ConditionNode node) hasAlternativeBranches = true; CorrectCommandArgument(jumpFalseIndex, _module.Code.Count); AddLineNumber(alternative.Location.LineNumber, CodeGenerationFlags.CodeStatistics); + PushBlock(BlockElse); VisitCodeBlock(alternative); + PopBlock(); } } @@ -850,17 +986,17 @@ private void GlobalCall(CallNode call, bool asFunction) if (asFunction) AddCommand(OperationCode.CallFunc, GetMethodRefNumber(methBinding)); else - AddCommand(OperationCode.CallProc, GetMethodRefNumber(methBinding)); + AddCommand(OperationCode.CallProc, GetMethodRefNumber(methBinding)); } else { // can be defined later - var forwarded = new ForwardedMethodDecl - { - identifier = identifier, - asFunction = asFunction, - location = identifierNode.Location, - factArguments = argList + var forwarded = new ForwardedMethodDecl + { + identifier = identifier, + asFunction = asFunction, + location = identifierNode.Location, + factArguments = argList }; PushCallArguments(call.ArgumentList); @@ -965,10 +1101,19 @@ protected override void VisitTryExceptNode(TryExceptNode node) protected override void VisitTryBlock(CodeBatchNode node) { PushTryNesting(); + PushBlock(BlockTry); base.VisitTryBlock(node); + PopBlock(); PopTryNesting(); } + protected override void VisitExceptBlock(CodeBatchNode node) + { + PushBlock(BlockExcept); + base.VisitExceptBlock(node); + PopBlock(); + } + protected override void VisitExecuteStatement(BslSyntaxNode node) { base.VisitExecuteStatement(node); @@ -1066,8 +1211,8 @@ private void MakeNewObjectStatic(NewObjectNode node) { PushCallArguments(node.ConstructorArguments); } - else - { + else + { AddCommand(OperationCode.ArgNum, 0); } @@ -1097,7 +1242,74 @@ private void PopTryNesting() _nestedLoops.Peek().tryNesting--; } } - + + private void PushBlock(string blockType) + { + _blockStack.Add((blockType, _blockIdCounter++)); + if (blockType == BlockTry) + _tryNestingCount++; + } + + private void PopBlock() + { + var last = _blockStack[_blockStack.Count - 1]; + _blockStack.RemoveAt(_blockStack.Count - 1); + if (last.type == BlockTry) + _tryNestingCount--; + } + + private List<(string type, int id)> SnapshotBlockStack() + { + return new List<(string type, int id)>(_blockStack); + } + + private static bool IsValidGotoTarget(List<(string type, int id)> gotoStack, List<(string type, int id)> labelStack) + { + if (labelStack.Count > gotoStack.Count) + return false; + for (int i = 0; i < labelStack.Count; i++) + { + if (labelStack[i].type != gotoStack[i].type || labelStack[i].id != gotoStack[i].id) + return false; + } + return true; + } + + private void ResetLabelState() + { + _labels.Clear(); + _pendingGotos.Clear(); + _blockStack.Clear(); + _tryNestingCount = 0; + _blockIdCounter = 0; + } + + private void GenerateLoopCleanup(List<(string type, int id)> gotoStack, List<(string type, int id)> labelStack) + { + // Generate cleanup from innermost to outermost for exited loops + for (int i = gotoStack.Count - 1; i >= labelStack.Count; i--) + { + if (gotoStack[i].type == BlockForEach) + AddCommand(OperationCode.StopIterator); + else if (gotoStack[i].type == BlockFor) + AddCommand(OperationCode.PopTmp, 1); + } + } + + private void CorrectCommand(int index, OperationCode code, int argument) + { + _module.Code[index] = new Command { Code = code, Argument = argument }; + } + + private void FinalizePendingGotos() + { + foreach (var pending in _pendingGotos) + { + AddError(CompilerErrors.UndefinedLabel(pending.labelName), pending.location); + } + _pendingGotos.Clear(); + } + private void CorrectCommandArgument(int index, int newArgument) { var cmd = _module.Code[index]; @@ -1319,7 +1531,7 @@ private int GetConstNumber(in ConstDefinition cDef) } private int GetIdentNumber(string ident) - { + { var idx = _module.Identifiers.IndexOf(ident); if (idx < 0) { diff --git a/src/Tests/OneScript.Core.Tests/GotoCodeGenerationTests.cs b/src/Tests/OneScript.Core.Tests/GotoCodeGenerationTests.cs new file mode 100644 index 000000000..f61c1eaad --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/GotoCodeGenerationTests.cs @@ -0,0 +1,403 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Moq; +using OneScript.Compilation.Binding; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Sources; +using ScriptEngine; +using ScriptEngine.Compiler; +using ScriptEngine.Machine; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class GotoCodeGenerationTests + { + private static StackRuntimeModule BuildModule(string code) + { + var lexer = new DefaultLexer(); + lexer.Iterator = SourceCodeBuilder.Create().FromString(code).Build().CreateIterator(); + var errSink = new ThrowingErrorSink(); + var parser = new DefaultBslParser( + lexer, + errSink, + Mock.Of()); + + var node = parser.ParseStatefulModule() as ModuleNode; + var ctx = new SymbolTable(); + ctx.PushScope(new SymbolScope(), ScopeBindingDescriptor.Static(null)); + var compiler = new StackMachineCodeGenerator(errSink, ExplicitImportsBehavior.Disabled); + return compiler.CreateModule(node, lexer.Iterator.Source, ctx, Mock.Of()); + } + + private static StackRuntimeModule BuildModuleWithErrors(string code, out List errors) + { + var lexer = new DefaultLexer(); + lexer.Iterator = SourceCodeBuilder.Create().FromString(code).Build().CreateIterator(); + var errSink = new ListErrorSink(); + var parser = new DefaultBslParser( + lexer, + errSink, + Mock.Of()); + + var node = parser.ParseStatefulModule() as ModuleNode; + var ctx = new SymbolTable(); + ctx.PushScope(new SymbolScope(), ScopeBindingDescriptor.Static(null)); + var compiler = new StackMachineCodeGenerator(errSink, ExplicitImportsBehavior.Disabled); + var module = compiler.CreateModule(node, lexer.Iterator.Source, ctx, Mock.Of()); + errors = new List(errSink.Errors); + return module; + } + + [Fact] + public void Forward_Goto_Compiles_Successfully() + { + var code = @" + А = 1; + Перейти ~Метка; + А = 2; + ~Метка: + А = 3;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + module.Code.Should().Contain(c => c.Code == OperationCode.Jmp); + } + + [Fact] + public void Backward_Goto_Compiles_Successfully() + { + var code = @" + А = 0; + ~Начало: + А = А + 1; + Если А < 5 Тогда + Перейти ~Начало; + КонецЕсли;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + module.Code.Should().Contain(c => c.Code == OperationCode.Jmp); + } + + [Fact] + public void Goto_Out_Of_Loop_Compiles_Successfully() + { + var code = @" + Для Инд = 1 По 10 Цикл + Для Инд2 = 1 По 10 Цикл + Если Инд2 = 5 Тогда + Перейти ~ВыходИзЦиклов; + КонецЕсли; + КонецЦикла; + КонецЦикла; + ~ВыходИзЦиклов: + А = 1;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + } + + [Fact] + public void Goto_Out_Of_If_Compiles_Successfully() + { + var code = @" + А = 1; + Если А = 1 Тогда + Перейти ~ПослеУсловия; + А = 2; + КонецЕсли; + ~ПослеУсловия: + А = 3;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + } + + [Fact] + public void Goto_Out_Of_Try_Compiles_With_ExitTry() + { + var code = @" + Попытка + Перейти ~ПослеПопытки; + Исключение + КонецПопытки; + ~ПослеПопытки: + А = 1;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + module.Code.Should().Contain(c => c.Code == OperationCode.ExitTry); + } + + [Fact] + public void Goto_In_Procedure_Compiles_Successfully() + { + var code = @" + Процедура Тест() + Перейти ~Конец; + А = 1; + ~Конец: + КонецПроцедуры"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + } + + [Fact] + public void Goto_Into_Loop_Is_Error() + { + var code = @" + Перейти ~Внутри; + Для Инд = 1 По 10 Цикл + ~Внутри: + А = 1; + КонецЦикла;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.InvalidGotoTarget)); + } + + [Fact] + public void Goto_Into_If_Is_Error() + { + var code = @" + Перейти ~Внутри; + Если Истина Тогда + ~Внутри: + А = 1; + КонецЕсли;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.InvalidGotoTarget)); + } + + [Fact] + public void Goto_Into_Try_Is_Error() + { + var code = @" + Перейти ~Внутри; + Попытка + ~Внутри: + А = 1; + Исключение + КонецПопытки;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.InvalidGotoTarget)); + } + + [Fact] + public void Goto_Into_Except_Is_Error() + { + var code = @" + Перейти ~Внутри; + Попытка + Исключение + ~Внутри: + А = 1; + КонецПопытки;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.InvalidGotoTarget)); + } + + [Fact] + public void Undefined_Label_Is_Error() + { + var code = @" + Перейти ~НесуществующаяМетка; + А = 1;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.UndefinedLabel)); + } + + [Fact] + public void Duplicate_Label_Is_Error() + { + var code = @" + ~Метка: + А = 1; + ~Метка: + А = 2;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.DuplicateLabelDefinition)); + } + + [Fact] + public void Goto_Between_Sibling_Blocks_Is_Error() + { + var code = @" + Для Инд = 1 По 10 Цикл + Перейти ~Цель; + КонецЦикла; + Пока Истина Цикл + ~Цель: + Прервать; + КонецЦикла;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.InvalidGotoTarget)); + } + + [Fact] + public void Labels_Do_Not_Leak_Between_Methods() + { + var code = @" + Процедура Первая() + ~Метка: + А = 1; + КонецПроцедуры + + Процедура Вторая() + Перейти ~Метка; + КонецПроцедуры"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.UndefinedLabel)); + } + + [Fact] + public void Goto_Out_Of_Except_Compiles_Successfully() + { + var code = @" + Попытка + А = 1 / 0; + Исключение + Перейти ~ПослеОбработки; + КонецПопытки; + ~ПослеОбработки: + А = 1;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + } + + [Fact] + public void Goto_From_Except_To_Try_Of_Same_Block_Is_Error() + { + var code = @" + Попытка + ~Внутри: + А = 1; + Исключение + Перейти ~Внутри; + КонецПопытки;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.InvalidGotoTarget)); + } + + [Fact] + public void Goto_Out_Of_Nested_Try_Generates_ExitTry_With_Correct_Depth() + { + var code = @" + Попытка + Попытка + Перейти ~Снаружи; + Исключение + КонецПопытки; + Исключение + КонецПопытки; + ~Снаружи: + А = 1;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + // должен быть ExitTry с аргументом 2 (выход из двух вложенных try) + module.Code.Should().Contain(c => c.Code == OperationCode.ExitTry && c.Argument == 2); + } + + [Fact] + public void Forward_Goto_Outside_Try_Does_Not_Generate_ExitTry() + { + var code = @" + А = 1; + Перейти ~Метка; + А = 2; + ~Метка: + А = 3;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + // ExitTry не должен генерироваться — goto вне try-блока + module.Code.Should().NotContain(c => c.Code == OperationCode.ExitTry); + } + + [Fact] + public void Goto_Out_Of_ForEach_Generates_StopIterator() + { + var code = @" + Массив = Новый Массив; + Для Каждого Элемент Из Массив Цикл + Перейти ~ПослеЦикла; + КонецЦикла; + ~ПослеЦикла: + А = 1;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + // goto из ForEach должен генерировать StopIterator для очистки итератора + module.Code.Should().Contain(c => c.Code == OperationCode.StopIterator); + } + + [Fact] + public void Goto_Out_Of_For_Generates_PopTmp() + { + var code = @" + Для Инд = 1 По 10 Цикл + Перейти ~ПослеЦикла; + КонецЦикла; + ~ПослеЦикла: + А = 1;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + // goto из Для должен генерировать PopTmp для очистки верхней границы + module.Code.Where(c => c.Code == OperationCode.PopTmp).Should().HaveCountGreaterThan(1); + } + + [Fact] + public void Goto_Between_Same_Type_Sibling_Blocks_Is_Error() + { + var code = @" + Если Истина Тогда + Перейти ~Цель; + КонецЕсли; + Если Истина Тогда + ~Цель: + А = 1; + КонецЕсли;"; + + BuildModuleWithErrors(code, out var errors); + errors.Should().Contain(e => e.ErrorId == nameof(CompilerErrors.InvalidGotoTarget)); + } + + [Fact] + public void Case_Insensitive_Labels_Work() + { + var code = @" + Перейти ~метка; + А = 999; + ~Метка: + А = 1;"; + + var module = BuildModule(code); + module.Should().NotBeNull(); + } + } +} diff --git a/tests/goto.os b/tests/goto.os new file mode 100644 index 000000000..80791b3e9 --- /dev/null +++ b/tests/goto.os @@ -0,0 +1,208 @@ +/////////////////////////////////////////////////////////////////////// +// +// Тест оператора Перейти (Goto) +// +/////////////////////////////////////////////////////////////////////// + +Перем юТест; + +//////////////////////////////////////////////////////////////////// +// Программный интерфейс + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереходВперед"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереходНазад"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВыходИзВложенныхЦиклов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВыходИзУсловия"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВыходИзПопытки"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереходВПроцедуре"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьНесколькоМетокВМодуле"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереходВФункции"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЭмуляциюЦиклаЧерезGoto"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЭмуляциюRepeatUntil"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьПереходВперед() Экспорт + + А = 1; + Перейти ~Метка; + А = 999; + ~Метка: + юТест.ПроверитьРавенство(А, 1, "Переход вперед: код между goto и меткой не должен выполняться"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПереходНазад() Экспорт + + Счетчик = 0; + ~Начало: + Счетчик = Счетчик + 1; + Если Счетчик < 5 Тогда + Перейти ~Начало; + КонецЕсли; + + юТест.ПроверитьРавенство(Счетчик, 5, "Переход назад: цикл через goto должен отработать 5 раз"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВыходИзВложенныхЦиклов() Экспорт + + Результат = 0; + Для Индекс1 = 1 По 10 Цикл + Для Индекс2 = 1 По 10 Цикл + Результат = Результат + 1; + Если Индекс2 = 3 Тогда + Перейти ~ВыходИзЦиклов; + КонецЕсли; + КонецЦикла; + КонецЦикла; + ~ВыходИзЦиклов: + + юТест.ПроверитьРавенство(Результат, 3, "Выход из вложенных циклов: должно быть 3 итерации внутреннего цикла"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВыходИзУсловия() Экспорт + + А = 1; + Б = 0; + Если А = 1 Тогда + Б = 10; + Перейти ~ПослеУсловия; + Б = 999; + КонецЕсли; + ~ПослеУсловия: + + юТест.ПроверитьРавенство(Б, 10, "Выход из условия: код после goto не должен выполняться"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВыходИзПопытки() Экспорт + + А = 0; + Попытка + А = 1; + Перейти ~ПослеПопытки; + А = 999; + Исключение + А = -1; + КонецПопытки; + ~ПослеПопытки: + + юТест.ПроверитьРавенство(А, 1, "Выход из попытки: должно быть значение до goto"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПереходВПроцедуре() Экспорт + + Результат = ВспомогательнаяПроцедураСGoto(); + юТест.ПроверитьРавенство(Результат, 42, "Переход в процедуре: goto внутри функции"); + +КонецПроцедуры + +Функция ВспомогательнаяПроцедураСGoto() + А = 42; + Перейти ~Конец; + А = 0; + ~Конец: + Возврат А; +КонецФункции + +Процедура ТестДолжен_ПроверитьНесколькоМетокВМодуле() Экспорт + + А = 0; + Б = 0; + + Перейти ~Первая; + А = 999; + ~Первая: + А = 1; + + Перейти ~Вторая; + Б = 999; + ~Вторая: + Б = 2; + + юТест.ПроверитьРавенство(А, 1, "Несколько меток: первая метка"); + юТест.ПроверитьРавенство(Б, 2, "Несколько меток: вторая метка"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПереходВФункции() Экспорт + + Результат = ФункцияСНесколькимиПереходами(3); + юТест.ПроверитьРавенство(Результат, "три", "Переход в функции с несколькими метками"); + + Результат = ФункцияСНесколькимиПереходами(1); + юТест.ПроверитьРавенство(Результат, "один", "Переход в функции с несколькими метками"); + + Результат = ФункцияСНесколькимиПереходами(99); + юТест.ПроверитьРавенство(Результат, "другое", "Переход в функции с несколькими метками"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЭмуляциюЦиклаЧерезGoto() Экспорт + + // Эмуляция цикла Для Сч = 1 По 10 через Goto + Сумма = 0; + Сч = 1; + ~НачалоЦикла: + Если Сч > 10 Тогда + Перейти ~ВыходЦикла; + КонецЕсли; + Сумма = Сумма + Сч; + Сч = Сч + 1; + Перейти ~НачалоЦикла; + ~ВыходЦикла: + + юТест.ПроверитьРавенство(Сумма, 55, "Эмуляция цикла: сумма чисел от 1 до 10 должна быть 55"); + юТест.ПроверитьРавенство(Сч, 11, "Эмуляция цикла: счетчик должен быть 11 после выхода"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЭмуляциюRepeatUntil() Экспорт + + // Эмуляция repeat...until: тело выполняется минимум 1 раз, + // условие выхода проверяется в конце + Сч = 0; + Произведение = 1; + ~ТелоЦикла: + Сч = Сч + 1; + Произведение = Произведение * Сч; + // until Сч >= 5 + Если Сч < 5 Тогда + Перейти ~ТелоЦикла; + КонецЕсли; + + // 1*2*3*4*5 = 120 + юТест.ПроверитьРавенство(Произведение, 120, "Repeat/Until: факториал 5 должен быть 120"); + юТест.ПроверитьРавенство(Сч, 5, "Repeat/Until: счетчик должен быть 5"); + +КонецПроцедуры + +Функция ФункцияСНесколькимиПереходами(Знач Номер) + Если Номер = 1 Тогда + Перейти ~Метка1; + ИначеЕсли Номер = 3 Тогда + Перейти ~Метка3; + Иначе + Перейти ~МеткаДругое; + КонецЕсли; + + ~Метка1: + Возврат "один"; + + ~Метка3: + Возврат "три"; + + ~МеткаДругое: + Возврат "другое"; +КонецФункции From 4f4ebffd97a7faa5806abf63b5396901f7bf8887 Mon Sep 17 00:00:00 2001 From: Diversus23 Date: Thu, 9 Apr 2026 13:49:17 +0300 Subject: [PATCH 03/65] =?UTF-8?q?refactor:=20=D0=A0=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20Goto=20\=20=D0=9F?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B9=D1=82=D0=B8=20=D0=BF=D0=BE=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BC=D0=B5=D1=87=D0=B0=D0=BD=D0=B8=D1=8F=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Compiler/CodeGeneratorPrivateTypes.cs | 18 ++++- .../Compiler/StackMachineCodeGenerator.cs | 65 ++++++++----------- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs b/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs index 945e91a23..69522b883 100644 --- a/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs +++ b/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs @@ -43,10 +43,22 @@ public static NestedLoopInfo New() public int tryNesting; } + private enum BlockType + { + While, + ForEach, + For, + If, + ElseIf, + Else, + Try, + Except + } + private class LabelInfo { public int codeIndex = DUMMY_ADDRESS; - public List<(string type, int id)> blockStack; + public List<(BlockType type, int id)> blockStack; public int tryNesting; } @@ -55,8 +67,8 @@ private struct PendingGoto public int commandIndex; public int exitTryIndex; public string labelName; - public List<(string type, int id)> blockStack; - public List<(int commandIndex, string loopType, int blockId)> loopCleanupSlots; + public List<(BlockType type, int id)> blockStack; + public List<(int commandIndex, BlockType loopType, int blockId)> loopCleanupSlots; public CodeRange location; public int tryNesting; } diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs index 6d774216d..05c77600e 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -43,19 +43,10 @@ public partial class StackMachineCodeGenerator : BslSyntaxWalker private readonly Stack _nestedLoops = new Stack(); private readonly Dictionary _labels = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly List _pendingGotos = new List(); - private readonly List<(string type, int id)> _blockStack = new List<(string type, int id)>(); + private readonly List<(BlockType type, int id)> _blockStack = new List<(BlockType type, int id)>(); private int _tryNestingCount; private int _blockIdCounter; - private const string BlockWhile = "while"; - private const string BlockForEach = "foreach"; - private const string BlockFor = "for"; - private const string BlockIf = "if"; - private const string BlockElseIf = "elseif"; - private const string BlockElse = "else"; - private const string BlockTry = "try"; - private const string BlockExcept = "except"; - private IBslProcess _compilerProcess; private HashSet _reportedOldProperties = new HashSet(); @@ -270,7 +261,7 @@ protected override void VisitGotoNode(NonTerminalNode node) if (_labels.TryGetValue(labelName, out var labelInfo) && labelInfo.codeIndex != DUMMY_ADDRESS) { - // Backward goto — label already known + // Обратный переход, метка уже определена var currentStack = SnapshotBlockStack(); if (!IsValidGotoTarget(currentStack, labelInfo.blockStack)) { @@ -288,18 +279,18 @@ protected override void VisitGotoNode(NonTerminalNode node) } else { - // Forward goto — label not yet known + // Прямой переход, метка еще не определена if (!_labels.ContainsKey(labelName)) _labels[labelName] = new LabelInfo(); var currentStack = SnapshotBlockStack(); - // Reserve cleanup slots for loops (innermost to outermost) - var cleanupSlots = new List<(int commandIndex, string loopType, int blockId)>(); + // Резервируем слоты очистки для циклов (от внутреннего к внешнему) + var cleanupSlots = new List<(int commandIndex, BlockType loopType, int blockId)>(); for (int i = currentStack.Count - 1; i >= 0; i--) { var block = currentStack[i]; - if (block.type == BlockForEach || block.type == BlockFor) + if (block.type == BlockType.ForEach || block.type == BlockType.For) { var idx = AddCommand(OperationCode.Nop); cleanupSlots.Add((idx, block.type, block.id)); @@ -340,7 +331,7 @@ protected override void VisitLabelNode(LabelNode node) labelInfo.tryNesting = _tryNestingCount; _labels[labelName] = labelInfo; - // Resolve pending forward gotos targeting this label + // Разрешаем отложенные прямые переходы, указывающие на эту метку for (int i = _pendingGotos.Count - 1; i >= 0; i--) { var pending = _pendingGotos[i]; @@ -353,7 +344,7 @@ protected override void VisitLabelNode(LabelNode node) } else { - // Patch loop cleanup slots + // Заполняем слоты очистки циклов var exitedBlockIds = new HashSet(); for (int j = labelInfo.blockStack.Count; j < pending.blockStack.Count; j++) exitedBlockIds.Add(pending.blockStack[j].id); @@ -362,9 +353,9 @@ protected override void VisitLabelNode(LabelNode node) { if (exitedBlockIds.Contains(slot.blockId)) { - if (slot.loopType == BlockForEach) + if (slot.loopType == BlockType.ForEach) CorrectCommand(slot.commandIndex, OperationCode.StopIterator, 0); - else if (slot.loopType == BlockFor) + else if (slot.loopType == BlockType.For) CorrectCommand(slot.commandIndex, OperationCode.PopTmp, 1); } } @@ -507,7 +498,7 @@ protected override void VisitWhileNode(WhileLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = conditionIndex; _nestedLoops.Push(loopRecord); - PushBlock(BlockWhile); + PushBlock(BlockType.While); base.VisitExpression(node.Children[0]); var jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); @@ -535,7 +526,7 @@ protected override void VisitForEachLoopNode(ForEachLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = loopBegin; _nestedLoops.Push(loopRecord); - PushBlock(BlockForEach); + PushBlock(BlockType.ForEach); VisitIteratorLoopBody(node.LoopBody); VisitBlockEnd(node.EndLocation); @@ -575,7 +566,7 @@ protected override void VisitForLoopNode(ForLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = indexLoopBegin; _nestedLoops.Push(loopRecord); - PushBlock(BlockFor); + PushBlock(BlockType.For); VisitCodeBlock(node.LoopBody); VisitBlockEnd(node.EndLocation); @@ -634,7 +625,7 @@ protected override void VisitIfNode(ConditionNode node) var jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); - PushBlock(BlockIf); + PushBlock(BlockType.If); VisitIfTruePart(node.TruePart); PopBlock(); exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); @@ -649,7 +640,7 @@ protected override void VisitIfNode(ConditionNode node) AddLineNumber(alternative.Location.LineNumber); VisitIfExpression(elif.Expression); jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); - PushBlock(BlockElseIf); + PushBlock(BlockType.ElseIf); VisitIfTruePart(elif.TruePart); PopBlock(); exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); @@ -659,7 +650,7 @@ protected override void VisitIfNode(ConditionNode node) hasAlternativeBranches = true; CorrectCommandArgument(jumpFalseIndex, _module.Code.Count); AddLineNumber(alternative.Location.LineNumber, CodeGenerationFlags.CodeStatistics); - PushBlock(BlockElse); + PushBlock(BlockType.Else); VisitCodeBlock(alternative); PopBlock(); } @@ -1101,7 +1092,7 @@ protected override void VisitTryExceptNode(TryExceptNode node) protected override void VisitTryBlock(CodeBatchNode node) { PushTryNesting(); - PushBlock(BlockTry); + PushBlock(BlockType.Try); base.VisitTryBlock(node); PopBlock(); PopTryNesting(); @@ -1109,7 +1100,7 @@ protected override void VisitTryBlock(CodeBatchNode node) protected override void VisitExceptBlock(CodeBatchNode node) { - PushBlock(BlockExcept); + PushBlock(BlockType.Except); base.VisitExceptBlock(node); PopBlock(); } @@ -1243,10 +1234,10 @@ private void PopTryNesting() } } - private void PushBlock(string blockType) + private void PushBlock(BlockType blockType) { _blockStack.Add((blockType, _blockIdCounter++)); - if (blockType == BlockTry) + if (blockType == BlockType.Try) _tryNestingCount++; } @@ -1254,16 +1245,16 @@ private void PopBlock() { var last = _blockStack[_blockStack.Count - 1]; _blockStack.RemoveAt(_blockStack.Count - 1); - if (last.type == BlockTry) + if (last.type == BlockType.Try) _tryNestingCount--; } - private List<(string type, int id)> SnapshotBlockStack() + private List<(BlockType type, int id)> SnapshotBlockStack() { - return new List<(string type, int id)>(_blockStack); + return new List<(BlockType type, int id)>(_blockStack); } - private static bool IsValidGotoTarget(List<(string type, int id)> gotoStack, List<(string type, int id)> labelStack) + private static bool IsValidGotoTarget(List<(BlockType type, int id)> gotoStack, List<(BlockType type, int id)> labelStack) { if (labelStack.Count > gotoStack.Count) return false; @@ -1284,14 +1275,14 @@ private void ResetLabelState() _blockIdCounter = 0; } - private void GenerateLoopCleanup(List<(string type, int id)> gotoStack, List<(string type, int id)> labelStack) + private void GenerateLoopCleanup(List<(BlockType type, int id)> gotoStack, List<(BlockType type, int id)> labelStack) { - // Generate cleanup from innermost to outermost for exited loops + // Генерация очистки стека от внутреннего цикла к внешнему при выходе через Перейти for (int i = gotoStack.Count - 1; i >= labelStack.Count; i--) { - if (gotoStack[i].type == BlockForEach) + if (gotoStack[i].type == BlockType.ForEach) AddCommand(OperationCode.StopIterator); - else if (gotoStack[i].type == BlockFor) + else if (gotoStack[i].type == BlockType.For) AddCommand(OperationCode.PopTmp, 1); } } From 49387e294e40f925ed30716beeff234ded1238ae Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Mon, 20 Apr 2026 11:31:47 +0300 Subject: [PATCH 04/65] =?UTF-8?q?=D0=91=D0=B0=D0=BC=D0=BF=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8=20=D0=B8=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BB=20rc=20=D0=B8=D0=B7=20=D0=BD=D0=BE=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 706ed496d..c7f656e59 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,8 +4,8 @@ pipeline { agent none environment { - VersionPrefix = '2.0.2' - VersionSuffix = 'rc.1'+"+${BUILD_NUMBER}" + VersionPrefix = '2.1.0' + VersionSuffix = 'dev'+"+${BUILD_NUMBER}" outputEnc = '65001' } From 9490621b1de892a80871c53f9f7c050cdd799a0b Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Wed, 29 Apr 2026 00:33:35 +0300 Subject: [PATCH 05/65] fix(text-read): remove legacy constructor defaults --- src/OneScript.StandardLibrary/Text/TextReadImpl.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/OneScript.StandardLibrary/Text/TextReadImpl.cs b/src/OneScript.StandardLibrary/Text/TextReadImpl.cs index e0d3a5c4e..02d7190de 100644 --- a/src/OneScript.StandardLibrary/Text/TextReadImpl.cs +++ b/src/OneScript.StandardLibrary/Text/TextReadImpl.cs @@ -171,8 +171,7 @@ private void RequireOpen() public static TextReadImpl Constructor (IValue input) { var reader = new TextReadImpl (); - reader.AnalyzeDefaultLineFeed = false; - reader.Open (input, null, "\n", "\r\n"); + reader.Open(input); return reader; } @@ -183,9 +182,7 @@ public static TextReadImpl Constructor (IValue input) [ScriptConstructor(Name = "Формирование неинициализированного объекта")] public static TextReadImpl Constructor() { - var reader = new TextReadImpl(); - reader.AnalyzeDefaultLineFeed = false; - return reader; + return new TextReadImpl(); } /// From ce3e465d10981012c2408e490d6c1a32dc8c9a49 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Wed, 29 Apr 2026 00:33:41 +0300 Subject: [PATCH 06/65] test(text-read): update compatibility expectations for default params --- tests/text-read.os | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/text-read.os b/tests/text-read.os index 7d1835d79..f29dfb1f4 100644 --- a/tests/text-read.os +++ b/tests/text-read.os @@ -144,8 +144,10 @@ // В конструкторе не указываем конвертируемый перевод строк Ч = Новый ЧтениеТекста(ИмяВременногоФайла); - юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "123123123\r", "\r в конце строки (через конструктор без параметров по-умолчанию)"); - юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "45/645/645/6\r", "\r в конце строки (через конструктор без параметров по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "123123123", "Нет \r в конце строки (через конструктор без параметров по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "", "Пустая строка вместо \r (через конструктор без параметров по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "45/645/645/6", "Нет \r в конце строки (через конструктор без параметров по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "", "Пустая строка вместо \r (через конструктор без параметров по-умолчанию)"); юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "789789789"); Ч.Закрыть(); @@ -163,8 +165,10 @@ // Используем конструктор по-умолчанию и открытие без указания конвертируемого перевода строк Ч = Новый ЧтениеТекста; Ч.Открыть(ИмяВременногоФайла); - юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "123123123\r", "\r в конце строки (через открытие без параметров по-умолчанию)"); - юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "45/645/645/6\r", "\r в конце строки (через открытие без параметров по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "123123123", "Нет \r в конце строки (через открытие без параметров по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "", "Пустая строка вместо \r (через открытие без параметров по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "45/645/645/6", "Нет \r в конце строки (через открытие без параметров по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "", "Пустая строка вместо \r (через открытие без параметров по-умолчанию)"); юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "789789789"); Ч.Закрыть(); @@ -172,8 +176,10 @@ // Используем конструктор по-умолчанию и открытие с указанием конвертируемого перевода строк "по-умолчанию" Ч = Новый ЧтениеТекста; Ч.Открыть(ИмяВременногоФайла,,,); - юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "123123123\r", "\r в конце строки (через открытие с параметрами по-умолчанию)"); - юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "45/645/645/6\r", "\r в конце строки (через открытие с параметрами по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "123123123", "Нет \r в конце строки (через открытие с параметрами по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "", "Пустая строка вместо \r (через открытие с параметрами по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "45/645/645/6", "Нет \r в конце строки (через открытие с параметрами по-умолчанию)"); + юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "", "Пустая строка вместо \r (через открытие с параметрами по-умолчанию)"); юТест.ПроверитьРавенство(ЭкранироватьПереводыСтрок(Ч.ПрочитатьСтроку()), "789789789"); Ч.Закрыть(); From f8af9ef45d54ec15450a3c43ac688bebcae04935 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Wed, 29 Apr 2026 11:11:34 +0300 Subject: [PATCH 07/65] =?UTF-8?q?docs:=20=D0=B0=D0=BA=D1=82=D1=83=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B4=D0=BE=D0=BA?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20=D0=BF=D0=BE=D0=B4=20.NE?= =?UTF-8?q?T=208=20=D0=B8=20=D1=84=D0=B0=D0=BA=D1=82=D0=B8=D1=87=D0=B5?= =?UTF-8?q?=D1=81=D0=BA=D1=83=D1=8E=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82?= =?UTF-8?q?=D1=83=D1=80=D1=83=20=D0=BA=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README.md / README-EN.md: убрано устаревшее упоминание .net6 (фактический таргет — net8.0), команды переведены на `dotnet msbuild`; добавлены отсутствовавшие таргеты `GatherLibrary`, `Test`, `UnitTests`, `ScriptedTests`, `PublishNuget` и параметры `NoCppCompiler`, `Configuration`; добавлены разделы «Тестирование» (с командами для модульных C# и приёмочных BSL-тестов через tests/run-bsl-tests.*) и «Документация для разработчиков» со ссылками на docs/ и CODESTYLE.md. Русская и английская версии приведены к одинаковой структуре. - docs/developer_docs.md: починена поломанная нумерация разделов (3.1–3.4, 7., 3.6–3.10 → последовательно 3.1–3.10), убран дубль заголовка в разделе про OneScript.Core, уточнены реальные пути к файлам (PreprocessingLexer.cs и LocalizedErrors.cs — в SyntaxAnalysis/; CompilerBackendSelector вместо BackendSelector; ExecutionFrame вместо Frame; ValueList/, ValueTable/, ValueTree/ как подкаталоги Collections/; актуальные классы OneScript.DebugServices и StandaloneRunner), исправлена опечатка ContextValuesMarhaller → ContextValuesMarshaller, добавлены ссылки на README.md, contexts.md, CODESTYLE.md и .github/copilot-instructions.md. - docs/contexts.md: починена битая ссылка docs/arhitecture_overview.md → docs/developer_docs.md; заголовки приведены к markdown-разметке; оглавление сделано кликабельным; дописаны обещанные в содержании, но отсутствовавшие разделы (i18n для API, Депрекейшен и едупреждения, Тестирование C# и BSL, Документация (OneScriptDocumenter), Безопасность, Чек-лист готовности). В C#-шаблонах исправлены using-и: `OneScript.Execution` вместо несуществующего `OneScript.Core.Execution`, добавлен `ScriptEngine.Machine` для IValue/ValueFactory; сверено с живыми контекстами из OneScript.StandardLibrary. --- README-EN.md | 83 ++++++++--- README.md | 84 ++++++++--- docs/contexts.md | 266 +++++++++++++++++++++++---------- docs/developer_docs.md | 330 ++++++++++++++++++++++------------------- 4 files changed, 499 insertions(+), 264 deletions(-) diff --git a/README-EN.md b/README-EN.md index 531111fc8..9c1476095 100644 --- a/README-EN.md +++ b/README-EN.md @@ -57,50 +57,97 @@ The OneScript distribution already includes a set of the most commonly used pack ## Preparation -Links to distributions are provided below, however, please note that links may change over time and their relevance is not guaranteed. You need dotnet SDK and C++ compiler, which can be downloaded from anywhere you can find. +To build the project you need: -* Install [MS BuildTools](https://visualstudio.microsoft.com/ru/thank-you-downloading-visual-studio/?sku=buildtools&rel=16), when installing enable targeting for .net6, .net4.8, install C++ compiler. +* [.NET SDK 8.0](https://dotnet.microsoft.com/download/dotnet/8.0) (the project's target framework is `net8.0`). +* A C++ compiler — only required to build the native bridge `ScriptEngine.NativeApi` (support for native add-ins compatible with the 1C NativeApi). On Windows the easiest way to get one is to install [MS Build Tools](https://visualstudio.microsoft.com/visual-studio-build-tools/) or Visual Studio with the "Desktop development with C++" workload. If a C++ compiler is not available, see the `NoCppCompiler` parameter below. + +> Distribution links may change over time, their relevance is not guaranteed. ## Build -Launch Developer Command Prompt (will appear in the Start menu after installing MSBuildTools or Visual Studio). Navigate to the OneScript repository directory. The following are commands in the Developer Command Prompt console. -Build is performed using msbuild. Targets: +The build is driven by MSBuild via the `Build.csproj` script in the repository root. Commands can be run with `msbuild` (Developer Command Prompt installed together with MS Build Tools/Visual Studio) or with `dotnet msbuild` (cross-platform). + +Main targets: -* CleanAll - clean previous build results -* BuildAll - prepare files for distribution -* MakeCPP;MakeFDD;MakeSCD;BuildDebugger - separate build targets for preparing different types of distributions -* PrepareDistributionFiles - build full distribution packages (including libraries) -* PackDistributions - prepare ZIP archives for distribution -* CreateNuget - create packages for publishing to NuGet +* `CleanAll` — clean previous build results; +* `BuildAll` — build binary files for distribution (FDD, SCD, debugger; native components are also built when a C++ compiler is available); +* `MakeCPP`, `MakeFDD`, `MakeSCD`, `BuildDebugger` — individual targets that build specific parts of the distribution; +* `GatherLibrary` — download and stage the base set of libraries (`opm`, `asserts`, `logos`, `fs`, `tempfiles`, `cli`); +* `PrepareDistributionFiles` — build full distribution contents (including libraries and documentation); +* `PackDistributions` — pack the distribution contents into ZIP archives for all supported platforms; +* `BuildDocumentation` — generate the platform reference (markdown + json); +* `CreateNuget` / `PublishNuget` — build and publish NuGet packages; +* `Test` (`UnitTests`, `ScriptedTests`) — run unit and acceptance (BSL) tests. **Build parameters** -* VersionPrefix - release number prefix, its main part, for example, 2.0.0 -* VersionSuffix - version suffix, which usually acts as an arbitrary versioning suffix according to semver, for example, beta-786 (optional) -* NoCppCompiler - if True - C++ compiler is not installed, C++ components (NativeApi support) will not be added to the build +* `VersionPrefix` — main part of the release number, for example `2.0.0` (defaults to `2.0.0`); +* `VersionSuffix` — optional SemVer suffix, for example `beta-786`; +* `NoCppCompiler` — when set to `True`, native C++ components (NativeApi) are not built and not included in the distribution (use it when no C++ compiler is installed); +* `Configuration` — build configuration, defaults to `Release`. For a debug build on Linux use `LinuxDebug`. -All distribution files will be placed in the `built` directory at the root of the 1Script repository +All build artifacts are placed in the `built` directory at the repository root. ### Building distribution contents in a separate directory ```bat -msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles +dotnet msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles ``` ### Building with manual version specification ```bat -msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles /p:VersionPrefix=2.0.0 +dotnet msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles /p:VersionPrefix=2.0.0 ``` ### Building ZIP distributions ```bat -msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles;PackDistributions /p:VersionPrefix=2.0.0 /p:VersionSuffix=preview223 +dotnet msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles;PackDistributions /p:VersionPrefix=2.0.0 /p:VersionSuffix=preview223 +``` + +### Building without C++ components (no NativeApi) + +```bat +dotnet msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles /p:NoCppCompiler=True ``` ### Documentation generation ```bat -msbuild Build.csproj /t:BuildDocumentation +dotnet msbuild Build.csproj /t:BuildDocumentation ``` + +# Testing + +The project has two layers of tests: + +* **C# unit tests** — located in `src/Tests/*` (xUnit/NUnit). Run them via `dotnet test` in the corresponding test project directory or all at once: + ```bat + dotnet msbuild Build.csproj /t:UnitTests + ``` +* **BSL acceptance tests** — located in the `tests/` directory and executed by `testrunner.os` against a freshly built `oscript`. The repository includes wrapper scripts: + ```bat + rem Windows + tests\run-bsl-tests.cmd src\oscript\bin\Debug\net8.0\oscript.exe + ``` + + ```bash + # Linux/macOS + tests/run-bsl-tests.sh src/oscript/bin/Debug/net8.0/oscript + ``` + + Before running the acceptance tests, build `oscript`: + ```bat + dotnet build src/oscript/oscript.csproj + ``` + +# Developer documentation + +If you want to contribute to the project, take a look at the additional documents in the [`docs/`](docs/) directory: + +* [`docs/developer_docs.md`](docs/developer_docs.md) — project architecture, solution layout and guided tour of the source code. +* [`docs/contexts.md`](docs/contexts.md) — a practical guide to adding BSL contexts, methods, properties and global functions. +* [`CODESTYLE.md`](CODESTYLE.md) — C# code style requirements. + diff --git a/README.md b/README.md index 959253f91..95779d67d 100644 --- a/README.md +++ b/README.md @@ -57,50 +57,96 @@ OneScript позволяет создавать и выполнять текст ## Подготовка -Ниже приведены ссылки на дистрибутивы, однако, учтите, что ссылки могут меняться со временем и их актуальность не гарантируется. Нужен dotnet SDK и компилятор C++, скачать можно из любого места, которое нагуглится. +Для сборки потребуется: -* Установить [MS BuildTools](https://visualstudio.microsoft.com/ru/thank-you-downloading-visual-studio/?sku=buildtools&rel=16), при установке включить таргетинг на .net6, .net4.8, установить компилятор C++. +* [.NET SDK 8.0](https://dotnet.microsoft.com/download/dotnet/8.0) (целевой фреймворк проекта — `net8.0`). +* Компилятор C++ — нужен только для сборки нативного моста `ScriptEngine.NativeApi` (поддержка внешних компонент стандарта 1С NativeApi). На Windows проще всего получить его, поставив [MS Build Tools](https://visualstudio.microsoft.com/visual-studio-build-tools/) или Visual Studio с компонентом «Разработка классических приложений на C++». Если C++ компилятора нет, см. параметр `NoCppCompiler` ниже. + +> Ссылки на дистрибутивы могут меняться со временем, их актуальность не гарантируется. ## Сборка -Запустить Developer Command Prompt (появится в меню Пуск после установки MSBuildTools или Visual Studio). Перейти в каталог репозитория OneScript. Далее приведены команды в консоли Developer Command Prompt -Сборка выполняется с помощью msbuild. Таргеты: +Сборка выполняется с помощью MSBuild и сценария `Build.csproj` в корне репозитория. Команды можно запускать как через `msbuild` (Developer Command Prompt после установки MS Build Tools/Visual Studio), так и через `dotnet msbuild` (кросс-платформенно). + +Основные таргеты: -* CleanAll - очистка результатов предыдущих сборок -* BuildAll - подготовить файлы для поставки -* MakeCPP;MakeFDD;MakeSCD;BuildDebugger - отдельные таргеты сборки для подготовки разных типов поставки -* PrepareDistributionFiles - сборка полных пакетов поставки (включая библиотеки) -* PackDistributions - подготовка ZIP архивов поставки -* CreateNuget - создать пакеты для публикации в NuGet +* `CleanAll` — очистка результатов предыдущих сборок; +* `BuildAll` — собрать бинарные файлы для поставки (FDD, SCD, отладчик; при наличии C++ — нативные компоненты); +* `MakeCPP`, `MakeFDD`, `MakeSCD`, `BuildDebugger` — отдельные таргеты сборки разных частей поставки; +* `GatherLibrary` — скачать и сложить базовый набор библиотек (`opm`, `asserts`, `logos`, `fs`, `tempfiles`, `cli`); +* `PrepareDistributionFiles` — собрать полные содержимые дистрибутивов (вкл. библиотеки и документацию); +* `PackDistributions` — упаковать содержимое в ZIP-архивы под все поддерживаемые платформы; +* `BuildDocumentation` — сгенерировать справку по платформе (markdown + json); +* `CreateNuget` / `PublishNuget` — собрать и опубликовать NuGet-пакеты; +* `Test` (`UnitTests`, `ScriptedTests`) — прогнать модульные и приёмочные (BSL) тесты. **Параметры сборки** -* VersionPrefix - префикс номера релиза, его основная часть, например, 2.0.0 -* VersionSuffix - суффикс номера, который обычно выступает в качестве произвольного суффикса версионирования по semver, например, beta-786 (необязателен) -* NoCppCompiler - если True - не установлен компилятор C++, в сборку не будут добавлены компоненты C++ (поддержка NativeApi) +* `VersionPrefix` — основная часть номера релиза, например `2.0.0` (по умолчанию `2.0.0`); +* `VersionSuffix` — необязательный suffix по SemVer, например `beta-786`; +* `NoCppCompiler` — если `True`, нативные компоненты C++ (NativeApi) не собираются и не включаются в дистрибутив (используйте, если компилятор C++ не установлен); +* `Configuration` — конфигурация сборки, по умолчанию `Release`. Для отладочной сборки на Linux используется `LinuxDebug`. -Все поставляемые файлы будут размещены в каталоге `built` в корне репозитория 1Script +Все артефакты сборки размещаются в каталоге `built` в корне репозитория. ### Сборка содержимого дистрибутивов в отдельном каталоге ```bat -msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles +dotnet msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles ``` ### Сборка с ручным указанием версии ```bat -msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles /p:VersionPrefix=2.0.0 +dotnet msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles /p:VersionPrefix=2.0.0 ``` ### Сборка ZIP-дистрибутивов ```bat -msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles;PackDistributions /p:VersionPrefix=2.0.0 /p:VersionSuffix=preview223 +dotnet msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles;PackDistributions /p:VersionPrefix=2.0.0 /p:VersionSuffix=preview223 +``` + +### Сборка без C++-компонент (без NativeApi) + +```bat +dotnet msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles /p:NoCppCompiler=True ``` ### Генерация документации ```bat -msbuild Build.csproj /t:BuildDocumentation -``` \ No newline at end of file +dotnet msbuild Build.csproj /t:BuildDocumentation +``` + +# Тестирование + +В проекте есть два уровня тестов: + +* **Модульные тесты на C#** — расположены в `src/Tests/*` (xUnit/NUnit), запускаются через `dotnet test` в каталоге соответствующего тестового проекта или одной командой: + ```bat + dotnet msbuild Build.csproj /t:UnitTests + ``` +* **Приёмочные тесты на BSL** — расположены в каталоге `tests/` и запускаются через `testrunner.os` на свежесобранном `oscript`. Для удобства в репозитории есть скрипты-обёртки: + ```bat + rem Windows + tests\run-bsl-tests.cmd src\oscript\bin\Debug\net8.0\oscript.exe + ``` + + ```bash + # Linux/macOS + tests/run-bsl-tests.sh src/oscript/bin/Debug/net8.0/oscript + ``` + + Перед запуском приёмочных тестов нужно собрать `oscript`: + ```bat + dotnet build src/oscript/oscript.csproj + ``` + +# Документация для разработчиков + +Если вы хотите контрибьютить в проект, познакомьтесь с дополнительными документами в каталоге [`docs/`](docs/): + +* [`docs/developer_docs.md`](docs/developer_docs.md) — архитектура проекта, состав решения и навигация по исходному коду. +* [`docs/contexts.md`](docs/contexts.md) — практическое руководство по добавлению BSL-контекстов, методов, свойств и глобальных функций. +* [`CODESTYLE.md`](CODESTYLE.md) — требования к стилю кода на C#. diff --git a/docs/contexts.md b/docs/contexts.md index 69b55c98f..f82c05f9e 100644 --- a/docs/contexts.md +++ b/docs/contexts.md @@ -1,52 +1,51 @@ # BSL-контексты и глобальные методы: руководство разработчика -Этот документ — практическая инструкция по добавлению в OneScript новых BSL‑контекстов (классов), методов и свойств, а также глобальных методов. Здесь собраны готовые сниппеты, чек‑лист и ссылки на ключевые места в исходниках. +Этот документ — практическая инструкция по добавлению в OneScript новых BSL-контекстов (классов), методов и свойств, а также глобальных методов. Здесь собраны готовые сниппеты, чек-лист и ссылки на ключевые места в исходниках. -См. также «Архитектурный обзор»: docs/arhitecture_overview.md (карта компонентов и «куда лезть»). +См. также [`docs/developer_docs.md`](developer_docs.md) — карта компонентов и «куда лезть» при доработках. -Содержание +## Содержание -- Что такое BSL‑контекст -- Добавление нового BSL‑класса (контекста) -- Добавление свойства -- Добавление метода -- Создание глобального контекста и глобальных методов -- Регистрация библиотек и package‑loader.os -- i18n для API (двуязычные имена) -- Депрекейшен и предупреждения -- Тестирование (C# и BSL) -- Документация (OneScriptDocumenter) -- Безопасность -- Чек‑лист готовности +1. [Что такое BSL-контекст](#1-что-такое-bsl-контекст) +2. [Добавление нового BSL-класса (контекста)](#2-добавление-нового-bsl-класса-контекста) +3. [Добавление свойства](#3-добавление-свойства) +4. [Добавление метода](#4-добавление-метода) +5. [Создание глобального контекста и глобальных методов](#5-создание-глобального-контекста-и-глобальных-методов) +6. [Регистрация библиотек и package-loader.os](#6-регистрация-библиотек-и-package-loaderos) +7. [i18n для API (двуязычные имена)](#7-i18n-для-api-двуязычные-имена) +8. [Депрекейшен и предупреждения](#8-депрекейшен-и-предупреждения) +9. [Тестирование (C# и BSL)](#9-тестирование-c-и-bsl) +10. [Документация (OneScriptDocumenter)](#10-документация-onescriptdocumenter) +11. [Безопасность](#11-безопасность) +12. [Чек-лист готовности](#12-чек-лист-готовности) -1. Что такое BSL‑контекст +## 1. Что такое BSL-контекст -- Контекст — это .NET‑класс, методы/свойства которого доступны из BSL. Экземпляр контекста может создаваться оператором Новый (класс‑контекст) или предоставляться глобально (глобальный контекст). +- Контекст — это .NET-класс, методы и свойства которого доступны из BSL. Экземпляр контекста может создаваться оператором `Новый` (класс-контекст) или предоставляться глобально (глобальный контекст). - Отражение и метаданные описываются атрибутами: - - [ContextClass("РусИмя", "EngName")] - - [ContextMethod("РусИмя", "EngName")] - - [ContextProperty("РусИмя", "EngName", CanRead = true, CanWrite = false, ...)] - - [GlobalContext(...)] для глобального контекста - - [ScriptConstructor] для создания объектов через Новый -- Двуязычные имена обязательны: все элементы публичного API должны иметь пару имен Рус/Eng. + - `[ContextClass("РусИмя", "EngName")]` — класс-контекст; + - `[ContextMethod("РусИмя", "EngName")]` — метод (процедура/функция); + - `[ContextProperty("РусИмя", "EngName", CanRead = true, CanWrite = false, ...)]` — свойство; + - `[GlobalContext(...)]` — глобальный контекст; + - `[ScriptConstructor]` — фабричный метод для создания объектов через `Новый`. +- Двуязычные имена обязательны: все элементы публичного API должны иметь пару имён Рус/Eng. -Где в коде смотреть +Где в коде смотреть: -- Атрибуты и метаданные: src/OneScript.Core/Contexts/* -- Базовые помощники контекстов: src/ScriptEngine/Machine/Contexts/* -- Глобальные контексты (база): GlobalContextBase — src/ScriptEngine/Machine/Contexts/GlobalContextBase.cs +- Атрибуты и метаданные: `src/OneScript.Core/Contexts/*`. +- Базовые помощники контекстов: `src/ScriptEngine/Machine/Contexts/*`. +- База глобальных контекстов: `GlobalContextBase` — `src/ScriptEngine/Machine/Contexts/GlobalContextBase.cs`. -2. Добавление нового BSL‑класса (контекста) +## 2. Добавление нового BSL-класса (контекста) -Минимальный шаблон +### Минимальный шаблон ```csharp -using OneScript.Core.Contexts; -using OneScript.Core.Types; -using OneScript.Core.Values; -using ScriptEngine; +using OneScript.Contexts; // ContextClass, ContextMethod, ContextProperty, ScriptConstructor +using OneScript.Execution; // IBslProcess +using OneScript.Types; // TypeActivationContext +using ScriptEngine.Machine; // IValue, ValueFactory using ScriptEngine.Machine.Contexts; // AutoContext -using OneScript.Core.Execution; // IBslProcess [ContextClass("ПримерКласс", "SampleClass")] public class SampleClass : AutoContext @@ -60,7 +59,7 @@ public class SampleClass : AutoContext [ContextProperty("Версия", "Version", CanWrite = false)] public IValue Version => ValueFactory.Create("1.0"); - // Процедура с доступом к bsl-процессу (возможность запускать свой код bsl из кода c#) + // Процедура с доступом к bsl-процессу (можно запускать BSL-код из C#) [ContextMethod("Сообщить", "Message")] public void Message(IBslProcess process, IValue text) { @@ -78,22 +77,20 @@ public class SampleClass : AutoContext } ``` -Комментарии к шаблону +### Комментарии к шаблону -- Наследуемся от AutoContext — это стандартная база для классов‑контекстов. -- [ScriptConstructor] — статический фабричный метод, возможно, принимающий TypeActivationContext. Можно объявить несколько перегрузок. -- IBslProcess можно внедрять первым параметром метода, чтобы получить доступ к сервисам/окружению выполнения. +- Наследуемся от `AutoContext` — это стандартная база для классов-контекстов. +- `[ScriptConstructor]` — статический фабричный метод, возможно принимающий `TypeActivationContext`. Можно объявить несколько перегрузок для разных сигнатур конструктора. +- `IBslProcess` можно внедрять первым параметром метода, чтобы получить доступ к сервисам/окружению выполнения. - Возвраты: - - Процедура — метод без возвращаемого значения (void). - - Функция — возвращает IValue или конвертируемый тип C# (см. ContextValuesMarshaller). + - **Процедура** — метод без возвращаемого значения (`void`). + - **Функция** — возвращает `IValue` или конвертируемый тип C# (см. `ContextValuesMarshaller`). -Регистрация в движке +### Регистрация в движке -- При старте ContextDiscoverer просканирует сборку и автоматически зарегистрирует в движке все классы, помеченные атрибутами ContextClass, GlobalContext, EnumerationType +При старте `ContextDiscoverer` (`src/ScriptEngine/Machine/Contexts/ContextDiscoverer.cs`) сканирует подключённые сборки и автоматически регистрирует все классы, помеченные атрибутами `ContextClass`, `GlobalContext` и `EnumerationType`. Дополнительная ручная регистрация при этом не требуется. -3. Добавление свойства - -Шаблон +## 3. Добавление свойства ```csharp [ContextProperty("Порог", "Threshold", CanRead = true, CanWrite = true)] @@ -102,44 +99,44 @@ public IValue Threshold get => ValueFactory.Create(_threshold); set => _threshold = value.AsNumber(); } + private decimal _threshold = 0m; ``` -Заметки - -- CanRead/CanWrite управляют доступностью геттера/сеттера из BSL, если не указаны, берутся наличия стандартных get/set у свойства. -- Маршаллинг значений свойства автоматический. +Заметки: +- `CanRead`/`CanWrite` управляют доступностью геттера и сеттера из BSL. Если параметры не указаны, используется наличие стандартных `get`/`set` у свойства. +- Маршаллинг значения свойства автоматический через `ContextValuesMarshaller`. +- Для значений, которые часто читаются и не меняются, имеет смысл вычислять и кешировать `IValue` в поле, а не пересоздавать его в геттере. -4. Добавление метода - -Шаблон процедуры и функции +## 4. Добавление метода ```csharp -// Процедура, изменяющая параметр по ссылке +// Функция, возвращающая удвоенное значение [ContextMethod("УдвоитьЧисло", "DoubleNumber")] public int DoubleNumber(int number) { - var doubled = number * 2; - return doubled; + return number * 2; } ``` -Значения параметров и результат метода будут автоматически сконвертированы из типов C# в тиаы bsl. +Значения параметров и результат метода будут автоматически сконвертированы из типов C# в типы BSL. -Заметки +Заметки: -- Для передачи аргумента по ссылке: используйте тип IVariable — в него можно присвоить новое значение через .Value. -- По значению: используйте типы C# напрямую, если они поддерживаются маршаллером, или IValue. +- **Передача аргумента по ссылке** — используйте тип `IVariable` в сигнатуре метода. В него можно присвоить новое значение через `.Value`. +- **Передача по значению** — используйте типы C# напрямую, если они поддерживаются маршаллером, или `IValue`. +- **Необязательные параметры** — задавайте через значения по умолчанию C# (`int count = 0`, `string mode = null`). Эти значения будут видны вызывающему коду BSL. +- **Перегрузки** — поддерживаются: можно объявить несколько методов с одним и тем же `ContextMethod`-именем, но с разными сигнатурами. -5. Создание глобального контекста и глобальных методов +## 5. Создание глобального контекста и глобальных методов -Глобальный контекст +### Глобальный контекст ```csharp -using OneScript.Core.Contexts; -using OneScript.Core.Values; -using ScriptEngine.Machine.Contexts; +using OneScript.Contexts; // GlobalContext, ContextMethod, IAttachableContext +using ScriptEngine.Machine; // IValue, ValueFactory +using ScriptEngine.Machine.Contexts; // GlobalContextBase [GlobalContext(Category = "Мои функции")] public class MyGlobals : GlobalContextBase @@ -155,20 +152,137 @@ public class MyGlobals : GlobalContextBase } ``` -Заметки +Заметки: -- По умолчанию глобальные контексты регистрируются автоматически (ManualRegistration = false). Достаточно, чтобы сборка была добавлена в окружение. -- Вручную можно внедрить через HostedScriptEngine.InjectObject или IRuntimeEnvironment.InjectObject. +- По умолчанию глобальные контексты регистрируются автоматически (`ManualRegistration = false`). Достаточно, чтобы сборка с контекстом была подключена к окружению. +- При необходимости можно внедрить контекст вручную через `HostedScriptEngine.InjectObject` или `IRuntimeEnvironment.InjectObject`. -Добавление метода в существующий глобальный контекст +### Добавление метода в существующий глобальный контекст -- Например, StandardGlobalContext: добавьте [ContextMethod] в соответствующий класс и реализуйте логику. +- Например, в `StandardGlobalContext` (`src/OneScript.StandardLibrary/StandardGlobalContext.cs`): добавьте `[ContextMethod]` в соответствующий класс и реализуйте логику. - Внимание: изменение публичного API стандартной библиотеки требует обсуждения с мэйнтейнерами. -6. Регистрация библиотек и package‑loader.os +## 6. Регистрация библиотек и package-loader.os + +- `HostedScript` ищет библиотеку и вызывает `package-loader.os` (дефолтный или кастомный из самой библиотеки). +- Основные операции загрузчика (см. `src/ScriptEngine.HostedScript/LibraryLoader.cs`): + - `ДобавитьКласс`/`AddClass("path", "ИмяКласса")` — регистрирует новый BSL-тип; + - `ДобавитьМодуль`/`AddModule("path", "ИмяМодуля")` — подключает модуль как глобальный; + - `ДобавитьМакет`/`AddTemplate` — регистрирует шаблон. +- Для отладки разрешения зависимостей полезно посмотреть `FileSystemDependencyResolver.cs` — порядок поиска и защита от циклических зависимостей. + +## 7. i18n для API (двуязычные имена) + +- Каждый публичный элемент API (класс, метод, свойство, перечисление, элемент перечисления) должен иметь две формы имени — русскую и английскую — задаваемые в атрибутах: + + ```csharp + [ContextClass("ИмяНаРусском", "EnglishName")] + [ContextMethod("ВыполнитьДействие", "Perform")] + [ContextProperty("Размер", "Size")] + ``` + +- Имена должны быть согласованы с уже существующим API: смотрите `OneScript.StandardLibrary` и сгенерированный справочник (см. раздел [Документация](#10-документация-onescriptdocumenter)). +- Имена параметров и текст исключений переводить не нужно — они остаются на одном языке (как правило, на русском, в соответствии с привычным стилем 1С). +- Категории глобальных контекстов (`GlobalContext(Category = "...")`) тоже желательно делать осмысленными — они используются в подсказках и группировках в редакторах. + +## 8. Депрекейшен и предупреждения + +Для обозначения устаревших имён, классов, методов и свойств используется атрибут `DeprecatedNameAttribute` (`src/OneScript.Core/Contexts/DeprecatedNameAttribute.cs`): + +```csharp +[ContextMethod("НоваяФункция", "NewFunc")] +[DeprecatedName("СтараяФункция")] +[DeprecatedName("OldFunc")] +public IValue NewFunc() { /* ... */ } +``` + +- Аргумент `name` — устаревший псевдоним. При обращении к нему из BSL вызов всё ещё работает, но в системный лог пишется предупреждение. +- Параметр `throwOnUse: true` превращает использование устаревшего имени в ошибку выполнения. Применяется, когда нужно жёстко удалить старый псевдоним. +- Атрибут можно ставить и на сам элемент (`[DeprecatedName("OldName")]` рядом с `[ContextMethod]`), и на отдельные перечисления/типы. +- Для классов, которые сами по себе устарели, реализуйте интерфейс `ISupportsDeprecation` (`src/OneScript.Core/Contexts/ISupportsDeprecation.cs`) — возвращайте `IsDeprecated = true`. Тогда система сможет логировать факт использования такого типа. + +Изменение и удаление публичных имён без депрекейшена считается ломающим изменением — старайтесь сначала пройти этап с предупреждением. + +## 9. Тестирование (C# и BSL) + +В проекте принято покрывать новые контексты двумя слоями тестов. + +### Модульные тесты (xUnit/NUnit) + +- Тесты лежат в `src/Tests/*` (xUnit/NUnit). +- Для контекстов из `OneScript.StandardLibrary` подходит проект `src/Tests/OneScript.StandardLibrary.Tests`. Там же есть пример проверки атрибута `DeprecatedName` (`ObsoleteEnumTest.cs`). +- Для проверки кода, который должен компилироваться, используйте инфраструктуру из `src/Tests/OneScript.Dynamic.Tests` (`CompilerTestBase`, `CompileHelper`). +- Запуск: `dotnet test` в каталоге соответствующего проекта или одной командой: + + ```bat + dotnet msbuild Build.csproj /t:UnitTests + ``` + +### Приёмочные тесты на BSL + +- Поведенческие сценарии лежат в каталоге `tests/*.os` и совместимы с форматом xUnitFor1C. +- Для нового контекста создайте файл `tests/<ваш-контекст>.os` со списком тестов. Минимальный шаблон: + + ```bsl + Перем юТест; + + Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + юТест = ЮнитТестирование; + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолжен_СоздатьЭкземпляр"); + Возврат ВсеТесты; + КонецФункции + + Процедура ТестДолжен_СоздатьЭкземпляр() Экспорт + Объект = Новый ПримерКласс(); + юТест.ПроверитьРавенство("1.0", Объект.Версия); + КонецПроцедуры + ``` + +- Запуск всего набора тестов через свежесобранный `oscript` (см. [`README.md`](../README.md), раздел «Тестирование»): + + ```bat + rem Windows + dotnet build src/oscript/oscript.csproj + tests\run-bsl-tests.cmd src\oscript\bin\Debug\net8.0\oscript.exe + ``` + + ```bash + # Linux/macOS + dotnet build src/oscript/oscript.csproj + tests/run-bsl-tests.sh src/oscript/bin/Debug/net8.0/oscript + ``` + +- Запуск одного теста: `<путь к oscript> tests/testrunner.os -run tests/<имя файла>.os`. + +## 10. Документация (OneScriptDocumenter) + +- Утилита `OneScriptDocumenter` (см. `src/OneScriptDocumenter`) формирует справку по платформе из XML-документации сборок и оглавления `default_toc.json`. +- Чтобы ваш контекст попал в справку: + - включите для проекта генерацию XML-документации (это сделано во всех публичных проектах через `oscommon.targets`); + - снабдите класс/методы/свойства XML-комментариями ``, ``, ``, `` — они извлекаются документатором; + - при необходимости — отредактируйте оглавление `src/OneScriptDocumenter/default_toc.json`, чтобы новый раздел появился в нужном месте. +- Сгенерировать локально: `dotnet msbuild Build.csproj /t:BuildDocumentation`. Результат окажется в `built/docs/` (markdown + json). + +## 11. Безопасность + +- Не выполняйте произвольный код, поступивший «снаружи». Если контекст исполняет BSL, который пришёл от пользователя, делайте это только через явные точки расширения (`IBslProcess.Run`/`Eval`), осознавая, какие глобальные имена и контексты доступны. +- При работе с файловой системой и сетью валидируйте пути и URL, проверяйте кодировки и таймауты. Для долгих операций предусматривайте отмену. +- Не логируйте секреты (пароли, токены, заголовки `Authorization`) в открытом виде. +- Не доверяйте внешним десериализаторам: для JSON/XML используйте уже существующие в `OneScript.StandardLibrary` обёртки и не добавляйте отключение защит «по умолчанию». +- Если ваш контекст представляет собой обёртку поверх системного API, явно указывайте в XML-документации, какие побочные эффекты возможны (запись на диск, запуск процессов, сетевые вызовы). +- При добавлении опасных по умолчанию операций предусматривайте явный «согласный» флаг в API, а не включайте их «втихую». + +## 12. Чек-лист готовности + +Перед открытием Pull Request убедитесь, что выполнены пункты: -- HostedScript ищет библиотеку и вызывает package‑loader.os (дефолтный или кастомный). -- Основные операции загрузчика (см. src/ScriptEngine.HostedScript/LibraryLoader.cs): - - ДобавитьКласс/AddClass("path", "ИмяКласса") — регистрирует новый BSL‑тип; - - ДобавитьМодуль/AddModule("path", "ИмяМодуля") — подключает модуль как глобальный; - - ДобавитьМакет/AddTemplate — регистрирует шаблон. \ No newline at end of file +- [ ] У всех публичных элементов API заданы оба имени — русское и английское. +- [ ] Класс/метод/свойство снабжены XML-комментариями (``, ``, ``). +- [ ] Параметры с разумными значениями по умолчанию указаны через дефолты C#, а не через перегрузки-обёртки. +- [ ] Если переименовываете существующее имя — добавлен `DeprecatedName` со старым псевдонимом. +- [ ] Добавлены модульные тесты на C# и/или приёмочные тесты на BSL. +- [ ] Все тесты проходят локально (`dotnet msbuild Build.csproj /t:Test`). +- [ ] Соблюдены требования [`CODESTYLE.md`](../CODESTYLE.md). +- [ ] Проверено, что новый контекст корректно отображается в справке (`/t:BuildDocumentation`), если он публичный. +- [ ] Описание изменений в PR содержит мотивацию и ссылки на тесты/issue. diff --git a/docs/developer_docs.md b/docs/developer_docs.md index b3c38febe..017e824fc 100644 --- a/docs/developer_docs.md +++ b/docs/developer_docs.md @@ -2,200 +2,228 @@ Этот документ помогает быстро разобраться, что лежит в репозитории, зачем это нужно, как устроено внутри и как использовать. -## 1 Картина целиком: из чего состоит OneScript +См. также: -OneScript — реализация языка, совместимого с синтаксисом 1С/BSL. В основе - стековая виртуальная машина. Проект включает реализацию компилятора, исполняющей среды, системы типов и стандартной библиотеки BSL. Поверх этого — инструменты (CLI, раннер), отладка (DAP), веб-обёртки и API для нативных расширений. +- [`README.md`](../README.md) — общее описание, установка, сборка, тесты; +- [`docs/contexts.md`](contexts.md) — практическое руководство по добавлению BSL-контекстов и глобальных методов; +- [`CODESTYLE.md`](../CODESTYLE.md) — требования к стилю кода на C#. -OneScript — открытая реализация языка 1С/BSL поверх .NET, со стековой ВМ и стандартной библиотекой. Сценарии исполняются CLI oscript либо внедряются в приложения через HostedScript. +## 1. Картина целиком: из чего состоит OneScript + +OneScript — открытая реализация языка, совместимого с синтаксисом 1С/BSL, поверх .NET. В основе — стековая виртуальная машина. Проект включает реализацию компилятора, исполняющей среды, системы типов и стандартной библиотеки BSL. Поверх этого — инструменты (CLI, раннер), отладка (DAP), веб-обёртки и API для нативных расширений. + +Сценарии исполняются CLI `oscript` либо встраиваются в приложения через `HostedScript`. Слои (сверху вниз): + - Приложения и инструменты: - - src/oscript — консольный хост, основное приложение; - - src/VSCode.DebugAdapter — адаптер DAP; - - src/OneScriptDocumenter — генерация документации; - - src/TestApp, src/Component — примеры использования. -- Хостинг и сервисы: src/ScriptEngine.HostedScript, src/OneScript.DebugServices, src/OneScript.Web.Server. + - `src/oscript` — консольный хост, основное приложение; + - `src/VSCode.DebugAdapter` — адаптер DAP; + - `src/OneScriptDocumenter` — генерация документации; + - `src/TestApp`, `src/Component` — примеры использования. +- Хостинг и сервисы: + - `src/ScriptEngine.HostedScript`, + - `src/OneScript.DebugServices`, + - `src/OneScript.Web.Server`. - Рантайм (компиляция/исполнение, встроенные функции): - - src/ScriptEngine (стековая ВМ) - - src/OneScript.Native (нативный бэкенд). + - `src/ScriptEngine` — стековая ВМ; + - `src/OneScript.Native` — нативный бэкенд (Expression Trees). - Ядро/язык: - - OneScript.Core (типы/контексты) — основные инфраструктурные компоненты для обоих рантаймов - - OneScript.Language (лексер/парсер/AST) + - `src/OneScript.Core` — типы/контексты, базовая инфраструктура для обоих рантаймов; + - `src/OneScript.Language` — лексер/парсер/AST. - Стандартная библиотека языка BSL: - - OneScript.StandardLibrary — реализация стандартных прикладных классов (массивы, работа с файлами, сетью и пр.) -- Интеграции: ScriptEngine.NativeApi (Мост к внешним компонентам на C++, совместимым с NativeApi 1С). -- Инструмент генерации автодокументации OneScriptDocumenter + - `src/OneScript.StandardLibrary` — стандартные прикладные классы (массивы, работа с файлами, сетью и пр.). +- Интеграции: + - `src/ScriptEngine.NativeApi` — мост к внешним компонентам на C++, совместимым с NativeApi 1С. +- Инструмент генерации автодокументации `src/OneScriptDocumenter`. + +## 2. Быстрый старт для контрибьютора -## 2 Быстрый старт для контрибьютора -- Где собирать: решение src/1Script.sln. -- Входной CLI: src/oscript (консольное приложение для запуска .os‑скриптов). -- Запуск тестов: проекты src/Tests/* (C#) и папка tests/* (скрипты .os). -- Если добавляете новый контекст/тип — обычно правки в OneScript.Core/ScriptEngine/StandardLibrary, плюс модульные тесты. +- Где собирать: решение `src/1Script.sln`. +- Целевой фреймворк: `net8.0` (см. `src/oscommon.targets`), `LangVersion 8.0`. +- Входной CLI: `src/oscript` (консольное приложение для запуска `.os`-скриптов). +- Запуск тестов: + - модульные C#-тесты — проекты `src/Tests/*` (xUnit/NUnit); + - приёмочные BSL-скрипты — каталог `tests/*.os`, запуск через `tests/run-bsl-tests.cmd`/`tests/run-bsl-tests.sh`. +- Сценарий полной сборки и прогона тестов описан в [`README.md`](../README.md) и [`.github/copilot-instructions.md`](../.github/copilot-instructions.md). +- Если добавляете новый контекст/тип — обычно правки в `OneScript.Core`/`ScriptEngine`/`StandardLibrary`, плюс модульные тесты. Подробности — в [`docs/contexts.md`](contexts.md). -Псевдонимы API приняты двуязычные: РусИмя/EngName (см. атрибуты ContextClass/Method/Property). +Соглашения: -Соглашение по ссылкам: указываются относительные пути репозитория. +- Псевдонимы API двуязычные: `РусИмя`/`EngName` (см. атрибуты `ContextClass`/`ContextMethod`/`ContextProperty`). +- Все ссылки в этом документе указываются относительно корня репозитория. -## 3 Обзор проектов (назначение, ключевые узлы) +## 3. Обзор проектов (назначение, ключевые узлы) Ниже по каждому проекту — зачем он нужен, где искать основную логику и какие классы отвечают за ключевые задачи. -### 3.1 OneScript.Language — лексер/препроцессор/парсер/AST +### 3.1. OneScript.Language — лексер/препроцессор/парсер/AST -Назначение: преобразует исходный BSL‑код в токены и синтаксическое дерево, обрабатывает директивы препроцессора. -Проект сделан максимально независимым и отчуждаемым, и должен иметь возможность использоваться в других решениях, не связанных с 1Script, как просто парсер BSL. +Назначение: преобразует исходный BSL-код в токены и синтаксическое дерево, обрабатывает директивы препроцессора. Проект сделан максимально независимым и отчуждаемым: его можно использовать в других решениях, не связанных с 1Script, как просто парсер BSL. -- Где в коде: - - LexicalAnalysis/* — лексер. DefaultLexer.cs, различные состояния (String/Number/Comment/PreprocessorDirective/etc). - - SyntaxAnalysis/* — парсер и AST: DefaultBslParser.cs, BslSyntaxWalker.cs, AstNodes/* (ModuleNode, MethodNode, CallNode, TryExceptNode, *LoopNode, Binary/UnaryOperationNode и др.). - - Препроцессор: PreprocessingLexer.cs, PreprocessorHandlers.cs, RegionDirectiveHandler.cs, ImportDirectivesHandler.cs, ModuleAnnotationDirectiveHandler.cs. - - Диагностика: CodeError.cs, ErrorPositionInfo.cs, SyntaxErrorException.cs, LocalizedErrors.cs. +- Где в коде (пути относительно `src/OneScript.Language/`): + - `LexicalAnalysis/*` — лексер. `DefaultLexer.cs`, различные состояния (`String`/`Number`/`Comment`/`PreprocessorDirective`/etc). + - `SyntaxAnalysis/*` — парсер и AST: `DefaultBslParser.cs`, `BslSyntaxWalker.cs`, `AstNodes/*` (`ModuleNode`, `MethodNode`, `CallNode`, `TryExceptNode`, `*LoopNode`, `BinaryOperationNode`/`UnaryOperationNode` и др.). + - Препроцессор (в `SyntaxAnalysis/`): `PreprocessingLexer.cs`, `PreprocessorHandlers.cs`, `RegionDirectiveHandler.cs`, `ImportDirectivesHandler.cs`, `ModuleAnnotationDirectiveHandler.cs`. + - Диагностика: `CodeError.cs`, `ErrorPositionInfo.cs`, `SyntaxErrorException.cs` (в корне проекта) и `SyntaxAnalysis/LocalizedErrors.cs`. - Жизненный цикл: - 1) Лексер производит Lexem с типом/токеном. - 2) Препроцессор обрабатывает директивы (#Если/#Область/#Использовать). - 3) Парсер строит AST (BslSyntaxNode), восстанавливается после ошибок (IErrorRecoveryStrategy). - 4) AST передаётся компилятору (CompilerFrontend) рантайма. + 1. Лексер производит `Lexem` с типом/токеном. + 2. Препроцессор обрабатывает директивы (`#Если`/`#Область`/`#Использовать`). + 3. Парсер строит AST (`BslSyntaxNode`), восстанавливается после ошибок (`IErrorRecoveryStrategy`). + 4. AST передаётся компилятору (`CompilerFrontend`) рантайма. - Точки расширения: - - собственные директивы препроцессора (IDirectiveHandler -> зарегистрировать в DI); - - обход AST через BslSyntaxWalker. + - собственные директивы препроцессора (`IDirectiveHandler` → зарегистрировать в DI); + - обход AST через `BslSyntaxWalker`. + +На выходе вы получаете `ModuleNode`/AST, пригодный для компиляции рантаймом/бэкендом или для обработки статическим анализатором. -На выходе вы получаете ModuleNode/AST, пригодный для компиляции рантаймом/бэкендом или обработки статическим анализатором. +### 3.2. OneScript.Core — система типов, значения, отражение контекстов -### 3.2 OneScript.Core — система типов, значения, отражение контекстов -Назначение: общий объектный каркас значений BSL, контекстов (объекты/методы/свойства), аннотаций и исключений. +Назначение: общий объектный каркас значений BSL, контекстов (объекты/методы/свойства), аннотаций и исключений. Содержит базовые `IValue`/`BslValue`, ссылки на значения, метаданные контекстов (классов/методов/свойств), атрибуты, исключения, символы компилятора. -OneScript.Core — система типов и контекстная модель -- Назначение: базовые IValue/BslValue, ссылки на значения, метаданные контекстов (классов/методов/свойств), атрибуты, исключения, символы компилятора. - Где в коде: - - Values/* — BslValue и производные: строки/числа/дата/Null/Undefined/Type/Object, сравнения/преобразования; ссылки: IValueReference/Variable/PropertyValueReference/IndexedValueReference. - - Contexts/* — атрибуты ContextClass/ContextMethod/ContextProperty, GlobalContextAttribute, ScriptConstructorAttribute; построители Bsl*Info, отражение классов, поддержка устаревания (ISupportsDeprecation, DeprecatedNameAttribute). - - Compilation/Binding/* — SymbolTable, SymbolScope, SymbolBinding, *Symbol интерфейсы. - - Exceptions/* — RuntimeException, TypeConversionException, PropertyAccessException и др. + - `Values/*` — `BslValue` и производные: строки/числа/дата/`Null`/`Undefined`/`Type`/`Object`, сравнения/преобразования; ссылки: `IValueReference`/`Variable`/`PropertyValueReference`/`IndexedValueReference`. + - `Contexts/*` — атрибуты `ContextClass`/`ContextMethod`/`ContextProperty`, `GlobalContextAttribute`, `ScriptConstructorAttribute`; построители `Bsl*Info`, отражение классов, поддержка устаревания (`ISupportsDeprecation`, `DeprecatedNameAttribute`). + - `Compilation/Binding/*` — `SymbolTable`, `SymbolScope`, `SymbolBinding`, `*Symbol`-интерфейсы. + - `Exceptions/*` — `RuntimeException`, `TypeConversionException`, `PropertyAccessException` и др. - Жизненный цикл контекстов: - 1) ContextDiscoverer (ScriptEngine.Machine.Contexts) сканирует сборки, находит [ContextClass]/[GlobalContext]/[EnumerationType]/[SystemEnum]. - 2) Регистрирует типы/глобальные контексты в IRuntimeEnvironment/IGlobalsManager. - 3) Отражение формирует Bsl*Info для рантайма/документации. + 1. `ContextDiscoverer` (в `ScriptEngine.Machine.Contexts`) сканирует сборки, находит `[ContextClass]`/`[GlobalContext]`/`[EnumerationType]`/`[SystemEnum]`. + 2. Регистрирует типы/глобальные контексты в `IRuntimeEnvironment`/`IGlobalsManager`. + 3. Отражение формирует `Bsl*Info` для рантайма/документации. -### 3.3 ScriptEngine — движок выполнения (стековая ВМ и бэкенд компилятора для стековой машины) +### 3.3. ScriptEngine — движок выполнения (стековая ВМ и бэкенд компилятора для стековой машины) Основная среда исполнения на базе стековой виртуальной машины. Назначение: организует выполнение скриптов, стек вызовов, области видимости, глобальные функции и интеграцию с отладкой. -- Где в коде: - - Compiler/* — CompilerFrontend, BackendSelector; StackMachineCodeGenerator (байткод), EvalCompiler; CodeGenerationFlags. - - Machine/* — StackMachineExecutor, MachineInstance (командный цикл, стек/кадры/исключения/итераторы), ExecutionContext/Frame, BuiltinFunctions, ValueFactory, GlobalInstancesManager. CodeStat/* — статистика покрытия кода. - - Hosting/* — DefaultEngineBuilder, DI (TinyIoC), EngineBuilderExtensions (регистрация сервисов, предобработчики). - - ScriptingEngine.cs — фасад движка: загрузка сборок, Initialize, NewProcess, компиляция. - - ContextValuesMarhaller — маршаллер (преобразователь) типов C# в типы BSL и обратно. +- Где в коде (пути относительно `src/ScriptEngine/`): + - `Compiler/*` — `CompilerFrontend`, `CompilerBackendSelector`, `StackMachineCodeGenerator` (байткод), `EvalCompiler`, `CodeGenerationFlags`. + - `Machine/*` — `StackMachineExecutor`, `MachineInstance` (командный цикл, стек/кадры/исключения/итераторы), `ExecutionContext`/`ExecutionFrame`, `BuiltinFunctions`, `ValueFactory`, `GlobalInstancesManager`. `CodeStat/*` — статистика покрытия кода. + - `Hosting/*` — `DefaultEngineBuilder`, DI (`TinyIoC`), `EngineBuilderExtensions` (регистрация сервисов, предобработчики). + - `ScriptingEngine.cs` — фасад движка: загрузка сборок, `Initialize`, `NewProcess`, компиляция. + - `ContextValuesMarshaller` — преобразователь типов C# в типы BSL и обратно. - Точки расширения: - - дополнительные IExecutorProvider (альтернативные рантаймы); - - предопределённые интерфейсы/итераторы (Interfaces/Iterables handlers); - - сбор кода-статистики (ICodeStatCollector) + - дополнительные `IExecutorProvider` (альтернативные рантаймы); + - предопределённые интерфейсы/итераторы (`Interfaces`/`Iterables` handlers); + - сбор статистики покрытия (`ICodeStatCollector`). + +### 3.4. OneScript.Native — нативный бэкенд (Expression Trees) — компилятор/исполнитель и встроенные функции -### 3.4 OneScript.Native — нативный бэкенд (Expression Trees) компилятор/исполнитель, встроенные функции +Альтернативная среда исполнения (не основная). Предоставляет компиляцию BSL в Expression Trees фреймворка .NET. Имеет ряд ограничений и в целом является экспериментом. В какой именно среде будет исполнен скрипт, решает директива в начале скрипта: -Альтернативная среда исполнения (не основная). Предоставляет компиляцию BSL в ExpressionTrees фреймворка .NET. -Данная среда исполнения имеет ряд ограничений и в целом является экспериментом. В какой именно среде будет исполнен скрипт решает директива в начале скрипта: +- `#native` — скрипт будет скомпилирован в Native Runtime; +- `#stack` или отсутствие директивы — скрипт будет исполнен основной стековой средой. -* `#native` - скрипт будет скомпилировать в Native Runtime -* `#stack` или отсутствие директивы - скрипт будет использован основной средой исполнения. +Назначение: преобразует AST + символы в исполняемую форму и предоставляет нативный бэкенд выполнения, включая встроенные функции. -Назначение: преобразует AST+символы в исполняемую форму и предоставляет нативный бэкенд выполнения, включая встроенные функции. -- Компиляция: Compiler/ - - ModuleCompiler.cs, MethodCompiler.cs — генерация модулей/методов. - - ExpressionTreeGeneratorBase.cs, BinaryOperationCompiler.cs — выражения/операции. - - BuiltInFunctionsCache.cs, ContextMethodsCache.cs — кеширование метаданных/встроенных функций. -- Исполнение: Runtime/ - - NativeExecutorProvider.cs — провайдер исполнителя. - - BuiltInFunctions.cs — реализации встроенных функций. - - DynamicOperations.cs — динамические операции. +- Компиляция: `Compiler/` + - `ModuleCompiler.cs`, `MethodCompiler.cs` — генерация модулей/методов. + - `ExpressionTreeGeneratorBase.cs`, `BinaryOperationCompiler.cs` — выражения/операции. + - `BuiltInFunctionsCache.cs`, `ContextMethodsCache.cs` — кеширование метаданных/встроенных функций. +- Исполнение: `Runtime/` + - `NativeExecutorProvider.cs` — провайдер исполнителя. + - `BuiltInFunctions.cs` — реализации встроенных функций. + - `DynamicOperations.cs` — динамические операции. -- Ограничения: может не поддерживать полный паритет со стековой машиной; выбор режима делает CompilerBackendSelector по соответствующим директивам в начале файла. +Ограничения: может не поддерживать полный паритет со стековой машиной. Выбор режима делает `CompilerBackendSelector` по соответствующим директивам в начале файла. + +### 3.5. ScriptEngine.HostedScript — хостинг, загрузка библиотек, конфигурация + +Назначение: безопасная обвязка движка для встраивания, инициализация стандартного глобального контекста, загрузка библиотек, конфигурирование. -7. ScriptEngine.HostedScript — хостинг, загрузка библиотек, конфигурация -- Назначение: безопасная обвязка движка для встраивания, инициализация стандартного глобального контекста, загрузка библиотек, конфигурирование. - Где в коде: - - HostedScriptEngine.cs — инициализация, глобальные контексты (SystemGlobalContext, DynamicLoadingFunctions), создание процессов. - - LibraryLoader.cs — package-loader.os, подключение .os модулей/классов/макетов; FileSystemDependencyResolver.cs — поиск библиотек, цикл обработки, защита от циклических зависимостей. - - Extensions/EngineBuilderExtensions.cs — UseSystemConfigFile/UseEnvironmentVariableConfig/UseEntrypointConfigFile; UseImports/UseFileSystemLibraries/UseNativeRuntime/UseEventHandlers. + - `HostedScriptEngine.cs` — инициализация, глобальные контексты (`SystemGlobalContext`, `DynamicLoadingFunctions`), создание процессов. + - `LibraryLoader.cs` — `package-loader.os`, подключение `.os`-модулей/классов/макетов; `FileSystemDependencyResolver.cs` — поиск библиотек, цикл обработки, защита от циклических зависимостей. + - `Extensions/EngineBuilderExtensions.cs` — `UseSystemConfigFile`/`UseEnvironmentVariableConfig`/`UseEntrypointConfigFile`; `UseImports`/`UseFileSystemLibraries`/`UseNativeRuntime`/`UseEventHandlers`. - Жизненный цикл: - 1) Читает настройки из системного `oscript.cfg`, файла `oscript.cfg` рядом с entrypoint, и переменной окружения OSCRIPT_CONFIG (в порядке возрастания приоритета, то есть переменная окружения перезаписывает все предыдущие настройки). - 2) Инициализация HostedScriptEngine → глобальные объекты → процесс → компиляция/исполнение модуля. - 3) Загрузка библиотек: default или кастомный package-loader.os, последующая регистрация символов и компиляция задержанных модулей. + 1. Читает настройки из системного `oscript.cfg`, файла `oscript.cfg` рядом с entrypoint и переменной окружения `OSCRIPT_CONFIG` (в порядке возрастания приоритета — переменная окружения перезаписывает все предыдущие настройки). + 2. Инициализация `HostedScriptEngine` → глобальные объекты → процесс → компиляция/исполнение модуля. + 3. Загрузка библиотек: дефолтный или кастомный `package-loader.os`, последующая регистрация символов и компиляция отложенных модулей. + +### 3.6. OneScript.StandardLibrary — стандартная библиотека -### 3.6 OneScript.StandardLibrary — стандартная библиотека Назначение: коллекции, файлы/потоки, текст и кодировки, сеть/HTTP, JSON/XML, ZIP, процессы, таймзоны и др. -- Коллекции: Collections/ - - ArrayImpl.cs, MapImpl.cs, StructureImpl.cs, ValueListImpl.cs. - - Таблицы/деревья значений: ValueTable.cs (+ ValueTableColumn/Row), ValueTree.cs. -- Файлы/потоки/текст: FileOperations.cs, FileContext.cs, Text/* (TextReadImpl.cs, TextWriteImpl.cs), CustomLineFeedStreamReader.cs. -- Сеть/HTTP: Http/* (HttpRequestContext.cs, HttpResponseContext.cs, HttpRequestBody*, InternetProxyContext.cs). -- JSON: Json/* (JSONReader.cs, JSONWriter.cs, JSONDataExtractor.cs, JSONWriterSettings.cs). -- XML/XSLT: Xml/* (XmlReaderImpl.cs, XmlWriterImpl.cs, XSLTransform.cs). -- ZIP: Zip/* (ZipReader.cs, ZipWriter.cs и перечисления параметров). -- Процессы: Processes/* (ProcessContext.cs, GlobalProcessesFunctions.cs). - - StandardGlobalContext.cs — набор полезных глобальных функций/свойств (например, Символы, Приостановить/Sleep, ЗначениеЗаполнено и т.п.). -- Разное: RandomNumberGenerator.cs, StringOperations.cs, Timezones/*. - -### 3.7 OneScript.Web.Server — веб-обёртки для BSL (ASP.NET Core) + +- Коллекции: `Collections/` + - `ArrayImpl.cs`, `MapImpl.cs`, `StructureImpl.cs`, `ValueList/ValueListImpl.cs`. + - Таблицы/деревья значений: `ValueTable/ValueTable.cs` (+ `ValueTableColumn.cs`/`ValueTableRow.cs`), `ValueTree/ValueTree.cs`. +- Файлы/потоки/текст: `FileOperations.cs`, `FileContext.cs`, `Text/*` (`TextReadImpl.cs`, `TextWriteImpl.cs`), `CustomLineFeedStreamReader.cs`. +- Сеть/HTTP: `Http/*` (`HttpRequestContext.cs`, `HttpResponseContext.cs`, `HttpRequestBody*`, `InternetProxyContext.cs`). +- JSON: `Json/*` (`JSONReader.cs`, `JSONWriter.cs`, `JSONDataExtractor.cs`, `JSONWriterSettings.cs`). +- XML/XSLT: `Xml/*` (`XmlReaderImpl.cs`, `XmlWriterImpl.cs`, `XSLTransform.cs`). +- ZIP: `Zip/*` (`ZipReader.cs`, `ZipWriter.cs` и перечисления параметров). +- Процессы: `Processes/*` (`ProcessContext.cs`, `GlobalProcessesFunctions.cs`). +- `StandardGlobalContext.cs` — набор полезных глобальных функций/свойств (например, `Символы`, `Приостановить`/`Sleep`, `ЗначениеЗаполнено` и т.п.). +- Разное: `RandomNumberGenerator.cs`, `StringOperations.cs`, `Timezones/*`. + +### 3.7. OneScript.Web.Server — веб-обёртки для BSL (ASP.NET Core) + Назначение: адаптеры поверх ASP.NET Core API, чтобы работать с HTTP/WebSocket из BSL. -- WebServer.cs — базовая обвязка. -- Http*Wrapper.cs — HttpContext/Request/Response/Cookies. -- WebSockets/* — WebSocketWrapper, WebSocketsManagerWrapper. - -### 3.8 Отладка: OneScript.DebugProtocol, OneScript.DebugServices, VSCode.DebugAdapter -- Протокол (OneScript.DebugProtocol): - - TcpServer/* (DefaultMessageServer.cs, JsonDtoChannel.cs, DispatchingServer.cs) — транспорт и сериализация. - - Модель отладки: Breakpoint.cs, StackFrame.cs, Variable.cs, DebuggerSettings.cs. -- Сервисы (OneScript.DebugServices): - - DefaultDebugService.cs, DefaultDebugController.cs — серверные сервисы отладки. - - ThreadManager.cs, TcpDebugServer.cs — управление потоками и TCP‑сервер. -- Адаптер для VS Code (VSCode.DebugAdapter): - - DebugSession.cs, OscriptDebugSession.cs — основная сессия и проксирование событий. - - ServerProcess.cs, DebugeeProcess.cs — управление процессом отлаживаемого приложения. - -### 3.9 Приложения/утилиты -- oscript (CLI): src/oscript - - Program.cs — входная точка. - - ConsoleHostBuilder.cs — сборка консольного хоста. - - Поведения (режимы): ExecuteScriptBehavior.cs, CheckSyntaxBehavior.cs, ShowVersionBehavior.cs, DebugBehavior.cs и др. -- StandaloneRunner: сборка самодостаточных пакетов/запуск - - Program.cs, StandaloneRunner.cs, ProcessLoader.cs. -- OneScriptDocumenter: генерация документации по сборкам - - На вход получает список DLL (в каталоге с ними рядом должны лежать файлы xml-docs от этих dll) и файл оглавления (в репозитории уже лежит готовый файл оглавления OneScriptDocumenter\default_toc.json) - - На выходе формирует файл с документацией формата Json и/или каталог с документацией в формате markdown. + +- `WebServer.cs` — базовая обвязка. +- `Http*Wrapper.cs` — `HttpContext`/`Request`/`Response`/`Cookies`. +- `WebSockets/*` — `WebSocketWrapper`, `WebSocketsManagerWrapper`. + +### 3.8. Отладка: OneScript.DebugProtocol, OneScript.DebugServices, VSCode.DebugAdapter + +- Протокол (`OneScript.DebugProtocol`): + - `TcpServer/*` (`DefaultMessageServer.cs`, `JsonDtoChannel.cs`, `DispatchingServer.cs`) — транспорт и сериализация. + - Модель отладки: `Breakpoint.cs`, `StackFrame.cs`, `Variable.cs`, `DebuggerSettings.cs`. +- Сервисы (`OneScript.DebugServices`): + - `DefaultDebugger.cs`, `DefaultBreakpointManager.cs`, `DefaultVariableVisualizer.cs` — серверные сервисы отладки и визуализации переменных. + - `ThreadManager.cs`, `TcpDebugServer.cs` — управление потоками и TCP-сервер. +- Адаптер для VS Code (`VSCode.DebugAdapter`): + - `DebugSession.cs`, `OscriptDebugSession.cs` — основная сессия и проксирование событий. + - `ServerProcess.cs`, `DebugeeProcess.cs` — управление процессом отлаживаемого приложения. + +### 3.9. Приложения/утилиты + +- `oscript` (CLI): `src/oscript` + - `Program.cs` — входная точка. + - `ConsoleHostBuilder.cs` — сборка консольного хоста. + - Поведения (режимы): `ExecuteScriptBehavior.cs`, `CheckSyntaxBehavior.cs`, `ShowVersionBehavior.cs`, `DebugBehavior.cs` и др. +- `StandaloneRunner` — сборка самодостаточных пакетов/запуск: + - `Program.cs`, `ProcessLoader.cs`, `StandaloneApplicationHost.cs`, `StandaloneProcess.cs`, `StandaloneTemplateFactory.cs`. +- `OneScriptDocumenter` — генерация документации по сборкам: + - на вход получает список DLL (рядом должны лежать xml-docs от этих dll) и файл оглавления (готовый `OneScriptDocumenter/default_toc.json` лежит в репозитории); + - на выходе формирует файл документации в формате JSON и/или каталог с документацией в формате Markdown. - Примеры/демо: - - Component — простая .NET‑библиотека/компонент и примеры использования. - - TestApp — WPF‑приложение‑песочница (подсветка синтаксиса, запуск модулей). + - `Component` — простая .NET-библиотека/компонент и примеры использования. + - `TestApp` — WPF-приложение-песочница (подсветка синтаксиса, запуск модулей). + +### 3.10. ScriptEngine.NativeApi — C++ API для нативных расширений -### 3.10 ScriptEngine.NativeApi — C++ API для нативных расширений -- Основные файлы: NativeApiProxy.cpp, NativeInterface.cpp, include/* (AddInDefBase.h, ComponentBase.h и др.). +- Основные файлы: `NativeApiProxy.cpp`, `NativeInterface.cpp`, `include/*` (`AddInDefBase.h`, `ComponentBase.h` и др.). - Задача: писать нативные аддины, видимые в BSL как объекты/контексты. -## 4 Как компоненты связаны между собой (словесная диаграмма) -- Language → даёт AST и ошибки компиляции. -- Core → базовые типы/контексты; используется Native, ScriptEngine, StandardLibrary. -- Native → компилирует и исполняет, опирается на Language/Core. -- ScriptEngine → организует выполнение (стек‑машина), использует Native и Core. -- HostedScript → высокий уровень хостинга поверх ScriptEngine. -- StandardLibrary → реализована на Core/ScriptEngine. -- DebugProtocol/DebugServices ↔ ScriptEngine — обмен данными отладки; VSCode.DebugAdapter ↔ DebugServices. -- Web.Server ↔ ASP.NET Core API — обёртки для работы из BSL. -- oscript/StandaloneRunner/Documenter/TestApp/Component — надстройки поверх ядра/рантайма. -- NativeApi ↔ ScriptEngine/Core — нативные расширения. - -## 5 Типичные сценарии доработок (куда лезть) -- Добавить объект/контекст с методами: OneScript.Core/Contexts/* (аннотации Context*Attribute). -- Добавить функцию в стандартную библиотеку: соответствующий раздел OneScript.StandardLibrary (например, Json/ или Collections/), плюс экспорт в общий контекст (StandardGlobalContext.cs или SymbolsContext.cs, если нужно). -- Встроенная функция языка/операция: OneScript.Native/Runtime/BuiltInFunctions.cs и/или Compiler/*, при необходимости — поддержка в ScriptEngine/Machine. -- Отладка: DebugServices/DebugProtocol — добавление/изменение событий или представления переменных; VSCode.DebugAdapter — проксирование. - -## 6 Навигация по тестам -- C#-тесты: src/Tests/*: - - Язык: src/Tests/OneScript.Language.Tests/* (лексер/парсер/препроцессор). - - Ядро/типы/контексты: src/Tests/OneScript.Core.Tests/*. - - Динамика/нативный рантайм: src/Tests/OneScript.Dynamic.Tests/*. - - Стандартная библиотека: src/Tests/OneScript.StandardLibrary.Tests/*. - - Отладчик: src/Tests/VSCode.DebugAdapter.Tests/, src/Tests/OneScript.DebugProtocol.Test/. -- Скриптовые тесты: tests/*.os (поведенческие сценарии языка/библиотек). \ No newline at end of file +## 4. Как компоненты связаны между собой (словесная диаграмма) + +- `OneScript.Language` → даёт AST и ошибки компиляции. +- `OneScript.Core` → базовые типы/контексты; используется в `OneScript.Native`, `ScriptEngine`, `OneScript.StandardLibrary`. +- `OneScript.Native` → компилирует и исполняет, опирается на `OneScript.Language`/`OneScript.Core`. +- `ScriptEngine` → организует выполнение (стековая ВМ), использует `OneScript.Native` и `OneScript.Core`. +- `ScriptEngine.HostedScript` → высокий уровень хостинга поверх `ScriptEngine`. +- `OneScript.StandardLibrary` → реализована на `OneScript.Core`/`ScriptEngine`. +- `OneScript.DebugProtocol`/`OneScript.DebugServices` ↔ `ScriptEngine` — обмен данными отладки; `VSCode.DebugAdapter` ↔ `OneScript.DebugServices`. +- `OneScript.Web.Server` ↔ ASP.NET Core API — обёртки для работы из BSL. +- `oscript`/`StandaloneRunner`/`OneScriptDocumenter`/`TestApp`/`Component` — надстройки поверх ядра/рантайма. +- `ScriptEngine.NativeApi` ↔ `ScriptEngine`/`OneScript.Core` — нативные расширения. + +## 5. Типичные сценарии доработок (куда лезть) + +- Добавить объект/контекст с методами: `OneScript.Core/Contexts/*` (атрибуты `Context*Attribute`); подробности и шаблоны — в [`docs/contexts.md`](contexts.md). +- Добавить функцию в стандартную библиотеку: соответствующий раздел `OneScript.StandardLibrary` (например, `Json/` или `Collections/`), плюс экспорт в общий контекст (`StandardGlobalContext.cs` или `SymbolsContext.cs`, если нужно). +- Встроенная функция языка/операция: `OneScript.Native/Runtime/BuiltInFunctions.cs` и/или `Compiler/*`, при необходимости — поддержка в `ScriptEngine/Machine`. +- Отладка: `OneScript.DebugServices`/`OneScript.DebugProtocol` — добавление/изменение событий или представления переменных; `VSCode.DebugAdapter` — проксирование. + +## 6. Навигация по тестам + +- C#-тесты: `src/Tests/*`: + - Язык: `src/Tests/OneScript.Language.Tests/*` (лексер/парсер/препроцессор). + - Ядро/типы/контексты: `src/Tests/OneScript.Core.Tests/*`. + - Динамика/нативный рантайм: `src/Tests/OneScript.Dynamic.Tests/*`. + - Стандартная библиотека: `src/Tests/OneScript.StandardLibrary.Tests/*`. + - Документатор: `src/Tests/DocumenterTests/*`. + - Отладчик: `src/Tests/VSCode.DebugAdapter.Tests/`, `src/Tests/OneScript.DebugProtocol.Test/`. +- Скриптовые тесты: `tests/*.os` — поведенческие сценарии языка и стандартной библиотеки. Запускаются скриптами `tests/run-bsl-tests.cmd`/`tests/run-bsl-tests.sh` через свежесобранный `oscript` (см. [`README.md`](../README.md), раздел «Тестирование»). From a7f38e320c5263e925ba7b5aac44b063c51cb5d7 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Thu, 30 Apr 2026 11:00:41 +0300 Subject: [PATCH 08/65] =?UTF-8?q?feat(binary):=20=D0=B2=D1=8B=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=D0=BB=D0=B8=20=D0=BF=D0=BE=D1=80=D0=BE=D0=B3=20=D0=BF?= =?UTF-8?q?=D0=B0=D0=BC=D1=8F=D1=82=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=94?= =?UTF-8?q?=D0=B2=D0=BE=D0=B8=D1=87=D0=BD=D1=8B=D0=B5=D0=94=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8?= =?UTF-8?q?=D0=B3=D1=83=D1=80=D0=B0=D1=86=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавили ключ binaryData.inMemoryMaxBytes (байты) в oscript.cfg и переменную OSCRIPT_CONFIG. Изменили OneScriptCoreOptions: разбор значения с проверкой и запасным значением по умолчанию (50 МБ). Добавили BinaryDataConfigurationDefaults, IBinaryDataMemoryLimit и регистрацию в SetDefaultOptions. Добавили BinaryDataRuntimeSettings (AsyncLocal): на время BslProcess.Run лимит берётся из DI; вне процесса — дефолт. Изменили BinaryDataContext и конструктор FileBackingStream без параметров для использования эффективного лимита. Изменили FileBackingConstants.DEFAULT_MEMORY_LIMIT — ссылка на общий дефолт из ScriptEngine. Изменили CGI: WebRequestContext принимает лимит из сервисов до CreateProcess. --- .../Binary/BinaryDataContext.cs | 2 +- .../Binary/FileBackingConstants.cs | 3 +- .../Binary/FileBackingStream.cs | 3 +- src/ScriptEngine.HostedScript/oscript.cfg | 6 ++- .../BinaryDataConfigurationDefaults.cs | 20 ++++++++++ src/ScriptEngine/BslProcess.cs | 2 + .../Hosting/EngineBuilderExtensions.cs | 3 ++ src/ScriptEngine/IBinaryDataMemoryLimit.cs | 28 +++++++++++++ .../Machine/BinaryDataRuntimeSettings.cs | 40 +++++++++++++++++++ src/ScriptEngine/OneScriptCoreOptions.cs | 29 ++++++++++++++ src/oscript/CgiBehavior.cs | 6 ++- src/oscript/Web/WebRequestContext.cs | 12 +++++- 12 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 src/ScriptEngine/BinaryDataConfigurationDefaults.cs create mode 100644 src/ScriptEngine/IBinaryDataMemoryLimit.cs create mode 100644 src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs index e56d15ed3..24dbc0dd4 100644 --- a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs @@ -49,7 +49,7 @@ public BinaryDataContext(Stream stream) private void ReadFromStream(Stream stream) { - if (stream.Length < FileBackingConstants.DEFAULT_MEMORY_LIMIT) + if (stream.Length < BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes()) { LoadToBuffer(stream); } diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs index eeb073ad9..c3f7e8ac9 100644 --- a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs +++ b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs @@ -6,12 +6,13 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using ScriptEngine; namespace OneScript.StandardLibrary.Binary { public static class FileBackingConstants { - public const int DEFAULT_MEMORY_LIMIT = 1024 * 1024 * 50; // 50 Mb + public const int DEFAULT_MEMORY_LIMIT = BinaryDataConfigurationDefaults.InMemoryMaxBytes; public const int SYSTEM_IN_MEMORY_LIMIT = Int32.MaxValue; } } \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs index 7a0502643..515a1f07e 100644 --- a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs +++ b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the using System; using System.IO; +using ScriptEngine.Machine; namespace OneScript.StandardLibrary.Binary { @@ -20,7 +21,7 @@ public sealed class FileBackingStream : Stream private string _backingFileName; - public FileBackingStream() : this(FileBackingConstants.DEFAULT_MEMORY_LIMIT) + public FileBackingStream() : this(BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes()) { } diff --git a/src/ScriptEngine.HostedScript/oscript.cfg b/src/ScriptEngine.HostedScript/oscript.cfg index 1ddb2504f..6a664aa51 100644 --- a/src/ScriptEngine.HostedScript/oscript.cfg +++ b/src/ScriptEngine.HostedScript/oscript.cfg @@ -19,4 +19,8 @@ lib.system = ../lib #encoding.script=utf-8 -#systemlanguage = ru \ No newline at end of file +#systemlanguage = ru + +# Порог в байтах: пока размер двоичных данных в памяти не превышает это значение, данные держатся в RAM; +# при большем размере используется временный файл. По умолчанию 52428800 (50 МБ). +#binaryData.inMemoryMaxBytes=52428800 \ No newline at end of file diff --git a/src/ScriptEngine/BinaryDataConfigurationDefaults.cs b/src/ScriptEngine/BinaryDataConfigurationDefaults.cs new file mode 100644 index 000000000..346858a5c --- /dev/null +++ b/src/ScriptEngine/BinaryDataConfigurationDefaults.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine +{ + /// + /// Значения по умолчанию для буферизации двоичных данных в памяти до перехода на временный файл. + /// + public static class BinaryDataConfigurationDefaults + { + /// + /// Лимит по умолчанию (50 МБ), если в конфигурации не задан ключ binaryData.inMemoryMaxBytes. + /// + public const int InMemoryMaxBytes = 1024 * 1024 * 50; + } +} diff --git a/src/ScriptEngine/BslProcess.cs b/src/ScriptEngine/BslProcess.cs index 65b8c9de2..56d31960c 100644 --- a/src/ScriptEngine/BslProcess.cs +++ b/src/ScriptEngine/BslProcess.cs @@ -42,6 +42,7 @@ public BslValue Run(BslObjectValue target, IExecutableModule module, BslScriptMe var notifyExecutors = !_isRunning; if (notifyExecutors) { + BinaryDataRuntimeSettings.PushFromServices(Services); Array.ForEach(_executorProviders, e => e.BeforeProcessStart(this)); } @@ -56,6 +57,7 @@ public BslValue Run(BslObjectValue target, IExecutableModule module, BslScriptMe if (notifyExecutors) { Array.ForEach(_executorProviders, e => e.AfterProcessExit(this)); + BinaryDataRuntimeSettings.Pop(); _isRunning = false; } } diff --git a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs index 37225644d..f62b03fc0 100644 --- a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs +++ b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs @@ -79,6 +79,9 @@ public static IEngineBuilder SetDefaultOptions(this IEngineBuilder builder) var holder = sp.Resolve(); return holder.GetConfig(); }); + + services.RegisterSingleton(sp => + new BinaryDataMemoryLimitFromOptions(sp.Resolve())); services.Register(); diff --git a/src/ScriptEngine/IBinaryDataMemoryLimit.cs b/src/ScriptEngine/IBinaryDataMemoryLimit.cs new file mode 100644 index 000000000..aeb5744ac --- /dev/null +++ b/src/ScriptEngine/IBinaryDataMemoryLimit.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine +{ + /// + /// Лимит объёма данных в памяти для объектов «ДвоичныеДанные» и смежных потоков + /// до выгрузки во временный файл (байты). + /// + public interface IBinaryDataMemoryLimit + { + int MaxBytesInMemory { get; } + } + + internal sealed class BinaryDataMemoryLimitFromOptions : IBinaryDataMemoryLimit + { + public BinaryDataMemoryLimitFromOptions(OneScriptCoreOptions options) + { + MaxBytesInMemory = options.BinaryDataInMemoryMaxBytes; + } + + public int MaxBytesInMemory { get; } + } +} diff --git a/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs b/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs new file mode 100644 index 000000000..7da9a26b4 --- /dev/null +++ b/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Threading; +using OneScript.DependencyInjection; + +namespace ScriptEngine.Machine +{ + /// + /// Лимит памяти для двоичных данных на время выполнения процесса BSL (AsyncLocal). + /// + public static class BinaryDataRuntimeSettings + { + private static readonly AsyncLocal InMemoryMaxBytes = new AsyncLocal(); + + internal static void PushFromServices(IServiceContainer services) + { + var opts = services.TryResolve(); + InMemoryMaxBytes.Value = opts?.MaxBytesInMemory ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; + } + + internal static void Pop() + { + InMemoryMaxBytes.Value = null; + } + + /// + /// Текущий лимит «в памяти до временного файла» для двоичных данных (байты). + /// Вне процесса BSL возвращает значение по умолчанию из . + /// + public static int GetEffectiveInMemoryMaxBytes() + { + return InMemoryMaxBytes.Value ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; + } + } +} diff --git a/src/ScriptEngine/OneScriptCoreOptions.cs b/src/ScriptEngine/OneScriptCoreOptions.cs index 7d90b0793..609901df7 100644 --- a/src/ScriptEngine/OneScriptCoreOptions.cs +++ b/src/ScriptEngine/OneScriptCoreOptions.cs @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using OneScript.Commons; using OneScript.Native.Compiler; @@ -21,6 +22,7 @@ public class OneScriptCoreOptions private const string PREPROCESSOR_DEFINITIONS_KEY = "preprocessor.define"; private const string DEFAULT_RUNTIME_KEY = "runtime.default"; private const string EXPLICIT_IMPORT = "lang.explicitImports"; + private const string BINARY_DATA_IN_MEMORY_MAX = "binaryData.inMemoryMaxBytes"; public OneScriptCoreOptions(KeyValueConfig config) { @@ -29,6 +31,7 @@ public OneScriptCoreOptions(KeyValueConfig config) PreprocessorDefinitions = SetupDefinitions(config[PREPROCESSOR_DEFINITIONS_KEY]); UseNativeAsDefaultRuntime = SetupDefaultRuntime(config[DEFAULT_RUNTIME_KEY]); ExplicitImports = SetupExplicitImports(config[EXPLICIT_IMPORT]); + BinaryDataInMemoryMaxBytes = SetupBinaryDataInMemoryMax(config[BINARY_DATA_IN_MEMORY_MAX]); } public string SystemLanguage { get; } @@ -41,6 +44,12 @@ public OneScriptCoreOptions(KeyValueConfig config) public ExplicitImportsBehavior ExplicitImports { get; } + /// + /// Максимальный объём двоичных данных в памяти до перехода на временный файл (байты). + /// Задаётся ключом binaryData.inMemoryMaxBytes в конфигурации. + /// + public int BinaryDataInMemoryMaxBytes { get; } + private static IEnumerable SetupDefinitions(string s) { return s?.Split(',') ?? Array.Empty(); @@ -79,5 +88,25 @@ private static ExplicitImportsBehavior SetupExplicitImports(string keyValue) return ExplicitImportsBehavior.Warn; } } + + private static int SetupBinaryDataInMemoryMax(string rawValue) + { + if (string.IsNullOrWhiteSpace(rawValue)) + return BinaryDataConfigurationDefaults.InMemoryMaxBytes; + + if (!int.TryParse(rawValue.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var bytes)) + { + SystemLogger.Write($"Invalid value for {BINARY_DATA_IN_MEMORY_MAX}: {rawValue}"); + return BinaryDataConfigurationDefaults.InMemoryMaxBytes; + } + + if (bytes <= 0 || bytes == int.MaxValue) + { + SystemLogger.Write($"Value for {BINARY_DATA_IN_MEMORY_MAX} must be between 1 and {int.MaxValue - 1}: {bytes}"); + return BinaryDataConfigurationDefaults.InMemoryMaxBytes; + } + + return bytes; + } } } \ No newline at end of file diff --git a/src/oscript/CgiBehavior.cs b/src/oscript/CgiBehavior.cs index 911782454..157133743 100644 --- a/src/oscript/CgiBehavior.cs +++ b/src/oscript/CgiBehavior.cs @@ -15,6 +15,7 @@ This Source Code Form is subject to the terms of the using OneScript.StandardLibrary; using oscript.Web; +using ScriptEngine; using ScriptEngine.HostedScript; using ScriptEngine.Hosting; using ScriptEngine.Machine; @@ -67,8 +68,9 @@ private int RunCGIMode(string scriptFile) }); var engine = ConsoleHostBuilder.Build(builder); - - var request = new WebRequestContext(); + + var binaryMemoryLimit = engine.Services.Resolve().MaxBytesInMemory; + var request = new WebRequestContext(binaryMemoryLimit); engine.InjectGlobalProperty("ВебЗапрос", "WebRequest", request, true); engine.InjectObject(this); diff --git a/src/oscript/Web/WebRequestContext.cs b/src/oscript/Web/WebRequestContext.cs index 30f46d52c..f364f169e 100644 --- a/src/oscript/Web/WebRequestContext.cs +++ b/src/oscript/Web/WebRequestContext.cs @@ -15,6 +15,7 @@ This Source Code Form is subject to the terms of the using OneScript.StandardLibrary.Collections; using OneScript.StandardLibrary.Text; using oscript.Web.Multipart; +using ScriptEngine; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; @@ -27,8 +28,15 @@ public sealed class WebRequestContext : AutoContext, IDisposa private FileBackingStream _postBody; - public WebRequestContext() + private readonly int _postBodyInMemoryMaxBytes; + + public WebRequestContext() : this(BinaryDataConfigurationDefaults.InMemoryMaxBytes) + { + } + + public WebRequestContext(int postBodyInMemoryMaxBytes) { + _postBodyInMemoryMaxBytes = postBodyInMemoryMaxBytes; var get = Environment.GetEnvironmentVariable("QUERY_STRING"); if (get != null) FillGetMap(get); @@ -68,7 +76,7 @@ private void ProcessPostData() var type = Environment.GetEnvironmentVariable("CONTENT_TYPE"); using var stdin = Console.OpenStandardInput(); - var dest = new FileBackingStream(FileBackingConstants.DEFAULT_MEMORY_LIMIT, len); + var dest = new FileBackingStream(_postBodyInMemoryMaxBytes, len); stdin.CopyTo(dest); dest.Position = 0; From 97f8bb7e86a58e948fa845b4807867c4957473ff Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Thu, 30 Apr 2026 11:11:45 +0300 Subject: [PATCH 09/65] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B0=20=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=BB=D1=8F=20Linux=20=D0=B2=20README.md=20?= =?UTF-8?q?=D0=B8=20README-EN.md=20=D1=81=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=D0=BC=20=D0=B8=D0=BD=D1=84=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BE=20=D0=B2=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B0=D0=BD=D1=82=D0=B0=D1=85=20ZIP-=D0=B0=D1=80?= =?UTF-8?q?=D1=85=D0=B8=D0=B2=D0=BE=D0=B2=20=D0=B4=D0=BB=D1=8F=20.NET=208.?= =?UTF-8?q?0=20=D0=B8=20=D0=BD=D0=B5=D0=BE=D0=B1=D1=85=D0=BE=D0=B4=D0=B8?= =?UTF-8?q?=D0=BC=D1=8B=D1=85=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-EN.md | 18 ++++++++++++++++++ README.md | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/README-EN.md b/README-EN.md index 9c1476095..56d94750c 100644 --- a/README-EN.md +++ b/README-EN.md @@ -34,6 +34,16 @@ The OneScript distribution already includes a set of the most commonly used pack ### Linux ### +#### v2.x (current branch) — .NET 8.0 based + +Two ZIP archive variants are available: + +| Variant | Description | External dependencies | +|-------------------------------|--------------------------------------------|------------------------------------------------------------------------------------| +| **SCD** (self-contained) | .NET Runtime is bundled inside the archive | None | +| **FDD** (framework-dependent) | Smaller archive | Requires [.NET Runtime 8.0](https://learn.microsoft.com/dotnet/core/install/linux) | + +Installation steps: - Download the ZIP archive for Linux from the [Releases](https://github.com/EvilBeaver/OneScript/releases) section or from the [official website](https://oscript.io). - Extract the archive to a convenient directory. - Set executable permissions: @@ -41,6 +51,14 @@ The OneScript distribution already includes a set of the most commonly used pack chmod +x oscript ``` +The FDD variant requires .NET Runtime 8.0 — see installation instructions for your distribution at [learn.microsoft.com/dotnet/core/install/linux](https://learn.microsoft.com/dotnet/core/install/linux). + +#### v1.x LTS — deb package (Mono) + +The LTS branch is distributed as a `.deb` package and runs on Mono. The package automatically installs the minimum required Mono components; however, **the debugger requires `mono-complete`**. + +> **Important for debugging on Ubuntu/Debian:** if breakpoints are not being hit, install `mono-complete` from the **official Mono Project repository** (not from the distribution's default repositories). For instructions specific to your system, see the [Mono Project website](https://www.mono-project.com/download/stable/#download-lin). + ### MacOS ### - Download the ZIP archive for macOS (x64 or arm64) from the [Releases](https://github.com/EvilBeaver/OneScript/releases) section or from the [official website](https://oscript.io). diff --git a/README.md b/README.md index 95779d67d..4e9589a04 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,16 @@ OneScript позволяет создавать и выполнять текст ### Linux ### +#### v2.x (текущая ветка) — на базе .NET 8.0 + +Существуют два варианта ZIP-архива: + +| Вариант | Описание | Внешние зависимости | +|-------------------------------|----------------------------------|-------------------------------------------------------------------------------------| +| **SCD** (self-contained) | .NET Runtime уже включён в архив | Нет | +| **FDD** (framework-dependent) | Более компактный архив | Требуется [.NET Runtime 8.0](https://learn.microsoft.com/dotnet/core/install/linux) | + +Шаги установки: - Скачать ZIP-архив для Linux со [страницы релизов](https://github.com/EvilBeaver/OneScript/releases) или с [официального сайта](https://oscript.io). - Распаковать архив в удобный каталог. - Установить права на выполнение: @@ -41,6 +51,14 @@ OneScript позволяет создавать и выполнять текст chmod +x oscript ``` +Для FDD-варианта необходим .NET Runtime 8.0 — инструкция по установке на вашем дистрибутиве: [learn.microsoft.com/dotnet/core/install/linux](https://learn.microsoft.com/dotnet/core/install/linux). + +#### v1.x LTS — deb-пакет (Mono) + +LTS-ветка распространяется в виде `.deb`-пакета и работает на базе Mono. Пакет автоматически устанавливает минимально необходимые компоненты Mono, однако **для работы отладчика** требуется `mono-complete`. + +> **Важно для отладки на Ubuntu/Debian:** если точки останова не срабатывают, установите `mono-complete` из **официального репозитория Mono Project** (не из репозиториев дистрибутива). Инструкция для вашей системы — на [сайте Mono Project](https://www.mono-project.com/download/stable/#download-lin). + ### MacOS ### - Скачать ZIP-архив для macOS (x64 или arm64) со [страницы релизов](https://github.com/EvilBeaver/OneScript/releases) или с [официального сайта](https://oscript.io). From 2c90bbf06ee9b04969a8ebda44bb5d4c66287006 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Thu, 30 Apr 2026 11:20:10 +0300 Subject: [PATCH 10/65] =?UTF-8?q?refactor(BslProcess):=20=D0=9E=D0=BF?= =?UTF-8?q?=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=83?= =?UTF-8?q?=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B8=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Логика уведомлений исполнителя перемещена в единый условный блок для большей ясности. Улучшена обработка ошибок при завершении процесса для обеспечения надлежащей очистки ресурсов. Обновлены параметры BinaryDataRuntimeSettings для обеспечения положительных ограничений памяти и резервных значений по умолчанию. Скорректирован WebRequestContext для проверки ограничений памяти при создании экземпляра. --- src/ScriptEngine/BslProcess.cs | 24 ++++++++++++------- .../Machine/BinaryDataRuntimeSettings.cs | 5 +++- src/oscript/CgiBehavior.cs | 6 ++++- src/oscript/Web/WebRequestContext.cs | 6 ++++- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/ScriptEngine/BslProcess.cs b/src/ScriptEngine/BslProcess.cs index 56d31960c..f4aa49d34 100644 --- a/src/ScriptEngine/BslProcess.cs +++ b/src/ScriptEngine/BslProcess.cs @@ -40,25 +40,33 @@ public BslProcess(int id, ExecutionContext context, IEnumerable e.BeforeProcessStart(this)); - } - - _isRunning = true; try { + if (notifyExecutors) + Array.ForEach(_executorProviders, e => e.BeforeProcessStart(this)); + + _isRunning = true; + return _bslExecutorsByModule[module.GetType()](this, target, module, method, arguments); } finally { if (notifyExecutors) { - Array.ForEach(_executorProviders, e => e.AfterProcessExit(this)); - BinaryDataRuntimeSettings.Pop(); - _isRunning = false; + try + { + if (_isRunning) + Array.ForEach(_executorProviders, e => e.AfterProcessExit(this)); + } + finally + { + BinaryDataRuntimeSettings.Pop(); + _isRunning = false; + } } } } diff --git a/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs b/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs index 7da9a26b4..6f0302e3c 100644 --- a/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs +++ b/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs @@ -20,7 +20,10 @@ public static class BinaryDataRuntimeSettings internal static void PushFromServices(IServiceContainer services) { var opts = services.TryResolve(); - InMemoryMaxBytes.Value = opts?.MaxBytesInMemory ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; + var maxBytes = opts?.MaxBytesInMemory ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; + if (maxBytes <= 0) + maxBytes = BinaryDataConfigurationDefaults.InMemoryMaxBytes; + InMemoryMaxBytes.Value = maxBytes; } internal static void Pop() diff --git a/src/oscript/CgiBehavior.cs b/src/oscript/CgiBehavior.cs index 157133743..13a103d65 100644 --- a/src/oscript/CgiBehavior.cs +++ b/src/oscript/CgiBehavior.cs @@ -69,7 +69,11 @@ private int RunCGIMode(string scriptFile) var engine = ConsoleHostBuilder.Build(builder); - var binaryMemoryLimit = engine.Services.Resolve().MaxBytesInMemory; + var binaryMemoryLimit = engine.Services.TryResolve()?.MaxBytesInMemory + ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; + if (binaryMemoryLimit <= 0) + binaryMemoryLimit = BinaryDataConfigurationDefaults.InMemoryMaxBytes; + var request = new WebRequestContext(binaryMemoryLimit); engine.InjectGlobalProperty("ВебЗапрос", "WebRequest", request, true); engine.InjectObject(this); diff --git a/src/oscript/Web/WebRequestContext.cs b/src/oscript/Web/WebRequestContext.cs index f364f169e..d80a1bf0c 100644 --- a/src/oscript/Web/WebRequestContext.cs +++ b/src/oscript/Web/WebRequestContext.cs @@ -36,6 +36,9 @@ public WebRequestContext() : this(BinaryDataConfigurationDefaults.InMemoryMaxByt public WebRequestContext(int postBodyInMemoryMaxBytes) { + if (postBodyInMemoryMaxBytes <= 0) + throw new ArgumentOutOfRangeException(nameof(postBodyInMemoryMaxBytes), + "Лимит памяти для тела POST-запроса должен быть положительным числом байт."); _postBodyInMemoryMaxBytes = postBodyInMemoryMaxBytes; var get = Environment.GetEnvironmentVariable("QUERY_STRING"); if (get != null) FillGetMap(get); @@ -76,7 +79,8 @@ private void ProcessPostData() var type = Environment.GetEnvironmentVariable("CONTENT_TYPE"); using var stdin = Console.OpenStandardInput(); - var dest = new FileBackingStream(_postBodyInMemoryMaxBytes, len); + var initialCapacity = Math.Min(len, _postBodyInMemoryMaxBytes); + var dest = new FileBackingStream(_postBodyInMemoryMaxBytes, initialCapacity); stdin.CopyTo(dest); dest.Position = 0; From 8c222025158813322b5700887a220c8d8eab3af8 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Thu, 30 Apr 2026 12:08:59 +0300 Subject: [PATCH 11/65] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=BE=D0=B2=20=D0=B2=20README.md=20=D0=B8=20?= =?UTF-8?q?README-EN.md=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B7=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D0=BE=D0=B2=20=D0=BE=20.NET=208.0=20=D0=B8=20LTS?= =?UTF-8?q?=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8,=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=B8=D0=BC?= =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D0=BE=D0=B2=20=D0=B7=D0=B0=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=88=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-EN.md | 6 +++--- README.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README-EN.md b/README-EN.md index 56d94750c..36c6e80cb 100644 --- a/README-EN.md +++ b/README-EN.md @@ -34,7 +34,7 @@ The OneScript distribution already includes a set of the most commonly used pack ### Linux ### -#### v2.x (current branch) — .NET 8.0 based +#### v2.x (current branch) — .NET 8.0 based #### Two ZIP archive variants are available: @@ -48,12 +48,12 @@ Installation steps: - Extract the archive to a convenient directory. - Set executable permissions: ```bash - chmod +x oscript + chmod +x ./oscript ``` The FDD variant requires .NET Runtime 8.0 — see installation instructions for your distribution at [learn.microsoft.com/dotnet/core/install/linux](https://learn.microsoft.com/dotnet/core/install/linux). -#### v1.x LTS — deb package (Mono) +#### v1.x LTS — deb package (Mono) #### The LTS branch is distributed as a `.deb` package and runs on Mono. The package automatically installs the minimum required Mono components; however, **the debugger requires `mono-complete`**. diff --git a/README.md b/README.md index 4e9589a04..55b917ba8 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ OneScript позволяет создавать и выполнять текст ### Linux ### -#### v2.x (текущая ветка) — на базе .NET 8.0 +#### v2.x (текущая ветка) — на базе .NET 8.0 #### Существуют два варианта ZIP-архива: @@ -53,7 +53,7 @@ OneScript позволяет создавать и выполнять текст Для FDD-варианта необходим .NET Runtime 8.0 — инструкция по установке на вашем дистрибутиве: [learn.microsoft.com/dotnet/core/install/linux](https://learn.microsoft.com/dotnet/core/install/linux). -#### v1.x LTS — deb-пакет (Mono) +#### v1.x LTS — deb-пакет (Mono) #### LTS-ветка распространяется в виде `.deb`-пакета и работает на базе Mono. Пакет автоматически устанавливает минимально необходимые компоненты Mono, однако **для работы отладчика** требуется `mono-complete`. From 126d144b3a2d05942e434c78c6cd6c8958fc9cf7 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Fri, 1 May 2026 20:45:09 +0300 Subject: [PATCH 12/65] test(text-write): update expected line count in text reading test --- tests/text-write.os | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/text-write.os b/tests/text-write.os index f050e067e..2e6e2c780 100644 --- a/tests/text-write.os +++ b/tests/text-write.os @@ -274,7 +274,7 @@ Текст.Открыть(ПутьФайла,"UTF-8"); Стр = Текст.Прочитать(); - юТест.ПроверитьРавенство(6, КоличествоСтрок); + юТест.ПроверитьРавенство(10, КоличествоСтрок); юТест.ПроверитьРавенство(125, СтрДлина(Стр)); КонецПроцедуры From 264d7d372a541bcccc6fe73a77b46656286fd1b1 Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Tue, 5 May 2026 22:43:20 +0300 Subject: [PATCH 13/65] =?UTF-8?q?Fix=20#1673:=20=D0=9F=D1=80=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=BE=D1=81=20=D0=BF=D1=80=D0=BE=D1=86=D0=B5=D1=81=D1=81?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=B8=20native-=D0=BA=D0=BE=D0=BC=D0=BF?= =?UTF-8?q?=D0=B8=D0=BB=D1=8F=D1=86=D0=B8=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Native/Compiler/MethodCompiler.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/OneScript.Native/Compiler/MethodCompiler.cs b/src/OneScript.Native/Compiler/MethodCompiler.cs index 190643ebf..728f280a1 100644 --- a/src/OneScript.Native/Compiler/MethodCompiler.cs +++ b/src/OneScript.Native/Compiler/MethodCompiler.cs @@ -1092,6 +1092,13 @@ protected override void VisitGlobalFunctionCall(CallNode node) _statementBuildParts.Push(expression); } + private static bool HasProcessParameter(MethodInfo mi) + { + var p = mi.GetParameters(); + if (p.Length > 0 && p[0].ParameterType == typeof(IBslProcess)) return true; + return false; + } + protected override void VisitObjectProcedureCall(BslSyntaxNode node) { var target = _statementBuildParts.Pop(); @@ -1102,7 +1109,8 @@ protected override void VisitObjectProcedureCall(BslSyntaxNode node) if (targetType.IsObjectValue()) { var methodInfo = FindMethodOfType(node, targetType, name); - var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), methodInfo is ContextMethodInfo { InjectsProcess: true }); + var injectProcess = methodInfo is ContextMethodInfo { InjectsProcess: true } || HasProcessParameter(methodInfo); + var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), injectProcess); _blocks.Add(Expression.Call(target, methodInfo, args)); } From 4806a1601c4d70c927be3f2b1ec3fec489119362 Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Wed, 6 May 2026 13:41:09 +0300 Subject: [PATCH 14/65] =?UTF-8?q?#1673:=20=D0=A2=D0=B5=D1=81=D1=82=D1=8B?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=20=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D0=BE=D0=B4=D0=BE=D0=B2=20=D1=81=20bsl-=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=BE=D0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NativeCompilerTest.cs | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs index 11ade2183..68c4a1c87 100644 --- a/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs +++ b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs @@ -867,7 +867,44 @@ public void Can_Call_Member_Procedures() array.Should().HaveCount(2); } - + + [Fact] + public void Can_Call_Member_ProceduresWithBslProcess() + { + var tm = new DefaultTypeManager(); + var testType = tm.RegisterClass(typeof(ValueListImpl)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Список", new BslTypeValue(testType)); + // в методе SortByValue первым параметром идет IBslProcess process + block.CodeBlock = "Список.СортироватьПоЗначению();"; + + var method = block.CreateDelegate>(); + var testValue = new ValueListImpl(); + method(testValue); + + } + + [Fact] + public void Can_Call_Member_FunctionsWithBslProcess() + { + var tm = new DefaultTypeManager(); + var targetTestType = tm.RegisterClass(typeof(ArrayImpl)); + var testType = tm.RegisterClass(typeof(ReflectorContext)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Рефлектор", new BslTypeValue(testType)); + block.Parameters.Insert("Массив", new BslTypeValue(targetTestType)); + // в методе CallMethod первым параметром идет IBslProcess process + block.CodeBlock = "Результат = Рефлектор.ВызватьМетод(Массив, \"Количество\");"; + + var method = block.CreateDelegate>(); + var reflector = ReflectorContext.CreateNew(new TypeActivationContext() { TypeManager = tm}); + var array = new ArrayImpl(); + method(reflector, array); + + } + [Fact] public void Can_Call_Member_Procedures_On_Dynamics() { From f9975adba0771ec95caf6cd569cdc50af9c357f6 Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Wed, 6 May 2026 13:42:24 +0300 Subject: [PATCH 15/65] =?UTF-8?q?#1673:=20=D0=9F=D1=80=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=BE=D1=81=20=D0=BF=D1=80=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=B0?= =?UTF-8?q?=20=D0=B2=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=20=D1=84=D1=83=D0=BD?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Compiler/MethodCompiler.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/OneScript.Native/Compiler/MethodCompiler.cs b/src/OneScript.Native/Compiler/MethodCompiler.cs index 728f280a1..718e48b17 100644 --- a/src/OneScript.Native/Compiler/MethodCompiler.cs +++ b/src/OneScript.Native/Compiler/MethodCompiler.cs @@ -1092,10 +1092,17 @@ protected override void VisitGlobalFunctionCall(CallNode node) _statementBuildParts.Push(expression); } - private static bool HasProcessParameter(MethodInfo mi) + private static bool InjectedProcessNeeded(MethodInfo methodInfo) { - var p = mi.GetParameters(); - if (p.Length > 0 && p[0].ParameterType == typeof(IBslProcess)) return true; + if (methodInfo is ContextMethodInfo { InjectsProcess: true }) + { + return true; + } + var p = methodInfo.GetParameters(); + if (p.Length > 0 && p[0].ParameterType == typeof(IBslProcess)) + { + return true; + } return false; } @@ -1109,7 +1116,7 @@ protected override void VisitObjectProcedureCall(BslSyntaxNode node) if (targetType.IsObjectValue()) { var methodInfo = FindMethodOfType(node, targetType, name); - var injectProcess = methodInfo is ContextMethodInfo { InjectsProcess: true } || HasProcessParameter(methodInfo); + var injectProcess = InjectedProcessNeeded(methodInfo); var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), injectProcess); _blocks.Add(Expression.Call(target, methodInfo, args)); @@ -1176,8 +1183,8 @@ protected override void VisitObjectFunctionCall(BslSyntaxNode node) $"Метод {targetType}.{name} не является функцией", $"Method {targetType}.{name} is not a function"), ToCodePosition(node.Location)); } - - var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), methodInfo is ContextMethodInfo { InjectsProcess: true }); + + var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), InjectedProcessNeeded(methodInfo)); _statementBuildParts.Push(Expression.Call(target, methodInfo, args)); } else if (targetType.IsContext()) @@ -1274,7 +1281,7 @@ private Expression CreateMethodCall(CallNode node) } var symbol = Symbols.GetScope(binding.ScopeNumber).Methods[binding.MemberNumber]; - var args = PrepareCallArguments(node.ArgumentList, symbol.Method.GetParameters(), symbol.Method is ContextMethodInfo { InjectsProcess: true }); + var args = PrepareCallArguments(node.ArgumentList, symbol.Method.GetParameters(), InjectedProcessNeeded(symbol.Method)); var methodInfo = symbol.Method; if (methodInfo is ContextMethodInfo contextMethod) From 391fe93ba703331c236d2dafed9a17245e3746af Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Thu, 7 May 2026 21:40:03 +0300 Subject: [PATCH 16/65] =?UTF-8?q?#1673:=20=D0=A2=D0=B5=D1=81=D1=82=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HelperClasses/TestContextClass.cs | 44 +++++++++++++++- .../NativeCompilerTest.cs | 51 +++++++++++-------- 2 files changed, 73 insertions(+), 22 deletions(-) diff --git a/src/Tests/OneScript.Core.Tests/HelperClasses/TestContextClass.cs b/src/Tests/OneScript.Core.Tests/HelperClasses/TestContextClass.cs index 0ce5e9f00..6992d49a4 100644 --- a/src/Tests/OneScript.Core.Tests/HelperClasses/TestContextClass.cs +++ b/src/Tests/OneScript.Core.Tests/HelperClasses/TestContextClass.cs @@ -5,13 +5,14 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System.Collections.Generic; using OneScript.Contexts; +using OneScript.Execution; using OneScript.Types; using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; using ScriptEngine.Types; +using System.Collections.Generic; namespace OneScript.Core.Tests { @@ -60,7 +61,46 @@ public override void SetIndexedValue(IValue index, IValue val) _indexedValues[(BslValue)index] = (BslValue)val; } - [ScriptConstructor] + #region IBslProcessTests + + [ContextMethod("Процедура0")] + public void Procedure0() { } + + [ContextMethod("Процедура0СПроцессом")] + public void Procedure0WithProcess(IBslProcess process) { } + + [ContextMethod("Процедура1")] + public void Procedure1(int intValue) { } + + [ContextMethod("Процедура1СПроцессом")] + public void Procedure1WithProcess(IBslProcess process, int intValue) { } + + [ContextMethod("Процедура1СУмолчанием")] + public void Procedure1WithDefault(int intValue = 0) { } + + [ContextMethod("Процедура1СУмолчаниемСПроцессом")] + public void Procedure1WithDefaultWithProcess(IBslProcess process, int intValue = 0) { } + + [ContextMethod("Функция0")] + public int Function0() { return 0; } + + [ContextMethod("Функция0СПроцессом")] + public int Function0WithProcess(IBslProcess process) { return 0; } + + [ContextMethod("Функция1")] + public int Function1(int intValue) { return intValue; } + + [ContextMethod("Функция1СПроцессом")] + public int Function1WithProcess(IBslProcess process, int intValue) { return intValue; } + + [ContextMethod("Функция1СУмолчанием")] + public int Function1WithDefault(int intValue = 0) { return intValue; } + + [ContextMethod("Функция1СУмолчаниемСПроцессом")] + public int Function1WithDefaultWithProcess(IBslProcess process, int intValue = 0) { return intValue; } + #endregion + + [ScriptConstructor] public static TestContextClass Constructor() { return new TestContextClass diff --git a/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs index 68c4a1c87..51f6c9ee8 100644 --- a/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs +++ b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs @@ -868,40 +868,51 @@ public void Can_Call_Member_Procedures() array.Should().HaveCount(2); } - [Fact] - public void Can_Call_Member_ProceduresWithBslProcess() + [Theory] + [InlineData("Объект.Процедура0();")] + [InlineData("Объект.Процедура0СПроцессом();")] + [InlineData("Объект.Процедура1(1);")] + [InlineData("Объект.Процедура1СПроцессом(1);")] + [InlineData("Объект.Процедура1СУмолчанием();")] + [InlineData("Объект.Процедура1СУмолчаниемСПроцессом();")] + [InlineData("Объект.Процедура1СУмолчанием(1);")] + [InlineData("Объект.Процедура1СУмолчаниемСПроцессом(1);")] + public void Can_Call_Member_ProceduresWithBslProcess(string code) { var tm = new DefaultTypeManager(); - var testType = tm.RegisterClass(typeof(ValueListImpl)); + var testType = tm.RegisterClass(typeof(TestContextClass)); var block = new CompiledBlock(default); - block.Parameters.Insert("Список", new BslTypeValue(testType)); - // в методе SortByValue первым параметром идет IBslProcess process - block.CodeBlock = "Список.СортироватьПоЗначению();"; + block.Parameters.Insert("Объект", new BslTypeValue(testType)); + block.CodeBlock = code; - var method = block.CreateDelegate>(); - var testValue = new ValueListImpl(); + var method = block.CreateDelegate>(); + var testValue = new TestContextClass(); method(testValue); } - [Fact] - public void Can_Call_Member_FunctionsWithBslProcess() + [Theory] + [InlineData("Объект.Функция0()")] + [InlineData("Объект.Функция0СПроцессом()")] + [InlineData("Объект.Функция1(1)")] + [InlineData("Объект.Функция1СПроцессом(1)")] + [InlineData("Объект.Функция1СУмолчанием()")] + [InlineData("Объект.Функция1СУмолчаниемСПроцессом()")] + [InlineData("Объект.Функция1СУмолчанием(1)")] + [InlineData("Объект.Функция1СУмолчаниемСПроцессом(1)")] + public void Can_Call_Member_FunctionsWithBslProcess(string code) { var tm = new DefaultTypeManager(); - var targetTestType = tm.RegisterClass(typeof(ArrayImpl)); - var testType = tm.RegisterClass(typeof(ReflectorContext)); + var testType = tm.RegisterClass(typeof(TestContextClass)); var block = new CompiledBlock(default); - block.Parameters.Insert("Рефлектор", new BslTypeValue(testType)); - block.Parameters.Insert("Массив", new BslTypeValue(targetTestType)); - // в методе CallMethod первым параметром идет IBslProcess process - block.CodeBlock = "Результат = Рефлектор.ВызватьМетод(Массив, \"Количество\");"; + block.Parameters.Insert("Объект", new BslTypeValue(testType)); + block.CodeBlock = $"Результат = {code};"; - var method = block.CreateDelegate>(); - var reflector = ReflectorContext.CreateNew(new TypeActivationContext() { TypeManager = tm}); - var array = new ArrayImpl(); - method(reflector, array); + var method = block.CreateDelegate>(); + var testValue = new TestContextClass(); + method(testValue); } From 24f4eba53a316bd4e84c428151b6706f4dc3e043 Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Fri, 8 May 2026 00:32:30 +0300 Subject: [PATCH 17/65] =?UTF-8?q?#1673:=20=D0=A2=D0=B5=D1=81=D1=82=D1=8B?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=BF=D1=80=D0=B5=D0=B2=D1=8B=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B2=D0=B0=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NativeCompilerTest.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs index 51f6c9ee8..c4029976f 100644 --- a/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs +++ b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs @@ -889,7 +889,6 @@ public void Can_Call_Member_ProceduresWithBslProcess(string code) var method = block.CreateDelegate>(); var testValue = new TestContextClass(); method(testValue); - } [Theory] @@ -913,7 +912,26 @@ public void Can_Call_Member_FunctionsWithBslProcess(string code) var method = block.CreateDelegate>(); var testValue = new TestContextClass(); method(testValue); + } + + [Theory] + [InlineData("Объект.Процедура0СПроцессом(1)")] + [InlineData("Объект.Функция0СПроцессом(1)")] + [InlineData("Объект.Функция1СУмолчаниемСПроцессом(1, 2)")] + [InlineData("Объект.Процедура1СУмолчаниемСПроцессом(1, 2, 3);")] + public void Cannot_Call_Member_Procedures_With_Wrong_Argument_Count(string code) + { + var tm = new DefaultTypeManager(); + var testType = tm.RegisterClass(typeof(TestContextClass)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Объект", new BslTypeValue(testType)); + block.CodeBlock = code; + var runtimeException = Assert.ThrowsAny(() => { + var method = block.CreateDelegate>(); + }); + Assert.Contains("Слишком много фактических параметров", runtimeException.Message); } [Fact] From 59f99f1fd5624782d9ee37767fea01db7eb54ec8 Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Fri, 8 May 2026 00:33:07 +0300 Subject: [PATCH 18/65] =?UTF-8?q?#1673:=20=D0=9E=D0=B1=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BF=D1=80=D0=B5=D0=B2=D1=8B=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA=D0=BE=D0=BB=D0=B8=D1=87=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D0=B0=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Native/Compiler/MethodCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OneScript.Native/Compiler/MethodCompiler.cs b/src/OneScript.Native/Compiler/MethodCompiler.cs index 718e48b17..c4187b040 100644 --- a/src/OneScript.Native/Compiler/MethodCompiler.cs +++ b/src/OneScript.Native/Compiler/MethodCompiler.cs @@ -1433,8 +1433,8 @@ private List PrepareCallArguments(BslSyntaxNode argList, ParameterIn ? ConvertToExpressionTree(passedArg.Children[0]) : null).ToArray(); - var parametersToProcess = declaredParameters.Length; var declStart = injectsProcess ? 1 : 0; + var parametersToProcess = declaredParameters.Length - declStart; for (int i = 0, decl = declStart; i < parameters.Length; i++, decl++) { if (parametersToProcess == 0) From 0ae2a9d7b007034118bcd3cc255c22460e204622 Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Sat, 9 May 2026 14:36:32 +0300 Subject: [PATCH 19/65] =?UTF-8?q?=D0=9D=D0=B5=D0=B2=D0=B5=D1=80=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=B1=D1=8B=D0=BB=20=D1=83=D0=BA=D0=B0=D0=B7=D0=B0?= =?UTF-8?q?=D0=BD=20=D1=82=D0=B8=D0=BF=20=D0=BF=D1=80=D0=B5=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=94=D0=B0=D1=82=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Native/Compiler/MethodCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OneScript.Native/Compiler/MethodCompiler.cs b/src/OneScript.Native/Compiler/MethodCompiler.cs index 190643ebf..b61dd0985 100644 --- a/src/OneScript.Native/Compiler/MethodCompiler.cs +++ b/src/OneScript.Native/Compiler/MethodCompiler.cs @@ -1310,7 +1310,7 @@ private Expression CreateBuiltInFunctionCall(CallNode node) result = DirectConversionCall(node, typeof(string)); break; case Token.Date: - result = DirectConversionCall(node, typeof(string)); + result = DirectConversionCall(node, typeof(DateTime)); break; case Token.Type: CheckArgumentsCount(node.ArgumentList, 1); From 5e4eb92a30cf7186f4fc55d636fece9e8a4d4431 Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Sat, 9 May 2026 15:28:51 +0300 Subject: [PATCH 20/65] =?UTF-8?q?=D0=A3=D1=87=D1=82=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=81=D1=80=D0=B5=D0=B4=D0=B0=20=D0=B7=D0=B0=D0=BF=D1=83=D1=81?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Тесты могут запускаться 1. без файла oscript.cfg - тогда тест не имеет смысла и его падение должно быть желтым, а не красным 2. с уже установленной переменной OSCRIPT_CONFIG - тогда переменную изначально надо очистить по завершении вернуть в исходное состояние. --- tests/config.os | 24 +++++++++++++++++------- tests/testrunner.os | 3 ++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/tests/config.os b/tests/config.os index e3d33f62e..530e47aa4 100644 --- a/tests/config.os +++ b/tests/config.os @@ -14,6 +14,10 @@ КонецФункции Процедура ТестДолженПроверитьПорядокПрименения_ПеременнаяОкруженияИмеетВысшийПриоритет() Экспорт + + ИсходноеЗначениеПеременной = ПолучитьПеременнуюСреды("OSCRIPT_CONFIG"); + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ОбновитьНастройкиСистемы(); // Получаем исходное значение языка системы ИсходноеЗначениеЯзыка = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); @@ -39,12 +43,12 @@ "Переменная окружения OSCRIPT_CONFIG должна иметь наивысший приоритет"); Исключение - // Очищаем переменную окружения даже в случае ошибки - УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + // Возвращаем переменную окружения в исходное состояние + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); ВызватьИсключение; КонецПопытки; - // Очищаем переменную окружения и возвращаем исходное состояние + // Очищаем переменную окружения УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); ОбновитьНастройкиСистемы(); @@ -53,6 +57,10 @@ юТест.ПроверитьРавенство(ИсходноеЗначениеЯзыка, ВосстановленноеЗначение, "После очистки переменной окружения значение должно вернуться к исходному"); + // Возвращаем переменную окружения в исходное состояние + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); + ОбновитьНастройкиСистемы(); + КонецПроцедуры Процедура ТестДолженПроверитьЧтениеЗначенияПоУмолчанию() Экспорт @@ -76,6 +84,8 @@ Процедура ТестДолженПроверитьПереопределениеНесколькихПараметровОдновременно() Экспорт + ИсходноеЗначениеПеременной = ПолучитьПеременнуюСреды("OSCRIPT_CONFIG"); + // Получаем исходные значения ИсходныйЯзык = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); ИсходнаяКодировка = ПолучитьЗначениеСистемнойНастройки("encoding.script"); @@ -107,13 +117,13 @@ "Кодировка должна измениться на " + НоваяКодировка); Исключение - // Очищаем переменную окружения даже в случае ошибки - УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + // Восстанавливаем переменную окружения даже в случае ошибки + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); ВызватьИсключение; КонецПопытки; - // Очищаем переменную окружения - УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + // Восстанавливаем переменную окружения + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); ОбновитьНастройкиСистемы(); // Проверяем, что значения вернулись (или стали Неопределено) diff --git a/tests/testrunner.os b/tests/testrunner.os index 6c413590b..7214f48e7 100644 --- a/tests/testrunner.os +++ b/tests/testrunner.os @@ -915,7 +915,8 @@ Функция ЕстьОшибка_МетодОбъектаНеОбнаружен(текстОшибки, имяМетода) Результат = Ложь; - Если (Найти(текстОшибки, "Метод объекта не обнаружен (") > 0 + Если (Найти(текстОшибки, "Метод объекта не обнаружен (") > 0 + ИЛИ Найти(текстОшибки, "Method not found (") > 0 ИЛИ Найти(текстОшибки, "Object method not found (") > 0) И (Найти(текстОшибки, ИмяМетода) > 0) Тогда Результат = Истина; From 3113d1f15dea452d057361a71d9670f628b833c0 Mon Sep 17 00:00:00 2001 From: Sergey Batanov Date: Sat, 9 May 2026 16:19:22 +0300 Subject: [PATCH 21/65] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9?= =?UTF-8?q?=D0=BA=D0=B8=20=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D1=8B=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BF=D0=B0=D0=B4=D0=B5=D0=BD=D0=B8=D0=B8?= =?UTF-8?q?=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=20config.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/config.os | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/config.os b/tests/config.os index 530e47aa4..99364e3cd 100644 --- a/tests/config.os +++ b/tests/config.os @@ -45,6 +45,7 @@ Исключение // Возвращаем переменную окружения в исходное состояние УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); + ОбновитьНастройкиСистемы(); ВызватьИсключение; КонецПопытки; @@ -119,6 +120,7 @@ Исключение // Восстанавливаем переменную окружения даже в случае ошибки УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); + ОбновитьНастройкиСистемы(); ВызватьИсключение; КонецПопытки; From 777fac191e87411761be0c21f3a522a399ef06aa Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Wed, 13 May 2026 12:02:55 +0400 Subject: [PATCH 22/65] =?UTF-8?q?fix=20#1679:=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=9E=D1=82=D0=BA=D1=80=D1=8B=D1=82=D1=8C=D0=9F=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=BA()=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82=D0=B0=20?= =?UTF-8?q?=D0=A7=D1=82=D0=B5=D0=BD=D0=B8=D0=B5JSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Json/JSONReader.cs | 37 ++++++++++++++++++- tests/json/test-json_reader.os | 12 ++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs index 444907ee2..f00b0514c 100644 --- a/src/OneScript.StandardLibrary/Json/JSONReader.cs +++ b/src/OneScript.StandardLibrary/Json/JSONReader.cs @@ -5,15 +5,16 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; -using System.IO; using Newtonsoft.Json; using OneScript.Commons; using OneScript.Contexts; using OneScript.Exceptions; +using OneScript.StandardLibrary.Binary; using OneScript.StandardLibrary.Text; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; +using System; +using System.IO; namespace OneScript.StandardLibrary.Json { @@ -266,6 +267,38 @@ public void OpenFile(string JSONFileName, IValue encoding = null) }; } + /// + /// Устанавливает поток для чтения JSON данным объектом. + /// Если перед вызовом данного метода уже производилось чтение JSON из другого файла, строки или потока, + /// то чтение прекращается и объект инициализируется для чтения из указанного потока. + /// + /// + /// Поток для чтения текста JSON. + /// + /// Позволяет задать кодировку входного потока. + [ContextMethod("ОткрытьПоток", "OpenStream")] + public void OpenStream(IValue streamContext, IValue encoding = null) + { + if (IsOpen()) + Close(); + + var stream = streamContext switch + { + GenericStream s => s.GetUnderlyingStream(), + FileStreamContext s => s.GetUnderlyingStream(), + MemoryStreamContext s => s.GetUnderlyingStream(), + + _ => throw RuntimeException.InvalidNthArgumentType(1) + }; + + var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8; + + _reader = new JsonReaderInternal(new StreamReader(stream, enc)) + { + SupportMultipleContent = true + }; + } + /// /// Если текущее значение – начало массива или объекта, то пропускает его содержимое и конец. /// Для остальных типов значений работает аналогично методу Прочитать(). diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index a3e98504a..4679e5b35 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -24,6 +24,8 @@ СписокТестов.Добавить("Тест_Должен_ПроверитьПропускНезавершенногоМассива"); СписокТестов.Добавить("Тест_Должен_ПроверитьПропускМассиваСВложенными"); СписокТестов.Добавить("Тест_Должен_ПроверитьПропускОбъектаСВложениями"); + + СписокТестов.Добавить("Тест_Должен_ПроверитьОткрытиеПотока"); Возврат СписокТестов; КонецФункции @@ -219,3 +221,13 @@ юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); юТест.ПроверитьРавенство(5, Чтение.ТекущееЗначение); КонецПроцедуры + +Процедура Тест_Должен_ПроверитьОткрытиеПотока() Экспорт + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}"); + Поток = Новый ПотокВПамяти(БДД); + Чтение = Новый ЧтениеJSON; + Чтение.ОткрытьПоток(Поток); + Json = ПрочитатьJSON(Чтение); + + юТест.ПроверитьРавенство(42, Json.Ответ); +КонецПроцедуры From 0297f46ee4cdcd5c09b4f35d5f84adb4641cf3c6 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Wed, 13 May 2026 21:42:40 +0400 Subject: [PATCH 23/65] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=BE=D0=B1=D1=89=D0=B8=D0=B9?= =?UTF-8?q?=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=20?= =?UTF-8?q?=D0=9F=D0=BE=D1=82=D0=BE=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Binary/FileStreamContext.cs | 2 +- src/OneScript.StandardLibrary/Json/JSONReader.cs | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/OneScript.StandardLibrary/Binary/FileStreamContext.cs b/src/OneScript.StandardLibrary/Binary/FileStreamContext.cs index 6386898a6..70f517935 100644 --- a/src/OneScript.StandardLibrary/Binary/FileStreamContext.cs +++ b/src/OneScript.StandardLibrary/Binary/FileStreamContext.cs @@ -22,7 +22,7 @@ namespace OneScript.StandardLibrary.Binary /// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно. /// [ContextClass("ФайловыйПоток", "FileStream")] - public class FileStreamContext : AutoContext, IStreamWrapper, IDisposable + public class FileStreamContext : AutoContext, IDisposable, IStreamWrapper { private readonly FileStream _underlyingStream; diff --git a/src/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs index f00b0514c..954f1f53d 100644 --- a/src/OneScript.StandardLibrary/Json/JSONReader.cs +++ b/src/OneScript.StandardLibrary/Json/JSONReader.cs @@ -272,25 +272,17 @@ public void OpenFile(string JSONFileName, IValue encoding = null) /// Если перед вызовом данного метода уже производилось чтение JSON из другого файла, строки или потока, /// то чтение прекращается и объект инициализируется для чтения из указанного потока. /// - /// + /// /// Поток для чтения текста JSON. /// /// Позволяет задать кодировку входного потока. [ContextMethod("ОткрытьПоток", "OpenStream")] - public void OpenStream(IValue streamContext, IValue encoding = null) + public void OpenStream(IStreamWrapper streamContext, IValue encoding = null) { if (IsOpen()) Close(); - - var stream = streamContext switch - { - GenericStream s => s.GetUnderlyingStream(), - FileStreamContext s => s.GetUnderlyingStream(), - MemoryStreamContext s => s.GetUnderlyingStream(), - _ => throw RuntimeException.InvalidNthArgumentType(1) - }; - + var stream = streamContext.GetUnderlyingStream(); var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8; _reader = new JsonReaderInternal(new StreamReader(stream, enc)) From 0ca87f7de220c33c7aa5f211ae1b1eebf64f4e18 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 14 May 2026 12:41:20 +0400 Subject: [PATCH 24/65] =?UTF-8?q?=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D1=8F=D1=82=D1=8C=20=D0=BF=D0=BE=D1=82=D0=BE=D0=BA=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D0=B8?= =?UTF-8?q?=20=D1=87=D1=82=D0=B5=D0=BD=D0=B8=D1=8F;=20=D1=83=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0=20=D0=BD=D0=B5=D0=BD=D1=83=D0=B6=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Json/JSONReader.cs | 11 ++++----- tests/json/test-json_reader.os | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs index 954f1f53d..18ae4a365 100644 --- a/src/OneScript.StandardLibrary/Json/JSONReader.cs +++ b/src/OneScript.StandardLibrary/Json/JSONReader.cs @@ -244,8 +244,7 @@ public void Close() [ContextMethod("ОткрытьФайл", "OpenFile")] public void OpenFile(string JSONFileName, IValue encoding = null) { - if (IsOpen()) - Close(); + Close(); StreamReader _fileReader; @@ -279,13 +278,12 @@ public void OpenFile(string JSONFileName, IValue encoding = null) [ContextMethod("ОткрытьПоток", "OpenStream")] public void OpenStream(IStreamWrapper streamContext, IValue encoding = null) { - if (IsOpen()) - Close(); + Close(); var stream = streamContext.GetUnderlyingStream(); var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8; - _reader = new JsonReaderInternal(new StreamReader(stream, enc)) + _reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen:true)) { SupportMultipleContent = true }; @@ -333,8 +331,7 @@ public bool Read() [ContextMethod("УстановитьСтроку", "SetString")] public void SetString(string JSONString) { - if (IsOpen()) - Close(); + Close(); _reader = new JsonReaderInternal(new StringReader(JSONString)) { diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index 4679e5b35..4a7cdf8b3 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -26,6 +26,8 @@ СписокТестов.Добавить("Тест_Должен_ПроверитьПропускОбъектаСВложениями"); СписокТестов.Добавить("Тест_Должен_ПроверитьОткрытиеПотока"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПовторноеОткрытиеПотока"); + СписокТестов.Добавить("Тест_Должен_ПроверитьСохранениеПотокаПослеЗакрытияЧтения"); Возврат СписокТестов; КонецФункции @@ -231,3 +233,25 @@ юТест.ПроверитьРавенство(42, Json.Ответ); КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПовторноеОткрытиеПотока() Экспорт + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}"); + Поток = Новый ПотокВПамяти(БДД); + Чтение = Новый ЧтениеJSON; + Чтение.ОткрытьПоток(Поток); + Чтение.ОткрытьПоток(Поток); // повторно + Json = ПрочитатьJSON(Чтение); + + юТест.ПроверитьРавенство(42, Json.Ответ); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьСохранениеПотокаПослеЗакрытияЧтения() Экспорт + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}"); + Поток = Новый ПотокВПамяти(БДД); + Чтение = Новый ЧтениеJSON; + Чтение.Закрыть(); + Чтение.ОткрытьПоток(Поток); // Поток должен остаться открытым + Json = ПрочитатьJSON(Чтение); + + юТест.ПроверитьРавенство(42, Json.Ответ); +КонецПроцедуры From 021d90f8bba1fec5a8562ee67acfd83781f7a292 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 14 May 2026 13:12:16 +0400 Subject: [PATCH 25/65] null-safety; some cleanup; fix test --- .../Json/JSONReader.cs | 60 ++++--------------- tests/json/test-json_reader.os | 1 + 2 files changed, 12 insertions(+), 49 deletions(-) diff --git a/src/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs index 18ae4a365..866cd54b9 100644 --- a/src/OneScript.StandardLibrary/Json/JSONReader.cs +++ b/src/OneScript.StandardLibrary/Json/JSONReader.cs @@ -41,7 +41,6 @@ public override bool Read() } /// - /// /// Предназначен для последовательного чтения JSON-данных из файла или строки. /// [ContextClass("ЧтениеJSON", "JSONReader")] @@ -51,7 +50,6 @@ public class JSONReader : AutoContext private JsonReaderInternal _reader; /// - /// /// Возвращает true если для объекта чтения json был задан текст для парсинга. /// private bool IsOpen() => _reader != null; @@ -72,49 +70,25 @@ public static JSONReader Constructor() } /// - /// /// Указывает на позицию, находящуюся сразу после прочитанного значения. /// При ошибке чтение остается на позиции последнего успешно считанного символа. /// /// Число (Number), Неопределено (Undefined) [ContextProperty("ТекущаяПозиция", "CurrentPosition")] public IValue CurrentPosition - { - get - { - if (IsOpen()) - { - return ValueFactory.Create(_reader.LinePosition); - } - - return ValueFactory.Create(); // Неопределено - } - } + => IsOpen() ? ValueFactory.Create(_reader.LinePosition) : ValueFactory.Create(); /// - /// /// Указывает на позицию сразу после прочитанного значения. /// Например, перед чтением первого элемента - 0, после чтения первого элемента -1 . /// /// Число (Number), Неопределено (Undefined) [ContextProperty("ТекущаяСтрока", "CurrentLine")] public IValue CurrentLine - { - get - { - if (IsOpen()) - { - return ValueFactory.Create(_reader.LineNumber); - } - - return ValueFactory.Create(); // Неопределено - } - } + => IsOpen() ? ValueFactory.Create(_reader.LineNumber) : ValueFactory.Create(); /// - /// /// Содержит текущее значение: - /// /// - Число - если ТипТекущегоЗначения имеет значение Число; /// - Строка - если ТипТекущегоЗначения имеет одно из следующих значений: /// - Комментарий, @@ -161,7 +135,6 @@ public IValue CurrentValue default: throw JSONReaderException.CannotGetValue(); - ; } } } @@ -169,7 +142,6 @@ public IValue CurrentValue public object ReaderValue => _reader.Value; /// - /// /// Тип текущего значения в документе JSON во внутреннем формате. /// null - если чтение еще не началось или достигнут конец файла. /// @@ -184,7 +156,6 @@ public JsonToken CurrentJsonTokenType } /// - /// /// Тип текущего значения в документе JSON. /// Неопределено - если чтение еще не началось или достигнут конец файла. /// @@ -218,10 +189,8 @@ public JSONValueTypeEnum CurrentValueType } /// - /// /// Завершает чтение текста JSON из файла или строки. /// - /// [ContextMethod("Закрыть", "Close")] public void Close() { @@ -233,10 +202,10 @@ public void Close() } /// - /// - /// Открывает JSON-файл для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанного файла. + /// Открывает JSON-файл для чтения данным объектом. + /// Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, + /// то чтение прекращается и объект инициализируется для чтения из указанного файла. /// - /// /// /// Имя файла, содержащего текст JSON. /// @@ -249,11 +218,9 @@ public void OpenFile(string JSONFileName, IValue encoding = null) StreamReader _fileReader; try - { - if (encoding != null) - _fileReader = FileOpener.OpenReader(JSONFileName, TextEncodingEnum.GetEncoding(encoding)); - else - _fileReader = FileOpener.OpenReader(JSONFileName, System.Text.Encoding.UTF8); + { + var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8; + _fileReader = FileOpener.OpenReader(JSONFileName, enc); } catch (Exception e) { @@ -280,7 +247,9 @@ public void OpenStream(IStreamWrapper streamContext, IValue encoding = null) { Close(); - var stream = streamContext.GetUnderlyingStream(); + var stream = streamContext?.GetUnderlyingStream() + ?? throw new ArgumentNullException(nameof(streamContext)); + var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8; _reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen:true)) @@ -293,7 +262,6 @@ public void OpenStream(IStreamWrapper streamContext, IValue encoding = null) /// Если текущее значение – начало массива или объекта, то пропускает его содержимое и конец. /// Для остальных типов значений работает аналогично методу Прочитать(). /// - /// [ContextMethod("Пропустить", "Skip")] public bool Skip() { @@ -310,24 +278,18 @@ public bool Skip() /// /// Выполняет чтение значения JSON. /// - /// [ContextMethod("Прочитать", "Read")] public bool Read() { CheckIfOpen(); return _reader.Read(); - } /// - /// /// Устанавливает строку, содержащую текст JSON для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанной строки. /// - /// /// /// Строка, содержащая текст в формате JSON. - /// - /// [ContextMethod("УстановитьСтроку", "SetString")] public void SetString(string JSONString) { diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index 4a7cdf8b3..fdc3b5dc5 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -249,6 +249,7 @@ БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}"); Поток = Новый ПотокВПамяти(БДД); Чтение = Новый ЧтениеJSON; + Чтение.ОткрытьПоток(Поток); Чтение.Закрыть(); Чтение.ОткрытьПоток(Поток); // Поток должен остаться открытым Json = ПрочитатьJSON(Чтение); From 2cecba6c4fcf87aaa1d86ffb3ef5e90942714c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Sat, 16 May 2026 10:30:03 +0300 Subject: [PATCH 26/65] fix #1681 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправлено поведение объекта ЧтениеJSON и глобальной функции ПрочитатьJSON, строки с датами не конвертируются автоматически в дату --- src/OneScript.StandardLibrary/Json/JSONReader.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs index 866cd54b9..17deed3d5 100644 --- a/src/OneScript.StandardLibrary/Json/JSONReader.cs +++ b/src/OneScript.StandardLibrary/Json/JSONReader.cs @@ -230,6 +230,7 @@ public void OpenFile(string JSONFileName, IValue encoding = null) _reader = new JsonReaderInternal(_fileReader) { SupportMultipleContent = true + ,DateParseHandling = DateParseHandling.None }; } @@ -255,6 +256,7 @@ public void OpenStream(IStreamWrapper streamContext, IValue encoding = null) _reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen:true)) { SupportMultipleContent = true + ,DateParseHandling = DateParseHandling.None }; } @@ -298,6 +300,7 @@ public void SetString(string JSONString) _reader = new JsonReaderInternal(new StringReader(JSONString)) { SupportMultipleContent = true + ,DateParseHandling = DateParseHandling.None }; } From 8efa13e19cb93302fbde544885502427e0dc57b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Sat, 16 May 2026 11:24:40 +0300 Subject: [PATCH 27/65] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=A7=D1=82=D0=B5=D0=BD=D0=B8=D0=B5JSON=20=D0=B8=20=D0=9F?= =?UTF-8?q?=D1=80=D0=BE=D1=87=D0=B8=D1=82=D0=B0=D1=82=D1=8CJSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/json/test-json_reader.os | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index fdc3b5dc5..4ae00bae4 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -28,6 +28,11 @@ СписокТестов.Добавить("Тест_Должен_ПроверитьОткрытиеПотока"); СписокТестов.Добавить("Тест_Должен_ПроверитьПовторноеОткрытиеПотока"); СписокТестов.Добавить("Тест_Должен_ПроверитьСохранениеПотокаПослеЗакрытияЧтения"); + + СписокТестов.Добавить("Тест_Должен_ОпределятьСтрокуСДатойВФорматеISOКакСтроку"); + СписокТестов.Добавить("Тест_Должен_ЧитатьСтрокуСДатойВФорматеISOКакСтроку"); + СписокТестов.Добавить("Тест_Должен_ЧитатьДатуВФорматеISOКакСтрокуПриИспользованииПрочитатьJSON"); + Возврат СписокТестов; КонецФункции @@ -256,3 +261,73 @@ юТест.ПроверитьРавенство(42, Json.Ответ); КонецПроцедуры + +Процедура Тест_Должен_ОпределятьСтрокуСДатойВФорматеISOКакСтроку() Экспорт + + // проверяемая строка json + СтрокаJSON = "{ ""lastSignInTime"": ""2026-05-13T22:51:46Z"" }"; + + // какие типы должны быть при последовательном чтении + ЭталонЗначения = Новый Массив; + ЭталонЗначения.Добавить( ТипЗначенияJSON.НачалоОбъекта ); + ЭталонЗначения.Добавить( ТипЗначенияJSON.ИмяСвойства ); + ЭталонЗначения.Добавить( ТипЗначенияJSON.Строка ); + ЭталонЗначения.Добавить( ТипЗначенияJSON.КонецОбъекта ); + + // читаем последовательно строку json и собираем типы значений по порядку + ЧтениеJSON = Новый ЧтениеJSON; + ЧтениеJSON.УстановитьСтроку(СтрокаJSON); + ФактЗначения = Новый Массив; + Пока ЧтениеJSON.Прочитать() Цикл + ФактЗначения.Добавить( ЧтениеJSON.ТипТекущегоЗначения ); + КонецЦикла; + ЧтениеJSON.Закрыть(); + + // проверяем что эталон и факт данные идентичны + Если ЭталонЗначения.Количество() <> ФактЗначения.Количество() Тогда + ВызватьИсключение "Разное количество эталонных и фактических данных"; + КонецЕсли; + + Для Сч = 0 По ЭталонЗначения.ВГраница() Цикл + юТест.ПроверитьРавенство(ЭталонЗначения[Сч], ФактЗначения[Сч]); + КонецЦикла; + +КонецПроцедуры + +Процедура Тест_Должен_ЧитатьСтрокуСДатойВФорматеISOКакСтроку() Экспорт + + // проверяемая строка json + СтрокаJSON = "{ ""lastSignInTime"": ""2026-05-13T22:51:46Z"" }"; + + // читаем последовательно строку json и собираем типы значений по порядку + ЧтениеJSON = Новый ЧтениеJSON; + ЧтениеJSON.УстановитьСтроку(СтрокаJSON); + ЧтениеJSON.Прочитать(); + ЧтениеJSON.Прочитать(); + ЧтениеJSON.Прочитать(); + + ТекущееЗначение = ЧтениеJSON.ТекущееЗначение; + + ЧтениеJSON.Закрыть(); + + юТест.ПроверитьРавенство(ТекущееЗначение, "2026-05-13T22:51:46Z"); + +КонецПроцедуры + +Процедура Тест_Должен_ЧитатьДатуВФорматеISOКакСтрокуПриИспользованииПрочитатьJSON() Экспорт + + // проверяемая строка json + СтрокаJSON = "{ ""lastSignInTime"": ""2026-05-13T22:51:46.230Z"" }"; + + // читаем json в структуру + ЧтениеJSON = Новый ЧтениеJSON; + ЧтениеJSON.УстановитьСтроку(СтрокаJSON); + Объект = ПрочитатьJSON(ЧтениеJSON); + ЧтениеJSON.Закрыть(); + + Ожидаем = Тип("Строка"); + Факт = ТипЗнч( Объект.lastSignInTime ); + юТест.ПроверитьРавенство(Ожидаем, Факт); + юТест.ПроверитьРавенство(Объект.lastSignInTime, "2026-05-13T22:51:46.230Z"); + +КонецПроцедуры \ No newline at end of file From 540032541be85db9ab5cdcb35c0ed1bc4a6de110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Sat, 16 May 2026 13:12:19 +0300 Subject: [PATCH 28/65] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B,=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D1=8F=D1=8E=D1=89=D0=B8=D0=B5=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20=D1=87=D1=82=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=81=20=D0=BF=D0=BE=D0=BC=D0=BE=D1=89=D1=8C?= =?UTF-8?q?=D1=8E=20=D0=9E=D1=82=D0=BA=D1=80=D1=8B=D1=82=D1=8C=D0=9F=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D0=BA()=20=D0=B8=20=D0=9E=D1=82=D0=BA=D1=80=D1=8B?= =?UTF-8?q?=D1=82=D1=8C=D0=A4=D0=B0=D0=B9=D0=BB()=20-=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BC=D0=B5=D1=87=D0=B0=D0=BD=D0=B8=D1=8F=D0=BC=20?= =?UTF-8?q?coderabbit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/json/json-mock_isodate.json | 1 + tests/json/test-json_reader.os | 49 ++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/json/json-mock_isodate.json diff --git a/tests/json/json-mock_isodate.json b/tests/json/json-mock_isodate.json new file mode 100644 index 000000000..9da105096 --- /dev/null +++ b/tests/json/json-mock_isodate.json @@ -0,0 +1 @@ +{ "lastSignInTime": "2026-05-13T22:51:46Z" } \ No newline at end of file diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index 4ae00bae4..c7e3d4e36 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -31,6 +31,8 @@ СписокТестов.Добавить("Тест_Должен_ОпределятьСтрокуСДатойВФорматеISOКакСтроку"); СписокТестов.Добавить("Тест_Должен_ЧитатьСтрокуСДатойВФорматеISOКакСтроку"); + СписокТестов.Добавить("Тест_Должен_ОткрытьПотокЧитаетСтрокуСДатойВФорматеISOКакСтроку"); + СписокТестов.Добавить("Тест_Должен_ОткрытьФайлЧитаетСтрокуСДатойВФорматеISOКакСтроку"); СписокТестов.Добавить("Тест_Должен_ЧитатьДатуВФорматеISOКакСтрокуПриИспользованииПрочитатьJSON"); Возврат СписокТестов; @@ -330,4 +332,49 @@ юТест.ПроверитьРавенство(Ожидаем, Факт); юТест.ПроверитьРавенство(Объект.lastSignInTime, "2026-05-13T22:51:46.230Z"); -КонецПроцедуры \ No newline at end of file +КонецПроцедуры + + +Процедура Тест_Должен_ОткрытьПотокЧитаетСтрокуСДатойВФорматеISOКакСтроку() Экспорт + + // проверяемая строка json + СтрокаJSON = "{ ""lastSignInTime"": ""2026-05-13T22:51:46Z"" }"; + + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки(СтрокаJSON); + Поток = Новый ПотокВПамяти(БДД); + Чтение = Новый ЧтениеJSON; + Чтение.ОткрытьПоток(Поток); + + ЧтениеJSON = Новый ЧтениеJSON; + ЧтениеJSON.УстановитьСтроку(СтрокаJSON); + ЧтениеJSON.Прочитать(); + ЧтениеJSON.Прочитать(); + ЧтениеJSON.Прочитать(); + + ТекущееЗначение = ЧтениеJSON.ТекущееЗначение; + ТипТекущегоЗначения = ЧтениеJSON.ТипТекущегоЗначения; + + ЧтениеJSON.Закрыть(); + + юТест.ПроверитьРавенство(ТекущееЗначение, "2026-05-13T22:51:46Z"); + юТест.ПроверитьРавенство(ТипТекущегоЗначения, ТипЗначенияJSON.Строка); + +КонецПроцедуры + +Процедура Тест_Должен_ОткрытьФайлЧитаетСтрокуСДатойВФорматеISOКакСтроку() Экспорт + + Чтение = Новый ЧтениеJSON; + Чтение.ОткрытьФайл("json/json-mock_isodate.json"); + ЧтениеJSON.Прочитать(); + ЧтениеJSON.Прочитать(); + ЧтениеJSON.Прочитать(); + + ТекущееЗначение = ЧтениеJSON.ТекущееЗначение; + ТипТекущегоЗначения = ЧтениеJSON.ТипТекущегоЗначения; + + ЧтениеJSON.Закрыть(); + + юТест.ПроверитьРавенство(ТекущееЗначение, "2026-05-13T22:51:46Z"); + юТест.ПроверитьРавенство(ТипТекущегоЗначения, ТипЗначенияJSON.Строка); + +КонецПроцедуры From 4c9ee3c69bd8c3822f50f737b5cb7b8a87849291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Sat, 16 May 2026 13:16:09 +0300 Subject: [PATCH 29/65] =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BB=D0=B8=D1=88=D0=BD=D1=8F=D1=8F=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/json/test-json_reader.os | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index c7e3d4e36..1bc444cc9 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -285,11 +285,6 @@ КонецЦикла; ЧтениеJSON.Закрыть(); - // проверяем что эталон и факт данные идентичны - Если ЭталонЗначения.Количество() <> ФактЗначения.Количество() Тогда - ВызватьИсключение "Разное количество эталонных и фактических данных"; - КонецЕсли; - Для Сч = 0 По ЭталонЗначения.ВГраница() Цикл юТест.ПроверитьРавенство(ЭталонЗначения[Сч], ФактЗначения[Сч]); КонецЦикла; From 3a71ca02f842207c1db27d2190e10fc235d529c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Sat, 16 May 2026 22:14:54 +0300 Subject: [PATCH 30/65] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82=D1=80=D1=8B?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BF=D0=B0=D1=80?= =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B0=20JsonReaderInternal=20=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D0=BE=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Json/JSONReader.cs | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs index 17deed3d5..aa588f4ba 100644 --- a/src/OneScript.StandardLibrary/Json/JSONReader.cs +++ b/src/OneScript.StandardLibrary/Json/JSONReader.cs @@ -23,6 +23,8 @@ internal class JsonReaderInternal: JsonTextReader // из библиотеки public JsonReaderInternal(TextReader reader) : base(reader) { Finished = false; + SupportMultipleContent = true; + DateParseHandling = DateParseHandling.None; } public override bool Read() @@ -227,11 +229,7 @@ public void OpenFile(string JSONFileName, IValue encoding = null) throw new RuntimeException(e.Message, e); } - _reader = new JsonReaderInternal(_fileReader) - { - SupportMultipleContent = true - ,DateParseHandling = DateParseHandling.None - }; + _reader = new JsonReaderInternal(_fileReader); } /// @@ -253,11 +251,7 @@ public void OpenStream(IStreamWrapper streamContext, IValue encoding = null) var enc = encoding != null ? TextEncodingEnum.GetEncoding(encoding) : System.Text.Encoding.UTF8; - _reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen:true)) - { - SupportMultipleContent = true - ,DateParseHandling = DateParseHandling.None - }; + _reader = new JsonReaderInternal(new StreamReader(stream, enc, leaveOpen:true)); } /// @@ -297,11 +291,7 @@ public void SetString(string JSONString) { Close(); - _reader = new JsonReaderInternal(new StringReader(JSONString)) - { - SupportMultipleContent = true - ,DateParseHandling = DateParseHandling.None - }; + _reader = new JsonReaderInternal(new StringReader(JSONString)); } } From ced66636d7ecee1db343836069c6a5a788b68a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Sat, 16 May 2026 22:17:57 +0300 Subject: [PATCH 31/65] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82,=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20=D0=BD=D0=B0=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B2=D0=BF=D0=B0=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D0=BC=D0=B5=D1=80=D0=BE=D0=B2=20=D0=BC=D0=B0=D1=81=D1=81?= =?UTF-8?q?=D0=B8=D0=B2=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/json/test-json_reader.os | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index 1bc444cc9..e7fc4a1f0 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -284,6 +284,8 @@ ФактЗначения.Добавить( ЧтениеJSON.ТипТекущегоЗначения ); КонецЦикла; ЧтениеJSON.Закрыть(); + + юТест.ПроверитьРавно( ЭталонЗначения.Количество(), ФактЗначения.Количество() ); Для Сч = 0 По ЭталонЗначения.ВГраница() Цикл юТест.ПроверитьРавенство(ЭталонЗначения[Сч], ФактЗначения[Сч]); From 43c3809ada78f7493a538135c80747b3cc3c74b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Sat, 16 May 2026 22:30:54 +0300 Subject: [PATCH 32/65] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=B2=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/json/test-json_reader.os | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index e7fc4a1f0..c5e58617b 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -301,15 +301,17 @@ // читаем последовательно строку json и собираем типы значений по порядку ЧтениеJSON = Новый ЧтениеJSON; ЧтениеJSON.УстановитьСтроку(СтрокаJSON); - ЧтениеJSON.Прочитать(); - ЧтениеJSON.Прочитать(); - ЧтениеJSON.Прочитать(); + ЧтениеJSON.Прочитать(); // НачалоОбъекта + ЧтениеJSON.Прочитать(); // ИмяСвойства + ЧтениеJSON.Прочитать(); // Строка ТекущееЗначение = ЧтениеJSON.ТекущееЗначение; + ТекущийТип = ЧтениеJSON.ТипТекущегоЗначения; ЧтениеJSON.Закрыть(); юТест.ПроверитьРавенство(ТекущееЗначение, "2026-05-13T22:51:46Z"); + юТест.ПроверитьРавенство(ТекущийТип, ТипЗначенияJSON.Строка); КонецПроцедуры @@ -339,14 +341,13 @@ БДД = ПолучитьБуферДвоичныхДанныхИзСтроки(СтрокаJSON); Поток = Новый ПотокВПамяти(БДД); - Чтение = Новый ЧтениеJSON; - Чтение.ОткрытьПоток(Поток); - + ЧтениеJSON = Новый ЧтениеJSON; - ЧтениеJSON.УстановитьСтроку(СтрокаJSON); - ЧтениеJSON.Прочитать(); - ЧтениеJSON.Прочитать(); - ЧтениеJSON.Прочитать(); + ЧтениеJSON.ОткрытьПоток(Поток); + + ЧтениеJSON.Прочитать(); // НачалоОбъекта + ЧтениеJSON.Прочитать(); // ИмяСвойства + ЧтениеJSON.Прочитать(); // Строка ТекущееЗначение = ЧтениеJSON.ТекущееЗначение; ТипТекущегоЗначения = ЧтениеJSON.ТипТекущегоЗначения; @@ -360,11 +361,12 @@ Процедура Тест_Должен_ОткрытьФайлЧитаетСтрокуСДатойВФорматеISOКакСтроку() Экспорт - Чтение = Новый ЧтениеJSON; - Чтение.ОткрытьФайл("json/json-mock_isodate.json"); - ЧтениеJSON.Прочитать(); - ЧтениеJSON.Прочитать(); - ЧтениеJSON.Прочитать(); + ЧтениеJSON = Новый ЧтениеJSON; + ЧтениеJSON.ОткрытьФайл("json/json-mock_isodate.json"); + + ЧтениеJSON.Прочитать(); // НачалоОбъекта + ЧтениеJSON.Прочитать(); // ИмяСвойства + ЧтениеJSON.Прочитать(); // Строка ТекущееЗначение = ЧтениеJSON.ТекущееЗначение; ТипТекущегоЗначения = ЧтениеJSON.ТипТекущегоЗначения; From ae3651f8c6cdf0b576cfd5ce30737c9e740391a2 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Mon, 18 May 2026 15:35:39 +0300 Subject: [PATCH 33/65] =?UTF-8?q?feat(binary):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=D0=B4=D0=B4?= =?UTF-8?q?=D0=B5=D1=80=D0=B6=D0=BA=D0=B8=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8?= =?UTF-8?q?=D0=B3=D1=83=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BB=D0=B8=D0=BC?= =?UTF-8?q?=D0=B8=D1=82=D0=B0=20=D0=BF=D0=B0=D0=BC=D1=8F=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=B4=D0=B2=D0=BE=D0=B8=D1=87=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Созданы новые классы и интерфейсы для управления лимитом памяти двоичных данных, включая BinaryDataConfigurationDefaults, IBinaryDataMemoryLimit и BinaryDataMemoryLimit. Реализована регистрация лимита памяти в EngineBuilderExtensions и обновлены соответствующие классы для использования нового механизма. Удален устаревший BinaryDataRuntimeSettings. Обновлены тестовые и основные приложения для интеграции новых настроек. --- src/1Script.sln | 155 ++++++++++++++++++ .../BinaryDataConfigurationDefaults.cs | 2 +- .../BinaryDataMemoryLimit.cs | 22 +++ .../BinaryDataMemoryLimitConfiguration.cs | 43 +++++ .../BinaryDataRuntimeSettings.cs | 39 +++++ .../IBinaryDataMemoryLimit.cs | 12 +- .../OneScript.BinaryData.csproj | 16 ++ .../Binary/BinaryDataContext.cs | 1 + .../Binary/FileBackingConstants.cs | 2 +- .../Binary/FileBackingStream.cs | 2 +- .../EngineBuilderExtensions.cs | 21 ++- src/ScriptEngine/BslProcess.cs | 26 +-- .../Hosting/EngineBuilderExtensions.cs | 3 - .../Machine/BinaryDataRuntimeSettings.cs | 43 ----- src/ScriptEngine/OneScriptCoreOptions.cs | 29 ---- src/ScriptEngine/ScriptEngine.csproj | 1 + src/ScriptEngine/ScriptingEngine.cs | 3 + src/TestApp/MainWindow.xaml.cs | 1 + src/oscript/CgiBehavior.cs | 8 +- src/oscript/ConsoleHostBuilder.cs | 1 + src/oscript/Web/WebRequestContext.cs | 5 +- 21 files changed, 327 insertions(+), 108 deletions(-) rename src/{ScriptEngine => OneScript.BinaryData}/BinaryDataConfigurationDefaults.cs (96%) create mode 100644 src/OneScript.BinaryData/BinaryDataMemoryLimit.cs create mode 100644 src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs create mode 100644 src/OneScript.BinaryData/BinaryDataRuntimeSettings.cs rename src/{ScriptEngine => OneScript.BinaryData}/IBinaryDataMemoryLimit.cs (67%) create mode 100644 src/OneScript.BinaryData/OneScript.BinaryData.csproj delete mode 100644 src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs diff --git a/src/1Script.sln b/src/1Script.sln index ee4c3a253..73cf7cf41 100644 --- a/src/1Script.sln +++ b/src/1Script.sln @@ -65,272 +65,427 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Web.Server", "One EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumenterTests", "Tests\DocumenterTests\DocumenterTests.csproj", "{BD385142-E9B4-43C1-8F88-067F24E5AF6D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OneScript.BinaryData", "OneScript.BinaryData\OneScript.BinaryData.csproj", "{5711D446-8CA5-4F01-9DCE-D630FEF3A21B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x86 = Debug|x86 + Debug|x64 = Debug|x64 LinuxDebug|Any CPU = LinuxDebug|Any CPU LinuxDebug|x86 = LinuxDebug|x86 + LinuxDebug|x64 = LinuxDebug|x64 Release|Any CPU = Release|Any CPU Release|x86 = Release|x86 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Debug|Any CPU.Build.0 = Debug|Any CPU {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Debug|x86.ActiveCfg = Release|Any CPU {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Debug|x86.Build.0 = Release|Any CPU + {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Debug|x64.ActiveCfg = Debug|Any CPU + {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Debug|x64.Build.0 = Debug|Any CPU {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.LinuxDebug|x86.ActiveCfg = LinuxDebug|x86 {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.LinuxDebug|x86.Build.0 = LinuxDebug|x86 + {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Release|Any CPU.ActiveCfg = Release|Any CPU {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Release|Any CPU.Build.0 = Release|Any CPU {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Release|x86.ActiveCfg = Release|x86 {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Release|x86.Build.0 = Release|x86 + {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Release|x64.ActiveCfg = Release|Any CPU + {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Release|x64.Build.0 = Release|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|x86.ActiveCfg = Debug|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|x86.Build.0 = Debug|Any CPU + {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|x64.ActiveCfg = Debug|Any CPU + {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|x64.Build.0 = Debug|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|Any CPU.Build.0 = Release|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|x86.ActiveCfg = Release|Any CPU {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|x86.Build.0 = Release|Any CPU + {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|x64.ActiveCfg = Release|Any CPU + {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|x64.Build.0 = Release|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|Any CPU.Build.0 = Debug|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|x86.ActiveCfg = Debug|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|x86.Build.0 = Debug|Any CPU + {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|x64.ActiveCfg = Debug|Any CPU + {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|x64.Build.0 = Debug|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|Any CPU.Build.0 = Release|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|x86.ActiveCfg = Release|Any CPU {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|x86.Build.0 = Release|Any CPU + {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|x64.ActiveCfg = Release|Any CPU + {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|x64.Build.0 = Release|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|x86.ActiveCfg = Debug|x86 {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|x86.Build.0 = Debug|x86 + {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|x64.ActiveCfg = Debug|Any CPU + {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|x64.Build.0 = Debug|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.LinuxDebug|x86.ActiveCfg = LinuxDebug|x86 {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.LinuxDebug|x86.Build.0 = LinuxDebug|x86 + {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|Any CPU.Build.0 = Release|Any CPU {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|x86.ActiveCfg = Release|x86 {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|x86.Build.0 = Release|x86 + {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|x64.ActiveCfg = Release|Any CPU + {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|x64.Build.0 = Release|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|x64.Build.0 = Debug|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|x86.ActiveCfg = Release|Any CPU + {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|x64.ActiveCfg = Release|Any CPU + {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|x64.Build.0 = Release|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|Any CPU.Build.0 = Debug|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|x86.ActiveCfg = Debug|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|x86.Build.0 = Debug|Any CPU + {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|x64.Build.0 = Debug|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {C979F151-AA29-47E4-B020-3039BA0986D9}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {C979F151-AA29-47E4-B020-3039BA0986D9}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|Any CPU.ActiveCfg = Release|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|Any CPU.Build.0 = Release|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|x86.ActiveCfg = Release|Any CPU {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|x86.Build.0 = Release|Any CPU + {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|x64.ActiveCfg = Release|Any CPU + {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|x64.Build.0 = Release|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|Any CPU.Build.0 = Debug|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|x86.ActiveCfg = Debug|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|x86.Build.0 = Debug|Any CPU + {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|x64.ActiveCfg = Debug|Any CPU + {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|x64.Build.0 = Debug|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {727A498F-BF50-42F8-8523-40C0B5B1B233}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {727A498F-BF50-42F8-8523-40C0B5B1B233}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|Any CPU.ActiveCfg = Release|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|Any CPU.Build.0 = Release|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|x86.ActiveCfg = Release|Any CPU {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|x86.Build.0 = Release|Any CPU + {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|x64.ActiveCfg = Release|Any CPU + {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|x64.Build.0 = Release|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Debug|x86.ActiveCfg = Debug|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Debug|x86.Build.0 = Debug|Any CPU + {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Debug|x64.ActiveCfg = Debug|Any CPU + {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Debug|x64.Build.0 = Debug|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Release|Any CPU.Build.0 = Release|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Release|x86.ActiveCfg = Release|Any CPU {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Release|x86.Build.0 = Release|Any CPU + {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Release|x64.ActiveCfg = Release|Any CPU + {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Release|x64.Build.0 = Release|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.Debug|Any CPU.Build.0 = Debug|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.Debug|x86.ActiveCfg = Debug|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.Debug|x86.Build.0 = Debug|Any CPU + {872BFF20-7AD9-4741-B163-CD648AD3418F}.Debug|x64.ActiveCfg = Debug|Any CPU + {872BFF20-7AD9-4741-B163-CD648AD3418F}.Debug|x64.Build.0 = Debug|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {872BFF20-7AD9-4741-B163-CD648AD3418F}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {872BFF20-7AD9-4741-B163-CD648AD3418F}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.Release|Any CPU.ActiveCfg = Release|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.Release|Any CPU.Build.0 = Release|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.Release|x86.ActiveCfg = Release|Any CPU {872BFF20-7AD9-4741-B163-CD648AD3418F}.Release|x86.Build.0 = Release|Any CPU + {872BFF20-7AD9-4741-B163-CD648AD3418F}.Release|x64.ActiveCfg = Release|Any CPU + {872BFF20-7AD9-4741-B163-CD648AD3418F}.Release|x64.Build.0 = Release|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.Debug|Any CPU.Build.0 = Debug|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.Debug|x86.ActiveCfg = Debug|Any CPU + {76F2521D-44D7-48C9-A678-074E22B46092}.Debug|x64.ActiveCfg = Debug|Any CPU + {76F2521D-44D7-48C9-A678-074E22B46092}.Debug|x64.Build.0 = Debug|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {76F2521D-44D7-48C9-A678-074E22B46092}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {76F2521D-44D7-48C9-A678-074E22B46092}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.Release|Any CPU.ActiveCfg = Release|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.Release|Any CPU.Build.0 = Release|Any CPU {76F2521D-44D7-48C9-A678-074E22B46092}.Release|x86.ActiveCfg = Release|Any CPU + {76F2521D-44D7-48C9-A678-074E22B46092}.Release|x64.ActiveCfg = Release|Any CPU + {76F2521D-44D7-48C9-A678-074E22B46092}.Release|x64.Build.0 = Release|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Debug|x86.ActiveCfg = Debug|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Debug|x86.Build.0 = Debug|Any CPU + {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Debug|x64.ActiveCfg = Debug|Any CPU + {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Debug|x64.Build.0 = Debug|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Release|Any CPU.ActiveCfg = Release|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Release|Any CPU.Build.0 = Release|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Release|x86.ActiveCfg = Release|Any CPU {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Release|x86.Build.0 = Release|Any CPU + {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Release|x64.ActiveCfg = Release|Any CPU + {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Release|x64.Build.0 = Release|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Debug|Any CPU.Build.0 = Debug|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Debug|x86.ActiveCfg = Debug|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Debug|x86.Build.0 = Debug|Any CPU + {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Debug|x64.ActiveCfg = Debug|Any CPU + {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Debug|x64.Build.0 = Debug|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Release|Any CPU.ActiveCfg = Release|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Release|Any CPU.Build.0 = Release|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Release|x86.ActiveCfg = Release|Any CPU {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Release|x86.Build.0 = Release|Any CPU + {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Release|x64.ActiveCfg = Release|Any CPU + {86CFEC6C-2835-4EEB-9842-14B6A455A80C}.Release|x64.Build.0 = Release|Any CPU {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Debug|Any CPU.ActiveCfg = Debug|x64 {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Debug|Any CPU.Build.0 = Debug|x64 {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Debug|x86.ActiveCfg = Debug|Win32 {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Debug|x86.Build.0 = Debug|Win32 + {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Debug|x64.ActiveCfg = Debug|x64 + {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Debug|x64.Build.0 = Debug|x64 {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.LinuxDebug|x64.ActiveCfg = LinuxDebug|x64 + {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.LinuxDebug|x64.Build.0 = LinuxDebug|x64 {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Release|Any CPU.ActiveCfg = Release|x64 {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Release|Any CPU.Build.0 = Release|x64 {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Release|x86.ActiveCfg = Release|Win32 {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Release|x86.Build.0 = Release|Win32 + {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Release|x64.ActiveCfg = Release|x64 + {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Release|x64.Build.0 = Release|x64 {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|Any CPU.ActiveCfg = Debug|x64 {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|Any CPU.Build.0 = Debug|x64 {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|x86.ActiveCfg = Debug|Win32 {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|x86.Build.0 = Debug|Win32 + {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|x64.ActiveCfg = Debug|x64 + {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|x64.Build.0 = Debug|x64 {55890DF2-D13E-4C89-A01D-79CAD6726246}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {55890DF2-D13E-4C89-A01D-79CAD6726246}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {55890DF2-D13E-4C89-A01D-79CAD6726246}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {55890DF2-D13E-4C89-A01D-79CAD6726246}.LinuxDebug|x64.ActiveCfg = LinuxDebug|x64 + {55890DF2-D13E-4C89-A01D-79CAD6726246}.LinuxDebug|x64.Build.0 = LinuxDebug|x64 {55890DF2-D13E-4C89-A01D-79CAD6726246}.Release|Any CPU.ActiveCfg = Release|x64 {55890DF2-D13E-4C89-A01D-79CAD6726246}.Release|Any CPU.Build.0 = Release|x64 {55890DF2-D13E-4C89-A01D-79CAD6726246}.Release|x86.ActiveCfg = Release|Win32 {55890DF2-D13E-4C89-A01D-79CAD6726246}.Release|x86.Build.0 = Release|Win32 + {55890DF2-D13E-4C89-A01D-79CAD6726246}.Release|x64.ActiveCfg = Release|x64 + {55890DF2-D13E-4C89-A01D-79CAD6726246}.Release|x64.Build.0 = Release|x64 {6D02017A-189F-45D7-B286-D67536AB4907}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.Debug|x86.ActiveCfg = Debug|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.Debug|x86.Build.0 = Debug|Any CPU + {6D02017A-189F-45D7-B286-D67536AB4907}.Debug|x64.ActiveCfg = Debug|Any CPU + {6D02017A-189F-45D7-B286-D67536AB4907}.Debug|x64.Build.0 = Debug|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {6D02017A-189F-45D7-B286-D67536AB4907}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {6D02017A-189F-45D7-B286-D67536AB4907}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.Release|Any CPU.Build.0 = Release|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.Release|x86.ActiveCfg = Release|Any CPU {6D02017A-189F-45D7-B286-D67536AB4907}.Release|x86.Build.0 = Release|Any CPU + {6D02017A-189F-45D7-B286-D67536AB4907}.Release|x64.ActiveCfg = Release|Any CPU + {6D02017A-189F-45D7-B286-D67536AB4907}.Release|x64.Build.0 = Release|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Debug|x86.ActiveCfg = Debug|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Debug|x86.Build.0 = Debug|Any CPU + {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Debug|x64.ActiveCfg = Debug|Any CPU + {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Debug|x64.Build.0 = Debug|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {B17AE4BE-3640-49CB-B771-C9D653067C5E}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {B17AE4BE-3640-49CB-B771-C9D653067C5E}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Release|Any CPU.Build.0 = Release|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Release|x86.ActiveCfg = Release|Any CPU {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Release|x86.Build.0 = Release|Any CPU + {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Release|x64.ActiveCfg = Release|Any CPU + {B17AE4BE-3640-49CB-B771-C9D653067C5E}.Release|x64.Build.0 = Release|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.Debug|Any CPU.Build.0 = Debug|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.Debug|x86.ActiveCfg = Debug|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.Debug|x86.Build.0 = Debug|Any CPU + {6A246728-007D-4C1E-830A-DC8420ACA864}.Debug|x64.ActiveCfg = Debug|Any CPU + {6A246728-007D-4C1E-830A-DC8420ACA864}.Debug|x64.Build.0 = Debug|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {6A246728-007D-4C1E-830A-DC8420ACA864}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {6A246728-007D-4C1E-830A-DC8420ACA864}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.Release|Any CPU.ActiveCfg = Release|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.Release|Any CPU.Build.0 = Release|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.Release|x86.ActiveCfg = Release|Any CPU {6A246728-007D-4C1E-830A-DC8420ACA864}.Release|x86.Build.0 = Release|Any CPU + {6A246728-007D-4C1E-830A-DC8420ACA864}.Release|x64.ActiveCfg = Release|Any CPU + {6A246728-007D-4C1E-830A-DC8420ACA864}.Release|x64.Build.0 = Release|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Debug|Any CPU.Build.0 = Debug|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Debug|x86.ActiveCfg = Debug|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Debug|x86.Build.0 = Debug|Any CPU + {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Debug|x64.ActiveCfg = Debug|Any CPU + {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Debug|x64.Build.0 = Debug|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Release|Any CPU.ActiveCfg = Release|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Release|Any CPU.Build.0 = Release|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Release|x86.ActiveCfg = Release|Any CPU {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Release|x86.Build.0 = Release|Any CPU + {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Release|x64.ActiveCfg = Release|Any CPU + {0F5E6099-39BA-41CF-B55F-357F7DF4DE00}.Release|x64.Build.0 = Release|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Debug|Any CPU.Build.0 = Debug|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Debug|x86.ActiveCfg = Debug|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Debug|x86.Build.0 = Debug|Any CPU + {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Debug|x64.ActiveCfg = Debug|Any CPU + {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Debug|x64.Build.0 = Debug|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Release|Any CPU.ActiveCfg = Release|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Release|Any CPU.Build.0 = Release|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Release|x86.ActiveCfg = Release|Any CPU {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Release|x86.Build.0 = Release|Any CPU + {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Release|x64.ActiveCfg = Release|Any CPU + {90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}.Release|x64.Build.0 = Release|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Debug|Any CPU.Build.0 = Debug|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Debug|x86.ActiveCfg = Debug|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Debug|x86.Build.0 = Debug|Any CPU + {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Debug|x64.ActiveCfg = Debug|Any CPU + {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Debug|x64.Build.0 = Debug|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.LinuxDebug|x86.ActiveCfg = Debug|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.LinuxDebug|x86.Build.0 = Debug|Any CPU + {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Release|Any CPU.Build.0 = Release|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Release|x86.ActiveCfg = Release|Any CPU {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Release|x86.Build.0 = Release|Any CPU + {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Release|x64.ActiveCfg = Release|Any CPU + {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}.Release|x64.Build.0 = Release|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.Debug|x86.ActiveCfg = Debug|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.Debug|x86.Build.0 = Debug|Any CPU + {8873BA09-919E-4439-8EEB-87CB8E74656C}.Debug|x64.ActiveCfg = Debug|Any CPU + {8873BA09-919E-4439-8EEB-87CB8E74656C}.Debug|x64.Build.0 = Debug|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.LinuxDebug|x86.ActiveCfg = Debug|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.LinuxDebug|x86.Build.0 = Debug|Any CPU + {8873BA09-919E-4439-8EEB-87CB8E74656C}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {8873BA09-919E-4439-8EEB-87CB8E74656C}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.Release|Any CPU.Build.0 = Release|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.Release|x86.ActiveCfg = Release|Any CPU {8873BA09-919E-4439-8EEB-87CB8E74656C}.Release|x86.Build.0 = Release|Any CPU + {8873BA09-919E-4439-8EEB-87CB8E74656C}.Release|x64.ActiveCfg = Release|Any CPU + {8873BA09-919E-4439-8EEB-87CB8E74656C}.Release|x64.Build.0 = Release|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Debug|Any CPU.Build.0 = Debug|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Debug|x86.ActiveCfg = Debug|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Debug|x86.Build.0 = Debug|Any CPU + {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Debug|x64.ActiveCfg = Debug|Any CPU + {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Debug|x64.Build.0 = Debug|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.LinuxDebug|x86.ActiveCfg = Debug|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.LinuxDebug|x86.Build.0 = Debug|Any CPU + {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|Any CPU.Build.0 = Release|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|x86.ActiveCfg = Release|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|x86.Build.0 = Release|Any CPU + {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|x64.ActiveCfg = Release|Any CPU + {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|x64.Build.0 = Release|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|x86.ActiveCfg = Debug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|x86.Build.0 = Debug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|x64.ActiveCfg = Debug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|x64.Build.0 = Debug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|Any CPU.Build.0 = Release|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|x86.ActiveCfg = Release|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|x86.Build.0 = Release|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|x64.ActiveCfg = Release|Any CPU + {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ScriptEngine/BinaryDataConfigurationDefaults.cs b/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs similarity index 96% rename from src/ScriptEngine/BinaryDataConfigurationDefaults.cs rename to src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs index 346858a5c..899562dc3 100644 --- a/src/ScriptEngine/BinaryDataConfigurationDefaults.cs +++ b/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine +namespace OneScript.BinaryData { /// /// Значения по умолчанию для буферизации двоичных данных в памяти до перехода на временный файл. diff --git a/src/OneScript.BinaryData/BinaryDataMemoryLimit.cs b/src/OneScript.BinaryData/BinaryDataMemoryLimit.cs new file mode 100644 index 000000000..d881ec589 --- /dev/null +++ b/src/OneScript.BinaryData/BinaryDataMemoryLimit.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace OneScript.BinaryData +{ + /// + /// Реализация с фиксированным числом байт. + /// + public sealed class BinaryDataMemoryLimit : IBinaryDataMemoryLimit + { + public BinaryDataMemoryLimit(int maxBytesInMemory) + { + MaxBytesInMemory = maxBytesInMemory; + } + + public int MaxBytesInMemory { get; } + } +} diff --git a/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs b/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs new file mode 100644 index 000000000..132f57b4c --- /dev/null +++ b/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Globalization; + +namespace OneScript.BinaryData +{ + /// + /// Ключ и разбор параметра конфигурации лимита памяти для двоичных данных (стандартная библиотека). + /// + public static class BinaryDataMemoryLimitConfiguration + { + public const string InMemoryMaxBytesConfigKey = "binaryData.inMemoryMaxBytes"; + + /// + /// Возвращает лимит в байтах из строки конфигурации; при ошибке — и сообщение через . + /// + public static int ResolveFromConfigString(string rawValue, Action logWarning) + { + if (string.IsNullOrWhiteSpace(rawValue)) + return BinaryDataConfigurationDefaults.InMemoryMaxBytes; + + if (!int.TryParse(rawValue.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var bytes)) + { + logWarning?.Invoke($"Invalid value for {InMemoryMaxBytesConfigKey}: {rawValue}"); + return BinaryDataConfigurationDefaults.InMemoryMaxBytes; + } + + if (bytes <= 0 || bytes == int.MaxValue) + { + logWarning?.Invoke($"Value for {InMemoryMaxBytesConfigKey} must be between 1 and {int.MaxValue - 1}: {bytes}"); + return BinaryDataConfigurationDefaults.InMemoryMaxBytes; + } + + return bytes; + } + } +} diff --git a/src/OneScript.BinaryData/BinaryDataRuntimeSettings.cs b/src/OneScript.BinaryData/BinaryDataRuntimeSettings.cs new file mode 100644 index 000000000..3dad7ce8e --- /dev/null +++ b/src/OneScript.BinaryData/BinaryDataRuntimeSettings.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, +You can obtain one at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.DependencyInjection; + +namespace OneScript.BinaryData +{ + /// + /// Лимит памяти для двоичных данных до перехода на временный файл. + /// Задаётся один раз при создании движка из конфигурации (см. регистрацию в стандартной библиотеке). + /// + public static class BinaryDataRuntimeSettings + { + private static volatile int _inMemoryMaxBytes = BinaryDataConfigurationDefaults.InMemoryMaxBytes; + + /// + /// Читает лимит из DI и сохраняет для использования кодом без доступа к контейнеру (конструктор «Файловый поток с буфером» и т.п.). + /// Вызывается при создании экземпляра основного движка скриптов. + /// + public static void ApplyFromServices(IServiceContainer services) + { + var opts = services.TryResolve(); + var maxBytes = opts?.MaxBytesInMemory ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; + if (maxBytes <= 0 || maxBytes == int.MaxValue) + maxBytes = BinaryDataConfigurationDefaults.InMemoryMaxBytes; + + _inMemoryMaxBytes = maxBytes; + } + + /// + /// Текущий лимит «в памяти до временного файла» для двоичных данных (байты). + /// До вызова равен . + /// + public static int GetEffectiveInMemoryMaxBytes() => _inMemoryMaxBytes; + } +} diff --git a/src/ScriptEngine/IBinaryDataMemoryLimit.cs b/src/OneScript.BinaryData/IBinaryDataMemoryLimit.cs similarity index 67% rename from src/ScriptEngine/IBinaryDataMemoryLimit.cs rename to src/OneScript.BinaryData/IBinaryDataMemoryLimit.cs index aeb5744ac..97dfd3134 100644 --- a/src/ScriptEngine/IBinaryDataMemoryLimit.cs +++ b/src/OneScript.BinaryData/IBinaryDataMemoryLimit.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine +namespace OneScript.BinaryData { /// /// Лимит объёма данных в памяти для объектов «ДвоичныеДанные» и смежных потоков @@ -15,14 +15,4 @@ public interface IBinaryDataMemoryLimit { int MaxBytesInMemory { get; } } - - internal sealed class BinaryDataMemoryLimitFromOptions : IBinaryDataMemoryLimit - { - public BinaryDataMemoryLimitFromOptions(OneScriptCoreOptions options) - { - MaxBytesInMemory = options.BinaryDataInMemoryMaxBytes; - } - - public int MaxBytesInMemory { get; } - } } diff --git a/src/OneScript.BinaryData/OneScript.BinaryData.csproj b/src/OneScript.BinaryData/OneScript.BinaryData.csproj new file mode 100644 index 000000000..a7b9f8263 --- /dev/null +++ b/src/OneScript.BinaryData/OneScript.BinaryData.csproj @@ -0,0 +1,16 @@ + + + + $(TargetFrameworkVersion) + Debug;Release;LinuxDebug + AnyCPU + OneScript.BinaryData + + + true + false + + + + + diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs index 24dbc0dd4..e384b9e50 100644 --- a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs @@ -12,6 +12,7 @@ This Source Code Form is subject to the terms of the using OneScript.Contexts; using OneScript.Exceptions; using OneScript.Types; +using OneScript.BinaryData; using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs index c3f7e8ac9..857824a7c 100644 --- a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs +++ b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs @@ -6,7 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using ScriptEngine; +using OneScript.BinaryData; namespace OneScript.StandardLibrary.Binary { diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs index 515a1f07e..d9380cb30 100644 --- a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs +++ b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs @@ -7,7 +7,7 @@ This Source Code Form is subject to the terms of the using System; using System.IO; -using ScriptEngine.Machine; +using OneScript.BinaryData; namespace OneScript.StandardLibrary.Binary { diff --git a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs index d38e2fbb6..438cc5476 100644 --- a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs +++ b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs @@ -5,7 +5,9 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.BinaryData; using OneScript.StandardLibrary.Collections; +using ScriptEngine; using ScriptEngine.Hosting; using ScriptEngine.Machine; @@ -13,9 +15,26 @@ namespace OneScript.StandardLibrary { public static class EngineBuilderExtensions { + /// + /// Регистрирует лимит памяти для «ДвоичныеДанные» из конфигурации движка (ключ ). + /// Вызывайте после SetDefaultOptions из ScriptEngine.Hosting.EngineBuilderExtensions. + /// + public static IEngineBuilder RegisterBinaryDataMemoryLimitFromConfig(this IEngineBuilder builder) + { + builder.Services.RegisterSingleton(sp => + { + var cfg = sp.Resolve(); + var raw = cfg[BinaryDataMemoryLimitConfiguration.InMemoryMaxBytesConfigKey]; + var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(raw, SystemLogger.Write); + return new BinaryDataMemoryLimit(bytes); + }); + + return builder; + } + public static ExecutionContext AddStandardLibrary(this ExecutionContext env) { return env.AddAssembly(typeof(ArrayImpl).Assembly); } } -} \ No newline at end of file +} diff --git a/src/ScriptEngine/BslProcess.cs b/src/ScriptEngine/BslProcess.cs index f4aa49d34..0ef93b311 100644 --- a/src/ScriptEngine/BslProcess.cs +++ b/src/ScriptEngine/BslProcess.cs @@ -1,7 +1,6 @@ /*---------------------------------------------------------- This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one +Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ @@ -40,14 +39,19 @@ public BslProcess(int id, ExecutionContext context, IEnumerable startedExecutors = null; try { if (notifyExecutors) - Array.ForEach(_executorProviders, e => e.BeforeProcessStart(this)); + { + startedExecutors = new List(_executorProviders.Length); + foreach (var executor in _executorProviders) + { + executor.BeforeProcessStart(this); + startedExecutors.Add(executor); + } + } _isRunning = true; @@ -59,16 +63,18 @@ public BslValue Run(BslObjectValue target, IExecutableModule module, BslScriptMe { try { - if (_isRunning) - Array.ForEach(_executorProviders, e => e.AfterProcessExit(this)); + if (startedExecutors != null) + { + for (var i = startedExecutors.Count - 1; i >= 0; i--) + startedExecutors[i].AfterProcessExit(this); + } } finally { - BinaryDataRuntimeSettings.Pop(); _isRunning = false; } } } } } -} \ No newline at end of file +} diff --git a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs index f62b03fc0..70f0a0324 100644 --- a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs +++ b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs @@ -80,9 +80,6 @@ public static IEngineBuilder SetDefaultOptions(this IEngineBuilder builder) return holder.GetConfig(); }); - services.RegisterSingleton(sp => - new BinaryDataMemoryLimitFromOptions(sp.Resolve())); - services.Register(); return builder; diff --git a/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs b/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs deleted file mode 100644 index 6f0302e3c..000000000 --- a/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs +++ /dev/null @@ -1,43 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System.Threading; -using OneScript.DependencyInjection; - -namespace ScriptEngine.Machine -{ - /// - /// Лимит памяти для двоичных данных на время выполнения процесса BSL (AsyncLocal). - /// - public static class BinaryDataRuntimeSettings - { - private static readonly AsyncLocal InMemoryMaxBytes = new AsyncLocal(); - - internal static void PushFromServices(IServiceContainer services) - { - var opts = services.TryResolve(); - var maxBytes = opts?.MaxBytesInMemory ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; - if (maxBytes <= 0) - maxBytes = BinaryDataConfigurationDefaults.InMemoryMaxBytes; - InMemoryMaxBytes.Value = maxBytes; - } - - internal static void Pop() - { - InMemoryMaxBytes.Value = null; - } - - /// - /// Текущий лимит «в памяти до временного файла» для двоичных данных (байты). - /// Вне процесса BSL возвращает значение по умолчанию из . - /// - public static int GetEffectiveInMemoryMaxBytes() - { - return InMemoryMaxBytes.Value ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; - } - } -} diff --git a/src/ScriptEngine/OneScriptCoreOptions.cs b/src/ScriptEngine/OneScriptCoreOptions.cs index 609901df7..7d90b0793 100644 --- a/src/ScriptEngine/OneScriptCoreOptions.cs +++ b/src/ScriptEngine/OneScriptCoreOptions.cs @@ -7,7 +7,6 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; -using System.Globalization; using System.Text; using OneScript.Commons; using OneScript.Native.Compiler; @@ -22,7 +21,6 @@ public class OneScriptCoreOptions private const string PREPROCESSOR_DEFINITIONS_KEY = "preprocessor.define"; private const string DEFAULT_RUNTIME_KEY = "runtime.default"; private const string EXPLICIT_IMPORT = "lang.explicitImports"; - private const string BINARY_DATA_IN_MEMORY_MAX = "binaryData.inMemoryMaxBytes"; public OneScriptCoreOptions(KeyValueConfig config) { @@ -31,7 +29,6 @@ public OneScriptCoreOptions(KeyValueConfig config) PreprocessorDefinitions = SetupDefinitions(config[PREPROCESSOR_DEFINITIONS_KEY]); UseNativeAsDefaultRuntime = SetupDefaultRuntime(config[DEFAULT_RUNTIME_KEY]); ExplicitImports = SetupExplicitImports(config[EXPLICIT_IMPORT]); - BinaryDataInMemoryMaxBytes = SetupBinaryDataInMemoryMax(config[BINARY_DATA_IN_MEMORY_MAX]); } public string SystemLanguage { get; } @@ -44,12 +41,6 @@ public OneScriptCoreOptions(KeyValueConfig config) public ExplicitImportsBehavior ExplicitImports { get; } - /// - /// Максимальный объём двоичных данных в памяти до перехода на временный файл (байты). - /// Задаётся ключом binaryData.inMemoryMaxBytes в конфигурации. - /// - public int BinaryDataInMemoryMaxBytes { get; } - private static IEnumerable SetupDefinitions(string s) { return s?.Split(',') ?? Array.Empty(); @@ -88,25 +79,5 @@ private static ExplicitImportsBehavior SetupExplicitImports(string keyValue) return ExplicitImportsBehavior.Warn; } } - - private static int SetupBinaryDataInMemoryMax(string rawValue) - { - if (string.IsNullOrWhiteSpace(rawValue)) - return BinaryDataConfigurationDefaults.InMemoryMaxBytes; - - if (!int.TryParse(rawValue.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var bytes)) - { - SystemLogger.Write($"Invalid value for {BINARY_DATA_IN_MEMORY_MAX}: {rawValue}"); - return BinaryDataConfigurationDefaults.InMemoryMaxBytes; - } - - if (bytes <= 0 || bytes == int.MaxValue) - { - SystemLogger.Write($"Value for {BINARY_DATA_IN_MEMORY_MAX} must be between 1 and {int.MaxValue - 1}: {bytes}"); - return BinaryDataConfigurationDefaults.InMemoryMaxBytes; - } - - return bytes; - } } } \ No newline at end of file diff --git a/src/ScriptEngine/ScriptEngine.csproj b/src/ScriptEngine/ScriptEngine.csproj index f81d68951..04ba4ff3c 100644 --- a/src/ScriptEngine/ScriptEngine.csproj +++ b/src/ScriptEngine/ScriptEngine.csproj @@ -41,6 +41,7 @@ + diff --git a/src/ScriptEngine/ScriptingEngine.cs b/src/ScriptEngine/ScriptingEngine.cs index 5b8340900..268b6ae4d 100644 --- a/src/ScriptEngine/ScriptingEngine.cs +++ b/src/ScriptEngine/ScriptingEngine.cs @@ -8,6 +8,7 @@ This Source Code Form is subject to the terms of the using OneScript.Compilation; using OneScript.Contexts; using OneScript.DependencyInjection; +using OneScript.BinaryData; using OneScript.Execution; using OneScript.Types; using ScriptEngine.Machine; @@ -51,6 +52,8 @@ public ScriptingEngine(ITypeManager types, } Loader.ReaderEncoding = options.FileReaderEncoding; + + BinaryDataRuntimeSettings.ApplyFromServices(services); } public IServiceContainer Services { get; } diff --git a/src/TestApp/MainWindow.xaml.cs b/src/TestApp/MainWindow.xaml.cs index 5c2adc40c..57bb923a2 100644 --- a/src/TestApp/MainWindow.xaml.cs +++ b/src/TestApp/MainWindow.xaml.cs @@ -137,6 +137,7 @@ private HostedScriptEngine CreateEngine() var builder = DefaultEngineBuilder .Create() .SetDefaultOptions() + .RegisterBinaryDataMemoryLimitFromConfig() .UseNativeRuntime() .UseImports() .SetupEnvironment(e => diff --git a/src/oscript/CgiBehavior.cs b/src/oscript/CgiBehavior.cs index 13a103d65..3501f26a6 100644 --- a/src/oscript/CgiBehavior.cs +++ b/src/oscript/CgiBehavior.cs @@ -11,6 +11,7 @@ This Source Code Form is subject to the terms of the using System.Text; using OneScript.Contexts; using OneScript.Exceptions; +using OneScript.BinaryData; using OneScript.Execution; using OneScript.StandardLibrary; using oscript.Web; @@ -69,12 +70,7 @@ private int RunCGIMode(string scriptFile) var engine = ConsoleHostBuilder.Build(builder); - var binaryMemoryLimit = engine.Services.TryResolve()?.MaxBytesInMemory - ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; - if (binaryMemoryLimit <= 0) - binaryMemoryLimit = BinaryDataConfigurationDefaults.InMemoryMaxBytes; - - var request = new WebRequestContext(binaryMemoryLimit); + var request = new WebRequestContext(BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes()); engine.InjectGlobalProperty("ВебЗапрос", "WebRequest", request, true); engine.InjectObject(this); diff --git a/src/oscript/ConsoleHostBuilder.cs b/src/oscript/ConsoleHostBuilder.cs index 675179567..d55f62e9e 100644 --- a/src/oscript/ConsoleHostBuilder.cs +++ b/src/oscript/ConsoleHostBuilder.cs @@ -48,6 +48,7 @@ public static HostedScriptEngine Build(IEngineBuilder builder) private static void BuildUpWithIoC(IEngineBuilder builder) { builder.SetDefaultOptions() + .RegisterBinaryDataMemoryLimitFromConfig() .UseImports() .UseFileSystemLibraries() .UseNativeRuntime() diff --git a/src/oscript/Web/WebRequestContext.cs b/src/oscript/Web/WebRequestContext.cs index d80a1bf0c..9ffb2724d 100644 --- a/src/oscript/Web/WebRequestContext.cs +++ b/src/oscript/Web/WebRequestContext.cs @@ -10,6 +10,7 @@ This Source Code Form is subject to the terms of the using System.IO; using System.Text; using System.Threading.Tasks; +using OneScript.BinaryData; using OneScript.Contexts; using OneScript.StandardLibrary.Binary; using OneScript.StandardLibrary.Collections; @@ -36,9 +37,9 @@ public WebRequestContext() : this(BinaryDataConfigurationDefaults.InMemoryMaxByt public WebRequestContext(int postBodyInMemoryMaxBytes) { - if (postBodyInMemoryMaxBytes <= 0) + if (postBodyInMemoryMaxBytes <= 0 || postBodyInMemoryMaxBytes == int.MaxValue) throw new ArgumentOutOfRangeException(nameof(postBodyInMemoryMaxBytes), - "Лимит памяти для тела POST-запроса должен быть положительным числом байт."); + "Лимит памяти для тела POST-запроса должен быть положительным числом байт и меньше Int32.MaxValue."); _postBodyInMemoryMaxBytes = postBodyInMemoryMaxBytes; var get = Environment.GetEnvironmentVariable("QUERY_STRING"); if (get != null) FillGetMap(get); From 379256c3b58a60acac760dbd51ec3342724ca1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 19 May 2026 00:09:57 +0300 Subject: [PATCH 34/65] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=B2=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/json/test-json_reader.os | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index c5e58617b..90487dec3 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -285,7 +285,7 @@ КонецЦикла; ЧтениеJSON.Закрыть(); - юТест.ПроверитьРавно( ЭталонЗначения.Количество(), ФактЗначения.Количество() ); + юТест.ПроверитьРавенство( ЭталонЗначения.Количество(), ФактЗначения.Количество() ); Для Сч = 0 По ЭталонЗначения.ВГраница() Цикл юТест.ПроверитьРавенство(ЭталонЗначения[Сч], ФактЗначения[Сч]); @@ -326,9 +326,9 @@ Объект = ПрочитатьJSON(ЧтениеJSON); ЧтениеJSON.Закрыть(); - Ожидаем = Тип("Строка"); - Факт = ТипЗнч( Объект.lastSignInTime ); - юТест.ПроверитьРавенство(Ожидаем, Факт); + Эталон = Тип("Строка"); + Факт = ТипЗнч( Объект.lastSignInTime ); + юТест.ПроверитьРавенство(Эталон, Факт); юТест.ПроверитьРавенство(Объект.lastSignInTime, "2026-05-13T22:51:46.230Z"); КонецПроцедуры From 23221a12b9b2874536fd5d58c0e0a8c829833f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=9A=D0=B0=D1=88=D0=B0?= =?UTF-8?q?=D1=84=D1=83=D1=82=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 19 May 2026 23:42:19 +0300 Subject: [PATCH 35/65] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=85=D0=B5=D1=88-=D1=81=D1=83=D0=BC=D0=BC?= =?UTF-8?q?=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2=20=D1=87=D1=82?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Теперь нет автоконвертации дат, поэтому хеши будут другие --- tests/json/test-json_reader.os | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index 90487dec3..3a49b33ab 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -76,7 +76,7 @@ СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock.json", Истина); - юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 960829385); + юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 3868110149); КонецПроцедуры @@ -84,7 +84,7 @@ СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock_struct.json", Ложь); - юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 2800700943); + юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 856251251); КонецПроцедуры From 2b1e0e6636b3c1db4fccd30da16a4499c21c4136 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Wed, 20 May 2026 10:52:52 +0300 Subject: [PATCH 36/65] =?UTF-8?q?fix(oscript.cfg):=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BD=D0=B5=D0=B4=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=8E=D1=89=D0=B8=D0=B9=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D0=BE=D0=B4=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8E=20=D0=B4=D0=BB=D1=8F=20=D0=BB=D0=B8=D0=BC?= =?UTF-8?q?=D0=B8=D1=82=D0=B0=20=D0=BF=D0=B0=D0=BC=D1=8F=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=B2=D0=BE=D0=B8=D1=87=D0=BD=D1=8B=D1=85=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ScriptEngine.HostedScript/oscript.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ScriptEngine.HostedScript/oscript.cfg b/src/ScriptEngine.HostedScript/oscript.cfg index 6a664aa51..25b2e0798 100644 --- a/src/ScriptEngine.HostedScript/oscript.cfg +++ b/src/ScriptEngine.HostedScript/oscript.cfg @@ -23,4 +23,4 @@ lib.system = ../lib # Порог в байтах: пока размер двоичных данных в памяти не превышает это значение, данные держатся в RAM; # при большем размере используется временный файл. По умолчанию 52428800 (50 МБ). -#binaryData.inMemoryMaxBytes=52428800 \ No newline at end of file +#binaryData.inMemoryMaxBytes=52428800 From 27a89b6e8cbd04fea24d6939666afd458bc04bb2 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Wed, 20 May 2026 13:23:06 +0300 Subject: [PATCH 37/65] =?UTF-8?q?refactor(binary):=20=D0=BE=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=B9=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BB=D0=B8=D0=BC=D0=B8=D1=82?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=B0=D0=BC=D1=8F=D1=82=D0=B8=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=B2=D0=BE=D0=B8=D1=87=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BinaryDataConfigurationDefaults.cs | 2 +- .../BinaryDataMemoryLimitConfiguration.cs | 56 +++++++++++++++-- .../EngineBuilderExtensions.cs | 4 +- src/ScriptEngine.HostedScript/oscript.cfg | 7 ++- ...BinaryDataMemoryLimitConfigurationTests.cs | 62 +++++++++++++++++++ .../OneScript.StandardLibrary.Tests.csproj | 1 + 6 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs diff --git a/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs b/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs index 899562dc3..29eb61a86 100644 --- a/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs +++ b/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs @@ -13,7 +13,7 @@ namespace OneScript.BinaryData public static class BinaryDataConfigurationDefaults { /// - /// Лимит по умолчанию (50 МБ), если в конфигурации не задан ключ binaryData.inMemoryMaxBytes. + /// Лимит по умолчанию (50 МБ), если в конфигурации не задан ключ binaryData.inMemoryMaxSize. /// public const int InMemoryMaxBytes = 1024 * 1024 * 50; } diff --git a/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs b/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs index 132f57b4c..267907c2c 100644 --- a/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs +++ b/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs @@ -15,29 +15,77 @@ namespace OneScript.BinaryData /// public static class BinaryDataMemoryLimitConfiguration { - public const string InMemoryMaxBytesConfigKey = "binaryData.inMemoryMaxBytes"; + public const string InMemoryMaxSizeConfigKey = "binaryData.inMemoryMaxSize"; /// /// Возвращает лимит в байтах из строки конфигурации; при ошибке — и сообщение через . + /// Допускается целое число байт или значение с суффиксом k, m, g (512k, 50m, 1g). /// public static int ResolveFromConfigString(string rawValue, Action logWarning) { if (string.IsNullOrWhiteSpace(rawValue)) return BinaryDataConfigurationDefaults.InMemoryMaxBytes; - if (!int.TryParse(rawValue.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var bytes)) + if (!TryParseByteSize(rawValue.Trim(), out var bytes)) { - logWarning?.Invoke($"Invalid value for {InMemoryMaxBytesConfigKey}: {rawValue}"); + logWarning?.Invoke($"Invalid value for {InMemoryMaxSizeConfigKey}: {rawValue}"); return BinaryDataConfigurationDefaults.InMemoryMaxBytes; } if (bytes <= 0 || bytes == int.MaxValue) { - logWarning?.Invoke($"Value for {InMemoryMaxBytesConfigKey} must be between 1 and {int.MaxValue - 1}: {bytes}"); + logWarning?.Invoke($"Value for {InMemoryMaxSizeConfigKey} must be between 1 and {int.MaxValue - 1}: {bytes}"); return BinaryDataConfigurationDefaults.InMemoryMaxBytes; } return bytes; } + + private static bool TryParseByteSize(string value, out int bytes) + { + bytes = 0; + + if (value.Length == 0) + return false; + + var suffix = value[value.Length - 1]; + long multiplier = 1; + var numberPart = value; + + switch (suffix) + { + case 'k': + case 'K': + multiplier = 1024L; + numberPart = value.Substring(0, value.Length - 1).TrimEnd(); + break; + case 'm': + case 'M': + multiplier = 1024L * 1024L; + numberPart = value.Substring(0, value.Length - 1).TrimEnd(); + break; + case 'g': + case 'G': + multiplier = 1024L * 1024L * 1024L; + numberPart = value.Substring(0, value.Length - 1).TrimEnd(); + break; + } + + if (numberPart.Length == 0) + return false; + + if (!long.TryParse(numberPart, NumberStyles.Integer, CultureInfo.InvariantCulture, out var number)) + return false; + + if (number <= 0) + return false; + + var result = number * multiplier; + if (result <= 0 || result >= int.MaxValue) + return false; + + bytes = (int)result; + return true; + } } } diff --git a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs index 438cc5476..4dae2491c 100644 --- a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs +++ b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs @@ -16,7 +16,7 @@ namespace OneScript.StandardLibrary public static class EngineBuilderExtensions { /// - /// Регистрирует лимит памяти для «ДвоичныеДанные» из конфигурации движка (ключ ). + /// Регистрирует лимит памяти для «ДвоичныеДанные» из конфигурации движка (ключ ). /// Вызывайте после SetDefaultOptions из ScriptEngine.Hosting.EngineBuilderExtensions. /// public static IEngineBuilder RegisterBinaryDataMemoryLimitFromConfig(this IEngineBuilder builder) @@ -24,7 +24,7 @@ public static IEngineBuilder RegisterBinaryDataMemoryLimitFromConfig(this IEngin builder.Services.RegisterSingleton(sp => { var cfg = sp.Resolve(); - var raw = cfg[BinaryDataMemoryLimitConfiguration.InMemoryMaxBytesConfigKey]; + var raw = cfg[BinaryDataMemoryLimitConfiguration.InMemoryMaxSizeConfigKey]; var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(raw, SystemLogger.Write); return new BinaryDataMemoryLimit(bytes); }); diff --git a/src/ScriptEngine.HostedScript/oscript.cfg b/src/ScriptEngine.HostedScript/oscript.cfg index 25b2e0798..524057f8c 100644 --- a/src/ScriptEngine.HostedScript/oscript.cfg +++ b/src/ScriptEngine.HostedScript/oscript.cfg @@ -21,6 +21,7 @@ lib.system = ../lib #systemlanguage = ru -# Порог в байтах: пока размер двоичных данных в памяти не превышает это значение, данные держатся в RAM; -# при большем размере используется временный файл. По умолчанию 52428800 (50 МБ). -#binaryData.inMemoryMaxBytes=52428800 +# Порог: пока размер двоичных данных в памяти не превышает это значение, данные держатся в RAM; +# при большем размере используется временный файл. По умолчанию 50m (52428800 байт). +# Значение — целое число байт или с суффиксом k, m, g (512k, 50m, 1g). +#binaryData.inMemoryMaxSize=50m diff --git a/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs b/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs new file mode 100644 index 000000000..342a03f30 --- /dev/null +++ b/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs @@ -0,0 +1,62 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using FluentAssertions; +using OneScript.BinaryData; +using Xunit; + +namespace OneScript.StandardLibrary.Tests +{ + public class BinaryDataMemoryLimitConfigurationTests + { + [Theory] + [InlineData("52428800", 52428800)] + [InlineData("512k", 512 * 1024)] + [InlineData("512K", 512 * 1024)] + [InlineData("50m", 50 * 1024 * 1024)] + [InlineData("50M", 50 * 1024 * 1024)] + [InlineData("1g", 1024 * 1024 * 1024)] + [InlineData("1G", 1024 * 1024 * 1024)] + public void ResolvesByteSizeWithOptionalSuffix(string rawValue, int expectedBytes) + { + var warnings = new List(); + + var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(rawValue, warnings.Add); + + bytes.Should().Be(expectedBytes); + warnings.Should().BeEmpty(); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void UsesDefaultWhenValueIsMissing(string rawValue) + { + var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(rawValue, _ => { }); + + bytes.Should().Be(BinaryDataConfigurationDefaults.InMemoryMaxBytes); + } + + [Theory] + [InlineData("512x")] + [InlineData("m512")] + [InlineData("0")] + [InlineData("-1")] + [InlineData("2g")] + public void UsesDefaultForInvalidValue(string rawValue) + { + var warnings = new List(); + + var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(rawValue, warnings.Add); + + bytes.Should().Be(BinaryDataConfigurationDefaults.InMemoryMaxBytes); + warnings.Should().NotBeEmpty(); + } + } +} diff --git a/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj b/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj index 4d93b540e..f63fbf2d3 100644 --- a/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj +++ b/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj @@ -23,6 +23,7 @@ + From 5ad4bfb557529864dd83a6247bcf52af20b5879a Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Wed, 20 May 2026 16:16:21 +0400 Subject: [PATCH 38/65] =?UTF-8?q?fix=20#1683:=20=D0=B8=D0=BD=D0=BA=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B5=20=D0=A5=D0=B5=D1=88=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=D0=94=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B1=D0=B5?= =?UTF-8?q?=D0=B7=20=D0=B7=D0=B0=D1=85=D0=B2=D0=B0=D1=82=D0=B0=20=D1=84?= =?UTF-8?q?=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.StandardLibrary/Hash/Crc32.cs | 27 ++- .../Hash/HashImpl.cs | 190 ++++++++++-------- tests/hash.os | 10 +- 3 files changed, 137 insertions(+), 90 deletions(-) diff --git a/src/OneScript.StandardLibrary/Hash/Crc32.cs b/src/OneScript.StandardLibrary/Hash/Crc32.cs index 3b1f812c8..83b8fbf80 100644 --- a/src/OneScript.StandardLibrary/Hash/Crc32.cs +++ b/src/OneScript.StandardLibrary/Hash/Crc32.cs @@ -69,12 +69,31 @@ protected override void HashCore(byte[] array, int ibStart, int cbSize) protected override byte[] HashFinal() { var result = BitConverter.GetBytes(~_crc); - if (BitConverter.IsLittleEndian) - Array.Reverse(result); HashValue = result; return result; - } - + } + + #region IncrementalHash + public void AppendData(byte[] array) + { + HashCore(array, 0, array.Length); + } + + public void AppendData(byte[] array, int offset, int count) + { + HashCore(array, offset, count); + } + + public byte[] GetCurrentHash() + { + return BitConverter.GetBytes(~_crc); + } + + public UInt32 GetCurrentHashAsUInt32() + { + return ~_crc; + } + #endregion } } diff --git a/src/OneScript.StandardLibrary/Hash/HashImpl.cs b/src/OneScript.StandardLibrary/Hash/HashImpl.cs index 78889b659..c2bce5226 100644 --- a/src/OneScript.StandardLibrary/Hash/HashImpl.cs +++ b/src/OneScript.StandardLibrary/Hash/HashImpl.cs @@ -5,46 +5,39 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; using OneScript.Contexts; using OneScript.Exceptions; using OneScript.StandardLibrary.Binary; using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; namespace OneScript.StandardLibrary.Hash -{ +{ [ContextClass("ХешированиеДанных", "DataHashing")] - public class HashImpl : AutoContext, IDisposable - { - private HashAlgorithm _provider; - private HashFunctionEnum _enumValue; - private CombinedStream _toCalculate=new CombinedStream(); + public class HashImpl : AutoContext + { + private const int BUFFER_SIZE = (1024 * 32); + + private readonly Crc32 _crc32; + private readonly IncrementalHash _provider; + private readonly HashFunctionEnum _enumValue; private bool _calculated; private byte[] _hash; - public HashImpl(HashAlgorithm provider, HashFunctionEnum enumValue) + public HashImpl(IncrementalHash provider, HashFunctionEnum enumValue) { _provider = provider; _enumValue = enumValue; _calculated = false; - } - - public byte[] InternalHash - { - get - { - if (!_calculated) - { - _hash = _provider.ComputeHash(_toCalculate); - _calculated = true; - } - return _hash; - } + if (enumValue == HashFunctionEnum.CRC32 || provider==null) + { + _crc32 = new Crc32(); + } } [ContextProperty("ХешФункция", "HashFunction")] @@ -55,16 +48,13 @@ public IValue Hash { get { - if (_provider is Crc32) - { - var buffer = new byte[4]; - Array.Copy(InternalHash, buffer, 4); - if (BitConverter.IsLittleEndian) - Array.Reverse(buffer); - var ret = BitConverter.ToUInt32(buffer, 0); - return ValueFactory.Create((decimal)ret); - } - return new BinaryDataContext(InternalHash); + if(!_calculated) + return ValueFactory.Create(0); + + if (_crc32 != null) + return ValueFactory.Create(_crc32.GetCurrentHashAsUInt32()); + + return new BinaryDataContext(_hash); } } @@ -72,35 +62,86 @@ public IValue Hash public string HashString { get - { + { + if (_crc32 != null) + return _crc32.GetCurrentHashAsUInt32().ToString("X8"); + var sb = new StringBuilder(); - for (int i = 0; i < InternalHash.Length; i++) - sb.Append(InternalHash[i].ToString("X2")); + for (int i = 0; i < _hash.Length; i++) + sb.Append(_hash[i].ToString("X2")); return sb.ToString(); } } + private void AppendData(byte[] data) + { + AppendData(data, data.Length); + } + + private void AppendData(byte[] data, int count) + { + if (_crc32 != null) + { + _crc32.AppendData(data,0,count); + } + else + { + _provider.AppendData(data,0,count); + _hash = _provider.GetCurrentHash(); + } + + _calculated = true; + } + + private void AppendStream(Stream stream) + { + var buffer = new byte[BUFFER_SIZE]; + while (true) + { + var read = stream.Read(buffer,0, BUFFER_SIZE); + if (read == 0) + break; + + AppendData(buffer, read); + } + } + + private void AppendStream(Stream stream, int count) + { + int bufSize = Math.Min(BUFFER_SIZE, count); + var buffer = new byte[bufSize]; + int toRead = count; + while (toRead > 0) + { + var read = stream.Read(buffer,0, Math.Min(toRead, bufSize)); + if (read == 0) + break; + + AppendData(buffer, read); + toRead -= read; + } + } [ContextMethod("Добавить", "Append")] - public void Append(BslValue toAdd, uint count = 0) + public void Append(BslValue toAdd, int count = 0) { switch (toAdd) { case BslStringValue s: - AddStream(new MemoryStream(Encoding.UTF8.GetBytes((string)s))); + AppendData(Encoding.UTF8.GetBytes((string)s)); break; - case BslObjectValue obj when obj is IStreamWrapper wrapper: + case IStreamWrapper wrapper: var stream = wrapper.GetUnderlyingStream(); - var readByte = (int)Math.Min(count == 0 ? stream.Length : count, stream.Length - stream.Position); - var buffer = new byte[readByte]; - stream.Read(buffer, 0, readByte); - AddStream(new MemoryStream(buffer)); + if (count == 0) + AppendStream(stream); + else + AppendStream(stream, count); break; - case BslObjectValue obj when obj is BinaryDataContext binaryData: - AddStream(binaryData.GetStream()); + case BinaryDataContext binaryData: + AppendStream(binaryData.GetStream()); break; default: - throw RuntimeException.InvalidArgumentType(nameof(toAdd)); + throw RuntimeException.InvalidNthArgumentType(1); } } @@ -109,52 +150,33 @@ public void AppendFile(string path) { if (!File.Exists(path)) throw RuntimeException.InvalidArgumentType(); - AddStream(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); + var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + AppendStream(stream); + stream.Close(); } - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - _toCalculate.Close(); - _toCalculate.Dispose(); - _toCalculate = new CombinedStream(); - _calculated = false; - } - - [ScriptConstructor(Name = "По указанной хеш-функции")] public static HashImpl Constructor(HashFunctionEnum providerEnum) - { - var objectProvider = GetProvider(providerEnum); + { + if (providerEnum == HashFunctionEnum.CRC32) + return new HashImpl(null, providerEnum); + + var objectProvider = IncrementalHash.CreateHash(GetAlgorithmName(providerEnum)); return new HashImpl(objectProvider, providerEnum); } - private static HashAlgorithm GetProvider(HashFunctionEnum algo) + private static HashAlgorithmName GetAlgorithmName(HashFunctionEnum algo) { - switch (algo) - { - case HashFunctionEnum.CRC32: - return new Crc32(); - default: - var ret = HashAlgorithm.Create(algo.ToString()); - if (ret == null) - throw RuntimeException.InvalidArgumentType(); - return ret; - } + return algo switch + { + HashFunctionEnum.MD5 => HashAlgorithmName.MD5, + HashFunctionEnum.SHA1 => HashAlgorithmName.SHA1, + HashFunctionEnum.SHA256 => HashAlgorithmName.SHA256, + HashFunctionEnum.SHA384 => HashAlgorithmName.SHA384, + HashFunctionEnum.SHA512 => HashAlgorithmName.SHA512, + _ => throw RuntimeException.InvalidArgumentValue() + }; } - public void Dispose() - { - _toCalculate.Close(); - _toCalculate.Dispose(); - } - - private void AddStream(Stream stream) - { - _toCalculate.AddStream(stream); - _toCalculate.Seek(0, SeekOrigin.Begin); - _calculated = false; - - } } } diff --git a/tests/hash.os b/tests/hash.os index ef74458e4..cd113412b 100644 --- a/tests/hash.os +++ b/tests/hash.os @@ -79,6 +79,7 @@ Провайдер.Добавить("456"); Провайдер.Добавить("789"); + юТест.ПроверитьРавенство(3421780262,Провайдер.ХешСумма); юТест.ПроверитьРавенство("CBF43926",Провайдер.ХешСуммаСтрокой); КонецПроцедуры @@ -93,11 +94,16 @@ Провайдер = Новый ХешированиеДанных(ХешФункция.MD5); Провайдер.ДобавитьФайл(Файл); - Провайдер2 = Новый ХешированиеДанных(ХешФункция.MD5); + Провайдер2 = Новый ХешированиеДанных(ХешФункция.CRC32); Провайдер2.ДобавитьФайл(Файл); + + УдалитьФайлы(Файл); Запись = Новый ЗаписьТекста(Файл); - Запись.Записать("Привет"); + Запись.Записать("Привет!"); Запись.Закрыть(); + + юТест.ПроверитьРавенство("56 45 D3 0C 39 EA 90 18 D1 CA B7 7D 5F 37 0A 8C",Строка(Провайдер.ХешСумма)); + юТест.ПроверитьРавенство(3139669858,Провайдер2.ХешСумма); КонецПроцедуры \ No newline at end of file From 1a2ad441be21f35e7492b5027d9b64756309bc71 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Wed, 20 May 2026 23:31:02 +0400 Subject: [PATCH 39/65] =?UTF-8?q?=D0=B1=D1=8B=D0=BB=20=D0=B1=D0=B0=D0=B3?= =?UTF-8?q?=20=D0=B2=20=D0=B8=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=D1=83=20?= =?UTF-8?q?=D0=B2=20Crc32?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.StandardLibrary/Hash/Crc32.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OneScript.StandardLibrary/Hash/Crc32.cs b/src/OneScript.StandardLibrary/Hash/Crc32.cs index 83b8fbf80..8575f4da3 100644 --- a/src/OneScript.StandardLibrary/Hash/Crc32.cs +++ b/src/OneScript.StandardLibrary/Hash/Crc32.cs @@ -62,7 +62,7 @@ public override void Initialize() protected override void HashCore(byte[] array, int ibStart, int cbSize) { - for (var i = ibStart; i < cbSize - ibStart; i++) + for (var i = ibStart; i < ibStart + cbSize; i++) _crc = (_crc >> 8) ^ table[array[i] ^ _crc & 0xff]; } From 0217082031b56c9764cb85911dca571cfb6ccfea Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 21 May 2026 00:14:31 +0400 Subject: [PATCH 40/65] =?UTF-8?q?=D0=BF=D0=BE=20=D0=B7=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D1=87=D0=B0=D0=BD=D0=B8=D1=8F=D0=BC:=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=BB=D1=8C=20null,=20=D0=BE=D1=82=D1=80?= =?UTF-8?q?=D0=B8=D1=86=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=81=D1=87=D0=B5=D1=82=D1=87=D0=B8=D0=BA,=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D0=B5=20=D0=BF=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.StandardLibrary/Hash/HashImpl.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/OneScript.StandardLibrary/Hash/HashImpl.cs b/src/OneScript.StandardLibrary/Hash/HashImpl.cs index c2bce5226..0b61a8043 100644 --- a/src/OneScript.StandardLibrary/Hash/HashImpl.cs +++ b/src/OneScript.StandardLibrary/Hash/HashImpl.cs @@ -34,10 +34,11 @@ public HashImpl(IncrementalHash provider, HashFunctionEnum enumValue) _provider = provider; _enumValue = enumValue; _calculated = false; - if (enumValue == HashFunctionEnum.CRC32 || provider==null) + if (enumValue == HashFunctionEnum.CRC32) { _crc32 = new Crc32(); } + else ArgumentNullException.ThrowIfNull(provider); } [ContextProperty("ХешФункция", "HashFunction")] @@ -63,6 +64,9 @@ public string HashString { get { + if (!_calculated) + return "0"; + if (_crc32 != null) return _crc32.GetCurrentHashAsUInt32().ToString("X8"); @@ -132,10 +136,10 @@ public void Append(BslValue toAdd, int count = 0) break; case IStreamWrapper wrapper: var stream = wrapper.GetUnderlyingStream(); - if (count == 0) + if (count <= 0) AppendStream(stream); else - AppendStream(stream, count); + AppendStream(stream, count); break; case BinaryDataContext binaryData: AppendStream(binaryData.GetStream()); @@ -149,10 +153,10 @@ public void Append(BslValue toAdd, int count = 0) public void AppendFile(string path) { if (!File.Exists(path)) - throw RuntimeException.InvalidArgumentType(); - var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - AppendStream(stream); - stream.Close(); + throw RuntimeException.InvalidArgumentType(); + + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + AppendStream(stream); } [ScriptConstructor(Name = "По указанной хеш-функции")] From 40d80cbf3aca02f305d07d1a7cb50fbb71514498 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 21 May 2026 14:03:50 +0300 Subject: [PATCH 41/65] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удалена отдельная библиотека управления размером - Знание о лимитах двоичных данных удалено из ядра и оставлено только в StandardLibrary и зависимых от него - Значение опции инжектируется в потребителей через DI --- src/1Script.sln | 20 ---- .../BinaryDataConfigurationDefaults.cs | 20 ---- .../BinaryDataMemoryLimit.cs | 22 ---- .../BinaryDataMemoryLimitConfiguration.cs | 91 ---------------- .../BinaryDataRuntimeSettings.cs | 39 ------- .../OneScript.BinaryData.csproj | 16 --- .../Binary/BinaryDataConstants.cs | 25 +++++ .../Binary/BinaryDataContext.cs | 19 ++-- .../Binary/BinaryDataOptions.cs | 101 ++++++++++++++++++ .../Binary/FileBackingConstants.cs | 18 ---- .../Binary/FileBackingStream.cs | 8 +- .../Binary/GlobalBinaryData.cs | 16 ++- .../Binary}/IBinaryDataMemoryLimit.cs | 2 +- .../EngineBuilderExtensions.cs | 20 +--- .../Http/HttpRequestBodyBinary.cs | 35 ++---- .../Http/HttpRequestContext.cs | 45 ++++++-- .../DefaultTemplatesFactory.cs | 11 +- .../Extensions/EngineBuilderExtensions.cs | 7 ++ .../FileSourceTemplate.cs | 7 +- .../ScriptEngine.HostedScript.csproj | 2 +- src/ScriptEngine/ScriptEngine.csproj | 1 - src/ScriptEngine/ScriptingEngine.cs | 3 - src/TestApp/MainWindow.xaml.cs | 5 +- ...BinaryDataMemoryLimitConfigurationTests.cs | 50 +++++++-- .../OneScript.StandardLibrary.Tests.csproj | 1 - src/oscript/CgiBehavior.cs | 4 +- src/oscript/ConsoleHostBuilder.cs | 6 +- src/oscript/Web/WebRequestContext.cs | 7 +- 28 files changed, 267 insertions(+), 334 deletions(-) delete mode 100644 src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs delete mode 100644 src/OneScript.BinaryData/BinaryDataMemoryLimit.cs delete mode 100644 src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs delete mode 100644 src/OneScript.BinaryData/BinaryDataRuntimeSettings.cs delete mode 100644 src/OneScript.BinaryData/OneScript.BinaryData.csproj create mode 100644 src/OneScript.StandardLibrary/Binary/BinaryDataConstants.cs create mode 100644 src/OneScript.StandardLibrary/Binary/BinaryDataOptions.cs delete mode 100644 src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs rename src/{OneScript.BinaryData => OneScript.StandardLibrary/Binary}/IBinaryDataMemoryLimit.cs (94%) diff --git a/src/1Script.sln b/src/1Script.sln index 73cf7cf41..86a0b9a0f 100644 --- a/src/1Script.sln +++ b/src/1Script.sln @@ -65,8 +65,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Web.Server", "One EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumenterTests", "Tests\DocumenterTests\DocumenterTests.csproj", "{BD385142-E9B4-43C1-8F88-067F24E5AF6D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OneScript.BinaryData", "OneScript.BinaryData\OneScript.BinaryData.csproj", "{5711D446-8CA5-4F01-9DCE-D630FEF3A21B}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -468,24 +466,6 @@ Global {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|x86.Build.0 = Release|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|x64.ActiveCfg = Release|Any CPU {BD385142-E9B4-43C1-8F88-067F24E5AF6D}.Release|x64.Build.0 = Release|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|x86.ActiveCfg = Debug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|x86.Build.0 = Debug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|x64.ActiveCfg = Debug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Debug|x64.Build.0 = Debug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|x86.ActiveCfg = LinuxDebug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|x86.Build.0 = LinuxDebug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|x64.ActiveCfg = LinuxDebug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.LinuxDebug|x64.Build.0 = LinuxDebug|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|Any CPU.Build.0 = Release|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|x86.ActiveCfg = Release|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|x86.Build.0 = Release|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|x64.ActiveCfg = Release|Any CPU - {5711D446-8CA5-4F01-9DCE-D630FEF3A21B}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs b/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs deleted file mode 100644 index 29eb61a86..000000000 --- a/src/OneScript.BinaryData/BinaryDataConfigurationDefaults.cs +++ /dev/null @@ -1,20 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace OneScript.BinaryData -{ - /// - /// Значения по умолчанию для буферизации двоичных данных в памяти до перехода на временный файл. - /// - public static class BinaryDataConfigurationDefaults - { - /// - /// Лимит по умолчанию (50 МБ), если в конфигурации не задан ключ binaryData.inMemoryMaxSize. - /// - public const int InMemoryMaxBytes = 1024 * 1024 * 50; - } -} diff --git a/src/OneScript.BinaryData/BinaryDataMemoryLimit.cs b/src/OneScript.BinaryData/BinaryDataMemoryLimit.cs deleted file mode 100644 index d881ec589..000000000 --- a/src/OneScript.BinaryData/BinaryDataMemoryLimit.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace OneScript.BinaryData -{ - /// - /// Реализация с фиксированным числом байт. - /// - public sealed class BinaryDataMemoryLimit : IBinaryDataMemoryLimit - { - public BinaryDataMemoryLimit(int maxBytesInMemory) - { - MaxBytesInMemory = maxBytesInMemory; - } - - public int MaxBytesInMemory { get; } - } -} diff --git a/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs b/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs deleted file mode 100644 index 267907c2c..000000000 --- a/src/OneScript.BinaryData/BinaryDataMemoryLimitConfiguration.cs +++ /dev/null @@ -1,91 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Globalization; - -namespace OneScript.BinaryData -{ - /// - /// Ключ и разбор параметра конфигурации лимита памяти для двоичных данных (стандартная библиотека). - /// - public static class BinaryDataMemoryLimitConfiguration - { - public const string InMemoryMaxSizeConfigKey = "binaryData.inMemoryMaxSize"; - - /// - /// Возвращает лимит в байтах из строки конфигурации; при ошибке — и сообщение через . - /// Допускается целое число байт или значение с суффиксом k, m, g (512k, 50m, 1g). - /// - public static int ResolveFromConfigString(string rawValue, Action logWarning) - { - if (string.IsNullOrWhiteSpace(rawValue)) - return BinaryDataConfigurationDefaults.InMemoryMaxBytes; - - if (!TryParseByteSize(rawValue.Trim(), out var bytes)) - { - logWarning?.Invoke($"Invalid value for {InMemoryMaxSizeConfigKey}: {rawValue}"); - return BinaryDataConfigurationDefaults.InMemoryMaxBytes; - } - - if (bytes <= 0 || bytes == int.MaxValue) - { - logWarning?.Invoke($"Value for {InMemoryMaxSizeConfigKey} must be between 1 and {int.MaxValue - 1}: {bytes}"); - return BinaryDataConfigurationDefaults.InMemoryMaxBytes; - } - - return bytes; - } - - private static bool TryParseByteSize(string value, out int bytes) - { - bytes = 0; - - if (value.Length == 0) - return false; - - var suffix = value[value.Length - 1]; - long multiplier = 1; - var numberPart = value; - - switch (suffix) - { - case 'k': - case 'K': - multiplier = 1024L; - numberPart = value.Substring(0, value.Length - 1).TrimEnd(); - break; - case 'm': - case 'M': - multiplier = 1024L * 1024L; - numberPart = value.Substring(0, value.Length - 1).TrimEnd(); - break; - case 'g': - case 'G': - multiplier = 1024L * 1024L * 1024L; - numberPart = value.Substring(0, value.Length - 1).TrimEnd(); - break; - } - - if (numberPart.Length == 0) - return false; - - if (!long.TryParse(numberPart, NumberStyles.Integer, CultureInfo.InvariantCulture, out var number)) - return false; - - if (number <= 0) - return false; - - var result = number * multiplier; - if (result <= 0 || result >= int.MaxValue) - return false; - - bytes = (int)result; - return true; - } - } -} diff --git a/src/OneScript.BinaryData/BinaryDataRuntimeSettings.cs b/src/OneScript.BinaryData/BinaryDataRuntimeSettings.cs deleted file mode 100644 index 3dad7ce8e..000000000 --- a/src/OneScript.BinaryData/BinaryDataRuntimeSettings.cs +++ /dev/null @@ -1,39 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, -You can obtain one at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using OneScript.DependencyInjection; - -namespace OneScript.BinaryData -{ - /// - /// Лимит памяти для двоичных данных до перехода на временный файл. - /// Задаётся один раз при создании движка из конфигурации (см. регистрацию в стандартной библиотеке). - /// - public static class BinaryDataRuntimeSettings - { - private static volatile int _inMemoryMaxBytes = BinaryDataConfigurationDefaults.InMemoryMaxBytes; - - /// - /// Читает лимит из DI и сохраняет для использования кодом без доступа к контейнеру (конструктор «Файловый поток с буфером» и т.п.). - /// Вызывается при создании экземпляра основного движка скриптов. - /// - public static void ApplyFromServices(IServiceContainer services) - { - var opts = services.TryResolve(); - var maxBytes = opts?.MaxBytesInMemory ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes; - if (maxBytes <= 0 || maxBytes == int.MaxValue) - maxBytes = BinaryDataConfigurationDefaults.InMemoryMaxBytes; - - _inMemoryMaxBytes = maxBytes; - } - - /// - /// Текущий лимит «в памяти до временного файла» для двоичных данных (байты). - /// До вызова равен . - /// - public static int GetEffectiveInMemoryMaxBytes() => _inMemoryMaxBytes; - } -} diff --git a/src/OneScript.BinaryData/OneScript.BinaryData.csproj b/src/OneScript.BinaryData/OneScript.BinaryData.csproj deleted file mode 100644 index a7b9f8263..000000000 --- a/src/OneScript.BinaryData/OneScript.BinaryData.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - $(TargetFrameworkVersion) - Debug;Release;LinuxDebug - AnyCPU - OneScript.BinaryData - - - true - false - - - - - diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataConstants.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataConstants.cs new file mode 100644 index 000000000..7a5dc01ed --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataConstants.cs @@ -0,0 +1,25 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; + +namespace OneScript.StandardLibrary.Binary +{ + public static class BinaryDataConstants + { + /// + /// Максимальный размер массива, доступный в среде выполнения. + /// Де-факто он чуть меньше 2Гб, он же Int32.MaxValue, поэтому используется системная константа + /// + public static readonly int SYSTEM_IN_MEMORY_LIMIT = Array.MaxLength; + + /// + /// Размер двоичных данных, хранимый в памяти по умолчанию. + /// + public const int DEFAULT_IN_MEMORY_LIMIT = 1024 * 1024 * 50; + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs index e384b9e50..4ef0ee605 100644 --- a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs @@ -12,7 +12,6 @@ This Source Code Form is subject to the terms of the using OneScript.Contexts; using OneScript.Exceptions; using OneScript.Types; -using OneScript.BinaryData; using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; @@ -25,10 +24,10 @@ public sealed class BinaryDataContext : AutoContext, IDisposa private byte[] _buffer; private BackingTemporaryFile _backingFile; - public BinaryDataContext(string filename) + public BinaryDataContext(string filename, int inMemLimit) { using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read); - ReadFromStream(fs); + ReadFromStream(fs, inMemLimit); } public BinaryDataContext(byte[] buffer) @@ -36,21 +35,21 @@ public BinaryDataContext(byte[] buffer) _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); } - public BinaryDataContext(Stream stream) + public BinaryDataContext(Stream stream, int inMemLimit) { long pos = 0; - ReadFromStream(stream); + ReadFromStream(stream, inMemLimit); stream.Position = pos; } - + /// /// Признак хранения данных в памяти /// public bool InMemory => _backingFile == null; - private void ReadFromStream(Stream stream) + private void ReadFromStream(Stream stream, int inMemLimit) { - if (stream.Length < BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes()) + if (stream.Length < inMemLimit) { LoadToBuffer(stream); } @@ -198,9 +197,9 @@ public GenericStream OpenStreamForRead() } [ScriptConstructor(Name = "На основании файла")] - public static BinaryDataContext Constructor(string filename) + public static BinaryDataContext Constructor(TypeActivationContext ctx, string filename) { - return new BinaryDataContext(filename); + return new BinaryDataContext(filename, ctx.Services.Resolve().MaxBytesInMemory); } public override bool Equals(BslValue other) diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataOptions.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataOptions.cs new file mode 100644 index 000000000..d5f84438f --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataOptions.cs @@ -0,0 +1,101 @@ +// /*---------------------------------------------------------- +// This Source Code Form is subject to the terms of the +// Mozilla Public License, v.2.0. If a copy of the MPL +// was not distributed with this file, You can obtain one +// at http://mozilla.org/MPL/2.0/. +// ----------------------------------------------------------*/ + +using System.Globalization; +using ScriptEngine; +using ScriptEngine.Hosting; + +namespace OneScript.StandardLibrary.Binary +{ + /// + /// Инкапсулирует логику чтения и хранения настроек двоичных данных + /// + public class BinaryDataOptions : IBinaryDataMemoryLimit + { + public const string IN_MEMORY_LIMIT_KEY_NAME = "binaryData.inMemoryMaxSize"; + public const string IN_MEMORY_MAX_MAGIC = "max"; + + public BinaryDataOptions(KeyValueConfig config) + { + var configValue = config[IN_MEMORY_LIMIT_KEY_NAME]; + MaxBytesInMemory = ResolveFromConfigString(configValue); + } + + public int MaxBytesInMemory { get; } + + private static int ResolveFromConfigString(string rawValue) + { + if (string.IsNullOrWhiteSpace(rawValue)) + return BinaryDataConstants.DEFAULT_IN_MEMORY_LIMIT; + + if (rawValue.Trim() == IN_MEMORY_MAX_MAGIC) + return BinaryDataConstants.SYSTEM_IN_MEMORY_LIMIT; + + if (!TryParseByteSize(rawValue.Trim(), out var bytes)) + { + SystemLogger.Write($"Invalid value for {IN_MEMORY_LIMIT_KEY_NAME}: {rawValue}"); + return BinaryDataConstants.DEFAULT_IN_MEMORY_LIMIT; + } + + if (bytes <= 0 || bytes >= BinaryDataConstants.SYSTEM_IN_MEMORY_LIMIT) + { + SystemLogger.Write($"Value for {IN_MEMORY_LIMIT_KEY_NAME} must be between 1 and {BinaryDataConstants.SYSTEM_IN_MEMORY_LIMIT - 1}: {bytes}"); + return BinaryDataConstants.DEFAULT_IN_MEMORY_LIMIT; + } + + return (int)bytes; + } + + private static bool TryParseByteSize(string value, out long bytes) + { + bytes = 0; + + if (value.Length == 0) + return false; + + var suffix = value[value.Length - 1]; + long multiplier = 1; + var numberPart = value; + + const long KILOBYTES = 1024L; + const long MEGABYTES = KILOBYTES * 1024L; + const long GIGABYTES = MEGABYTES * 1024L; + + switch (suffix) + { + case 'k': + case 'K': + multiplier = KILOBYTES; + numberPart = value.Substring(0, value.Length - 1).TrimEnd(); + break; + case 'm': + case 'M': + multiplier = MEGABYTES; + numberPart = value.Substring(0, value.Length - 1).TrimEnd(); + break; + case 'g': + case 'G': + multiplier = GIGABYTES; + numberPart = value.Substring(0, value.Length - 1).TrimEnd(); + break; + } + + if (numberPart.Length == 0) + return false; + + if (!long.TryParse(numberPart, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture, out var number)) + return false; + + if (number <= 0) + return false; + + bytes = number * multiplier; + + return true; + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs deleted file mode 100644 index 857824a7c..000000000 --- a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs +++ /dev/null @@ -1,18 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using OneScript.BinaryData; - -namespace OneScript.StandardLibrary.Binary -{ - public static class FileBackingConstants - { - public const int DEFAULT_MEMORY_LIMIT = BinaryDataConfigurationDefaults.InMemoryMaxBytes; - public const int SYSTEM_IN_MEMORY_LIMIT = Int32.MaxValue; - } -} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs index d9380cb30..c0a381c5a 100644 --- a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs +++ b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs @@ -7,7 +7,6 @@ This Source Code Form is subject to the terms of the using System; using System.IO; -using OneScript.BinaryData; namespace OneScript.StandardLibrary.Binary { @@ -21,13 +20,9 @@ public sealed class FileBackingStream : Stream private string _backingFileName; - public FileBackingStream() : this(BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes()) - { - } - public FileBackingStream(int inMemoryLimit, int capacity = 0) { - if (inMemoryLimit == FileBackingConstants.SYSTEM_IN_MEMORY_LIMIT) + if (inMemoryLimit == BinaryDataConstants.SYSTEM_IN_MEMORY_LIMIT) throw new ArgumentException("Use MemoryStream instead"); _inMemoryLimit = inMemoryLimit; @@ -86,6 +81,7 @@ public override long Position public bool HasBackingFile => _backingFileName != null; public string FileName => _backingFileName; + public int InMemoryThreshold => _inMemoryLimit; public void SwitchToMemory() { diff --git a/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs b/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs index f5f34141a..7bc79fc20 100644 --- a/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs +++ b/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs @@ -11,6 +11,7 @@ This Source Code Form is subject to the terms of the using System.Text; using OneScript.Contexts; using OneScript.Exceptions; +using OneScript.Types; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; namespace OneScript.StandardLibrary.Binary @@ -21,6 +22,13 @@ namespace OneScript.StandardLibrary.Binary [GlobalContext(Category = "Процедуры и функции работы с двоичными данными")] public sealed class GlobalBinaryData : GlobalContextBase { + private readonly int _memoryLimitMaxBytesInMemory; + + private GlobalBinaryData(int memoryLimitMaxBytesInMemory) + { + _memoryLimitMaxBytesInMemory = memoryLimitMaxBytesInMemory; + } + private static byte[] HexStringToByteArray(string hex) { var newHex = System.Text.RegularExpressions.Regex.Replace(hex, @"[^0-9A-Fa-f]", ""); @@ -142,9 +150,9 @@ private static void CheckAndThrowIfNull(AutoContext obj, int argNumber, st throw RuntimeException.InvalidArgumentType(argNumber, argName); } - public static IAttachableContext CreateInstance() + public static IAttachableContext CreateInstance(IBinaryDataMemoryLimit memoryLimit) { - return new GlobalBinaryData(); + return new GlobalBinaryData(memoryLimit.MaxBytesInMemory); } /// @@ -174,7 +182,7 @@ public BinaryDataContext ConcatBinaryData(ArrayImpl array) } stream.Position = 0; - return new BinaryDataContext(stream); + return new BinaryDataContext(stream, _memoryLimitMaxBytesInMemory); } /// @@ -244,7 +252,7 @@ public BinaryDataContext GetBinaryDataFromString(string str, IValue encoding = n stream.Write(inputString, 0, inputString.Length); stream.Position = 0; - return new BinaryDataContext(stream); + return new BinaryDataContext(stream, _memoryLimitMaxBytesInMemory); } /// diff --git a/src/OneScript.BinaryData/IBinaryDataMemoryLimit.cs b/src/OneScript.StandardLibrary/Binary/IBinaryDataMemoryLimit.cs similarity index 94% rename from src/OneScript.BinaryData/IBinaryDataMemoryLimit.cs rename to src/OneScript.StandardLibrary/Binary/IBinaryDataMemoryLimit.cs index 97dfd3134..49497dd78 100644 --- a/src/OneScript.BinaryData/IBinaryDataMemoryLimit.cs +++ b/src/OneScript.StandardLibrary/Binary/IBinaryDataMemoryLimit.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace OneScript.BinaryData +namespace OneScript.StandardLibrary.Binary { /// /// Лимит объёма данных в памяти для объектов «ДвоичныеДанные» и смежных потоков diff --git a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs index 4dae2491c..6e3a9875a 100644 --- a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs +++ b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs @@ -5,9 +5,8 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using OneScript.BinaryData; +using OneScript.StandardLibrary.Binary; using OneScript.StandardLibrary.Collections; -using ScriptEngine; using ScriptEngine.Hosting; using ScriptEngine.Machine; @@ -15,23 +14,12 @@ namespace OneScript.StandardLibrary { public static class EngineBuilderExtensions { - /// - /// Регистрирует лимит памяти для «ДвоичныеДанные» из конфигурации движка (ключ ). - /// Вызывайте после SetDefaultOptions из ScriptEngine.Hosting.EngineBuilderExtensions. - /// - public static IEngineBuilder RegisterBinaryDataMemoryLimitFromConfig(this IEngineBuilder builder) + public static IEngineBuilder UseBinaryDataOptions(this IEngineBuilder builder) { - builder.Services.RegisterSingleton(sp => - { - var cfg = sp.Resolve(); - var raw = cfg[BinaryDataMemoryLimitConfiguration.InMemoryMaxSizeConfigKey]; - var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(raw, SystemLogger.Write); - return new BinaryDataMemoryLimit(bytes); - }); - + builder.Services.RegisterSingleton(); return builder; } - + public static ExecutionContext AddStandardLibrary(this ExecutionContext env) { return env.AddAssembly(typeof(ArrayImpl).Assembly); diff --git a/src/OneScript.StandardLibrary/Http/HttpRequestBodyBinary.cs b/src/OneScript.StandardLibrary/Http/HttpRequestBodyBinary.cs index 0057e2b79..83871f37e 100644 --- a/src/OneScript.StandardLibrary/Http/HttpRequestBodyBinary.cs +++ b/src/OneScript.StandardLibrary/Http/HttpRequestBodyBinary.cs @@ -17,42 +17,19 @@ namespace OneScript.StandardLibrary.Http { class HttpRequestBodyBinary : IHttpRequestBody { - private readonly FileBackingStream _storage = new FileBackingStream(); + private readonly FileBackingStream _storage; - public HttpRequestBodyBinary() + internal HttpRequestBodyBinary(int inMemoryBodyLimit) { + _storage = new FileBackingStream(inMemoryBodyLimit); } - public HttpRequestBodyBinary(BinaryDataContext data) + internal HttpRequestBodyBinary(int inMemoryBodyLimit, BinaryDataContext data) { + _storage = new FileBackingStream(inMemoryBodyLimit); data.CopyTo(_storage); } - public HttpRequestBodyBinary(string body, IValue encoding = null, - ByteOrderMarkUsageEnum bomUsage = ByteOrderMarkUsageEnum.Auto) - { - var useBom = bomUsage == ByteOrderMarkUsageEnum.Auto || - bomUsage == ByteOrderMarkUsageEnum.Use; - - Encoding encoder; - if (encoding == null) - { - encoder = new UTF8Encoding(useBom); - } - else if (encoding.SystemType == BasicTypes.String) - { - var utfs = new List {"utf-16", "utf-32"}; - encoder = TextEncodingEnum.GetEncoding(encoding, utfs.Contains(encoding.ToString()) && useBom); - } - else - { - encoder = TextEncodingEnum.GetEncoding(encoding); - } - - var byteArray = encoder.GetBytes(body); - _storage.Write(byteArray, 0, byteArray.Length); - } - public IValue GetAsString() { _storage.Seek(0, SeekOrigin.Begin); @@ -63,7 +40,7 @@ public IValue GetAsString() public IValue GetAsBinary() { _storage.Seek(0, SeekOrigin.Begin); - return new BinaryDataContext(_storage); + return new BinaryDataContext(_storage, _storage.InMemoryThreshold); } public IValue GetAsFilename() diff --git a/src/OneScript.StandardLibrary/Http/HttpRequestContext.cs b/src/OneScript.StandardLibrary/Http/HttpRequestContext.cs index d28946b1a..aa77d4001 100644 --- a/src/OneScript.StandardLibrary/Http/HttpRequestContext.cs +++ b/src/OneScript.StandardLibrary/Http/HttpRequestContext.cs @@ -6,12 +6,15 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Collections.Generic; using System.IO; +using System.Text; using OneScript.Contexts; using OneScript.Exceptions; using OneScript.StandardLibrary.Binary; using OneScript.StandardLibrary.Collections; using OneScript.StandardLibrary.Text; +using OneScript.Types; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; @@ -23,10 +26,12 @@ namespace OneScript.StandardLibrary.Http [ContextClass("HTTPЗапрос", "HTTPRequest")] public class HttpRequestContext : AutoContext, IDisposable { + private readonly int _inMemoryBodyLimit; private IHttpRequestBody _body; - private HttpRequestContext() + private HttpRequestContext(int inMemoryBodyLimit) { + _inMemoryBodyLimit = inMemoryBodyLimit; ResourceAddress = ""; Headers = new MapImpl(); } @@ -80,7 +85,7 @@ public IValue GetBodyFileName() [ContextMethod("УстановитьТелоИзДвоичныхДанных", "SetBodyFromBinaryData")] public void SetBodyFromBinary(BinaryDataContext data) { - SetBody(new HttpRequestBodyBinary(data)); + SetBody(new HttpRequestBodyBinary(_inMemoryBodyLimit, data)); } [DeprecatedName("GetBodyAsBinary")] @@ -99,7 +104,27 @@ public IValue GetBodyFromBinary() [ContextMethod("УстановитьТелоИзСтроки", "SetBodyFromString")] public void SetBodyFromString(string data, IValue encoding = null, ByteOrderMarkUsageEnum bomUsage = ByteOrderMarkUsageEnum.Auto) { - SetBody(new HttpRequestBodyBinary(data, encoding, bomUsage)); + var useBom = bomUsage == ByteOrderMarkUsageEnum.Auto || + bomUsage == ByteOrderMarkUsageEnum.Use; + + Encoding encoder; + if (encoding == null) + { + encoder = new UTF8Encoding(useBom); + } + else if (encoding.SystemType == BasicTypes.String) + { + var utfs = new List {"utf-16", "utf-32"}; + encoder = TextEncodingEnum.GetEncoding(encoding, utfs.Contains(encoding.ToString()) && useBom); + } + else + { + encoder = TextEncodingEnum.GetEncoding(encoding); + } + + var byteArray = encoder.GetBytes(data); + + SetBody(new HttpRequestBodyBinary(_inMemoryBodyLimit, new BinaryDataContext(byteArray))); } [ContextMethod("ПолучитьТелоКакСтроку", "GetBodyAsString")] @@ -111,20 +136,24 @@ public IValue GetBodyAsString() [ContextMethod("ПолучитьТелоКакПоток", "GetBodyAsStream")] public GenericStream GetBodyAsStream() { - _body = _body ?? new HttpRequestBodyBinary(); + _body ??= new HttpRequestBodyBinary(_inMemoryBodyLimit); return new GenericStream(_body.GetDataStream()); } [ScriptConstructor(Name = "Формирование неинициализированного объекта")] - public static HttpRequestContext Constructor() + public static HttpRequestContext Constructor(TypeActivationContext context) { - return new HttpRequestContext(); + return new HttpRequestContext(context.Services.Resolve().MaxBytesInMemory); } [ScriptConstructor(Name = "По адресу ресурса и заголовкам")] - public static HttpRequestContext Constructor(string resource, IValue headers = null) + public static HttpRequestContext Constructor(TypeActivationContext context, string resource, IValue headers = null) { - var ctx = new HttpRequestContext {ResourceAddress = resource}; + var ctx = new HttpRequestContext(context.Services.Resolve().MaxBytesInMemory) + { + ResourceAddress = resource + }; + if (headers == null) return ctx; diff --git a/src/ScriptEngine.HostedScript/DefaultTemplatesFactory.cs b/src/ScriptEngine.HostedScript/DefaultTemplatesFactory.cs index d852af168..00150a124 100644 --- a/src/ScriptEngine.HostedScript/DefaultTemplatesFactory.cs +++ b/src/ScriptEngine.HostedScript/DefaultTemplatesFactory.cs @@ -5,13 +5,22 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.StandardLibrary.Binary; + namespace ScriptEngine.HostedScript { public class DefaultTemplatesFactory : ITemplateFactory { + private readonly int _inMemLimit; + + public DefaultTemplatesFactory(IBinaryDataMemoryLimit memoryLimit) + { + _inMemLimit = memoryLimit.MaxBytesInMemory; + } + public ITemplate CreateTemplate(string file, TemplateKind kind) { - return new FileSourceTemplate(file, kind); + return new FileSourceTemplate(_inMemLimit, file, kind); } } } \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs b/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs index 185612566..25c7c89b9 100644 --- a/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs +++ b/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs @@ -9,6 +9,7 @@ This Source Code Form is subject to the terms of the using System.IO; using OneScript.Contexts; using OneScript.Native.Extensions; +using OneScript.StandardLibrary; using ScriptEngine.Hosting; using ScriptEngine.Machine; @@ -64,6 +65,12 @@ public static ConfigurationProviders UseEnvironmentVariableConfig(this Configura providers.Add(reader); return providers; } + + public static IEngineBuilder UseDefaultHosting(this IEngineBuilder b) + { + return b.UseFileSystemLibraries() + .UseBinaryDataOptions(); + } public static IEngineBuilder UseFileSystemLibraries(this IEngineBuilder b) { diff --git a/src/ScriptEngine.HostedScript/FileSourceTemplate.cs b/src/ScriptEngine.HostedScript/FileSourceTemplate.cs index 991c4c57d..176c2b415 100644 --- a/src/ScriptEngine.HostedScript/FileSourceTemplate.cs +++ b/src/ScriptEngine.HostedScript/FileSourceTemplate.cs @@ -11,8 +11,11 @@ namespace ScriptEngine.HostedScript { public class FileSourceTemplate : ITemplate { - public FileSourceTemplate(string file, TemplateKind kind) + private readonly int _inMemLimit; + + public FileSourceTemplate(int inMemLimit, string file, TemplateKind kind) { + _inMemLimit = inMemLimit; Kind = kind; Filename = file; } @@ -28,7 +31,7 @@ public string GetFilename() public BinaryDataContext GetBinaryData() { - return new BinaryDataContext(Filename); + return new BinaryDataContext(Filename, _inMemLimit); } public void Dispose() diff --git a/src/ScriptEngine.HostedScript/ScriptEngine.HostedScript.csproj b/src/ScriptEngine.HostedScript/ScriptEngine.HostedScript.csproj index 4d5769efc..78adeb5c7 100644 --- a/src/ScriptEngine.HostedScript/ScriptEngine.HostedScript.csproj +++ b/src/ScriptEngine.HostedScript/ScriptEngine.HostedScript.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/ScriptEngine/ScriptEngine.csproj b/src/ScriptEngine/ScriptEngine.csproj index 04ba4ff3c..f81d68951 100644 --- a/src/ScriptEngine/ScriptEngine.csproj +++ b/src/ScriptEngine/ScriptEngine.csproj @@ -41,7 +41,6 @@ - diff --git a/src/ScriptEngine/ScriptingEngine.cs b/src/ScriptEngine/ScriptingEngine.cs index 268b6ae4d..5b8340900 100644 --- a/src/ScriptEngine/ScriptingEngine.cs +++ b/src/ScriptEngine/ScriptingEngine.cs @@ -8,7 +8,6 @@ This Source Code Form is subject to the terms of the using OneScript.Compilation; using OneScript.Contexts; using OneScript.DependencyInjection; -using OneScript.BinaryData; using OneScript.Execution; using OneScript.Types; using ScriptEngine.Machine; @@ -52,8 +51,6 @@ public ScriptingEngine(ITypeManager types, } Loader.ReaderEncoding = options.FileReaderEncoding; - - BinaryDataRuntimeSettings.ApplyFromServices(services); } public IServiceContainer Services { get; } diff --git a/src/TestApp/MainWindow.xaml.cs b/src/TestApp/MainWindow.xaml.cs index 57bb923a2..320401b17 100644 --- a/src/TestApp/MainWindow.xaml.cs +++ b/src/TestApp/MainWindow.xaml.cs @@ -137,9 +137,9 @@ private HostedScriptEngine CreateEngine() var builder = DefaultEngineBuilder .Create() .SetDefaultOptions() - .RegisterBinaryDataMemoryLimitFromConfig() .UseNativeRuntime() .UseImports() + .UseDefaultHosting() .SetupEnvironment(e => { e.AddStandardLibrary(); @@ -150,8 +150,7 @@ private HostedScriptEngine CreateEngine() x.UseSystemConfigFile() .UseEntrypointConfigFile(_currentDocPath); }); - - builder.UseFileSystemLibraries(); + var engine = builder.Build(); var mainEngine = new HostedScriptEngine(engine); diff --git a/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs b/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs index 342a03f30..18229c890 100644 --- a/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs +++ b/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs @@ -7,13 +7,28 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using FluentAssertions; -using OneScript.BinaryData; +using Moq; +using OneScript.StandardLibrary.Binary; +using ScriptEngine; +using ScriptEngine.Hosting; using Xunit; namespace OneScript.StandardLibrary.Tests { + [Collection("SystemLogger")] public class BinaryDataMemoryLimitConfigurationTests { + List _messages = new List(); + + public BinaryDataMemoryLimitConfigurationTests() + { + var mock = new Mock(); + mock.Setup(x => x.Write(It.IsAny())) + .Callback(str => _messages.Add(str)); + + SystemLogger.SetWriter(mock.Object); + } + [Theory] [InlineData("52428800", 52428800)] [InlineData("512k", 512 * 1024)] @@ -24,12 +39,21 @@ public class BinaryDataMemoryLimitConfigurationTests [InlineData("1G", 1024 * 1024 * 1024)] public void ResolvesByteSizeWithOptionalSuffix(string rawValue, int expectedBytes) { - var warnings = new List(); - - var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(rawValue, warnings.Add); + var bytes = MockConfig(rawValue).MaxBytesInMemory; bytes.Should().Be(expectedBytes); - warnings.Should().BeEmpty(); + _messages.Should().BeEmpty(); + } + + private static IBinaryDataMemoryLimit MockConfig(string rawValue) + { + var kvStore = new KeyValueConfig(); + kvStore.Merge(new Dictionary + { + {BinaryDataOptions.IN_MEMORY_LIMIT_KEY_NAME, rawValue} + }, Mock.Of()); + + return new BinaryDataOptions(kvStore); } [Theory] @@ -38,9 +62,9 @@ public void ResolvesByteSizeWithOptionalSuffix(string rawValue, int expectedByte [InlineData(" ")] public void UsesDefaultWhenValueIsMissing(string rawValue) { - var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(rawValue, _ => { }); + var bytes = MockConfig(rawValue).MaxBytesInMemory; - bytes.Should().Be(BinaryDataConfigurationDefaults.InMemoryMaxBytes); + bytes.Should().Be(BinaryDataConstants.DEFAULT_IN_MEMORY_LIMIT); } [Theory] @@ -51,12 +75,16 @@ public void UsesDefaultWhenValueIsMissing(string rawValue) [InlineData("2g")] public void UsesDefaultForInvalidValue(string rawValue) { - var warnings = new List(); + var bytes = MockConfig(rawValue).MaxBytesInMemory; - var bytes = BinaryDataMemoryLimitConfiguration.ResolveFromConfigString(rawValue, warnings.Add); + bytes.Should().Be(BinaryDataConstants.DEFAULT_IN_MEMORY_LIMIT); + _messages.Should().NotBeEmpty(); + } - bytes.Should().Be(BinaryDataConfigurationDefaults.InMemoryMaxBytes); - warnings.Should().NotBeEmpty(); + [Fact] + public void TestMagicMaxValue() + { + MockConfig(BinaryDataOptions.IN_MEMORY_MAX_MAGIC).MaxBytesInMemory.Should().Be(BinaryDataConstants.SYSTEM_IN_MEMORY_LIMIT); } } } diff --git a/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj b/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj index f63fbf2d3..4d93b540e 100644 --- a/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj +++ b/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj @@ -23,7 +23,6 @@ - diff --git a/src/oscript/CgiBehavior.cs b/src/oscript/CgiBehavior.cs index 3501f26a6..3354a0597 100644 --- a/src/oscript/CgiBehavior.cs +++ b/src/oscript/CgiBehavior.cs @@ -11,9 +11,9 @@ This Source Code Form is subject to the terms of the using System.Text; using OneScript.Contexts; using OneScript.Exceptions; -using OneScript.BinaryData; using OneScript.Execution; using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Binary; using oscript.Web; using ScriptEngine; @@ -70,7 +70,7 @@ private int RunCGIMode(string scriptFile) var engine = ConsoleHostBuilder.Build(builder); - var request = new WebRequestContext(BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes()); + var request = new WebRequestContext(engine.Services.Resolve().MaxBytesInMemory); engine.InjectGlobalProperty("ВебЗапрос", "WebRequest", request, true); engine.InjectObject(this); diff --git a/src/oscript/ConsoleHostBuilder.cs b/src/oscript/ConsoleHostBuilder.cs index d55f62e9e..031fc29e8 100644 --- a/src/oscript/ConsoleHostBuilder.cs +++ b/src/oscript/ConsoleHostBuilder.cs @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Binary; using ScriptEngine.HostedScript; using ScriptEngine.Hosting; using ScriptEngine.HostedScript.Extensions; @@ -31,7 +32,7 @@ public static IEngineBuilder Create(string codePath) { env.AddStandardLibrary() .AddWebServer() - .UseTemplateFactory(new DefaultTemplatesFactory()); + .UseTemplateFactory(new DefaultTemplatesFactory(env.Services.Resolve())); }); return builder; @@ -48,9 +49,8 @@ public static HostedScriptEngine Build(IEngineBuilder builder) private static void BuildUpWithIoC(IEngineBuilder builder) { builder.SetDefaultOptions() - .RegisterBinaryDataMemoryLimitFromConfig() .UseImports() - .UseFileSystemLibraries() + .UseDefaultHosting() .UseNativeRuntime() .UseEventHandlers(); } diff --git a/src/oscript/Web/WebRequestContext.cs b/src/oscript/Web/WebRequestContext.cs index 9ffb2724d..88c17de14 100644 --- a/src/oscript/Web/WebRequestContext.cs +++ b/src/oscript/Web/WebRequestContext.cs @@ -10,7 +10,6 @@ This Source Code Form is subject to the terms of the using System.IO; using System.Text; using System.Threading.Tasks; -using OneScript.BinaryData; using OneScript.Contexts; using OneScript.StandardLibrary.Binary; using OneScript.StandardLibrary.Collections; @@ -31,10 +30,6 @@ public sealed class WebRequestContext : AutoContext, IDisposa private readonly int _postBodyInMemoryMaxBytes; - public WebRequestContext() : this(BinaryDataConfigurationDefaults.InMemoryMaxBytes) - { - } - public WebRequestContext(int postBodyInMemoryMaxBytes) { if (postBodyInMemoryMaxBytes <= 0 || postBodyInMemoryMaxBytes == int.MaxValue) @@ -120,7 +115,7 @@ private void FillGetMap(string get) public BinaryDataContext GetBodyAsBinaryData() { _postBody.Position = 0; - return new BinaryDataContext(_postBody); + return new BinaryDataContext(_postBody, _postBodyInMemoryMaxBytes); } [ContextMethod("ПолучитьТелоКакСтроку", "GetBodyAsString")] From 64511633e07c0889f832d1d9489e84de2b457bc7 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 21 May 2026 17:05:42 +0400 Subject: [PATCH 42/65] =?UTF-8?q?=D0=B8=D0=BD=D0=B8=D1=86=D0=B8=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=85=D0=B5=D1=88?= =?UTF-8?q?=D0=B5=D0=B9=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=83=D1=81=D1=82?= =?UTF-8?q?=D1=8B=D1=85=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85;=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=B8=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Hash/HashImpl.cs | 23 +-- tests/datahashing-from-stream.os | 156 +++++++++++------- tests/hash.os | 103 +++++++++++- 3 files changed, 209 insertions(+), 73 deletions(-) diff --git a/src/OneScript.StandardLibrary/Hash/HashImpl.cs b/src/OneScript.StandardLibrary/Hash/HashImpl.cs index 0b61a8043..ed8a89e62 100644 --- a/src/OneScript.StandardLibrary/Hash/HashImpl.cs +++ b/src/OneScript.StandardLibrary/Hash/HashImpl.cs @@ -26,21 +26,21 @@ public class HashImpl : AutoContext private readonly Crc32 _crc32; private readonly IncrementalHash _provider; private readonly HashFunctionEnum _enumValue; - private bool _calculated; private byte[] _hash; public HashImpl(IncrementalHash provider, HashFunctionEnum enumValue) { _provider = provider; _enumValue = enumValue; - _calculated = false; if (enumValue == HashFunctionEnum.CRC32) { _crc32 = new Crc32(); } else ArgumentNullException.ThrowIfNull(provider); - } - + + AppendData(Array.Empty()); + } + [ContextProperty("ХешФункция", "HashFunction")] public HashFunctionEnum Extension => _enumValue; @@ -49,9 +49,6 @@ public IValue Hash { get { - if(!_calculated) - return ValueFactory.Create(0); - if (_crc32 != null) return ValueFactory.Create(_crc32.GetCurrentHashAsUInt32()); @@ -64,9 +61,6 @@ public string HashString { get { - if (!_calculated) - return "0"; - if (_crc32 != null) return _crc32.GetCurrentHashAsUInt32().ToString("X8"); @@ -93,8 +87,6 @@ private void AppendData(byte[] data, int count) _provider.AppendData(data,0,count); _hash = _provider.GetCurrentHash(); } - - _calculated = true; } private void AppendStream(Stream stream) @@ -155,8 +147,11 @@ public void AppendFile(string path) if (!File.Exists(path)) throw RuntimeException.InvalidArgumentType(); - using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - AppendStream(stream); + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + AppendStream(stream); + stream.Close(); + } } [ScriptConstructor(Name = "По указанной хеш-функции")] diff --git a/tests/datahashing-from-stream.os b/tests/datahashing-from-stream.os index f5a9e5213..ff11f6494 100644 --- a/tests/datahashing-from-stream.os +++ b/tests/datahashing-from-stream.os @@ -1,58 +1,98 @@ -Перем юТест; - -Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт - - юТест = ЮнитТестирование; - - ВсеТесты = Новый Массив; - ВсеТесты.Добавить("ТестДолжен_ПроверитьХешированниеДанныхИзПотокаВПамяти"); - - Возврат ВсеТесты; - -КонецФункции - -Функция ТестДолжен_ПроверитьХешированниеДанныхИзПотокаВПамяти() Экспорт - - БДД = ПолучитьБуферДвоичныхДанныхИзHexСтроки("313233"); - Поток = Новый ПотокВПамяти(); - Структура = Новый Структура("Поток", Поток); - Поток.Записать(БДД, 0, 3); - Поток.Перейти(0,ПозицияВПотоке.Начало); - ХД = Новый ХешированиеДанных(ХешФункция.MD5); - ХД.Добавить(Поток); - юТест.ПроверитьРавенство(Строка(ХД.ХешСумма), - "20 2C B9 62 AC 59 07 5B 96 4B 07 15 2D 23 4B 70", - "Не получен ожидаемый hash - 20 2C B9 62 AC 59 07 5B 96 4B 07 15 2D 23 4B 70" - + "Получен hash - " + Строка(ХД.ХешСумма) ); - - - ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки("1b1e c416 6a11 c03b 3afe faea 442e 7709"); - Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения(); - - ИмяВременногоФайла = ПолучитьИмяВременногоФайла(); - ЗаписьТекста = Новый ЗаписьТекста(ИмяВременногоФайла); - ЗаписьТекста.ЗаписатьСтроку("1b1e c416 6a11 c03b 3afe faea 442e 7709"); - ЗаписьТекста.Закрыть(); - - ФайловыйПоток = Новый ФайловыйПоток(ИмяВременногоФайла, РежимОткрытияФайла.Открыть); - - Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256); - Хеширование.Добавить(Поток, 2); - юТест.ПроверитьРавенство("0b427acd4088d248408e38b34fb378172a05bd71e58b384f5b279beea7aea467" - , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); - - Хеширование.Добавить(Поток, 12); - юТест.ПроверитьРавенство("2a0815b87888a03406d7ab482b965361eb96112db88e8ab6ea214cb107ee8469" - , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); - - Хеширование.Добавить(ФайловыйПоток, 2); - юТест.ПроверитьРавенство("8f0ae87f4e7fa52ed08127867a7430003b312ca3777319a533ae417daa6255cc" - , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); - - Хеширование.Добавить(ФайловыйПоток, 12); - юТест.ПроверитьРавенство("4ec0ce7ec4e12c6e8922f5f1c28a582557e88731a32845a0429cda74ee7fa649" - , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); - - - -КонецФункции \ No newline at end of file +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешированниеДанныхИзПотокаВПамяти"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешированниеДанныхИзФайловогоПотока"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешированниеДанныхИзРазныхПотоков"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьХешированниеДанныхИзПотокаВПамяти() Экспорт + + БДД = ПолучитьБуферДвоичныхДанныхИзHexСтроки("313233"); + Поток = Новый ПотокВПамяти(); + Поток.Записать(БДД, 0, 3); + Поток.Перейти(0,ПозицияВПотоке.Начало); + ХД = Новый ХешированиеДанных(ХешФункция.MD5); + ХД.Добавить(Поток); + юТест.ПроверитьРавенство(Строка(ХД.ХешСумма), + "20 2C B9 62 AC 59 07 5B 96 4B 07 15 2D 23 4B 70", + "Не получен ожидаемый hash - 20 2C B9 62 AC 59 07 5B 96 4B 07 15 2D 23 4B 70" + + "Получен hash - " + Строка(ХД.ХешСумма) ); + + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки("1b1e c416 6a11 c03b 3afe faea 442e 7709"); + Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения(); + + + Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256); + Хеширование.Добавить(Поток, 2); + юТест.ПроверитьРавенство("0b427acd4088d248408e38b34fb378172a05bd71e58b384f5b279beea7aea467" + , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); + + Хеширование.Добавить(Поток, 12); + юТест.ПроверитьРавенство("2a0815b87888a03406d7ab482b965361eb96112db88e8ab6ea214cb107ee8469" + , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьХешированниеДанныхИзФайловогоПотока() Экспорт + + ИмяВременногоФайла = юТест.ИмяВременногоФайла(); + ЗаписьТекста = Новый ЗаписьТекста(ИмяВременногоФайла); + ЗаписьТекста.ЗаписатьСтроку("1b1e c416 6a11 c03b 3afe faea 442e 7709"); + ЗаписьТекста.Закрыть(); + + ФайловыйПоток = Новый ФайловыйПоток(ИмяВременногоФайла, РежимОткрытияФайла.Открыть); + + Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256); + + Хеширование.Добавить(ФайловыйПоток, 2); + юТест.ПроверитьРавенство("f257f0501a5e137710e26f1c35ddd32ce2b5752f20274a648cb6d0347849a5a9" + , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); + + Хеширование.Добавить(ФайловыйПоток, 12); + Сообщить(Хеширование.ХешСуммаСтрокой); + юТест.ПроверитьРавенство("3bc6abfa41bb73c8bcc4c0f5f4d4db42f10f38295a0432afc592fe998465cb9a" + , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); + + ФайловыйПоток.Закрыть(); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьХешированниеДанныхИзРазныхПотоков() Экспорт + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки("1b1e c416 6a11 c03b 3afe faea 442e 7709"); + Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения(); + + ИмяВременногоФайла = юТест.ИмяВременногоФайла(); + ЗаписьТекста = Новый ЗаписьТекста(ИмяВременногоФайла); + ЗаписьТекста.ЗаписатьСтроку("1b1e c416 6a11 c03b 3afe faea 442e 7709"); + ЗаписьТекста.Закрыть(); + + ФайловыйПоток = Новый ФайловыйПоток(ИмяВременногоФайла, РежимОткрытияФайла.Открыть); + + Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256); + Хеширование.Добавить(Поток, 2); + юТест.ПроверитьРавенство("0b427acd4088d248408e38b34fb378172a05bd71e58b384f5b279beea7aea467" + , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); + + Хеширование.Добавить(Поток, 12); + юТест.ПроверитьРавенство("2a0815b87888a03406d7ab482b965361eb96112db88e8ab6ea214cb107ee8469" + , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); + + Хеширование.Добавить(ФайловыйПоток, 2); + юТест.ПроверитьРавенство("8f0ae87f4e7fa52ed08127867a7430003b312ca3777319a533ae417daa6255cc" + , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); + + Хеширование.Добавить(ФайловыйПоток, 12); + юТест.ПроверитьРавенство("4ec0ce7ec4e12c6e8922f5f1c28a582557e88731a32845a0429cda74ee7fa649" + , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); + + ФайловыйПоток.Закрыть(); +КонецПроцедуры \ No newline at end of file diff --git a/tests/hash.os b/tests/hash.os index cd113412b..b1c903a7d 100644 --- a/tests/hash.os +++ b/tests/hash.os @@ -14,6 +14,10 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьSHA512"); ВсеТесты.Добавить("ТестДолжен_ПроверитьCRC32"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоХешированиеНеЗанимаетФайл"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешиБезДанных"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешиПустойСтроки"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешиПустогоПотока"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИнкрементальность"); Возврат ВсеТесты; КонецФункции @@ -106,4 +110,101 @@ юТест.ПроверитьРавенство("56 45 D3 0C 39 EA 90 18 D1 CA B7 7D 5F 37 0A 8C",Строка(Провайдер.ХешСумма)); юТест.ПроверитьРавенство(3139669858,Провайдер2.ХешСумма); -КонецПроцедуры \ No newline at end of file +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьХешиБезДанных() Экспорт + + ПустыеХеши = Новый Соответствие(); + ПустыеХеши.Вставить(ХешФункция.CRC32,"0"); + ПустыеХеши.Вставить(ХешФункция.MD5,"D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E"); + ПустыеХеши.Вставить(ХешФункция.SHA1,"DA 39 A3 EE 5E 6B 4B 0D 32 55 BF EF 95 60 18 90 AF D8 07 09"); + ПустыеХеши.Вставить(ХешФункция.SHA256,"E3 B0 C4 42 98 FC 1C 14 9A FB F4 C8 99 6F B9 24 27 AE 41 E4 64 9B 93 4C A4 95 99 1B 78 52 B8 55"); + ПустыеХеши.Вставить(ХешФункция.SHA384, + "38 B0 60 A7 51 AC 96 38 4C D9 32 7E B1 B1 E3 6A 21 FD B7 11 14 BE 07 43 4C 0C C7 BF 63 F6 E1 DA 27 4E DE BF E7 6F 65 FB D5 1A D2 F1 48 98 B9 5B"); + ПустыеХеши.Вставить(ХешФункция.SHA512, + "CF 83 E1 35 7E EF B8 BD F1 54 28 50 D6 6D 80 07 D6 20 E4 05 0B 57 15 DC 83 F4 A9 21 D3 6C E9 CE 47 D0 D1 3C 5D 85 F2 B0 FF 83 18 D2 87 7E EC 2F 63 B9 31 BD 47 41 7A 81 A5 38 32 7A F9 27 DA 3E"); + + Для Каждого ХФ из ХешФункция Цикл + Провайдер = Новый ХешированиеДанных(ХФ); + юТест.ПроверитьРавенство(ПустыеХеши[ХФ], Строка(Провайдер.ХешСумма)); + КонецЦикла + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьХешиПустойСтроки() Экспорт + + ПустыеХеши = Новый Соответствие(); + ПустыеХеши.Вставить(ХешФункция.CRC32,"0"); + ПустыеХеши.Вставить(ХешФункция.MD5,"D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E"); + ПустыеХеши.Вставить(ХешФункция.SHA1,"DA 39 A3 EE 5E 6B 4B 0D 32 55 BF EF 95 60 18 90 AF D8 07 09"); + ПустыеХеши.Вставить(ХешФункция.SHA256,"E3 B0 C4 42 98 FC 1C 14 9A FB F4 C8 99 6F B9 24 27 AE 41 E4 64 9B 93 4C A4 95 99 1B 78 52 B8 55"); + ПустыеХеши.Вставить(ХешФункция.SHA384, + "38 B0 60 A7 51 AC 96 38 4C D9 32 7E B1 B1 E3 6A 21 FD B7 11 14 BE 07 43 4C 0C C7 BF 63 F6 E1 DA 27 4E DE BF E7 6F 65 FB D5 1A D2 F1 48 98 B9 5B"); + ПустыеХеши.Вставить(ХешФункция.SHA512, + "CF 83 E1 35 7E EF B8 BD F1 54 28 50 D6 6D 80 07 D6 20 E4 05 0B 57 15 DC 83 F4 A9 21 D3 6C E9 CE 47 D0 D1 3C 5D 85 F2 B0 FF 83 18 D2 87 7E EC 2F 63 B9 31 BD 47 41 7A 81 A5 38 32 7A F9 27 DA 3E"); + + Для Каждого ХФ из ХешФункция Цикл + Провайдер = Новый ХешированиеДанных(ХФ); + Провайдер.Добавить(""); + юТест.ПроверитьРавенство(ПустыеХеши[ХФ], Строка(Провайдер.ХешСумма)); + КонецЦикла + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьХешиПустогоПотока() Экспорт + + ПустыеХеши = Новый Соответствие(); + ПустыеХеши.Вставить(ХешФункция.CRC32,"0"); + ПустыеХеши.Вставить(ХешФункция.MD5,"D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E"); + ПустыеХеши.Вставить(ХешФункция.SHA1,"DA 39 A3 EE 5E 6B 4B 0D 32 55 BF EF 95 60 18 90 AF D8 07 09"); + ПустыеХеши.Вставить(ХешФункция.SHA256,"E3 B0 C4 42 98 FC 1C 14 9A FB F4 C8 99 6F B9 24 27 AE 41 E4 64 9B 93 4C A4 95 99 1B 78 52 B8 55"); + ПустыеХеши.Вставить(ХешФункция.SHA384, + "38 B0 60 A7 51 AC 96 38 4C D9 32 7E B1 B1 E3 6A 21 FD B7 11 14 BE 07 43 4C 0C C7 BF 63 F6 E1 DA 27 4E DE BF E7 6F 65 FB D5 1A D2 F1 48 98 B9 5B"); + ПустыеХеши.Вставить(ХешФункция.SHA512, + "CF 83 E1 35 7E EF B8 BD F1 54 28 50 D6 6D 80 07 D6 20 E4 05 0B 57 15 DC 83 F4 A9 21 D3 6C E9 CE 47 D0 D1 3C 5D 85 F2 B0 FF 83 18 D2 87 7E EC 2F 63 B9 31 BD 47 41 7A 81 A5 38 32 7A F9 27 DA 3E"); + + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки(""); + Поток = Новый ПотокВПамяти(БДД); + + Для Каждого ХФ из ХешФункция Цикл + Провайдер = Новый ХешированиеДанных(ХФ); + Провайдер.Добавить(Поток); + юТест.ПроверитьРавенство(ПустыеХеши[ХФ], Строка(Провайдер.ХешСумма)); + Поток.Перейти(0); + КонецЦикла + +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьИнкрементальность() Экспорт + + Хеши123 = Новый Соответствие(); + Хеши123.Вставить(ХешФункция.CRC32,"2286445522"); + Хеши123.Вставить(ХешФункция.MD5,"20 2C B9 62 AC 59 07 5B 96 4B 07 15 2D 23 4B 70"); + Хеши123.Вставить(ХешФункция.SHA1,"40 BD 00 15 63 08 5F C3 51 65 32 9E A1 FF 5C 5E CB DB BE EF"); + Хеши123.Вставить(ХешФункция.SHA256,"A6 65 A4 59 20 42 2F 9D 41 7E 48 67 EF DC 4F B8 A0 4A 1F 3F FF 1F A0 7E 99 8E 86 F7 F7 A2 7A E3"); + Хеши123.Вставить(ХешФункция.SHA384, + "9A 0A 82 F0 C0 CF 31 47 0D 7A FF ED E3 40 6C C9 AA 84 10 67 15 20 B7 27 04 4E DA 15 B4 C2 55 32 A9 B5 CD 8A AF 9C EC 49 19 D7 62 55 B6 BF B0 0F"); + Хеши123.Вставить(ХешФункция.SHA512, + "3C 99 09 AF EC 25 35 4D 55 1D AE 21 59 0B B2 6E 38 D5 3F 21 73 B8 D3 DC 3E EE 4C 04 7E 7A B1 C1 EB 8B 85 10 3E 3B E7 BA 61 3B 31 BB 5C 9C 36 21 4D C9 F1 4A 42 FD 7A 2F DB 84 85 6B CA 5C 44 C2"); + + ХешиПолн = Новый Соответствие(); + ХешиПолн.Вставить(ХешФункция.CRC32,"158520161"); + ХешиПолн.Вставить(ХешФункция.MD5,"E1 0A DC 39 49 BA 59 AB BE 56 E0 57 F2 0F 88 3E"); + ХешиПолн.Вставить(ХешФункция.SHA1,"7C 4A 8D 09 CA 37 62 AF 61 E5 95 20 94 3D C2 64 94 F8 94 1B"); + ХешиПолн.Вставить(ХешФункция.SHA256,"8D 96 9E EF 6E CA D3 C2 9A 3A 62 92 80 E6 86 CF 0C 3F 5D 5A 86 AF F3 CA 12 02 0C 92 3A DC 6C 92"); + ХешиПолн.Вставить(ХешФункция.SHA384, + "0A 98 9E BC 4A 77 B5 6A 6E 2B B7 B1 9D 99 5D 18 5C E4 40 90 C1 3E 29 84 B7 EC C6 D4 46 D4 B6 1E A9 99 1B 76 A4 C2 F0 4B 1B 4D 24 48 41 44 94 54"); + ХешиПолн.Вставить(ХешФункция.SHA512, + "BA 32 53 87 6A ED 6B C2 2D 4A 6F F5 3D 84 06 C6 AD 86 41 95 ED 14 4A B5 C8 76 21 B6 C2 33 B5 48 BA EA E6 95 6D F3 46 EC 8C 17 F5 EA 10 F3 5E E3 CB C5 14 79 7E D7 DD D3 14 54 64 E2 A0 BA B4 13"); + + Для Каждого ХФ из ХешФункция Цикл + Провайдер = Новый ХешированиеДанных(ХФ); + Провайдер.Добавить("123"); + юТест.ПроверитьРавенство(Хеши123[ХФ], Строка(Провайдер.ХешСумма)); + Провайдер.Добавить("456"); + юТест.ПроверитьРавенство(ХешиПолн[ХФ], Строка(Провайдер.ХешСумма)); + КонецЦикла + +КонецПроцедуры + From 15b12eb84754292f18470e8f2ba2df694b53acb8 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 21 May 2026 18:05:39 +0400 Subject: [PATCH 43/65] =?UTF-8?q?Dispose=20=D1=85=D0=B5=D1=88-=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=B9=D0=B4=D0=B5=D1=80=D0=B0=20=D0=B8=20?= =?UTF-8?q?=D0=BC=D0=B5=D0=BB=D0=BE=D1=87=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.StandardLibrary/Hash/HashImpl.cs | 17 +++++++++-------- tests/datahashing-from-stream.os | 1 - 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/OneScript.StandardLibrary/Hash/HashImpl.cs b/src/OneScript.StandardLibrary/Hash/HashImpl.cs index ed8a89e62..8035e75bc 100644 --- a/src/OneScript.StandardLibrary/Hash/HashImpl.cs +++ b/src/OneScript.StandardLibrary/Hash/HashImpl.cs @@ -19,7 +19,7 @@ This Source Code Form is subject to the terms of the namespace OneScript.StandardLibrary.Hash { [ContextClass("ХешированиеДанных", "DataHashing")] - public class HashImpl : AutoContext + public class HashImpl : AutoContext, IDisposable { private const int BUFFER_SIZE = (1024 * 32); @@ -131,7 +131,7 @@ public void Append(BslValue toAdd, int count = 0) if (count <= 0) AppendStream(stream); else - AppendStream(stream, count); + AppendStream(stream, count); break; case BinaryDataContext binaryData: AppendStream(binaryData.GetStream()); @@ -147,11 +147,8 @@ public void AppendFile(string path) if (!File.Exists(path)) throw RuntimeException.InvalidArgumentType(); - using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - AppendStream(stream); - stream.Close(); - } + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + AppendStream(stream); } [ScriptConstructor(Name = "По указанной хеш-функции")] @@ -175,7 +172,11 @@ private static HashAlgorithmName GetAlgorithmName(HashFunctionEnum algo) HashFunctionEnum.SHA512 => HashAlgorithmName.SHA512, _ => throw RuntimeException.InvalidArgumentValue() }; + } + + public void Dispose() + { + _provider?.Dispose(); } - } } diff --git a/tests/datahashing-from-stream.os b/tests/datahashing-from-stream.os index ff11f6494..ba2e7b441 100644 --- a/tests/datahashing-from-stream.os +++ b/tests/datahashing-from-stream.os @@ -58,7 +58,6 @@ , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); Хеширование.Добавить(ФайловыйПоток, 12); - Сообщить(Хеширование.ХешСуммаСтрокой); юТест.ПроверитьРавенство("3bc6abfa41bb73c8bcc4c0f5f4d4db42f10f38295a0432afc592fe998465cb9a" , НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); From ce76a419525fb80232e22af6c91656cabc9b7dc7 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Fri, 22 May 2026 17:48:45 +0400 Subject: [PATCH 44/65] =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D1=80=D0=B0=20=D0=9A=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=BE=20(=D1=81=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Hash/HashImpl.cs | 42 +++++++++++++++---- tests/hash.os | 20 +++++++++ 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/OneScript.StandardLibrary/Hash/HashImpl.cs b/src/OneScript.StandardLibrary/Hash/HashImpl.cs index 8035e75bc..f933f44e9 100644 --- a/src/OneScript.StandardLibrary/Hash/HashImpl.cs +++ b/src/OneScript.StandardLibrary/Hash/HashImpl.cs @@ -13,6 +13,7 @@ This Source Code Form is subject to the terms of the using ScriptEngine.Machine.Contexts; using System; using System.IO; +using System.Runtime.Intrinsics.X86; using System.Security.Cryptography; using System.Text; @@ -104,6 +105,12 @@ private void AppendStream(Stream stream) private void AppendStream(Stream stream, int count) { + if (count <= 0) + { + AppendStream(stream); + return; + } + int bufSize = Math.Min(BUFFER_SIZE, count); var buffer = new byte[bufSize]; int toRead = count; @@ -119,23 +126,42 @@ private void AppendStream(Stream stream, int count) } [ContextMethod("Добавить", "Append")] - public void Append(BslValue toAdd, int count = 0) + public void Append(BslValue toAdd, BslValue count = null) { switch (toAdd) { case BslStringValue s: AppendData(Encoding.UTF8.GetBytes((string)s)); break; - case IStreamWrapper wrapper: - var stream = wrapper.GetUnderlyingStream(); - if (count <= 0) - AppendStream(stream); - else - AppendStream(stream, count); - break; case BinaryDataContext binaryData: AppendStream(binaryData.GetStream()); break; + + case IStreamWrapper wrapper: + var stream = wrapper.GetUnderlyingStream(); + if (count == null) + { + AppendStream(stream); + } + else + { + int cnt; + try + { + cnt = (int)count; + } + catch + { + if (count is BslStringValue) + throw RuntimeException.InvalidNthArgumentValue(2); + else + throw RuntimeException.InvalidNthArgumentType(2); + } + + AppendStream(stream, cnt); + } + break; + default: throw RuntimeException.InvalidNthArgumentType(1); } diff --git a/tests/hash.os b/tests/hash.os index b1c903a7d..9fe2ec101 100644 --- a/tests/hash.os +++ b/tests/hash.os @@ -18,6 +18,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьХешиПустойСтроки"); ВсеТесты.Добавить("ТестДолжен_ПроверитьХешиПустогоПотока"); ВсеТесты.Добавить("ТестДолжен_ПроверитьИнкрементальность"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИгнорированиеВторогоПараметра"); Возврат ВсеТесты; КонецФункции @@ -208,3 +209,22 @@ КонецПроцедуры +Процедура ТестДолжен_ПроверитьИгнорированиеВторогоПараметра() Экспорт + Хеш = 158520161; + + Провайдер = Новый ХешированиеДанных(ХешФункция.CRC32); + Провайдер.Добавить("123456",); + юТест.ПроверитьРавенство(Хеш, Провайдер.ХешСумма); + + Провайдер = Новый ХешированиеДанных(ХешФункция.CRC32); + Провайдер.Добавить("123456", 3); + юТест.ПроверитьРавенство(Хеш, Провайдер.ХешСумма); + + Провайдер = Новый ХешированиеДанных(ХешФункция.CRC32); + Провайдер.Добавить("123456", '0001-01-01'); + юТест.ПроверитьРавенство(Хеш, Провайдер.ХешСумма); + + Провайдер = Новый ХешированиеДанных(ХешФункция.CRC32); + Провайдер.Добавить("123456", Новый Массив); + юТест.ПроверитьРавенство(Хеш, Провайдер.ХешСумма); +КонецПроцедуры From d29b7ec69c5e3283cafd97232a1bef7be5691a31 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Fri, 22 May 2026 18:11:27 +0400 Subject: [PATCH 45/65] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Hash/HashImpl.cs | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/OneScript.StandardLibrary/Hash/HashImpl.cs b/src/OneScript.StandardLibrary/Hash/HashImpl.cs index f933f44e9..f8ab3ef0a 100644 --- a/src/OneScript.StandardLibrary/Hash/HashImpl.cs +++ b/src/OneScript.StandardLibrary/Hash/HashImpl.cs @@ -13,12 +13,15 @@ This Source Code Form is subject to the terms of the using ScriptEngine.Machine.Contexts; using System; using System.IO; -using System.Runtime.Intrinsics.X86; using System.Security.Cryptography; using System.Text; namespace OneScript.StandardLibrary.Hash { + /// + /// Реализует инкрементальный расчет хеш-суммы по добавленным данным. + /// Тип вычисляемого значения определяются типом хеш-функции. + /// [ContextClass("ХешированиеДанных", "DataHashing")] public class HashImpl : AutoContext, IDisposable { @@ -42,9 +45,19 @@ public HashImpl(IncrementalHash provider, HashFunctionEnum enumValue) AppendData(Array.Empty()); } + /// + /// Вид хеш-функции, определяющий способ вычисления хеш-суммы. + /// Только для чтения + /// + /// Перечисление ХешФункция [ContextProperty("ХешФункция", "HashFunction")] - public HashFunctionEnum Extension => _enumValue; - + public HashFunctionEnum HashFunction => _enumValue; + + /// + /// Текущее значение хеш-суммы. Только для чтения + /// + /// Для хеш-функции CRC32 - Число, для остальных - ДвоичныеДанные + /// [ContextProperty("ХешСумма", "HashSum")] public IValue Hash { @@ -55,8 +68,13 @@ public IValue Hash return new BinaryDataContext(_hash); } - } - + } + + /// + /// Нестандартное расширение! + /// Строковое представление текущего значения хеш-суммы. Только для чтения + /// + /// Для хеш-функции CRC32 - Число, для остальных - ДвоичныеДанные [ContextProperty("ХешСуммаСтрокой", "HashSumOfString")] public string HashString { @@ -123,8 +141,16 @@ private void AppendStream(Stream stream, int count) AppendData(buffer, read); toRead -= read; } - } - + } + + /// + /// Добавляет данные и обновляет хеш-сумму + /// + /// Источник данных. Строка, ДвоичныеДанные или Поток + /// Для источника данных типов Строка или ДвоичныеДанные - игнорируется. + /// Для источника данных типа Поток - Количество байтов, которые читаются из потока. + /// Если количество не задано, нулевое или отрицательное, то читаются все данные до конца потока. + /// [ContextMethod("Добавить", "Append")] public void Append(BslValue toAdd, BslValue count = null) { @@ -167,6 +193,10 @@ public void Append(BslValue toAdd, BslValue count = null) } } + /// + /// Добавляет двоичные данные из файла и обновляет хеш-сумму + /// + /// Имя файла, из которого читаются данные. Тип: Строка [ContextMethod("ДобавитьФайл", "AppendFile")] public void AppendFile(string path) { From 40bc661277c80a8e5c7dc1f0b60044f9dfcecbba Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Sat, 23 May 2026 20:29:26 +0300 Subject: [PATCH 46/65] =?UTF-8?q?fix(BinaryDataOptions,=20BslProcess):=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BB=D0=B8=D1=86=D0=B5=D0=BD=D0=B7=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D0=B8=D1=8F=20=D1=81=D1=82=D0=B8=D0=BB=D1=8E?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=B4=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Binary/BinaryDataOptions.cs | 14 +++++++------- src/ScriptEngine/BslProcess.cs | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataOptions.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataOptions.cs index d5f84438f..ec9d0ef26 100644 --- a/src/OneScript.StandardLibrary/Binary/BinaryDataOptions.cs +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataOptions.cs @@ -1,9 +1,9 @@ -// /*---------------------------------------------------------- -// This Source Code Form is subject to the terms of the -// Mozilla Public License, v.2.0. If a copy of the MPL -// was not distributed with this file, You can obtain one -// at http://mozilla.org/MPL/2.0/. -// ----------------------------------------------------------*/ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ using System.Globalization; using ScriptEngine; @@ -98,4 +98,4 @@ private static bool TryParseByteSize(string value, out long bytes) return true; } } -} \ No newline at end of file +} diff --git a/src/ScriptEngine/BslProcess.cs b/src/ScriptEngine/BslProcess.cs index 0ef93b311..b8fd599f5 100644 --- a/src/ScriptEngine/BslProcess.cs +++ b/src/ScriptEngine/BslProcess.cs @@ -1,6 +1,7 @@ /*---------------------------------------------------------- This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ From 3e487c9f01cfb64dae4dae5314fc9015b756b721 Mon Sep 17 00:00:00 2001 From: Ivan Karlo Date: Sun, 24 May 2026 19:36:37 +0300 Subject: [PATCH 47/65] =?UTF-8?q?refactor(BslProcess):=20=D0=A3=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BB=D0=BE=D0=B3?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=20=D1=83=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D0=BB=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Оптимизирована обработка уведомлений исполнителей, заменив циклы на методы массива для повышения читаемости. Удалены временные переменные и улучшена структура кода для лучшей ясности. --- src/ScriptEngine/BslProcess.cs | 35 +++++++++------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/src/ScriptEngine/BslProcess.cs b/src/ScriptEngine/BslProcess.cs index b8fd599f5..65b8c9de2 100644 --- a/src/ScriptEngine/BslProcess.cs +++ b/src/ScriptEngine/BslProcess.cs @@ -40,42 +40,25 @@ public BslProcess(int id, ExecutionContext context, IEnumerable startedExecutors = null; - - try + if (notifyExecutors) { - if (notifyExecutors) - { - startedExecutors = new List(_executorProviders.Length); - foreach (var executor in _executorProviders) - { - executor.BeforeProcessStart(this); - startedExecutors.Add(executor); - } - } + Array.ForEach(_executorProviders, e => e.BeforeProcessStart(this)); + } - _isRunning = true; + _isRunning = true; + try + { return _bslExecutorsByModule[module.GetType()](this, target, module, method, arguments); } finally { if (notifyExecutors) { - try - { - if (startedExecutors != null) - { - for (var i = startedExecutors.Count - 1; i >= 0; i--) - startedExecutors[i].AfterProcessExit(this); - } - } - finally - { - _isRunning = false; - } + Array.ForEach(_executorProviders, e => e.AfterProcessExit(this)); + _isRunning = false; } } } } -} +} \ No newline at end of file From a73172850733f61b3def3fb40bd2d22c7e3d7fb1 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Tue, 26 May 2026 11:25:32 +0300 Subject: [PATCH 48/65] =?UTF-8?q?=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BB=20IHostApplica?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IHostApplication.cs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/ScriptEngine.HostedScript/IHostApplication.cs b/src/ScriptEngine.HostedScript/IHostApplication.cs index c387888db..298862c66 100644 --- a/src/ScriptEngine.HostedScript/IHostApplication.cs +++ b/src/ScriptEngine.HostedScript/IHostApplication.cs @@ -9,11 +9,36 @@ This Source Code Form is subject to the terms of the namespace ScriptEngine.HostedScript { + /// + /// Интерфейс взаимодействия с приложением, которое хостит движок скрипта. + /// Хост-приложение может быть не консольным, а 3D-игрой или GUI-окном, поэтому движок не знает + /// куда выводить текст в каждом конкретном случае. Это определяет реализация IHostApplication. + /// public interface IHostApplication { - void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary); - void ShowExceptionInfo(Exception exc); + /// + /// Вывод пользовательских сообщений методом Сообщить. + /// + /// Строка + /// Статус сообщения (второй параметр метода Сообщить) + void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary); + + /// + /// Показать информацию о неперехваченном исключении. + /// + /// Неперехваченное исключение + void ShowExceptionInfo(Exception exc); + + /// + /// Ввод данных методом ВвестиСтроку. Хост должен предоставить средства ввода текста. + /// bool InputString(out string result, string prompt, int maxLen, bool multiline); + + /// + /// Аргументы командной строки, доступные скрипту в коллекции АргументыКоманднойСтроки. + /// Хост может при запуске получать собственные аргументы, которые не нужны или не должны быть видны в скриптах, + /// поэтому хост может отфильтровать строку запуска и предоставить скрипту только аргументы непосредственно скрипта. + /// string[] GetCommandLineArguments(); } } From 63ae1a11820bb313739e15f33049b1a0ae68ed75 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Tue, 26 May 2026 11:33:26 +0300 Subject: [PATCH 49/65] =?UTF-8?q?fixed=20#1678=20=D0=A1=D0=B2=D0=BE=D0=B9?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D0=BE=20=D0=A1=D0=B8=D1=81=D1=82=D0=B5=D0=BC?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=D0=98=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F.=D0=9F=D0=BE=D0=BB=D0=BD=D0=B0=D1=8F=D0=9A?= =?UTF-8?q?=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=BD=D0=B0=D1=8F=D0=A1=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.StandardLibrary/SystemEnvironmentContext.cs | 6 ++++++ tests/sysinfo.os | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs index 57a3f4c81..c8505a210 100644 --- a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs +++ b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs @@ -41,6 +41,12 @@ public class SystemEnvironmentContext : AutoContext /// [ContextProperty("ВерсияОС", "OSVersion")] public string OSVersion => Environment.OSVersion.VersionString; + + /// + /// Полная командная строка выполняемого приложения, включая исполняемый файл и все аргументы командной строки. + /// + [ContextProperty("ПолнаяКоманднаяСтрока", "FullCommandLine")] + public string FullCommandLine => Environment.CommandLine; /// /// Имя ядра ОС/ diff --git a/tests/sysinfo.os b/tests/sysinfo.os index 4180d7e05..0e63bf5a3 100644 --- a/tests/sysinfo.os +++ b/tests/sysinfo.os @@ -47,6 +47,7 @@ #КонецЕсли ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_ПрофильПользователя"); ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_ОбщийКаталогДанныхПриложения"); + ВсеТесты.Добавить("ТестДолжен_ПолучитьПолнуюКоманднуюСтроку"); Возврат ВсеТесты; КонецФункции @@ -194,4 +195,9 @@ Процедура ТестДолжен_ПолучитьПолучитьПутьПапки_ОбщийКаталогДанныхПриложения() Экспорт Си = Новый СистемнаяИнформация(); юТест.ПроверитьНеРавенство(СИ.ПолучитьПутьПапки(СпециальнаяПапка.ОбщийКаталогДанныхПриложения),""); +КонецПроцедуры + +Процедура ТестДолжен_ПолучитьПолнуюКоманднуюСтроку() Экспорт + Си = Новый СистемнаяИнформация(); + юТест.ПроверитьИстину(СтрНайти(Си.ПолнаяКоманднаяСтрока, "testrunner.os") > 1); КонецПроцедуры \ No newline at end of file From 342a7901f14abe3f9a78447aecd8abdf449a2f69 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Tue, 26 May 2026 16:01:46 +0400 Subject: [PATCH 50/65] =?UTF-8?q?fix=20#1675:=20=D0=B2=D0=BE=D0=B7=D0=B2?= =?UTF-8?q?=D1=80=D0=B0=D1=89=D0=B0=D0=B5=D0=BC=D1=8B=D0=B9=20=D1=82=D0=B8?= =?UTF-8?q?=D0=BF=20=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8=D1=82=D1=8C?= =?UTF-8?q?=D0=A1=D1=86=D0=B5=D0=BD=D0=B0=D1=80=D0=B8=D0=B9()=20=D0=B8=20?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BE?= =?UTF-8?q?=D1=88=D0=B8=D0=B1=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DynamicLoadingFunctions.cs | 73 +++++++++---------- tests/native-lib/test-native-use.os | 9 ++- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs index a1fc47fa3..12083f86b 100644 --- a/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs +++ b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs @@ -11,11 +11,13 @@ This Source Code Form is subject to the terms of the using OneScript.Exceptions; using OneScript.Execution; using OneScript.Language; +using OneScript.Localization; using OneScript.StandardLibrary.Collections; using OneScript.StandardLibrary.NativeApi; using ScriptEngine; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; +using System; namespace OneScript.StandardLibrary { @@ -48,19 +50,9 @@ public void AttachScript(IBslProcess process, string path, string typeName) { _engine.AttachedScriptsFactory.AttachByPath(compiler, path, typeName, process); } - catch (SyntaxErrorException e) - { - // обернем в RuntimeException - throw new RuntimeException( - Locale.NStr("ru = 'Ошибка компиляции подключаемого скрипта';en = 'Error compiling attached script'"), - e); - } - catch (Compilation.CompilerException e) - { - // обернем в RuntimeException - throw new RuntimeException( - Locale.NStr("ru = 'Ошибка компиляции подключаемого скрипта';en = 'Error compiling attached script'"), - e); + catch (Exception e) when (e is SyntaxErrorException || e is CompilerException) + { + throw ScriptCompilingException(e); } } @@ -97,20 +89,10 @@ public UserScriptContextInstance LoadScriptFromString(IBslProcess process, try { return _engine.AttachedScriptsFactory.LoadFromString(compiler, code, process, extData); - } - catch (SyntaxErrorException e) - { - // обернем в RuntimeException - throw new RuntimeException( - Locale.NStr("ru = 'Ошибка компиляции подключаемого скрипта';en = 'Error compiling attached script'"), - e); - } - catch (CompilerException e) - { - // обернем в RuntimeException - throw new RuntimeException( - Locale.NStr("ru = 'Ошибка компиляции подключаемого скрипта';en = 'Error compiling attached script'"), - e); + } + catch (Exception e) when (e is SyntaxErrorException || e is CompilerException) + { + throw ScriptCompilingException(e); } } } @@ -128,22 +110,28 @@ public UserScriptContextInstance LoadScriptFromString(IBslProcess process, /// // В коде скрипта somescript.os будет доступна глобальная переменная "ЧислоПи" /// Объект = ЗагрузитьСценарий("somescript.os", Контекст); [ContextMethod("ЗагрузитьСценарий", "LoadScript")] - public IRuntimeContextInstance LoadScript(IBslProcess process, string path, StructureImpl externalContext = null) - { + public UserScriptContextInstance LoadScript(IBslProcess process, string path, StructureImpl externalContext = null) + { var compiler = _engine.GetCompilerService(); - if(externalContext == null) - return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, process); - else - { - ExternalContextData extData = new ExternalContextData(); - - foreach (var item in externalContext) + try + { + if(externalContext == null) + return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, process); + else { - extData.Add(item.Key.ToString()!, item.Value); - } + ExternalContextData extData = new ExternalContextData(); - return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, extData, process); + foreach (var item in externalContext) + { + extData.Add(item.Key.ToString()!, item.Value); + } + return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, extData, process); + } + } + catch (Exception e) when (e is SyntaxErrorException || e is CompilerException) + { + throw ScriptCompilingException(e); } } @@ -189,5 +177,12 @@ public bool AttachAddIn(string dllPath, string name = "", NativeApiEnums type = return NativeApiFactory.Register(dllPath, name, _engine.TypeManager); } } + + private static RuntimeException ScriptCompilingException(Exception e) + { + return new RuntimeException(BilingualString.Localize( + "Ошибка компиляции подключаемого сценария:\n", + "Error compiling attached script:\n") + e.Message); + } } } \ No newline at end of file diff --git a/tests/native-lib/test-native-use.os b/tests/native-lib/test-native-use.os index 07488fe90..4ae1f8ea9 100644 --- a/tests/native-lib/test-native-use.os +++ b/tests/native-lib/test-native-use.os @@ -15,7 +15,7 @@ Тесты.Добавить("ТестДолжен_ПроверитьВызовМетодовСОшибочноПропущеннымиПараметрами"); Тесты.Добавить("ТестДолжен_ПроверитьВызовМетодовСЛишнимиПараметрами"); Тесты.Добавить("ТестДолжен_ПроверитьВызовКонструктораСПараметрамиПоУмолчанию"); - + Тесты.Добавить("ТестДолжен_ПроверитьПодключениеСценарияСОбъявленнойПеременной"); Возврат Тесты; КонецФункции @@ -92,3 +92,10 @@ Процедура ТестДолжен_ПроверитьВызовКонструктораСПараметрамиПоУмолчанию() Экспорт Сценарий = Новый ПараметрыКонструктораПоУмолчанию("Парам1"); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПодключениеСценарияСОбъявленнойПеременной() Экспорт + Перем Сценарий; + Сценарий = ЗагрузитьСценарий(ОбъединитьПути(ТекущийСценарий().Каталог, "Классы", "ТестовыйСценарий.os")); + юТест.ПроверитьРавенство(Сценарий.Поле1, 1); +КонецПроцедуры + From 538ff0487e0bf8c5e5297d503b29ed72d610d0af Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Tue, 26 May 2026 16:52:06 +0400 Subject: [PATCH 51/65] =?UTF-8?q?=D0=B2=D1=8B=D0=B4=D0=B5=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F=20?= =?UTF-8?q?=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=BD?= =?UTF-8?q?=D0=B5=D1=88=D0=BD=D0=B8=D1=85=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85;=20=D0=BE=D0=B4=D0=B8=D0=BD=D0=B0=D0=BA=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B8=D1=81=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DynamicLoadingFunctions.cs | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs index 12083f86b..0e32a4ef1 100644 --- a/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs +++ b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs @@ -72,29 +72,19 @@ public UserScriptContextInstance LoadScriptFromString(IBslProcess process, string code, StructureImpl externalContext = null) { - var compiler = _engine.GetCompilerService(); - if (externalContext == null) - { - return _engine.AttachedScriptsFactory.LoadFromString(compiler, code, process); - } - else - { - var extData = new ExternalContextData(); - - foreach (var item in externalContext) - { - extData.Add(item.Key.ToString()!, item.Value); - } + var compiler = _engine.GetCompilerService(); + try + { + if (externalContext == null) + return _engine.AttachedScriptsFactory.LoadFromString(compiler, code, process); - try - { - return _engine.AttachedScriptsFactory.LoadFromString(compiler, code, process, extData); - } - catch (Exception e) when (e is SyntaxErrorException || e is CompilerException) - { - throw ScriptCompilingException(e); - } - } + var extData = CreateContextData(externalContext); + return _engine.AttachedScriptsFactory.LoadFromString(compiler, code, process, extData); + } + catch (Exception e) when (e is SyntaxErrorException || e is CompilerException) + { + throw ScriptCompilingException(e); + } } /// @@ -117,24 +107,31 @@ public UserScriptContextInstance LoadScript(IBslProcess process, string path, St { if(externalContext == null) return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, process); - else - { - ExternalContextData extData = new ExternalContextData(); - - foreach (var item in externalContext) - { - extData.Add(item.Key.ToString()!, item.Value); - } - return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, extData, process); - } + var extData = CreateContextData(externalContext); + return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, extData, process); } catch (Exception e) when (e is SyntaxErrorException || e is CompilerException) { throw ScriptCompilingException(e); } - } - + } + + private static ExternalContextData CreateContextData(StructureImpl externalContext) + { + if (externalContext == null) + return null; + + ExternalContextData extData = new ExternalContextData(); + + foreach (var item in externalContext) + { + extData.Add(item.Key.ToString()!, item.Value); + } + + return extData; + } + /// /// Подключает внешнюю сборку среды .NET (*.dll) и регистрирует классы 1Script, объявленные в этой сборке. /// Публичные классы, отмеченные в dll атрибутом ContextClass, будут импортированы аналогично встроенным классам 1Script. @@ -182,7 +179,7 @@ private static RuntimeException ScriptCompilingException(Exception e) { return new RuntimeException(BilingualString.Localize( "Ошибка компиляции подключаемого сценария:\n", - "Error compiling attached script:\n") + e.Message); + "Error compiling attached script:\n") + e.Message, e); } } } \ No newline at end of file From 9d10d7d1037a1e8df49d922c0734ad8ae4f8e589 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Tue, 26 May 2026 17:00:32 +0400 Subject: [PATCH 52/65] =?UTF-8?q?=D0=BE=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B2=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DynamicLoadingFunctions.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs index 0e32a4ef1..c0a902c34 100644 --- a/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs +++ b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs @@ -36,7 +36,7 @@ public DynamicLoadingFunctions(ScriptingEngine engine) /// /// Подключает сторонний файл сценария к текущей системе типов. - /// Подключенный сценарий выступает, как самостоятельный класс, создаваемый оператором Новый + /// Подключенный сценарий выступает как самостоятельный класс, создаваемый оператором Новый /// /// Путь к подключаемому сценарию /// Имя типа, которое будет иметь новый класс. Экземпляры класса создаются оператором Новый. @@ -58,7 +58,7 @@ public void AttachScript(IBslProcess process, string path, string typeName) /// /// Создает экземпляр объекта на основании стороннего файла сценария. - /// Загруженный сценарий возвращается, как самостоятельный объект. + /// Загруженный сценарий возвращается как самостоятельный объект. /// Экспортные свойства и методы скрипта доступны для вызова. /// /// @@ -89,7 +89,7 @@ public UserScriptContextInstance LoadScriptFromString(IBslProcess process, /// /// Создает экземпляр объекта на основании стороннего файла сценария. - /// Загруженный сценарий возвращается, как самостоятельный объект. + /// Загруженный сценарий возвращается как самостоятельный объект. /// Экспортные свойства и методы скрипта доступны для вызова. /// /// Путь к подключаемому сценарию @@ -137,7 +137,7 @@ private static ExternalContextData CreateContextData(StructureImpl externalConte /// Публичные классы, отмеченные в dll атрибутом ContextClass, будут импортированы аналогично встроенным классам 1Script. /// Загружаемая сборка должна ссылаться на сборку ScriptEngine.dll /// - /// Также подключает вншение компонеты, разработанные по технологии Native API, + /// Также подключает внешние компоненты, разработанные по технологии Native API, /// поставляемые в виде отдельных DLL или упакованные в ZIP-архив. /// /// @@ -166,7 +166,8 @@ public bool AttachAddIn(string dllPath, string name = "", NativeApiEnums type = _engine.AttachExternalAssembly(assembly); return true; } - else { + else + { if (!Utils.IsValidIdentifier(name)) { throw RuntimeException.InvalidArgumentValue(name); From de17102c03ec8f54b03a5b1fd8e79513d5a6ad8d Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Wed, 27 May 2026 00:17:51 +0400 Subject: [PATCH 53/65] =?UTF-8?q?fix=20#1685:=20=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=B2=20=D1=82=D0=B5=D0=BB=D0=B5=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D1=83=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SyntaxAnalysis/DefaultBslParser.cs | 12 +++++++++--- .../SyntaxAnalysis/LocalizedErrors.cs | 4 ++++ .../OneScript.Language.Tests/ParserTests.cs | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index ad0839376..94c19feed 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -516,7 +516,7 @@ private bool BuildDefaultParameterValue(NonTerminalNode param, NodeKind nodeKind private void BuildModuleBody() { - if (!_lexer.Iterator.MoveToContent()) + if (_lastExtractedLexem.Token == Token.EndOfText) return; var moduleBody = new NonTerminalNode(NodeKind.ModuleBody, _lastExtractedLexem); @@ -746,7 +746,11 @@ private void BuildComplexStructureStatement() } else { - AddError(LocalizedErrors.TokenExpected(_tokenStack.Peek())); + var expected = _tokenStack.Peek(); + if (expected.Length == 1 && expected[0] == Token.EndOfText) + AddError(LocalizedErrors.UnexpectedKeyword(_lastExtractedLexem.Token)); + else + AddError(LocalizedErrors.TokenExpected(expected)); } break; } @@ -1552,7 +1556,9 @@ private void SkipToNextStatement(Token[] additionalStops = null) private void AddError(CodeError err, bool doFastForward = true) { err.Position = _lexer.GetErrorPosition(); - err.Position.ColumnNumber -= _lastExtractedLexem.Content?.Length ?? 1; + var adjustColumn = _lastExtractedLexem.Content?.Length ?? 1; + if (err.Position.ColumnNumber >= adjustColumn) + err.Position.ColumnNumber -= adjustColumn; ErrorSink.AddError(err); if (doFastForward) diff --git a/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs b/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs index a6e23d267..864bd4ede 100644 --- a/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs +++ b/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs @@ -80,6 +80,10 @@ public static CodeError AwaitMustBeInAsyncMethod() => Create( public static CodeError NumberExpected() => Create("Ожидается числовая константа", "Numeric constant expected"); + public static CodeError UnexpectedKeyword(Token unexpected) => Create( + $"Неожиданное ключевое слово: {LanguageDef.GetTokenName(unexpected)}", + $"Unexpected keyword: {LanguageDef.GetTokenAlias(unexpected)}"); + public static CodeError UnexpectedEof() => Create("Неожиданный конец модуля", "Unexpected end of text"); diff --git a/src/Tests/OneScript.Language.Tests/ParserTests.cs b/src/Tests/OneScript.Language.Tests/ParserTests.cs index 92d498423..20381b820 100644 --- a/src/Tests/OneScript.Language.Tests/ParserTests.cs +++ b/src/Tests/OneScript.Language.Tests/ParserTests.cs @@ -1609,8 +1609,24 @@ public void Async_And_Await_Are_Ordinary_Identifiers_In_NonAsync_Scope() var asyncNode = validator.NextChild().Is(NodeKind.Assignment) .NextChildIs(NodeKind.Identifier) .ChildItself().Value.Should().Be("Асинх"); + } + + [Fact] + public void Throws_When_Error_In_Module_Body() + { + var code = @"Function F() + Return 0 + EndFunction + EndFunction"; + + var parser = PrepareParser(code); + parser.ParseStatefulModule(); + + parser.Errors.Should().NotBeEmpty("Expression syntax error"); + parser.Errors.First().ErrorId.Should().Be("UnexpectedKeyword"); } + private static void CatchParsingError(string code) { var parser = PrepareParser(code); From 7cb7250b528b28ebd6ddbff691098f28bf5179d7 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Wed, 27 May 2026 12:41:17 +0400 Subject: [PATCH 54/65] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=B0=D0=B4=D0=B0=D1=8E=D1=89=D0=B8?= =?UTF-8?q?=D0=B9=20=D1=82=D0=B5=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Tests/DocumenterTests/XmlDocConversionTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tests/DocumenterTests/XmlDocConversionTest.cs b/src/Tests/DocumenterTests/XmlDocConversionTest.cs index fe57cbbc6..19534c9c0 100644 --- a/src/Tests/DocumenterTests/XmlDocConversionTest.cs +++ b/src/Tests/DocumenterTests/XmlDocConversionTest.cs @@ -27,7 +27,7 @@ public void TestConversionOfTextBlock() var exampleNode = doc.Root.Elements("example").First(); var converter = new XmlDocConverter(Mock.Of()); - var text = converter.ConvertTextBlock(exampleNode); + var text = converter.ConvertTextBlock(exampleNode).ReplaceLineEndings("\n"); var expected = @"Для Каждого Переменная Из ПеременныеСреды() Цикл Сообщить(Переменная.Ключ + "" = "" + Переменная.Значение); From fb90e9ce9a8eefdacc1f80c7cd5c5c6f500a26b5 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Tue, 26 May 2026 22:28:39 +0400 Subject: [PATCH 55/65] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B2=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D1=8E=20C#=20=D0=B2=20=D1=81=D0=BE=D0=BE=D1=82=D0=B2?= =?UTF-8?q?=D0=B5=D1=82=D1=81=D1=82=D0=B2=D0=B8=D0=B8=20=D1=81=20=D1=84?= =?UTF-8?q?=D1=80=D0=B5=D0=B9=D0=BC=D0=B2=D0=BE=D1=80=D0=BA=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/oscommon.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oscommon.targets b/src/oscommon.targets index 019db5bd6..39d0e4266 100644 --- a/src/oscommon.targets +++ b/src/oscommon.targets @@ -6,7 +6,7 @@ 0 Release x86 - 8.0 + default net8.0 $(NoWarn);CS1591 False From a2d15bd9e3f8a113169d9bba4928641f1b5897fd Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Wed, 27 May 2026 13:10:04 +0400 Subject: [PATCH 56/65] =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20?= =?UTF-8?q?=D1=8F=D0=B2=D0=BD=D1=8B=D0=B5=20=D1=83=D0=BA=D0=B0=D0=B7=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8;=20?= =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=BE=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=D0=BE=20cursor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/langversion.mdc | 3 ++- src/OneScript.Web.Server/OneScript.Web.Server.csproj | 1 - src/OneScriptDocumenter/OneScriptDocumenter.csproj | 1 - src/Tests/DocumenterTests/DocumenterTests.csproj | 1 - src/Tests/OneScript.Core.Tests/OneScript.Core.Tests.csproj | 1 - .../OneScript.Dynamic.Tests/OneScript.Dynamic.Tests.csproj | 1 - 6 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.cursor/rules/langversion.mdc b/.cursor/rules/langversion.mdc index d91672a00..448682791 100644 --- a/.cursor/rules/langversion.mdc +++ b/.cursor/rules/langversion.mdc @@ -2,4 +2,5 @@ alwaysApply: true --- -Проект использует .net 8.0 и LangVersion 8, поэтому при генерации кода не используй языковые фичи C#, которые отсутствуют в C# 8. +Проект VSCode.DebugAdapter использует net framework 4.8, поэтому при генерации кода для него не используй языковые фичи C#, которые отсутствуют в C# 7.3. +Остальные проекты используют .net 8, можно применять C# 12. diff --git a/src/OneScript.Web.Server/OneScript.Web.Server.csproj b/src/OneScript.Web.Server/OneScript.Web.Server.csproj index 721b45cc9..05ced63af 100644 --- a/src/OneScript.Web.Server/OneScript.Web.Server.csproj +++ b/src/OneScript.Web.Server/OneScript.Web.Server.csproj @@ -12,7 +12,6 @@ OneScript.Web.Server OneScript web server implementation Akpaev E.A. - 10.0 diff --git a/src/OneScriptDocumenter/OneScriptDocumenter.csproj b/src/OneScriptDocumenter/OneScriptDocumenter.csproj index 63368fda1..f5c3e7d00 100644 --- a/src/OneScriptDocumenter/OneScriptDocumenter.csproj +++ b/src/OneScriptDocumenter/OneScriptDocumenter.csproj @@ -9,7 +9,6 @@ OneScript Libraries Documenter Debug;Release;LinuxDebug AnyCPU - 8 diff --git a/src/Tests/DocumenterTests/DocumenterTests.csproj b/src/Tests/DocumenterTests/DocumenterTests.csproj index b039de817..f1f5ca747 100644 --- a/src/Tests/DocumenterTests/DocumenterTests.csproj +++ b/src/Tests/DocumenterTests/DocumenterTests.csproj @@ -4,7 +4,6 @@ $(TargetFrameworkVersion) false true - 8 diff --git a/src/Tests/OneScript.Core.Tests/OneScript.Core.Tests.csproj b/src/Tests/OneScript.Core.Tests/OneScript.Core.Tests.csproj index 6cc2978cc..f1708dd92 100644 --- a/src/Tests/OneScript.Core.Tests/OneScript.Core.Tests.csproj +++ b/src/Tests/OneScript.Core.Tests/OneScript.Core.Tests.csproj @@ -6,7 +6,6 @@ false Debug;Release;LinuxDebug - 10.0 AnyCPU diff --git a/src/Tests/OneScript.Dynamic.Tests/OneScript.Dynamic.Tests.csproj b/src/Tests/OneScript.Dynamic.Tests/OneScript.Dynamic.Tests.csproj index 46f92b10e..57788dfea 100644 --- a/src/Tests/OneScript.Dynamic.Tests/OneScript.Dynamic.Tests.csproj +++ b/src/Tests/OneScript.Dynamic.Tests/OneScript.Dynamic.Tests.csproj @@ -6,7 +6,6 @@ false Debug;Release;LinuxDebug - 10.0 AnyCPU From a66bafedefcc0366f9eb606ecd98b2042430dcb4 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 28 May 2026 12:35:48 +0300 Subject: [PATCH 57/65] =?UTF-8?q?=D0=A1=D0=BA=D0=B8=D0=BB=D0=BB=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20release-notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/skills/release-notes/SKILL.md | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .cursor/skills/release-notes/SKILL.md diff --git a/.cursor/skills/release-notes/SKILL.md b/.cursor/skills/release-notes/SKILL.md new file mode 100644 index 000000000..7f495ac89 --- /dev/null +++ b/.cursor/skills/release-notes/SKILL.md @@ -0,0 +1,49 @@ +--- +name: release-notes +description: This skill creates release notes for new release by reading git commit log. Use it only when explicitly specified +--- + +# Release notes + +Когда нужно выпустить новый релиз, в репозитории нужно изменить файл install/release-notes.md (путь относительно корня репозитория) + +Для того, чтобы собрать данные, нужно использовать git log и вызвать команду + +```bat +set LC_ALL=C.UTF-8 +set TAGNAME=%1 + +git --no-pager log --pretty="* %%s" --no-merges %TAGNAME%..HEAD +``` + +где переменная TAGNAME должна быть git-тэгом предыдущего релиза. Результатом выполнения команды будут тексты коммитов, между TAGNAME и текущим состоянием ветки. + +Ты должен запросить у пользователя тег предыдущей версии, либо пользователь тебе сообщит его в промпте сразу. + +Далее, ты должен прочитать тексты коммитов, в них могут встречаться ссылки на github issues в виде указания номера задачи через решетку, например вот так: + +`fixes #1234 bla-bla-bla` + +Текст коммита также может не содержать упоминания задачи, или быть вовсе техническим/промежуточным коммитом, который в release notes попадать не должен, т.к. не описывает конечное изменение. + +Твоя задача и цель - взять тексты коммитов и вывести из них изменения в версии, которые будут включены в файл install/release-notes.md. + +Те коммиты, которые содержат ссылки на github - пойти на github в соответствующую задачу в репозитории https://github.com/EvilBeaver/OneScript/issues/<НомерЗадачи> и прочитать ее суть. + +Те коммиты, которые ссылок на гитхаб не содержат, ты должен просто прочитать и решить - стоит их включать в release-notes или нет, содержат ли они описание конкретной сделанной функциональности, или это промежуточный, технический и не требующий публикации коммит. + +Далее, ты должен сделать итоговый дайджест того, что было сделано в версии и записать это в файл install/release-notes.md. Каждая задача это отдельный bullet-point, причем ссылки на github должны быть сделаны в markdown реальными кликабельными ссылками. + +## Формат файла release-notes + +В начале записывается заголовок "Версия <НомерВерсии>. Какая сейчас конкретно версия ты должен посмотреть в файле Jenkinsfile в параметре VersionPrefix и взять его. + +Далее идет перечень сделанных задач, как описано выше. +Задачи разбиваются на две группы: исправление ошибок и новые возможности. Соответственно в release notes добавляются два раздела уровня "заголовок 2" для "Новые возможности" и "Исправление ошибок". Если задач по какой-то категории нет (например, были только исправления, а новых фич не было), то соответствующий раздел не добавляется в файл совсем. + +## Instructions + +1. Определи текущую выпускаемую версию, прочитав файл Jenkinsfile и его параметр VersionPrefix в самом начале +2. Определи тег предыдущей версии. Для этого запроси его у пользователя, если тег не был сразу задан в промпте +3. Выполни команду gitlog, как показано выше, для получения истории коммитов между версиями +4. Составь дайджест изменений и запиши их в install/release-notes.md From d617881dc621ce4bf0d16b4c5b52a24515d184ab Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 28 May 2026 12:36:08 +0300 Subject: [PATCH 58/65] =?UTF-8?q?release-notes=20=D0=B2=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D0=B8=202.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/release-notes.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/install/release-notes.md b/install/release-notes.md index 8eeb7d412..4a7fd6dd2 100644 --- a/install/release-notes.md +++ b/install/release-notes.md @@ -1,15 +1,22 @@ -# Версия 2.0.1 +# Версия 2.1.0 + +## Новые возможности + +* Реализован конфигурационный параметр `binaryData.inMemoryMaxBytes` для настройки порога хранения двоичных данных в памяти или на диске [#1667](https://github.com/EvilBeaver/OneScript/issues/1667) +* Добавлено свойство `СистемнаяИнформация.ПолнаяКоманднаяСтрока` для получения полной строки запуска процесса: [#1678](https://github.com/EvilBeaver/OneScript/issues/1678). +* Реализована поддержка оператора `Перейти` / `Goto` и меток: [#1468](https://github.com/EvilBeaver/OneScript/issues/1468), [#1329](https://github.com/EvilBeaver/OneScript/issues/1329), [#6](https://github.com/EvilBeaver/OneScript/issues/6). +* В метод `Процесс.Завершить()` добавлен параметр завершения дочерних процессов [#1660](https://github.com/EvilBeaver/OneScript/pull/1660). +* В `ЧтениеJSON` добавлен метод `ОткрытьПоток()`, чтобы объект мог работать с потоками [#1679](https://github.com/EvilBeaver/OneScript/issues/1679). ## Исправление ошибок -* #1646 Ошибка метода ЗаполнитьЗначенияСвойств на ФиксированнойСтруктуре -* #1647 Исправлена вставка ключа Null в соответствие -* #1649 Исправлена обработка символов '\0' в каталогах WebDAV -* #1654 В методе можно было объявлять параметры, не разделяя их запятой -* #1652 Исправлена обработка аннотаций для списка переменных, объявленных через запятую -* #1657 Исправлен приоритет активации источников в конфиге +* Исправлена работа `ЗагрузитьСценарий()` в native-режиме: корректный возвращаемый тип и обработка ошибок компиляции скрипта: [#1675](https://github.com/EvilBeaver/OneScript/issues/1675). +* Исправлена native-компиляция вызовов методов с дополнительным параметром процесса (включая кейс `ТаблицаЗначений.Сортировать()`): [#1673](https://github.com/EvilBeaver/OneScript/issues/1673). +* Исправлено поведение `ХешированиеДанных`: инкрементальное хеширование, освобождение файлов и совместимость обработки параметра `Количество`: [#1683](https://github.com/EvilBeaver/OneScript/issues/1683). +* Исправлено чтение JSON-строк, содержащих ISO-дату: они больше не преобразуются ошибочно в `Дата`: [#1681](https://github.com/EvilBeaver/OneScript/issues/1681). +* Исправлена ошибка компилятора при некорректном "висячем" окончании синтаксического блока (улучшена диагностика ошибок): [#1685](https://github.com/EvilBeaver/OneScript/issues/1685). +* Метод `ТаблицаЗначений.Найти` теперь использует индекс таблицы значений. Ранее индекс использовался только методом `НайтиСтроки` [#1661](https://github.com/EvilBeaver/OneScript/issues/1661) -## Рефакторинг и оптимизация +## Прочее -* В парсере применен приоритет операторов вместо рекурсивного спуска (улучшена скорость компиляции) -* Загрузчик библиотек теперь разделяет ситуацию ненайденных библиотек и тех, которые не удается загрузить (улучшена диагностика ненайденных библиотек) +* Обновлена документация проекта и разделы установки под актуальную платформу .NET 8. \ No newline at end of file From e8b6ccf8609fff45e6cc168aaa7e32f9e13cad48 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Sat, 30 May 2026 11:23:36 +0200 Subject: [PATCH 59/65] =?UTF-8?q?fix(docs):=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BE=D0=BF=D0=B5=D1=87=D0=B0?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20=D0=B2=20xml-doc=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D1=8F=D1=85=20Standa?= =?UTF-8?q?rdLibrary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SystemEnvironmentContext.KernelName: «Имя ядра ОС/» → «Имя ядра ОС» (лишний завершающий слеш). - XSIdentityConstraintCategory.Unique: «по опредению» → «по определению». Опечатки попадают в синтакс-помощник через OneScriptDocumenter. Co-Authored-By: Claude Opus 4.8 --- src/OneScript.StandardLibrary/SystemEnvironmentContext.cs | 2 +- .../XMLSchema/Enumerations/XSIdentityConstraintCategory.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs index c8505a210..86e7e1a3f 100644 --- a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs +++ b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs @@ -49,7 +49,7 @@ public class SystemEnvironmentContext : AutoContext public string FullCommandLine => Environment.CommandLine; /// - /// Имя ядра ОС/ + /// Имя ядра ОС /// [ContextProperty("ИмяЯдра", "KernelName")] public string KernelName => _osKernelName; // позволит различать linux/mac/hp-ux/sunos/... diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs index 0a78b22e1..24019002a 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs @@ -33,7 +33,7 @@ public enum XSIdentityConstraintCategory KeyRef, /// - /// Ограничение идентичности по опредению уникальности + /// Ограничение идентичности по определению уникальности /// /// [EnumValue("Уникальность", "Unique")] From 4bc8829ae4deb8a0808ae62020dd5fcd8d803b4d Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Sat, 30 May 2026 11:47:53 +0200 Subject: [PATCH 60/65] =?UTF-8?q?fix(docs):=20=D0=BE=D0=BF=D0=B5=D1=87?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B0=20=D0=B2=20xml-doc=20ConsoleContext?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit «низкоуровнего» → «низкоуровневого». Co-Authored-By: Claude Opus 4.8 --- src/OneScript.StandardLibrary/Text/ConsoleContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OneScript.StandardLibrary/Text/ConsoleContext.cs b/src/OneScript.StandardLibrary/Text/ConsoleContext.cs index ccc2dca56..2ebe5f7c3 100644 --- a/src/OneScript.StandardLibrary/Text/ConsoleContext.cs +++ b/src/OneScript.StandardLibrary/Text/ConsoleContext.cs @@ -19,7 +19,7 @@ namespace OneScript.StandardLibrary.Text { /// /// Класс представляет собой инструмент доступа к системной консоли. - /// Предназначен для низкоуровнего манипулирования выводом в консоль. + /// Предназначен для низкоуровневого манипулирования выводом в консоль. /// /// Поддерживается регистрация обработчика для нажатия Ctrl+C. /// Обработчик регистрируется для события с именем CancelKeyPressed. Стоит учитывать, что обработчик вызывается From 4f062593990fcafd15b0ece64abeb430073e0379 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 31 May 2026 15:32:09 +0400 Subject: [PATCH 61/65] =?UTF-8?q?=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B2=20=D1=81=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D0=B8=20?= =?UTF-8?q?=D1=81=20=D0=B4=D0=B0=D1=82=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Native/Runtime/DynamicOperations.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/OneScript.Native/Runtime/DynamicOperations.cs b/src/OneScript.Native/Runtime/DynamicOperations.cs index 4cfc523a0..2307a2570 100644 --- a/src/OneScript.Native/Runtime/DynamicOperations.cs +++ b/src/OneScript.Native/Runtime/DynamicOperations.cs @@ -28,10 +28,8 @@ public static BslValue Add(BslValue left, BslValue right) if (left is BslStringValue str) return BslStringValue.Create(str + right); - if (left is BslDateValue bslDate && right is BslNumericValue num) - { - return BslDateValue.Create(bslDate - (decimal) num); - } + if (left is BslDateValue bslDate) + return BslDateValue.Create(bslDate + (decimal)right); var dLeft = (decimal)left; var dRight = (decimal)right; From 7f3a4dc4162270b149c313691f90c008eb1e4c02 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 31 May 2026 15:41:25 +0400 Subject: [PATCH 62/65] =?UTF-8?q?=D0=B2=D1=8B=D1=87=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=B7=20=D0=B4=D0=B0=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=20=D0=BF=D1=80=D0=B8=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=D0=BC=20=D1=82=D0=B8=D0=BF=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/DynamicOperations.cs | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/OneScript.Native/Runtime/DynamicOperations.cs b/src/OneScript.Native/Runtime/DynamicOperations.cs index 2307a2570..a9966086f 100644 --- a/src/OneScript.Native/Runtime/DynamicOperations.cs +++ b/src/OneScript.Native/Runtime/DynamicOperations.cs @@ -33,40 +33,25 @@ public static BslValue Add(BslValue left, BslValue right) var dLeft = (decimal)left; var dRight = (decimal)right; - return BslNumericValue.Create(dLeft + dRight); + return BslNumericValue.Create(dLeft + dRight); // or throw ConvertToNumberException(); } public static BslValue Subtract(BslValue left, BslValue right) { if (left is BslNumericValue num) + return BslNumericValue.Create(num - (decimal)right); + + if (left is BslDateValue date) { - var result = num - (decimal)right; - return BslNumericValue.Create(result); - } - else if (left is BslDateValue date) - { - switch (right) - { - case BslNumericValue numRight: - { - var result = date - numRight; - return BslDateValue.Create(result); - } - case BslDateValue dateRight: - { - var result = date - dateRight; - return BslNumericValue.Create(result); - } - } - } - else - { - var dLeft = (decimal)left; - var dRight = (decimal)right; - return BslNumericValue.Create(dLeft - dRight); + if (right is BslDateValue dateRight) + return BslNumericValue.Create(date - dateRight); + + return BslDateValue.Create(date - (decimal)right); } - throw BslExceptions.ConvertToNumberException(); + var dLeft = (decimal)left; + var dRight = (decimal)right; + return BslNumericValue.Create(dLeft - dRight); // or throw ConvertToNumberException(); } public static bool ToBoolean(BslValue value) From 09a504474003e4a0b5644ec5410b723b10b72e53 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 31 May 2026 16:05:39 +0400 Subject: [PATCH 63/65] =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BC=D0=B5=D0=BB=D0=BA=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/DynamicOperations.cs | 110 ++++++++---------- 1 file changed, 46 insertions(+), 64 deletions(-) diff --git a/src/OneScript.Native/Runtime/DynamicOperations.cs b/src/OneScript.Native/Runtime/DynamicOperations.cs index a9966086f..13289a4c2 100644 --- a/src/OneScript.Native/Runtime/DynamicOperations.cs +++ b/src/OneScript.Native/Runtime/DynamicOperations.cs @@ -52,27 +52,15 @@ public static BslValue Subtract(BslValue left, BslValue right) var dLeft = (decimal)left; var dRight = (decimal)right; return BslNumericValue.Create(dLeft - dRight); // or throw ConvertToNumberException(); - } - - public static bool ToBoolean(BslValue value) - { - return (bool)value; - } - - public static decimal ToNumber(BslValue value) - { - return (decimal)value; - } - - public static DateTime ToDate(BslValue value) - { - return (DateTime)value; - } - - public static string ToString(BslValue value) - { - return value.ToString(); - } + } + + public static bool ToBoolean(BslValue value) => (bool)value; + + public static decimal ToNumber(BslValue value) => (decimal)value; + + public static DateTime ToDate(BslValue value) => (DateTime)value; + + public static string ToString(BslValue value) => value.ToString(); // FIXME: тут не должно быть Null, но из-за несовершенства мира они тут бывают. Когда задолбает - надо починить и убрать отсюда проверки на null public static bool Equality(BslValue left, BslValue right) @@ -99,32 +87,32 @@ public static int Comparison(BslValue left, BslValue right) } public static BslValue WrapClrObjectToValue(object value) - { + { return value switch { - null => BslUndefinedValue.Instance, - string s => BslStringValue.Create(s), - decimal d => BslNumericValue.Create(d), - - int n => BslNumericValue.Create(n), - uint n => BslNumericValue.Create(n), - short n => BslNumericValue.Create(n), - ushort n => BslNumericValue.Create(n), - byte n => BslNumericValue.Create(n), - sbyte n => BslNumericValue.Create(n), - long l => BslNumericValue.Create(l), - ulong l => BslNumericValue.Create(l), - - double dbl => BslNumericValue.Create((decimal) dbl), - bool boolean => BslBooleanValue.Create(boolean), - DateTime date => BslDateValue.Create(date), - BslValue bslValue => bslValue, - _ => throw new TypeConversionException(new BilingualString( - $"Невозможно преобразовать {value.GetType()} в тип {nameof(BslValue)}", - $"Can't Convert {value.GetType()} to {nameof(BslValue)}")) - }; - } - + null => BslUndefinedValue.Instance, + string s => BslStringValue.Create(s), + decimal d => BslNumericValue.Create(d), + + int n => BslNumericValue.Create(n), + uint n => BslNumericValue.Create(n), + short n => BslNumericValue.Create(n), + ushort n => BslNumericValue.Create(n), + byte n => BslNumericValue.Create(n), + sbyte n => BslNumericValue.Create(n), + long l => BslNumericValue.Create(l), + ulong l => BslNumericValue.Create(l), + + double dbl => BslNumericValue.Create((decimal) dbl), + bool boolean => BslBooleanValue.Create(boolean), + DateTime date => BslDateValue.Create(date), + BslValue bslValue => bslValue, + _ => throw new TypeConversionException(new BilingualString( + $"Невозможно преобразовать {value.GetType()} в тип {nameof(BslValue)}", + $"Can't Convert {value.GetType()} to {nameof(BslValue)}")) + }; + } + public static BslValue ConstructorCall(ITypeManager typeManager, IServiceContainer services, string typeName, IBslProcess process, BslValue[] args) { var type = typeManager.GetTypeByName(typeName); @@ -138,39 +126,33 @@ public static BslValue ConstructorCall(ITypeManager typeManager, IServiceContain }; return (BslValue) factory.Activate(context, args.Cast().ToArray()); - } - - // TODO: Сделать прямой маппинг на статические фабрики-методы, а не через Factory.Activate - public static T StrictConstructorCall(ITypeManager typeManager, IServiceContainer services, string typeName, IBslProcess process, BslValue[] args) + } + + // TODO: Сделать прямой маппинг на статические фабрики-методы, а не через Factory.Activate + public static T StrictConstructorCall(ITypeManager typeManager, IServiceContainer services, + string typeName, IBslProcess process, BslValue[] args) where T : BslValue - { - return (T) ConstructorCall(typeManager, services, typeName, process, args); - } + => (T)ConstructorCall(typeManager, services, typeName, process, args); public static BslObjectValue GetExceptionInfo(IExceptionInfoFactory factory, Exception e) - { - return factory.GetExceptionInfo(e); - } + => factory.GetExceptionInfo(e); public static BslTypeValue GetTypeByName(ITypeManager manager, string name) - { - var foundType = manager.GetTypeByName(name); - return new BslTypeValue(foundType); - } + => new(manager.GetTypeByName(name)); public static BslValue GetIndexedValue(object target, BslValue index) { - if (!(target is IRuntimeContextInstance context) || !context.IsIndexed) + if (target is not IRuntimeContextInstance context || !context.IsIndexed) { throw RuntimeException.IndexedAccessIsNotSupportedException(); } - return (BslValue)context.GetIndexedValue((IValue)index); + return (BslValue)context.GetIndexedValue(index); } public static void SetIndexedValue(object target, BslValue index, BslValue value) { - if (!(target is IRuntimeContextInstance context) || !context.IsIndexed) + if (target is not IRuntimeContextInstance context || !context.IsIndexed) { throw RuntimeException.IndexedAccessIsNotSupportedException(); } @@ -180,7 +162,7 @@ public static void SetIndexedValue(object target, BslValue index, BslValue value public static BslValue GetPropertyValue(object target, string propertyName) { - if (!(target is IRuntimeContextInstance context)) + if (target is not IRuntimeContextInstance context) throw BslExceptions.ValueIsNotObjectException(); var propIndex = context.GetPropertyNumber(propertyName); @@ -189,7 +171,7 @@ public static BslValue GetPropertyValue(object target, string propertyName) public static BslValue TryCallContextMethod(BslValue instance, string methodName, IBslProcess process, BslValue[] arguments) { - if (!(instance is IRuntimeContextInstance context)) + if (instance is not IRuntimeContextInstance context) throw BslExceptions.ValueIsNotObjectException(); return CallContextMethod(context, methodName, process, arguments); From 990a284640fb2cdb341cf19f570f0d3cafebe761 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Mon, 1 Jun 2026 13:44:10 +0300 Subject: [PATCH 64/65] =?UTF-8?q?=D0=94=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B8=D0=BB=20release-notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/install/release-notes.md b/install/release-notes.md index 4a7fd6dd2..2351dee9c 100644 --- a/install/release-notes.md +++ b/install/release-notes.md @@ -16,6 +16,7 @@ * Исправлено чтение JSON-строк, содержащих ISO-дату: они больше не преобразуются ошибочно в `Дата`: [#1681](https://github.com/EvilBeaver/OneScript/issues/1681). * Исправлена ошибка компилятора при некорректном "висячем" окончании синтаксического блока (улучшена диагностика ошибок): [#1685](https://github.com/EvilBeaver/OneScript/issues/1685). * Метод `ТаблицаЗначений.Найти` теперь использует индекс таблицы значений. Ранее индекс использовался только методом `НайтиСтроки` [#1661](https://github.com/EvilBeaver/OneScript/issues/1661) +* В режиме Native некорректно работало сложение даты с числом. ## Прочее From ca9391251375508ff867efabd0bd769ef85860c6 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Mon, 1 Jun 2026 13:59:50 +0300 Subject: [PATCH 65/65] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BD=D0=B5=D1=81=D0=BA=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=BA=D0=BE=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=87=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=A1=D0=BE=D0=BD=D0=B0=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TcpServer/FormatReconcileUtils.cs | 1 - .../SyntaxAnalysis/DefaultBslParser.cs | 602 +++++++++--------- .../Compiler/CompilerHelpers.cs | 3 +- 3 files changed, 304 insertions(+), 302 deletions(-) diff --git a/src/OneScript.DebugProtocol/TcpServer/FormatReconcileUtils.cs b/src/OneScript.DebugProtocol/TcpServer/FormatReconcileUtils.cs index 99fa26ef3..85c184c27 100644 --- a/src/OneScript.DebugProtocol/TcpServer/FormatReconcileUtils.cs +++ b/src/OneScript.DebugProtocol/TcpServer/FormatReconcileUtils.cs @@ -27,7 +27,6 @@ public static byte[] GetReconcileMagic() writer.WriteHeader(1, 1); writer.WriteLibrary(typeof(RpcCall).Assembly, 1); writer.WriteClassWithNoFields(typeof(RpcCall), 1, 1); - //writer.WriteInstance(new object[]{Array.Empty(), "$NonExistentMethod$", nameof(IDebuggerService)}); writer.WriteEnd(); } diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 94c19feed..ac608f076 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -21,8 +21,8 @@ public class DefaultBslParser private readonly ILexer _lexer; private readonly PreprocessorHandlers _preprocessorHandlers; - private Lexem _lastExtractedLexem; - + private Lexem _lastExtractedLexem; + private bool _inMethodScope; private bool _isMethodsDefined; private bool _isStatementsDefined; @@ -32,8 +32,8 @@ public class DefaultBslParser private readonly Stack _tokenStack = new Stack(); private bool _isInLoopScope; - private bool _enableException; - + private bool _enableException; + private readonly List _annotations = new List(); public DefaultBslParser( @@ -47,21 +47,21 @@ public DefaultBslParser( _nodeContext = new ParserContext(); } - private IErrorSink ErrorSink { get; } - - public IEnumerable Errors => ErrorSink.Errors ?? Array.Empty(); - + private IErrorSink ErrorSink { get; } + + public IEnumerable Errors => ErrorSink.Errors ?? Array.Empty(); + public BslSyntaxNode ParseStatefulModule() { - ModuleNode node; - + ModuleNode node; + _preprocessorHandlers.OnModuleEnter(); - NextLexem(); - - node = new ModuleNode(_lexer.Iterator.Source, _lastExtractedLexem); - PushContext(node); + NextLexem(); + + node = new ModuleNode(_lexer.Iterator.Source, _lastExtractedLexem); + PushContext(node); try - { + { ParseModuleSections(); } finally @@ -69,8 +69,8 @@ public BslSyntaxNode ParseStatefulModule() PopContext(); } - _preprocessorHandlers.OnModuleLeave(); - + _preprocessorHandlers.OnModuleLeave(); + return node; } @@ -105,8 +105,8 @@ public BslSyntaxNode ParseExpression() return module; } - private void PushContext(NonTerminalNode node) => _nodeContext.PushContext(node); - + private void PushContext(NonTerminalNode node) => _nodeContext.PushContext(node); + private NonTerminalNode PopContext() => _nodeContext.PopContext(); private NonTerminalNode CurrentParent => _nodeContext.CurrentParent; @@ -114,13 +114,13 @@ public BslSyntaxNode ParseExpression() private void ParseModuleAnnotation() { if (_lastExtractedLexem.Type != LexemType.PreprocessorDirective) - return; - + return; + var annotationParser = _preprocessorHandlers .Slice(x => x is ModuleAnnotationDirectiveHandler) .Cast() - .ToList(); - + .ToList(); + if (annotationParser.Count == 0) return; @@ -139,8 +139,8 @@ private void ParseModuleAnnotation() { AddError(LocalizedErrors.DirectiveNotSupported(directive)); } - } - + } + foreach (var handler in annotationParser) { handler.OnModuleLeave(); @@ -151,16 +151,16 @@ private void ParseModuleSections() { ParseModuleAnnotation(); BuildVariablesSection(); - BuildMethodsSection(); - if (_annotations.Count != 0) - { - AddError(LocalizedErrors.AnnotationNotAllowed()); - } + BuildMethodsSection(); + if (_annotations.Count != 0) + { + AddError(LocalizedErrors.AnnotationNotAllowed()); + } BuildModuleBody(); - } - + } + #region Variables - + private void BuildVariablesSection() { if (_lastExtractedLexem.Token != Token.VarDef && _lastExtractedLexem.Type != LexemType.Annotation) @@ -176,99 +176,99 @@ private void BuildVariablesSection() { while (true) { - BuildAnnotations(); - - if (_lastExtractedLexem.Token != Token.VarDef) - break; - - if (!hasVars) - { - hasVars = true; - parent.AddChild(allVarsSection); - } - + BuildAnnotations(); + + if (_lastExtractedLexem.Token != Token.VarDef) + break; + + if (!hasVars) + { + hasVars = true; + parent.AddChild(allVarsSection); + } + BuildVariablesDefinition(); } } finally { PopContext(); - } - + } + } private void BuildVariablesDefinition() - { - if (_inMethodScope) - { - if (_isStatementsDefined) - { - AddError(LocalizedErrors.LateVarDefinition()); - return; - } - } - else if (_isMethodsDefined) - { - AddError(LocalizedErrors.LateVarDefinition()); - return; - } - + { + if (_inMethodScope) + { + if (_isStatementsDefined) + { + AddError(LocalizedErrors.LateVarDefinition()); + return; + } + } + else if (_isMethodsDefined) + { + AddError(LocalizedErrors.LateVarDefinition()); + return; + } + while (true) { - NextLexem(); // skip opening VarDef or Comma - - if (!IsUserSymbol(_lastExtractedLexem)) - { - if(_lastExtractedLexem.Type == LexemType.Annotation) - AddError(LocalizedErrors.AnnotationNotAllowed()); - else - AddError(LocalizedErrors.IdentifierExpected()); - return; - } - - BuildVariable(); - - if (_lastExtractedLexem.Token == Token.Semicolon) - { - break; - } - - if (_lastExtractedLexem.Token != Token.Comma) - { - AddError(LocalizedErrors.SemicolonExpected()); - return; - } - } - - NextLexem(); // skip Semicolon + NextLexem(); // skip opening VarDef or Comma + + if (!IsUserSymbol(_lastExtractedLexem)) + { + if(_lastExtractedLexem.Type == LexemType.Annotation) + AddError(LocalizedErrors.AnnotationNotAllowed()); + else + AddError(LocalizedErrors.IdentifierExpected()); + return; + } + + BuildVariable(); + + if (_lastExtractedLexem.Token == Token.Semicolon) + { + break; + } + + if (_lastExtractedLexem.Token != Token.Comma) + { + AddError(LocalizedErrors.SemicolonExpected()); + return; + } + } + + NextLexem(); // skip Semicolon _annotations.Clear(); - } - - private void BuildVariable() - { - var variable = _nodeContext.AddChild(new VariableDefinitionNode(_lastExtractedLexem)); - if (!_inMethodScope) - foreach (var astNode in _annotations) - { - variable.AddChild(astNode); + } + + private void BuildVariable() + { + var variable = _nodeContext.AddChild(new VariableDefinitionNode(_lastExtractedLexem)); + if (!_inMethodScope) + foreach (var astNode in _annotations) + { + variable.AddChild(astNode); + } + + var symbolicName = _lastExtractedLexem.Content; + CreateChild(variable, NodeKind.Identifier, _lastExtractedLexem); + + NextLexem(); + if (_lastExtractedLexem.Token == Token.Export) + { + if (_inMethodScope) + { + AddError(LocalizedErrors.ExportedLocalVar(symbolicName)); + return; } - - var symbolicName = _lastExtractedLexem.Content; - CreateChild(variable, NodeKind.Identifier, _lastExtractedLexem); - - NextLexem(); - if (_lastExtractedLexem.Token == Token.Export) - { - if (_inMethodScope) - { - AddError(LocalizedErrors.ExportedLocalVar(symbolicName)); - return; - } - CreateChild(variable, NodeKind.ExportFlag, _lastExtractedLexem); - NextLexem(); - } - } - + CreateChild(variable, NodeKind.ExportFlag, _lastExtractedLexem); + NextLexem(); + } + } + private void ApplyAnnotations(AnnotatableNode annotatable) { foreach (var astNode in _annotations) @@ -284,7 +284,7 @@ private void ApplyAnnotations(AnnotatableNode annotatable) private void BuildMethodsSection() { - if (_lastExtractedLexem.Type != LexemType.Annotation + if (_lastExtractedLexem.Type != LexemType.Annotation && !IsStartOfMethod(_lastExtractedLexem)) { return; @@ -301,15 +301,15 @@ private void BuildMethodsSection() { BuildAnnotations(); if (!IsStartOfMethod(_lastExtractedLexem)) - break; - - if (!sectionExist) - { - sectionExist = true; - _isMethodsDefined = true; - parent.AddChild(allMethodsSection); - } - + break; + + if (!sectionExist) + { + sectionExist = true; + _isMethodsDefined = true; + parent.AddChild(allMethodsSection); + } + BuildMethod(); } } @@ -322,14 +322,14 @@ private void BuildMethodsSection() private static bool IsStartOfMethod(in Lexem lex) { return lex.Token == Token.Async || lex.Token == Token.Procedure || lex.Token == Token.Function; - } - + } + private void BuildMethod() { Debug.Assert(IsStartOfMethod(_lastExtractedLexem)); - var method = _nodeContext.AddChild(new MethodNode()); - + var method = _nodeContext.AddChild(new MethodNode()); + ApplyAnnotations(method); PushContext(method); if (_lastExtractedLexem.Token == Token.Async) @@ -337,8 +337,8 @@ private void BuildMethod() method.IsAsync = true; _isInAsyncMethod = true; NextLexem(); - } - + } + try { BuildMethodSignature(); @@ -382,8 +382,8 @@ private void BuildMethodBody() finally { PopContext(); - } - + } + CreateChild(CurrentParent, NodeKind.BlockEnd, _lastExtractedLexem); NextLexem(); } @@ -419,58 +419,58 @@ private void BuildMethodParameters(MethodSignatureNode signature) } var paramList = new NonTerminalNode(NodeKind.MethodParameters, _lastExtractedLexem); - signature.AddChild(paramList); - + signature.AddChild(paramList); + NextLexem(); // ( - if (_lastExtractedLexem.Token != Token.ClosePar) - while (true) - { - BuildMethodParameter(paramList); - - if (_lastExtractedLexem.Token == Token.ClosePar) - { - break; - } - - if (_lastExtractedLexem.Token == Token.Comma) - { - NextLexem(); - } - else - { - AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); - return; - } - } - + if (_lastExtractedLexem.Token != Token.ClosePar) + while (true) + { + BuildMethodParameter(paramList); + + if (_lastExtractedLexem.Token == Token.ClosePar) + { + break; + } + + if (_lastExtractedLexem.Token == Token.Comma) + { + NextLexem(); + } + else + { + AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); + return; + } + } + NextLexem(); // ) } - private void BuildMethodParameter(NonTerminalNode paramList) - { - BuildAnnotations(); - var param = new MethodParameterNode(); - paramList.AddChild(param); - ApplyAnnotations(param); - // [Знач] Identifier [= Literal],... - if (_lastExtractedLexem.Token == Token.ByValParam) - { - CreateChild(param, NodeKind.ByValModifier, _lastExtractedLexem); - NextLexem(); - } - - if (!IsUserSymbol(_lastExtractedLexem)) - { - AddError(LocalizedErrors.IdentifierExpected()); - return; - } - CreateChild(param, NodeKind.Identifier, _lastExtractedLexem); - NextLexem(); - if (_lastExtractedLexem.Token == Token.Equal) - { - NextLexem(); - BuildDefaultParameterValue(param, NodeKind.ParameterDefaultValue); + private void BuildMethodParameter(NonTerminalNode paramList) + { + BuildAnnotations(); + var param = new MethodParameterNode(); + paramList.AddChild(param); + ApplyAnnotations(param); + // [Знач] Identifier [= Literal],... + if (_lastExtractedLexem.Token == Token.ByValParam) + { + CreateChild(param, NodeKind.ByValModifier, _lastExtractedLexem); + NextLexem(); + } + + if (!IsUserSymbol(_lastExtractedLexem)) + { + AddError(LocalizedErrors.IdentifierExpected()); + return; + } + CreateChild(param, NodeKind.Identifier, _lastExtractedLexem); + NextLexem(); + if (_lastExtractedLexem.Token == Token.Equal) + { + NextLexem(); + BuildDefaultParameterValue(param, NodeKind.ParameterDefaultValue); } } @@ -510,15 +510,15 @@ private bool BuildDefaultParameterValue(NonTerminalNode param, NodeKind nodeKind } return true; - } - + } + #endregion - + private void BuildModuleBody() { if (_lastExtractedLexem.Token == Token.EndOfText) - return; - + return; + var moduleBody = new NonTerminalNode(NodeKind.ModuleBody, _lastExtractedLexem); var node = moduleBody.AddNode(new CodeBatchNode(_lastExtractedLexem)); PushContext(node); @@ -531,19 +531,19 @@ private void BuildModuleBody() PopContext(); } CurrentParent.AddChild(moduleBody); - } - + } + #region Annotations private void BuildAnnotations() { while (_lastExtractedLexem.Type == LexemType.Annotation) - { - if (_inMethodScope) - { - AddError(LocalizedErrors.AnnotationNotAllowed()); - return; - } - + { + if (_inMethodScope) + { + AddError(LocalizedErrors.AnnotationNotAllowed()); + return; + } + var node = BuildAnnotationDefinition(); _annotations.Add(node); } @@ -564,29 +564,31 @@ private void BuildAnnotationParameters(AnnotationNode annotation) NextLexem(); if (_lastExtractedLexem.Token != Token.ClosePar) - while (true) { - BuildAnnotationParameter(annotation); - - if (_lastExtractedLexem.Token == Token.ClosePar) - { - break; - } - - if (_lastExtractedLexem.Token == Token.Comma) + while (true) { - NextLexem(); - } - else - { - AddError(LocalizedErrors.TokenExpected(Token.ClosePar), false); - return; + BuildAnnotationParameter(annotation); + + if (_lastExtractedLexem.Token == Token.ClosePar) + { + break; + } + + if (_lastExtractedLexem.Token == Token.Comma) + { + NextLexem(); + } + else + { + AddError(LocalizedErrors.TokenExpected(Token.ClosePar), false); + return; + } } - } - + } + NextLexem(); // ) - } - + } + private void BuildAnnotationParameter(AnnotationNode annotation) { bool success = true; @@ -650,9 +652,9 @@ private void BuildCodeBatch(params Token[] endTokens) if (_lastExtractedLexem.Type != LexemType.Identifier && _lastExtractedLexem.Token != Token.EndOfText) { - if (_lastExtractedLexem.Type == LexemType.Annotation) - AddError(LocalizedErrors.AnnotationNotAllowed()); - else + if (_lastExtractedLexem.Type == LexemType.Annotation) + AddError(LocalizedErrors.AnnotationNotAllowed()); + else AddError(LocalizedErrors.UnexpectedOperation()); continue; } @@ -749,7 +751,7 @@ private void BuildComplexStructureStatement() var expected = _tokenStack.Peek(); if (expected.Length == 1 && expected[0] == Token.EndOfText) AddError(LocalizedErrors.UnexpectedKeyword(_lastExtractedLexem.Token)); - else + else AddError(LocalizedErrors.TokenExpected(expected)); } break; @@ -758,8 +760,8 @@ private void BuildComplexStructureStatement() private void BuildGlobalCallAwaitOperator() { - Debug.Assert(_lastExtractedLexem.Token == Token.Await); - + Debug.Assert(_lastExtractedLexem.Token == Token.Await); + CurrentParent.AddChild(TerminalNode()); } @@ -879,7 +881,7 @@ private void BuildWhileStatement() } private void BuildForStatement() - { + { NextLexem(); NodeKind loopKind; @@ -919,8 +921,8 @@ private void BuildCountableForStatement(NonTerminalNode loopNode) AddError(LocalizedErrors.IdentifierExpected()); BuildBatchWithContext(loopNode, Token.EndLoop); return; - } - + } + var counter = _lastExtractedLexem; if (!NextExpected(Token.Equal)) { @@ -939,8 +941,8 @@ private void BuildCountableForStatement(NonTerminalNode loopNode) var limit = new NonTerminalNode(NodeKind.ForLimit, _lastExtractedLexem); BuildExpressionUpTo(limit, Token.Loop); - loopNode.AddChild(limit); - + loopNode.AddChild(limit); + BuildBatchWithContext(loopNode, Token.EndLoop); CreateChild(loopNode, NodeKind.BlockEnd, _lastExtractedLexem); @@ -1190,41 +1192,43 @@ private void BuildCallParameters(NonTerminalNode callNode) { PopStructureToken(); } - } - + } + private void BuildCallArguments(NonTerminalNode node) - { - if (_lastExtractedLexem.Token != Token.ClosePar) - while (true) - { - BuildOptionalCallArgument(node); - - if (_lastExtractedLexem.Token == Token.ClosePar) - { - break; - } - - if (_lastExtractedLexem.Token == Token.Comma) - { - NextLexem(); - } - else - { - AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); - return; - } - } + { + if (_lastExtractedLexem.Token == Token.ClosePar) + return; + + while (true) + { + BuildOptionalCallArgument(node); + + if (_lastExtractedLexem.Token == Token.ClosePar) + { + break; + } + + if (_lastExtractedLexem.Token == Token.Comma) + { + NextLexem(); + } + else + { + AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); + return; + } + } } private void BuildOptionalCallArgument(NonTerminalNode argsList) - { - var arg = argsList.AddNode(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); + { + var arg = argsList.AddNode(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); if (_lastExtractedLexem.Token == Token.Comma || _lastExtractedLexem.Token == Token.ClosePar) { return; - } - + } + arg.AddNode( BuildExpression(0) ); } @@ -1257,47 +1261,47 @@ private BslSyntaxNode BuildExpression(int prio) } return firstArg; - } - + } + private BslSyntaxNode BuildPrimaryExpression() { if (_lastExtractedLexem.Token == Token.OpenPar) - { - return BuildParenthesis(); + { + return BuildParenthesis(); } - var operation = _lastExtractedLexem; - var prio = LanguageDef.GetUnaryPriority(operation.Token); + var operation = _lastExtractedLexem; + var prio = LanguageDef.GetUnaryPriority(operation.Token); if (prio == LanguageDef.MAX_OPERATION_PRIORITY) - { - return TerminalNode(); - } - - NextLexem(); - + { + return TerminalNode(); + } + + NextLexem(); + if (operation.Token == Token.Plus) operation.Token = Token.UnaryPlus; - else if (operation.Token == Token.Minus) - { - operation.Token = Token.UnaryMinus; - if (_lastExtractedLexem.Type == LexemType.NumberLiteral) //TODO:move it to lexer - { - _lastExtractedLexem.Content = '-' + _lastExtractedLexem.Content; - return TerminalNode(); - } - } - - if (LanguageDef.GetUnaryPriority(_lastExtractedLexem.Token) <= prio) - { - return CreateError(LocalizedErrors.ExpressionSyntax()); - } - - var arg = BuildExpression(prio); - return new UnaryOperationNode(arg, operation); - } - - + else if (operation.Token == Token.Minus) + { + operation.Token = Token.UnaryMinus; + if (_lastExtractedLexem.Type == LexemType.NumberLiteral) //TODO:move it to lexer + { + _lastExtractedLexem.Content = '-' + _lastExtractedLexem.Content; + return TerminalNode(); + } + } + + if (LanguageDef.GetUnaryPriority(_lastExtractedLexem.Token) <= prio) + { + return CreateError(LocalizedErrors.ExpressionSyntax()); + } + + var arg = BuildExpression(prio); + return new UnaryOperationNode(arg, operation); + } + + private BslSyntaxNode BuildExpressionUpTo(NonTerminalNode parent, Token stopToken) { var node = BuildExpression(parent, stopToken); @@ -1334,15 +1338,15 @@ private void BuildOptionalExpression(NonTerminalNode parent, Token stopToken) #region Operators private BslSyntaxNode BuildParenthesis() - { - NextLexem(); - var expr = BuildExpression(0); - if (_lastExtractedLexem.Token != Token.ClosePar) - { - return CreateError(LocalizedErrors.TokenExpected(Token.ClosePar)); - } - NextLexem(); - + { + NextLexem(); + var expr = BuildExpression(0); + if (_lastExtractedLexem.Token != Token.ClosePar) + { + return CreateError(LocalizedErrors.TokenExpected(Token.ClosePar)); + } + NextLexem(); + return BuildDereference(expr); } @@ -1395,8 +1399,8 @@ private BslSyntaxNode BuildQuestionOperator() { var node = new NonTerminalNode(NodeKind.TernaryOperator, _lastExtractedLexem); if (!NextExpected(Token.OpenPar)) - return CreateError(LocalizedErrors.TokenExpected(Token.OpenPar)); - + return CreateError(LocalizedErrors.TokenExpected(Token.OpenPar)); + NextLexem(); if (!TryParseNode(() => @@ -1556,8 +1560,8 @@ private void SkipToNextStatement(Token[] additionalStops = null) private void AddError(CodeError err, bool doFastForward = true) { err.Position = _lexer.GetErrorPosition(); - var adjustColumn = _lastExtractedLexem.Content?.Length ?? 1; - if (err.Position.ColumnNumber >= adjustColumn) + var adjustColumn = _lastExtractedLexem.Content?.Length ?? 1; + if (err.Position.ColumnNumber >= adjustColumn) err.Position.ColumnNumber -= adjustColumn; ErrorSink.AddError(err); @@ -1573,11 +1577,11 @@ private void AddError(CodeError err, bool doFastForward = true) throw new InternalParseException(err); } - private ErrorTerminalNode CreateError(CodeError error, bool doFastForward = true) - { - var lexem = _lastExtractedLexem; - AddError(error, doFastForward); - return new ErrorTerminalNode(lexem); + private ErrorTerminalNode CreateError(CodeError error, bool doFastForward = true) + { + var lexem = _lastExtractedLexem; + AddError(error, doFastForward); + return new ErrorTerminalNode(lexem); } private bool IsUserSymbol(in Lexem lex) diff --git a/src/OneScript.Native/Compiler/CompilerHelpers.cs b/src/OneScript.Native/Compiler/CompilerHelpers.cs index e8cc930a1..900889593 100644 --- a/src/OneScript.Native/Compiler/CompilerHelpers.cs +++ b/src/OneScript.Native/Compiler/CompilerHelpers.cs @@ -99,8 +99,7 @@ private static BslAnnotationParameter MakeAnnotationParameter(AnnotationParamete } result = new BslAnnotationParameter(param.Name, runtimeValue); } - else - if (param.Value.Type != LexemType.NotALexem) + else if (param.Value.Type != LexemType.NotALexem) { var runtimeValue = ValueFromLiteral(param.Value); result = new BslAnnotationParameter(param.Name, runtimeValue);