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/.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 diff --git a/Jenkinsfile b/Jenkinsfile index da6624d37..18c55f012 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,7 +4,7 @@ pipeline { agent none environment { - VersionPrefix = '2.0.2' + VersionPrefix = '2.1.0' outputEnc = '65001' } diff --git a/README-EN.md b/README-EN.md index 531111fc8..36c6e80cb 100644 --- a/README-EN.md +++ b/README-EN.md @@ -34,13 +34,31 @@ 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: ```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) #### + +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). @@ -57,50 +75,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: + +* [.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. -* 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. +> 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). -* 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 +Main targets: + +* `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..55b917ba8 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). @@ -57,50 +75,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), раздел «Тестирование»). diff --git a/install/release-notes.md b/install/release-notes.md index bc3c6122b..2351dee9c 100644 --- a/install/release-notes.md +++ b/install/release-notes.md @@ -1,6 +1,23 @@ -# Версия 2.0.2 +# Версия 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). ## Исправление ошибок -* Метод Найти в индексированной таблице значений не использовал индекс (в отличие от метода НайтиСтроки). -* Некорректно работали многомерные массивы +* Исправлена работа `ЗагрузитьСценарий()` в 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) +* В режиме Native некорректно работало сложение даты с числом. + +## Прочее + +* Обновлена документация проекта и разделы установки под актуальную платформу .NET 8. \ No newline at end of file diff --git a/src/1Script.sln b/src/1Script.sln index ee4c3a253..86a0b9a0f 100644 --- a/src/1Script.sln +++ b/src/1Script.sln @@ -69,268 +69,403 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE 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 ad0839376..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 (!_lexer.Iterator.MoveToContent()) - return; - + if (_lastExtractedLexem.Token == Token.EndOfText) + 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; } @@ -746,7 +748,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; } @@ -754,8 +760,8 @@ private void BuildComplexStructureStatement() private void BuildGlobalCallAwaitOperator() { - Debug.Assert(_lastExtractedLexem.Token == Token.Await); - + Debug.Assert(_lastExtractedLexem.Token == Token.Await); + CurrentParent.AddChild(TerminalNode()); } @@ -875,7 +881,7 @@ private void BuildWhileStatement() } private void BuildForStatement() - { + { NextLexem(); NodeKind loopKind; @@ -915,8 +921,8 @@ private void BuildCountableForStatement(NonTerminalNode loopNode) AddError(LocalizedErrors.IdentifierExpected()); BuildBatchWithContext(loopNode, Token.EndLoop); return; - } - + } + var counter = _lastExtractedLexem; if (!NextExpected(Token.Equal)) { @@ -935,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); @@ -1186,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) ); } @@ -1253,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); @@ -1330,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); } @@ -1391,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(() => @@ -1552,7 +1560,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) @@ -1567,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.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/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); diff --git a/src/OneScript.Native/Compiler/MethodCompiler.cs b/src/OneScript.Native/Compiler/MethodCompiler.cs index 190643ebf..a2be74b8c 100644 --- a/src/OneScript.Native/Compiler/MethodCompiler.cs +++ b/src/OneScript.Native/Compiler/MethodCompiler.cs @@ -1092,6 +1092,20 @@ protected override void VisitGlobalFunctionCall(CallNode node) _statementBuildParts.Push(expression); } + private static bool InjectedProcessNeeded(MethodInfo methodInfo) + { + 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; + } + protected override void VisitObjectProcedureCall(BslSyntaxNode node) { var target = _statementBuildParts.Pop(); @@ -1102,7 +1116,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 = InjectedProcessNeeded(methodInfo); + var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), injectProcess); _blocks.Add(Expression.Call(target, methodInfo, args)); } @@ -1168,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()) @@ -1266,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) @@ -1310,7 +1325,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); @@ -1418,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) diff --git a/src/OneScript.Native/Runtime/DynamicOperations.cs b/src/OneScript.Native/Runtime/DynamicOperations.cs index 4cfc523a0..13289a4c2 100644 --- a/src/OneScript.Native/Runtime/DynamicOperations.cs +++ b/src/OneScript.Native/Runtime/DynamicOperations.cs @@ -28,68 +28,39 @@ 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; - 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(); - } - - 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(); - } + var dLeft = (decimal)left; + var dRight = (decimal)right; + return BslNumericValue.Create(dLeft - dRight); // or throw ConvertToNumberException(); + } + + 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) @@ -116,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); @@ -155,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(); } @@ -197,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); @@ -206,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); 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 e56d15ed3..4ef0ee605 100644 --- a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs @@ -24,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) @@ -35,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 < FileBackingConstants.DEFAULT_MEMORY_LIMIT) + if (stream.Length < inMemLimit) { LoadToBuffer(stream); } @@ -197,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..ec9d0ef26 --- /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; + } + } +} diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs deleted file mode 100644 index eeb073ad9..000000000 --- a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs +++ /dev/null @@ -1,17 +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; - -namespace OneScript.StandardLibrary.Binary -{ - public static class FileBackingConstants - { - public const int DEFAULT_MEMORY_LIMIT = 1024 * 1024 * 50; // 50 Mb - 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..c0a381c5a 100644 --- a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs +++ b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs @@ -20,13 +20,9 @@ public sealed class FileBackingStream : Stream private string _backingFileName; - public FileBackingStream() : this(FileBackingConstants.DEFAULT_MEMORY_LIMIT) - { - } - 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; @@ -85,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/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/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.StandardLibrary/Binary/IBinaryDataMemoryLimit.cs b/src/OneScript.StandardLibrary/Binary/IBinaryDataMemoryLimit.cs new file mode 100644 index 000000000..49497dd78 --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/IBinaryDataMemoryLimit.cs @@ -0,0 +1,18 @@ +/*---------------------------------------------------------- +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.StandardLibrary.Binary +{ + /// + /// Лимит объёма данных в памяти для объектов «ДвоичныеДанные» и смежных потоков + /// до выгрузки во временный файл (байты). + /// + public interface IBinaryDataMemoryLimit + { + int MaxBytesInMemory { get; } + } +} diff --git a/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs index a1fc47fa3..c0a902c34 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 { @@ -34,7 +36,7 @@ public DynamicLoadingFunctions(ScriptingEngine engine) /// /// Подключает сторонний файл сценария к текущей системе типов. - /// Подключенный сценарий выступает, как самостоятельный класс, создаваемый оператором Новый + /// Подключенный сценарий выступает как самостоятельный класс, создаваемый оператором Новый /// /// Путь к подключаемому сценарию /// Имя типа, которое будет иметь новый класс. Экземпляры класса создаются оператором Новый. @@ -48,25 +50,15 @@ 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); } } /// /// Создает экземпляр объекта на основании стороннего файла сценария. - /// Загруженный сценарий возвращается, как самостоятельный объект. + /// Загруженный сценарий возвращается как самостоятельный объект. /// Экспортные свойства и методы скрипта доступны для вызова. /// /// @@ -80,44 +72,24 @@ 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(); + var compiler = _engine.GetCompilerService(); + try + { + if (externalContext == null) + return _engine.AttachedScriptsFactory.LoadFromString(compiler, code, process); - foreach (var item in externalContext) - { - extData.Add(item.Key.ToString()!, item.Value); - } - - 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); - } - } + 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); + } } /// /// Создает экземпляр объекта на основании стороннего файла сценария. - /// Загруженный сценарий возвращается, как самостоятельный объект. + /// Загруженный сценарий возвращается как самостоятельный объект. /// Экспортные свойства и методы скрипта доступны для вызова. /// /// Путь к подключаемому сценарию @@ -128,31 +100,44 @@ 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) - { - extData.Add(item.Key.ToString()!, item.Value); - } + try + { + if(externalContext == null) + return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, 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. /// Загружаемая сборка должна ссылаться на сборку ScriptEngine.dll /// - /// Также подключает вншение компонеты, разработанные по технологии Native API, + /// Также подключает внешние компоненты, разработанные по технологии Native API, /// поставляемые в виде отдельных DLL или упакованные в ZIP-архив. /// /// @@ -181,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); @@ -189,5 +175,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, e); + } } } \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs index d38e2fbb6..6e3a9875a 100644 --- a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs +++ b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs @@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.StandardLibrary.Binary; using OneScript.StandardLibrary.Collections; using ScriptEngine.Hosting; using ScriptEngine.Machine; @@ -13,9 +14,15 @@ namespace OneScript.StandardLibrary { public static class EngineBuilderExtensions { + public static IEngineBuilder UseBinaryDataOptions(this IEngineBuilder builder) + { + builder.Services.RegisterSingleton(); + 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/OneScript.StandardLibrary/Hash/Crc32.cs b/src/OneScript.StandardLibrary/Hash/Crc32.cs index 3b1f812c8..8575f4da3 100644 --- a/src/OneScript.StandardLibrary/Hash/Crc32.cs +++ b/src/OneScript.StandardLibrary/Hash/Crc32.cs @@ -62,19 +62,38 @@ 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]; } 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..f8ab3ef0a 100644 --- a/src/OneScript.StandardLibrary/Hash/HashImpl.cs +++ b/src/OneScript.StandardLibrary/Hash/HashImpl.cs @@ -5,156 +5,234 @@ 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(); - private bool _calculated; + { + private const int BUFFER_SIZE = (1024 * 32); + + private readonly Crc32 _crc32; + private readonly IncrementalHash _provider; + private readonly HashFunctionEnum _enumValue; 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) + { + _crc32 = new Crc32(); + } + else ArgumentNullException.ThrowIfNull(provider); + + AppendData(Array.Empty()); + } + + /// + /// Вид хеш-функции, определяющий способ вычисления хеш-суммы. + /// Только для чтения + /// + /// Перечисление ХешФункция [ContextProperty("ХешФункция", "HashFunction")] - public HashFunctionEnum Extension => _enumValue; - + public HashFunctionEnum HashFunction => _enumValue; + + /// + /// Текущее значение хеш-суммы. Только для чтения + /// + /// Для хеш-функции CRC32 - Число, для остальных - ДвоичныеДанные + /// [ContextProperty("ХешСумма", "HashSum")] 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 (_crc32 != null) + return ValueFactory.Create(_crc32.GetCurrentHashAsUInt32()); + + return new BinaryDataContext(_hash); } - } - + } + + /// + /// Нестандартное расширение! + /// Строковое представление текущего значения хеш-суммы. Только для чтения + /// + /// Для хеш-функции CRC32 - Число, для остальных - ДвоичныеДанные [ContextProperty("ХешСуммаСтрокой", "HashSumOfString")] 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(); + } + } + + 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) + { + if (count <= 0) + { + AppendStream(stream); + return; + } + + 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, BslValue count = null) { 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: - 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)); + case BinaryDataContext binaryData: + AppendStream(binaryData.GetStream()); break; - case BslObjectValue obj when obj is BinaryDataContext binaryData: - AddStream(binaryData.GetStream()); + + 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.InvalidArgumentType(nameof(toAdd)); + throw RuntimeException.InvalidNthArgumentType(1); } } + /// + /// Добавляет двоичные данные из файла и обновляет хеш-сумму + /// + /// Имя файла, из которого читаются данные. Тип: Строка [ContextMethod("ДобавитьФайл", "AppendFile")] public void AppendFile(string path) { if (!File.Exists(path)) - throw RuntimeException.InvalidArgumentType(); - AddStream(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); + throw RuntimeException.InvalidArgumentType(); + + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + AppendStream(stream); } - [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; - + _provider?.Dispose(); } } } 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/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs index 444907ee2..aa588f4ba 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 { @@ -22,6 +23,8 @@ internal class JsonReaderInternal: JsonTextReader // из библиотеки public JsonReaderInternal(TextReader reader) : base(reader) { Finished = false; + SupportMultipleContent = true; + DateParseHandling = DateParseHandling.None; } public override bool Read() @@ -40,7 +43,6 @@ public override bool Read() } /// - /// /// Предназначен для последовательного чтения JSON-данных из файла или строки. /// [ContextClass("ЧтениеJSON", "JSONReader")] @@ -50,7 +52,6 @@ public class JSONReader : AutoContext private JsonReaderInternal _reader; /// - /// /// Возвращает true если для объекта чтения json был задан текст для парсинга. /// private bool IsOpen() => _reader != null; @@ -71,49 +72,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(); /// - /// /// Содержит текущее значение: - /// /// - Число - если ТипТекущегоЗначения имеет значение Число; /// - Строка - если ТипТекущегоЗначения имеет одно из следующих значений: /// - Комментарий, @@ -160,7 +137,6 @@ public IValue CurrentValue default: throw JSONReaderException.CannotGetValue(); - ; } } } @@ -168,7 +144,6 @@ public IValue CurrentValue public object ReaderValue => _reader.Value; /// - /// /// Тип текущего значения в документе JSON во внутреннем формате. /// null - если чтение еще не началось или достигнут конец файла. /// @@ -183,7 +158,6 @@ public JsonToken CurrentJsonTokenType } /// - /// /// Тип текущего значения в документе JSON. /// Неопределено - если чтение еще не началось или достигнут конец файла. /// @@ -217,10 +191,8 @@ public JSONValueTypeEnum CurrentValueType } /// - /// /// Завершает чтение текста JSON из файла или строки. /// - /// [ContextMethod("Закрыть", "Close")] public void Close() { @@ -232,10 +204,10 @@ public void Close() } /// - /// - /// Открывает JSON-файл для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанного файла. + /// Открывает JSON-файл для чтения данным объектом. + /// Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, + /// то чтение прекращается и объект инициализируется для чтения из указанного файла. /// - /// /// /// Имя файла, содержащего текст JSON. /// @@ -243,34 +215,49 @@ public void Close() [ContextMethod("ОткрытьФайл", "OpenFile")] public void OpenFile(string JSONFileName, IValue encoding = null) { - if (IsOpen()) - Close(); + Close(); 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) { throw new RuntimeException(e.Message, e); } - _reader = new JsonReaderInternal(_fileReader) - { - SupportMultipleContent = true - }; + _reader = new JsonReaderInternal(_fileReader); + } + + /// + /// Устанавливает поток для чтения JSON данным объектом. + /// Если перед вызовом данного метода уже производилось чтение JSON из другого файла, строки или потока, + /// то чтение прекращается и объект инициализируется для чтения из указанного потока. + /// + /// + /// Поток для чтения текста JSON. + /// + /// Позволяет задать кодировку входного потока. + [ContextMethod("ОткрытьПоток", "OpenStream")] + public void OpenStream(IStreamWrapper streamContext, IValue encoding = null) + { + Close(); + + 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)); } /// /// Если текущее значение – начало массива или объекта, то пропускает его содержимое и конец. /// Для остальных типов значений работает аналогично методу Прочитать(). /// - /// [ContextMethod("Пропустить", "Skip")] public bool Skip() { @@ -287,34 +274,24 @@ public bool Skip() /// /// Выполняет чтение значения JSON. /// - /// [ContextMethod("Прочитать", "Read")] public bool Read() { CheckIfOpen(); return _reader.Read(); - } /// - /// /// Устанавливает строку, содержащую текст JSON для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанной строки. /// - /// /// /// Строка, содержащая текст в формате JSON. - /// - /// [ContextMethod("УстановитьСтроку", "SetString")] public void SetString(string JSONString) { - if (IsOpen()) - Close(); + Close(); - _reader = new JsonReaderInternal(new StringReader(JSONString)) - { - SupportMultipleContent = true - }; + _reader = new JsonReaderInternal(new StringReader(JSONString)); } } 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() diff --git a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs index 57a3f4c81..86e7e1a3f 100644 --- a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs +++ b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs @@ -41,9 +41,15 @@ public class SystemEnvironmentContext : AutoContext /// [ContextProperty("ВерсияОС", "OSVersion")] public string OSVersion => Environment.OSVersion.VersionString; + + /// + /// Полная командная строка выполняемого приложения, включая исполняемый файл и все аргументы командной строки. + /// + [ContextProperty("ПолнаяКоманднаяСтрока", "FullCommandLine")] + public string FullCommandLine => Environment.CommandLine; /// - /// Имя ядра ОС/ + /// Имя ядра ОС /// [ContextProperty("ИмяЯдра", "KernelName")] public string KernelName => _osKernelName; // позволит различать linux/mac/hp-ux/sunos/... 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. Стоит учитывать, что обработчик вызывается 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(); } /// 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")] 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/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/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(); } } 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.HostedScript/oscript.cfg b/src/ScriptEngine.HostedScript/oscript.cfg index 1ddb2504f..524057f8c 100644 --- a/src/ScriptEngine.HostedScript/oscript.cfg +++ b/src/ScriptEngine.HostedScript/oscript.cfg @@ -19,4 +19,9 @@ lib.system = ../lib #encoding.script=utf-8 -#systemlanguage = ru \ No newline at end of file +#systemlanguage = ru + +# Порог: пока размер двоичных данных в памяти не превышает это значение, данные держатся в RAM; +# при большем размере используется временный файл. По умолчанию 50m (52428800 байт). +# Значение — целое число байт или с суффиксом k, m, g (512k, 50m, 1g). +#binaryData.inMemoryMaxSize=50m diff --git a/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs b/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs index 28b7b4af7..69522b883 100644 --- a/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs +++ b/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs @@ -42,5 +42,35 @@ public static NestedLoopInfo New() public List breakStatements; public int tryNesting; } + + private enum BlockType + { + While, + ForEach, + For, + If, + ElseIf, + Else, + Try, + Except + } + + private class LabelInfo + { + public int codeIndex = DUMMY_ADDRESS; + public List<(BlockType type, int id)> blockStack; + public int tryNesting; + } + + private struct PendingGoto + { + public int commandIndex; + public int exitTryIndex; + public string labelName; + public List<(BlockType type, int id)> blockStack; + public List<(int commandIndex, BlockType 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..05c77600e 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -41,6 +41,11 @@ 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<(BlockType type, int id)> _blockStack = new List<(BlockType type, int id)>(); + private int _tryNestingCount; + private int _blockIdCounter; private IBslProcess _compilerProcess; @@ -197,6 +202,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 +217,7 @@ protected override void VisitModuleBody(BslSyntaxNode child) throw; } + FinalizePendingGotos(); _ctx.PopScope(); var topIdx = _ctx.ScopeCount - 1; @@ -249,12 +256,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) + { + // Обратный переход, метка уже определена + 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 + { + // Прямой переход, метка еще не определена + if (!_labels.ContainsKey(labelName)) + _labels[labelName] = new LabelInfo(); + + var currentStack = SnapshotBlockStack(); + + // Резервируем слоты очистки для циклов (от внутреннего к внешнему) + 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 == BlockType.ForEach || block.type == BlockType.For) + { + 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; + + // Разрешаем отложенные прямые переходы, указывающие на эту метку + 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 + { + // Заполняем слоты очистки циклов + 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 == BlockType.ForEach) + CorrectCommand(slot.commandIndex, OperationCode.StopIterator, 0); + else if (slot.loopType == BlockType.For) + 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 +445,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 +498,17 @@ protected override void VisitWhileNode(WhileLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = conditionIndex; _nestedLoops.Push(loopRecord); + PushBlock(BlockType.While); 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 +526,16 @@ protected override void VisitForEachLoopNode(ForEachLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = loopBegin; _nestedLoops.Push(loopRecord); - + PushBlock(BlockType.ForEach); + 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 +566,7 @@ protected override void VisitForLoopNode(ForLoopNode node) var loopRecord = NestedLoopInfo.New(); loopRecord.startPoint = indexLoopBegin; _nestedLoops.Push(loopRecord); + PushBlock(BlockType.For); VisitCodeBlock(node.LoopBody); VisitBlockEnd(node.EndLocation); @@ -456,6 +576,7 @@ protected override void VisitForLoopNode(ForLoopNode node) var indexLoopEnd = AddCommand(OperationCode.PopTmp, 1); CorrectCommandArgument(conditionIndex, indexLoopEnd); + PopBlock(); CorrectBreakStatements(_nestedLoops.Pop(), indexLoopEnd); } @@ -504,11 +625,13 @@ protected override void VisitIfNode(ConditionNode node) var jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); + PushBlock(BlockType.If); 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 +640,9 @@ protected override void VisitIfNode(ConditionNode node) AddLineNumber(alternative.Location.LineNumber); VisitIfExpression(elif.Expression); jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); + PushBlock(BlockType.ElseIf); VisitIfTruePart(elif.TruePart); + PopBlock(); exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); } else @@ -525,7 +650,9 @@ protected override void VisitIfNode(ConditionNode node) hasAlternativeBranches = true; CorrectCommandArgument(jumpFalseIndex, _module.Code.Count); AddLineNumber(alternative.Location.LineNumber, CodeGenerationFlags.CodeStatistics); + PushBlock(BlockType.Else); VisitCodeBlock(alternative); + PopBlock(); } } @@ -850,17 +977,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 +1092,19 @@ protected override void VisitTryExceptNode(TryExceptNode node) protected override void VisitTryBlock(CodeBatchNode node) { PushTryNesting(); + PushBlock(BlockType.Try); base.VisitTryBlock(node); + PopBlock(); PopTryNesting(); } + protected override void VisitExceptBlock(CodeBatchNode node) + { + PushBlock(BlockType.Except); + base.VisitExceptBlock(node); + PopBlock(); + } + protected override void VisitExecuteStatement(BslSyntaxNode node) { base.VisitExecuteStatement(node); @@ -1066,8 +1202,8 @@ private void MakeNewObjectStatic(NewObjectNode node) { PushCallArguments(node.ConstructorArguments); } - else - { + else + { AddCommand(OperationCode.ArgNum, 0); } @@ -1097,7 +1233,74 @@ private void PopTryNesting() _nestedLoops.Peek().tryNesting--; } } - + + private void PushBlock(BlockType blockType) + { + _blockStack.Add((blockType, _blockIdCounter++)); + if (blockType == BlockType.Try) + _tryNestingCount++; + } + + private void PopBlock() + { + var last = _blockStack[_blockStack.Count - 1]; + _blockStack.RemoveAt(_blockStack.Count - 1); + if (last.type == BlockType.Try) + _tryNestingCount--; + } + + private List<(BlockType type, int id)> SnapshotBlockStack() + { + return new List<(BlockType type, int id)>(_blockStack); + } + + private static bool IsValidGotoTarget(List<(BlockType type, int id)> gotoStack, List<(BlockType 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<(BlockType type, int id)> gotoStack, List<(BlockType type, int id)> labelStack) + { + // Генерация очистки стека от внутреннего цикла к внешнему при выходе через Перейти + for (int i = gotoStack.Count - 1; i >= labelStack.Count; i--) + { + if (gotoStack[i].type == BlockType.ForEach) + AddCommand(OperationCode.StopIterator); + else if (gotoStack[i].type == BlockType.For) + 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 +1522,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/ScriptEngine/Hosting/EngineBuilderExtensions.cs b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs index 37225644d..70f0a0324 100644 --- a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs +++ b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs @@ -79,7 +79,7 @@ public static IEngineBuilder SetDefaultOptions(this IEngineBuilder builder) var holder = sp.Resolve(); return holder.GetConfig(); }); - + services.Register(); return builder; diff --git a/src/TestApp/MainWindow.xaml.cs b/src/TestApp/MainWindow.xaml.cs index 5c2adc40c..320401b17 100644 --- a/src/TestApp/MainWindow.xaml.cs +++ b/src/TestApp/MainWindow.xaml.cs @@ -139,6 +139,7 @@ private HostedScriptEngine CreateEngine() .SetDefaultOptions() .UseNativeRuntime() .UseImports() + .UseDefaultHosting() .SetupEnvironment(e => { e.AddStandardLibrary(); @@ -149,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/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/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 = @"Для Каждого Переменная Из ПеременныеСреды() Цикл Сообщить(Переменная.Ключ + "" = "" + Переменная.Значение); 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/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 11ade2183..c4029976f 100644 --- a/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs +++ b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs @@ -867,7 +867,73 @@ public void Can_Call_Member_Procedures() array.Should().HaveCount(2); } - + + [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(TestContextClass)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Объект", new BslTypeValue(testType)); + block.CodeBlock = code; + + var method = block.CreateDelegate>(); + var testValue = new TestContextClass(); + method(testValue); + } + + [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 testType = tm.RegisterClass(typeof(TestContextClass)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Объект", new BslTypeValue(testType)); + block.CodeBlock = $"Результат = {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] public void Can_Call_Member_Procedures_On_Dynamics() { 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 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); diff --git a/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs b/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs new file mode 100644 index 000000000..18229c890 --- /dev/null +++ b/src/Tests/OneScript.StandardLibrary.Tests/BinaryDataMemoryLimitConfigurationTests.cs @@ -0,0 +1,90 @@ +/*---------------------------------------------------------- +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 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)] + [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 bytes = MockConfig(rawValue).MaxBytesInMemory; + + bytes.Should().Be(expectedBytes); + _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] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void UsesDefaultWhenValueIsMissing(string rawValue) + { + var bytes = MockConfig(rawValue).MaxBytesInMemory; + + bytes.Should().Be(BinaryDataConstants.DEFAULT_IN_MEMORY_LIMIT); + } + + [Theory] + [InlineData("512x")] + [InlineData("m512")] + [InlineData("0")] + [InlineData("-1")] + [InlineData("2g")] + public void UsesDefaultForInvalidValue(string rawValue) + { + var bytes = MockConfig(rawValue).MaxBytesInMemory; + + bytes.Should().Be(BinaryDataConstants.DEFAULT_IN_MEMORY_LIMIT); + _messages.Should().NotBeEmpty(); + } + + [Fact] + public void TestMagicMaxValue() + { + MockConfig(BinaryDataOptions.IN_MEMORY_MAX_MAGIC).MaxBytesInMemory.Should().Be(BinaryDataConstants.SYSTEM_IN_MEMORY_LIMIT); + } + } +} 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 diff --git a/src/oscript/CgiBehavior.cs b/src/oscript/CgiBehavior.cs index 911782454..3354a0597 100644 --- a/src/oscript/CgiBehavior.cs +++ b/src/oscript/CgiBehavior.cs @@ -13,8 +13,10 @@ This Source Code Form is subject to the terms of the using OneScript.Exceptions; using OneScript.Execution; using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Binary; using oscript.Web; +using ScriptEngine; using ScriptEngine.HostedScript; using ScriptEngine.Hosting; using ScriptEngine.Machine; @@ -67,8 +69,8 @@ private int RunCGIMode(string scriptFile) }); var engine = ConsoleHostBuilder.Build(builder); - - var request = new WebRequestContext(); + + 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 675179567..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; @@ -49,7 +50,7 @@ private static void BuildUpWithIoC(IEngineBuilder builder) { builder.SetDefaultOptions() .UseImports() - .UseFileSystemLibraries() + .UseDefaultHosting() .UseNativeRuntime() .UseEventHandlers(); } diff --git a/src/oscript/Web/WebRequestContext.cs b/src/oscript/Web/WebRequestContext.cs index 30f46d52c..88c17de14 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,14 @@ public sealed class WebRequestContext : AutoContext, IDisposa private FileBackingStream _postBody; - public WebRequestContext() + private readonly int _postBodyInMemoryMaxBytes; + + public WebRequestContext(int postBodyInMemoryMaxBytes) { + if (postBodyInMemoryMaxBytes <= 0 || postBodyInMemoryMaxBytes == int.MaxValue) + throw new ArgumentOutOfRangeException(nameof(postBodyInMemoryMaxBytes), + "Лимит памяти для тела POST-запроса должен быть положительным числом байт и меньше Int32.MaxValue."); + _postBodyInMemoryMaxBytes = postBodyInMemoryMaxBytes; var get = Environment.GetEnvironmentVariable("QUERY_STRING"); if (get != null) FillGetMap(get); @@ -68,7 +75,8 @@ private void ProcessPostData() var type = Environment.GetEnvironmentVariable("CONTENT_TYPE"); using var stdin = Console.OpenStandardInput(); - var dest = new FileBackingStream(FileBackingConstants.DEFAULT_MEMORY_LIMIT, len); + var initialCapacity = Math.Min(len, _postBodyInMemoryMaxBytes); + var dest = new FileBackingStream(_postBodyInMemoryMaxBytes, initialCapacity); stdin.CopyTo(dest); dest.Position = 0; @@ -107,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")] diff --git a/tests/config.os b/tests/config.os index e3d33f62e..99364e3cd 100644 --- a/tests/config.os +++ b/tests/config.os @@ -14,6 +14,10 @@ КонецФункции Процедура ТестДолженПроверитьПорядокПрименения_ПеременнаяОкруженияИмеетВысшийПриоритет() Экспорт + + ИсходноеЗначениеПеременной = ПолучитьПеременнуюСреды("OSCRIPT_CONFIG"); + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ОбновитьНастройкиСистемы(); // Получаем исходное значение языка системы ИсходноеЗначениеЯзыка = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); @@ -39,12 +43,13 @@ "Переменная окружения OSCRIPT_CONFIG должна иметь наивысший приоритет"); Исключение - // Очищаем переменную окружения даже в случае ошибки - УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + // Возвращаем переменную окружения в исходное состояние + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); + ОбновитьНастройкиСистемы(); ВызватьИсключение; КонецПопытки; - // Очищаем переменную окружения и возвращаем исходное состояние + // Очищаем переменную окружения УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); ОбновитьНастройкиСистемы(); @@ -53,6 +58,10 @@ юТест.ПроверитьРавенство(ИсходноеЗначениеЯзыка, ВосстановленноеЗначение, "После очистки переменной окружения значение должно вернуться к исходному"); + // Возвращаем переменную окружения в исходное состояние + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); + ОбновитьНастройкиСистемы(); + КонецПроцедуры Процедура ТестДолженПроверитьЧтениеЗначенияПоУмолчанию() Экспорт @@ -76,6 +85,8 @@ Процедура ТестДолженПроверитьПереопределениеНесколькихПараметровОдновременно() Экспорт + ИсходноеЗначениеПеременной = ПолучитьПеременнуюСреды("OSCRIPT_CONFIG"); + // Получаем исходные значения ИсходныйЯзык = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); ИсходнаяКодировка = ПолучитьЗначениеСистемнойНастройки("encoding.script"); @@ -107,13 +118,14 @@ "Кодировка должна измениться на " + НоваяКодировка); Исключение - // Очищаем переменную окружения даже в случае ошибки - УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + // Восстанавливаем переменную окружения даже в случае ошибки + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); + ОбновитьНастройкиСистемы(); ВызватьИсключение; КонецПопытки; - // Очищаем переменную окружения - УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + // Восстанавливаем переменную окружения + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ИсходноеЗначениеПеременной); ОбновитьНастройкиСистемы(); // Проверяем, что значения вернулись (или стали Неопределено) diff --git a/tests/datahashing-from-stream.os b/tests/datahashing-from-stream.os index f5a9e5213..ba2e7b441 100644 --- a/tests/datahashing-from-stream.os +++ b/tests/datahashing-from-stream.os @@ -1,58 +1,97 @@ -Перем юТест; - -Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт - - юТест = ЮнитТестирование; - - ВсеТесты = Новый Массив; - ВсеТесты.Добавить("ТестДолжен_ПроверитьХешированниеДанныхИзПотокаВПамяти"); - - Возврат ВсеТесты; - -КонецФункции - -Функция ТестДолжен_ПроверитьХешированниеДанныхИзПотокаВПамяти() Экспорт - - БДД = ПолучитьБуферДвоичныхДанныхИз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/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: + Возврат "три"; + + ~МеткаДругое: + Возврат "другое"; +КонецФункции diff --git a/tests/hash.os b/tests/hash.os index ef74458e4..9fe2ec101 100644 --- a/tests/hash.os +++ b/tests/hash.os @@ -14,6 +14,11 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьSHA512"); ВсеТесты.Добавить("ТестДолжен_ПроверитьCRC32"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоХешированиеНеЗанимаетФайл"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешиБезДанных"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешиПустойСтроки"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешиПустогоПотока"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИнкрементальность"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИгнорированиеВторогоПараметра"); Возврат ВсеТесты; КонецФункции @@ -79,6 +84,7 @@ Провайдер.Добавить("456"); Провайдер.Добавить("789"); + юТест.ПроверитьРавенство(3421780262,Провайдер.ХешСумма); юТест.ПроверитьРавенство("CBF43926",Провайдер.ХешСуммаСтрокой); КонецПроцедуры @@ -93,11 +99,132 @@ Провайдер = Новый ХешированиеДанных(ХешФункция.MD5); Провайдер.ДобавитьФайл(Файл); - Провайдер2 = Новый ХешированиеДанных(ХешФункция.MD5); + Провайдер2 = Новый ХешированиеДанных(ХешФункция.CRC32); Провайдер2.ДобавитьФайл(Файл); + + УдалитьФайлы(Файл); Запись = Новый ЗаписьТекста(Файл); - Запись.Записать("Привет"); + Запись.Записать("Привет!"); Запись.Закрыть(); + + юТест.ПроверитьРавенство("56 45 D3 0C 39 EA 90 18 D1 CA B7 7D 5F 37 0A 8C",Строка(Провайдер.ХешСумма)); + юТест.ПроверитьРавенство(3139669858,Провайдер2.ХешСумма); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьХешиБезДанных() Экспорт + + ПустыеХеши = Новый Соответствие(); + ПустыеХеши.Вставить(ХешФункция.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"); + + Для Каждого ХФ из ХешФункция Цикл + Провайдер = Новый ХешированиеДанных(ХФ); + Провайдер.Добавить(""); + юТест.ПроверитьРавенство(ПустыеХеши[ХФ], Строка(Провайдер.ХешСумма)); + КонецЦикла + +КонецПроцедуры -КонецПроцедуры \ 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"); + + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки(""); + Поток = Новый ПотокВПамяти(БДД); + + Для Каждого ХФ из ХешФункция Цикл + Провайдер = Новый ХешированиеДанных(ХФ); + Провайдер.Добавить(Поток); + юТест.ПроверитьРавенство(ПустыеХеши[ХФ], Строка(Провайдер.ХешСумма)); + Поток.Перейти(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"); + юТест.ПроверитьРавенство(ХешиПолн[ХФ], Строка(Провайдер.ХешСумма)); + КонецЦикла + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИгнорированиеВторогоПараметра() Экспорт + Хеш = 158520161; + + Провайдер = Новый ХешированиеДанных(ХешФункция.CRC32); + Провайдер.Добавить("123456",); + юТест.ПроверитьРавенство(Хеш, Провайдер.ХешСумма); + + Провайдер = Новый ХешированиеДанных(ХешФункция.CRC32); + Провайдер.Добавить("123456", 3); + юТест.ПроверитьРавенство(Хеш, Провайдер.ХешСумма); + + Провайдер = Новый ХешированиеДанных(ХешФункция.CRC32); + Провайдер.Добавить("123456", '0001-01-01'); + юТест.ПроверитьРавенство(Хеш, Провайдер.ХешСумма); + + Провайдер = Новый ХешированиеДанных(ХешФункция.CRC32); + Провайдер.Добавить("123456", Новый Массив); + юТест.ПроверитьРавенство(Хеш, Провайдер.ХешСумма); +КонецПроцедуры 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 a3e98504a..3a49b33ab 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -24,6 +24,17 @@ СписокТестов.Добавить("Тест_Должен_ПроверитьПропускНезавершенногоМассива"); СписокТестов.Добавить("Тест_Должен_ПроверитьПропускМассиваСВложенными"); СписокТестов.Добавить("Тест_Должен_ПроверитьПропускОбъектаСВложениями"); + + СписокТестов.Добавить("Тест_Должен_ПроверитьОткрытиеПотока"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПовторноеОткрытиеПотока"); + СписокТестов.Добавить("Тест_Должен_ПроверитьСохранениеПотокаПослеЗакрытияЧтения"); + + СписокТестов.Добавить("Тест_Должен_ОпределятьСтрокуСДатойВФорматеISOКакСтроку"); + СписокТестов.Добавить("Тест_Должен_ЧитатьСтрокуСДатойВФорматеISOКакСтроку"); + СписокТестов.Добавить("Тест_Должен_ОткрытьПотокЧитаетСтрокуСДатойВФорматеISOКакСтроку"); + СписокТестов.Добавить("Тест_Должен_ОткрытьФайлЧитаетСтрокуСДатойВФорматеISOКакСтроку"); + СписокТестов.Добавить("Тест_Должен_ЧитатьДатуВФорматеISOКакСтрокуПриИспользованииПрочитатьJSON"); + Возврат СписокТестов; КонецФункции @@ -65,7 +76,7 @@ СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock.json", Истина); - юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 960829385); + юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 3868110149); КонецПроцедуры @@ -73,7 +84,7 @@ СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock_struct.json", Ложь); - юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 2800700943); + юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 856251251); КонецПроцедуры @@ -219,3 +230,150 @@ юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); юТест.ПроверитьРавенство(5, Чтение.ТекущееЗначение); КонецПроцедуры + +Процедура Тест_Должен_ПроверитьОткрытиеПотока() Экспорт + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}"); + Поток = Новый ПотокВПамяти(БДД); + Чтение = Новый ЧтениеJSON; + Чтение.ОткрытьПоток(Поток); + Json = ПрочитатьJSON(Чтение); + + юТест.ПроверитьРавенство(42, Json.Ответ); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПовторноеОткрытиеПотока() Экспорт + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}"); + Поток = Новый ПотокВПамяти(БДД); + Чтение = Новый ЧтениеJSON; + Чтение.ОткрытьПоток(Поток); + Чтение.ОткрытьПоток(Поток); // повторно + Json = ПрочитатьJSON(Чтение); + + юТест.ПроверитьРавенство(42, Json.Ответ); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьСохранениеПотокаПослеЗакрытияЧтения() Экспорт + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки("{""ответ"":42}"); + Поток = Новый ПотокВПамяти(БДД); + Чтение = Новый ЧтениеJSON; + Чтение.ОткрытьПоток(Поток); + Чтение.Закрыть(); + Чтение.ОткрытьПоток(Поток); // Поток должен остаться открытым + Json = ПрочитатьJSON(Чтение); + + юТест.ПроверитьРавенство(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.ТипТекущегоЗначения; + + ЧтениеJSON.Закрыть(); + + юТест.ПроверитьРавенство(ТекущееЗначение, "2026-05-13T22:51:46Z"); + юТест.ПроверитьРавенство(ТекущийТип, ТипЗначенияJSON.Строка); + +КонецПроцедуры + +Процедура Тест_Должен_ЧитатьДатуВФормате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"); + +КонецПроцедуры + + +Процедура Тест_Должен_ОткрытьПотокЧитаетСтрокуСДатойВФорматеISOКакСтроку() Экспорт + + // проверяемая строка json + СтрокаJSON = "{ ""lastSignInTime"": ""2026-05-13T22:51:46Z"" }"; + + БДД = ПолучитьБуферДвоичныхДанныхИзСтроки(СтрокаJSON); + Поток = Новый ПотокВПамяти(БДД); + + ЧтениеJSON = Новый ЧтениеJSON; + ЧтениеJSON.ОткрытьПоток(Поток); + + ЧтениеJSON.Прочитать(); // НачалоОбъекта + ЧтениеJSON.Прочитать(); // ИмяСвойства + ЧтениеJSON.Прочитать(); // Строка + + ТекущееЗначение = ЧтениеJSON.ТекущееЗначение; + ТипТекущегоЗначения = ЧтениеJSON.ТипТекущегоЗначения; + + ЧтениеJSON.Закрыть(); + + юТест.ПроверитьРавенство(ТекущееЗначение, "2026-05-13T22:51:46Z"); + юТест.ПроверитьРавенство(ТипТекущегоЗначения, ТипЗначенияJSON.Строка); + +КонецПроцедуры + +Процедура Тест_Должен_ОткрытьФайлЧитаетСтрокуСДатойВФорматеISOКакСтроку() Экспорт + + ЧтениеJSON = Новый ЧтениеJSON; + ЧтениеJSON.ОткрытьФайл("json/json-mock_isodate.json"); + + ЧтениеJSON.Прочитать(); // НачалоОбъекта + ЧтениеJSON.Прочитать(); // ИмяСвойства + ЧтениеJSON.Прочитать(); // Строка + + ТекущееЗначение = ЧтениеJSON.ТекущееЗначение; + ТипТекущегоЗначения = ЧтениеJSON.ТипТекущегоЗначения; + + ЧтениеJSON.Закрыть(); + + юТест.ПроверитьРавенство(ТекущееЗначение, "2026-05-13T22:51:46Z"); + юТест.ПроверитьРавенство(ТипТекущегоЗначения, ТипЗначенияJSON.Строка); + +КонецПроцедуры 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); +КонецПроцедуры + 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 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) Тогда Результат = Истина; 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"); Ч.Закрыть(); 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, СтрДлина(Стр)); КонецПроцедуры